summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt69
-rw-r--r--indra/newview/app_settings/commands.xml17
-rw-r--r--indra/newview/app_settings/settings.xml291
-rw-r--r--indra/newview/app_settings/settings_per_account.xml111
-rwxr-xr-xindra/newview/llagent.cpp114
-rw-r--r--indra/newview/llagent.h9
-rw-r--r--indra/newview/llagentwearablesfetch.cpp2
-rw-r--r--indra/newview/llappearancemgr.cpp8
-rw-r--r--indra/newview/llappviewer.cpp24
-rw-r--r--indra/newview/llautoreplace.cpp99
-rw-r--r--indra/newview/llautoreplace.h54
-rwxr-xr-xindra/newview/llavataractions.cpp211
-rw-r--r--indra/newview/llavataractions.h43
-rwxr-xr-xindra/newview/llavatariconctrl.cpp37
-rw-r--r--indra/newview/llavatariconctrl.h20
-rw-r--r--indra/newview/llavatarlist.cpp61
-rw-r--r--indra/newview/llavatarlist.h3
-rw-r--r--indra/newview/llavatarlistitem.cpp51
-rw-r--r--indra/newview/llavatarlistitem.h6
-rw-r--r--indra/newview/llblockedlistitem.cpp114
-rw-r--r--indra/newview/llblockedlistitem.h73
-rw-r--r--indra/newview/llblocklist.cpp284
-rw-r--r--indra/newview/llblocklist.h138
-rw-r--r--indra/newview/llbrowsernotification.cpp8
-rw-r--r--indra/newview/llcallfloater.cpp821
-rw-r--r--indra/newview/llcallfloater.h273
-rw-r--r--indra/newview/llcallingcard.cpp16
-rw-r--r--indra/newview/llchannelmanager.cpp6
-rw-r--r--indra/newview/llchatbar.cpp44
-rw-r--r--indra/newview/llchathistory.cpp252
-rw-r--r--indra/newview/llchathistory.h8
-rw-r--r--indra/newview/llchatitemscontainerctrl.cpp52
-rw-r--r--indra/newview/llchatitemscontainerctrl.h8
-rw-r--r--indra/newview/llchiclet.cpp924
-rw-r--r--indra/newview/llchiclet.h457
-rw-r--r--indra/newview/llchicletbar.cpp101
-rw-r--r--indra/newview/llchicletbar.h14
-rw-r--r--indra/newview/llcommunicationchannel.cpp113
-rw-r--r--indra/newview/llcommunicationchannel.h66
-rw-r--r--indra/newview/llconversationlog.cpp615
-rw-r--r--indra/newview/llconversationlog.h216
-rw-r--r--indra/newview/llconversationloglist.cpp533
-rw-r--r--indra/newview/llconversationloglist.h153
-rw-r--r--indra/newview/llconversationloglistitem.cpp184
-rw-r--r--indra/newview/llconversationloglistitem.h86
-rw-r--r--indra/newview/llconversationmodel.cpp702
-rwxr-xr-xindra/newview/llconversationmodel.h314
-rwxr-xr-xindra/newview/llconversationview.cpp696
-rwxr-xr-xindra/newview/llconversationview.h177
-rw-r--r--indra/newview/lldeferredsounds.cpp45
-rw-r--r--indra/newview/lldeferredsounds.h46
-rw-r--r--indra/newview/lldelayedgestureerror.cpp8
-rw-r--r--indra/newview/lldonotdisturbnotificationstorage.cpp346
-rw-r--r--indra/newview/lldonotdisturbnotificationstorage.h78
-rw-r--r--indra/newview/lldrawable.cpp24
-rw-r--r--indra/newview/llexpandabletextbox.cpp4
-rw-r--r--indra/newview/llfavoritesbar.cpp312
-rw-r--r--indra/newview/llfavoritesbar.h111
-rw-r--r--indra/newview/llfirstuse.cpp2
-rw-r--r--indra/newview/llflexibleobject.cpp33
-rw-r--r--indra/newview/llfloateravatarpicker.cpp240
-rw-r--r--indra/newview/llfloateravatarpicker.h17
-rw-r--r--indra/newview/llfloaterchatvoicevolume.cpp44
-rw-r--r--indra/newview/llfloaterchatvoicevolume.h44
-rw-r--r--indra/newview/llfloatercolorpicker.cpp36
-rw-r--r--indra/newview/llfloatercolorpicker.h4
-rw-r--r--indra/newview/llfloaterconversationlog.cpp134
-rw-r--r--indra/newview/llfloaterconversationlog.h56
-rw-r--r--indra/newview/llfloaterconversationpreview.cpp186
-rw-r--r--indra/newview/llfloaterconversationpreview.h64
-rw-r--r--indra/newview/llfloaterdisplayname.cpp20
-rw-r--r--indra/newview/llfloatergodtools.cpp6
-rw-r--r--indra/newview/llfloaterimcontainer.cpp1982
-rw-r--r--indra/newview/llfloaterimcontainer.h209
-rw-r--r--indra/newview/llfloaterimnearbychat.cpp881
-rw-r--r--indra/newview/llfloaterimnearbychat.h (renamed from indra/newview/llnearbychatbar.h)69
-rw-r--r--indra/newview/llfloaterimnearbychathandler.cpp (renamed from indra/newview/llnearbychathandler.cpp)201
-rw-r--r--indra/newview/llfloaterimnearbychathandler.h (renamed from indra/newview/llnearbychathandler.h)17
-rw-r--r--indra/newview/llfloaterimnearbychatlistener.cpp (renamed from indra/newview/llnearbychatbarlistener.cpp)14
-rw-r--r--indra/newview/llfloaterimnearbychatlistener.h (renamed from indra/newview/llnearbychatbarlistener.h)18
-rw-r--r--indra/newview/llfloaterimsession.cpp1274
-rw-r--r--indra/newview/llfloaterimsession.h (renamed from indra/newview/llimfloater.h)159
-rw-r--r--indra/newview/llfloaterimsessiontab.cpp1082
-rw-r--r--indra/newview/llfloaterimsessiontab.h214
-rw-r--r--indra/newview/llfloaterinspect.cpp46
-rw-r--r--indra/newview/llfloaterinspect.h8
-rw-r--r--indra/newview/llfloaterland.cpp14
-rw-r--r--indra/newview/llfloaternotificationsconsole.cpp38
-rw-r--r--indra/newview/llfloateroutbox.cpp84
-rw-r--r--indra/newview/llfloateroutbox.h2
-rwxr-xr-xindra/newview/llfloaterpreference.cpp333
-rw-r--r--indra/newview/llfloaterpreference.h39
-rw-r--r--indra/newview/llfloaterregioninfo.cpp50
-rw-r--r--indra/newview/llfloaterreporter.cpp23
-rw-r--r--indra/newview/llfloaterreporter.h1
-rw-r--r--indra/newview/llfloaterscriptlimits.cpp15
-rw-r--r--indra/newview/llfloatersellland.cpp22
-rw-r--r--indra/newview/llfloatersidepanelcontainer.cpp7
-rw-r--r--indra/newview/llfloatertools.cpp4
-rw-r--r--indra/newview/llfloatertopobjects.cpp14
-rw-r--r--indra/newview/llfloatertranslationsettings.cpp23
-rw-r--r--indra/newview/llfloatertranslationsettings.h1
-rw-r--r--indra/newview/llfloatervoicevolume.cpp220
-rw-r--r--indra/newview/llfloatervoicevolume.h35
-rw-r--r--indra/newview/llfolderview.cpp2640
-rw-r--r--indra/newview/llfolderview.h377
-rw-r--r--indra/newview/llfoldervieweventlistener.h103
-rw-r--r--indra/newview/llfolderviewitem.cpp2901
-rw-r--r--indra/newview/llfolderviewitem.h577
-rw-r--r--indra/newview/llfolderviewmodelinventory.cpp314
-rw-r--r--indra/newview/llfolderviewmodelinventory.h118
-rw-r--r--indra/newview/llfollowcam.cpp1
-rw-r--r--indra/newview/llfriendcard.cpp6
-rw-r--r--indra/newview/llgesturemgr.cpp6
-rw-r--r--indra/newview/llgroupactions.cpp4
-rw-r--r--indra/newview/llgroupiconctrl.cpp2
-rw-r--r--indra/newview/llgrouplist.cpp6
-rw-r--r--indra/newview/llgrouplist.h13
-rw-r--r--indra/newview/llhudnametag.cpp282
-rw-r--r--indra/newview/llhudnametag.h2
-rw-r--r--indra/newview/llimfloater.cpp1195
-rw-r--r--indra/newview/llimfloatercontainer.cpp165
-rw-r--r--indra/newview/llimfloatercontainer.h70
-rw-r--r--indra/newview/llimhandler.cpp118
-rw-r--r--indra/newview/llimpanel.cpp2
-rw-r--r--indra/newview/llimview.cpp582
-rw-r--r--indra/newview/llimview.h66
-rw-r--r--indra/newview/llinspectavatar.cpp511
-rw-r--r--indra/newview/llinventorybridge.cpp630
-rw-r--r--indra/newview/llinventorybridge.h104
-rw-r--r--indra/newview/llinventoryfilter.cpp403
-rw-r--r--indra/newview/llinventoryfilter.h182
-rw-r--r--indra/newview/llinventoryfunctions.cpp135
-rw-r--r--indra/newview/llinventoryfunctions.h58
-rw-r--r--indra/newview/llinventoryicon.h2
-rw-r--r--indra/newview/llinventorymodel.cpp131
-rw-r--r--indra/newview/llinventorymodel.h18
-rw-r--r--indra/newview/llinventorypanel.cpp642
-rw-r--r--indra/newview/llinventorypanel.h120
-rw-r--r--indra/newview/lllistcontextmenu.h2
-rw-r--r--indra/newview/lllogchat.cpp496
-rw-r--r--indra/newview/lllogchat.h35
-rw-r--r--indra/newview/llnamelistctrl.cpp19
-rw-r--r--indra/newview/llnamelistctrl.h8
-rw-r--r--indra/newview/llnearbychat.cpp338
-rw-r--r--indra/newview/llnearbychat.h83
-rw-r--r--indra/newview/llnearbychatbar.cpp684
-rw-r--r--indra/newview/llnotificationalerthandler.cpp117
-rw-r--r--indra/newview/llnotificationgrouphandler.cpp65
-rw-r--r--indra/newview/llnotificationhandler.h237
-rw-r--r--indra/newview/llnotificationhandlerutil.cpp269
-rw-r--r--indra/newview/llnotificationhinthandler.cpp27
-rw-r--r--indra/newview/llnotificationmanager.cpp107
-rw-r--r--indra/newview/llnotificationmanager.h17
-rw-r--r--indra/newview/llnotificationofferhandler.cpp199
-rw-r--r--indra/newview/llnotificationscripthandler.cpp104
-rw-r--r--indra/newview/llnotificationstorage.cpp246
-rw-r--r--indra/newview/llnotificationstorage.h35
-rw-r--r--indra/newview/llnotificationtiphandler.cpp121
-rw-r--r--indra/newview/lloutputmonitorctrl.cpp114
-rw-r--r--indra/newview/lloutputmonitorctrl.h19
-rw-r--r--indra/newview/llpanelblockedlist.cpp168
-rw-r--r--indra/newview/llpanelblockedlist.h38
-rw-r--r--indra/newview/llpanelgroupgeneral.cpp18
-rw-r--r--indra/newview/llpanelgroupgeneral.h1
-rw-r--r--indra/newview/llpanelgroupinvite.cpp46
-rw-r--r--indra/newview/llpanelgroupnotices.cpp5
-rw-r--r--indra/newview/llpanelgrouproles.cpp21
-rw-r--r--indra/newview/llpanelgrouproles.h1
-rw-r--r--indra/newview/llpanelimcontrolpanel.cpp397
-rw-r--r--indra/newview/llpanelimcontrolpanel.h56
-rw-r--r--indra/newview/llpanellandmarks.cpp181
-rw-r--r--indra/newview/llpanellandmarks.h2
-rw-r--r--indra/newview/llpanelmaininventory.cpp122
-rw-r--r--indra/newview/llpanelmarketplaceinbox.cpp16
-rw-r--r--indra/newview/llpanelmarketplaceinboxinventory.cpp146
-rw-r--r--indra/newview/llpanelmarketplaceinboxinventory.h19
-rw-r--r--indra/newview/llpanelmarketplaceoutboxinventory.cpp156
-rw-r--r--indra/newview/llpanelmarketplaceoutboxinventory.h78
-rw-r--r--indra/newview/llpanelobjectinventory.cpp184
-rw-r--r--indra/newview/llpanelobjectinventory.h11
-rw-r--r--indra/newview/llpaneloutfitedit.cpp45
-rw-r--r--indra/newview/llpanelpeople.cpp375
-rw-r--r--indra/newview/llpanelpeople.h34
-rw-r--r--indra/newview/llpanelpeoplemenus.cpp141
-rw-r--r--indra/newview/llpanelpeoplemenus.h20
-rw-r--r--indra/newview/llpanelplaceprofile.cpp8
-rw-r--r--indra/newview/llpanelplaceprofile.h2
-rw-r--r--indra/newview/llpaneltopinfobar.cpp2
-rw-r--r--indra/newview/llparticipantlist.cpp738
-rw-r--r--indra/newview/llparticipantlist.h152
-rw-r--r--indra/newview/llpathfindingobject.cpp1
-rw-r--r--indra/newview/llpersistentnotificationstorage.cpp145
-rw-r--r--indra/newview/llpersistentnotificationstorage.h63
-rw-r--r--indra/newview/llplacesfolderview.cpp74
-rw-r--r--indra/newview/llplacesfolderview.h72
-rw-r--r--indra/newview/llplacesinventorybridge.cpp50
-rw-r--r--indra/newview/llplacesinventorybridge.h3
-rw-r--r--indra/newview/llplacesinventorypanel.cpp120
-rw-r--r--indra/newview/llplacesinventorypanel.h37
-rw-r--r--indra/newview/llpreviewscript.cpp6
-rw-r--r--indra/newview/llprogressview.cpp4
-rw-r--r--indra/newview/llscreenchannel.cpp137
-rw-r--r--indra/newview/llscreenchannel.h16
-rw-r--r--indra/newview/llscriptfloater.cpp123
-rw-r--r--indra/newview/llsidepanelappearance.cpp6
-rw-r--r--indra/newview/llsidepanelinventory.cpp22
-rw-r--r--indra/newview/llsidepanelinventory.h2
-rw-r--r--indra/newview/llspatialpartition.cpp4
-rw-r--r--indra/newview/llspeakers.cpp205
-rw-r--r--indra/newview/llspeakers.h15
-rw-r--r--indra/newview/llspeakingindicatormanager.cpp46
-rw-r--r--indra/newview/llspeakingindicatormanager.h5
-rw-r--r--indra/newview/llstartup.cpp36
-rw-r--r--indra/newview/llsyswellwindow.cpp346
-rw-r--r--indra/newview/llsyswellwindow.h76
-rw-r--r--indra/newview/lltexturectrl.cpp46
-rw-r--r--indra/newview/lltoast.h7
-rw-r--r--indra/newview/lltoastgroupnotifypanel.cpp10
-rw-r--r--indra/newview/lltoastgroupnotifypanel.h5
-rw-r--r--indra/newview/lltoastimpanel.cpp4
-rw-r--r--indra/newview/lltoastimpanel.h12
-rw-r--r--indra/newview/lltoastnotifypanel.cpp582
-rw-r--r--indra/newview/lltoastnotifypanel.h31
-rw-r--r--indra/newview/lltoastpanel.cpp40
-rw-r--r--indra/newview/lltoastpanel.h9
-rw-r--r--indra/newview/lltoastscriptquestion.cpp4
-rw-r--r--indra/newview/lltoastscripttextbox.cpp2
-rw-r--r--indra/newview/lltoastscripttextbox.h2
-rw-r--r--indra/newview/lltoolbarview.cpp10
-rw-r--r--indra/newview/lltooldraganddrop.cpp97
-rw-r--r--indra/newview/lltooldraganddrop.h3
-rw-r--r--indra/newview/lltoolpie.cpp23
-rw-r--r--indra/newview/llviewerassettype.cpp2
-rw-r--r--indra/newview/llvieweraudio.cpp7
-rw-r--r--indra/newview/llviewerdisplay.cpp1
-rw-r--r--indra/newview/llviewerdisplayname.cpp13
-rw-r--r--indra/newview/llviewerfloaterreg.cpp24
-rwxr-xr-x[-rw-r--r--]indra/newview/llviewerfoldertype.cpp0
-rw-r--r--indra/newview/llviewergesture.cpp6
-rw-r--r--indra/newview/llviewerinventory.cpp364
-rw-r--r--indra/newview/llviewerinventory.h27
-rw-r--r--indra/newview/llviewerkeyboard.cpp11
-rw-r--r--indra/newview/llviewermenu.cpp109
-rw-r--r--indra/newview/llviewermenu.h4
-rwxr-xr-xindra/newview/llviewermessage.cpp499
-rw-r--r--indra/newview/llviewermessage.h4
-rw-r--r--indra/newview/llviewerobjectlist.cpp4
-rwxr-xr-xindra/newview/llviewerwindow.cpp84
-rw-r--r--indra/newview/llviewerwindow.h7
-rw-r--r--indra/newview/llvoavatar.cpp680
-rw-r--r--indra/newview/llvoavatar.h17
-rw-r--r--indra/newview/llvoicechannel.cpp2
-rw-r--r--indra/newview/llvoicechannel.h2
-rw-r--r--indra/newview/llvoiceclient.cpp2
-rw-r--r--indra/newview/llvoiceclient.h6
-rw-r--r--indra/newview/llvoicevisualizer.cpp46
-rw-r--r--indra/newview/llvoicevisualizer.h11
-rw-r--r--indra/newview/llvoicevivox.cpp166
-rw-r--r--indra/newview/llvoicevivox.h6
-rw-r--r--indra/newview/llvopartgroup.cpp4
-rw-r--r--indra/newview/llworld.cpp2
-rw-r--r--indra/newview/llworldmapview.cpp3
-rw-r--r--indra/newview/pipeline.cpp9
-rw-r--r--indra/newview/skins/default/colors.xml1592
-rw-r--r--indra/newview/skins/default/textures/bottomtray/Unread_IM.pngbin458 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl1_Dark.pngbin602 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl2_Dark.pngbin669 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl3_Dark.pngbin639 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/bottomtray/VoicePTT_Off_Dark.pngbin547 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/bottomtray/VoicePTT_On_Dark.pngbin526 -> 0 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/Conv_log_inbox.pngbin0 -> 556 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_add_person.pngbin0 -> 373 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.pngbin0 -> 215 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.pngbin0 -> 211 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_call_log.pngbin0 -> 546 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_close.pngbin0 -> 275 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_collapse.pngbin0 -> 345 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_expand.pngbin0 -> 342 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.pngbin0 -> 459 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_open_call.pngbin0 -> 366 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_plus.pngbin0 -> 144 bytes
-rwxr-xr-xindra/newview/skins/default/textures/icons/Conv_toolbar_sort.pngbin0 -> 230 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/collapse_to_one_line.pngbin0 -> 538 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/expand_one_liner.pngbin0 -> 545 bytes
-rw-r--r--indra/newview/skins/default/textures/icons/nearby_chat_icon.pngbin0 -> 399 bytes
-rw-r--r--indra/newview/skins/default/textures/textures.xml26
-rw-r--r--indra/newview/skins/default/xui/da/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/da/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/de/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/de/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/de/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_camera.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_chat_bar.xml85
-rw-r--r--indra/newview/skins/default/xui/en/floater_conversation_log.xml84
-rw-r--r--indra/newview/skins/default/xui/en/floater_conversation_preview.xml64
-rw-r--r--indra/newview/skins/default/xui/en/floater_destinations.xml6
-rw-r--r--indra/newview/skins/default/xui/en/floater_im_container.xml203
-rw-r--r--indra/newview/skins/default/xui/en/floater_im_session.xml418
-rw-r--r--indra/newview/skins/default/xui/en/floater_incoming_call.xml58
-rw-r--r--indra/newview/skins/default/xui/en/floater_moveview.xml4
-rw-r--r--indra/newview/skins/default/xui/en/floater_pathfinding_console.xml2
-rw-r--r--indra/newview/skins/default/xui/en/floater_people.xml14
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_chat_volume.xml48
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_controls.xml155
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_effect.xml3
-rw-r--r--indra/newview/skins/default/xui/en/floater_voice_volume.xml59
-rw-r--r--indra/newview/skins/default/xui/en/inspect_avatar.xml100
-rw-r--r--indra/newview/skins/default/xui/en/menu_cof_gear.xml2
-rw-r--r--indra/newview/skins/default/xui/en/menu_conversation.xml203
-rw-r--r--indra/newview/skins/default/xui/en/menu_conversation_log_gear.xml142
-rw-r--r--indra/newview/skins/default/xui/en/menu_conversation_log_view.xml45
-rw-r--r--indra/newview/skins/default/xui/en/menu_group_plus.xml4
-rw-r--r--indra/newview/skins/default/xui/en/menu_im_conversation.xml101
-rw-r--r--indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml49
-rw-r--r--indra/newview/skins/default/xui/en/menu_im_well_button.xml16
-rw-r--r--indra/newview/skins/default/xui/en/menu_inspect_avatar_gear.xml143
-rw-r--r--indra/newview/skins/default/xui/en/menu_inspect_self_gear.xml252
-rw-r--r--indra/newview/skins/default/xui/en/menu_object_icon.xml18
-rw-r--r--indra/newview/skins/default/xui/en/menu_participant_view.xml112
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_blocked_gear.xml26
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_blocked_plus.xml20
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_blocked_view.xml26
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_friends_view.xml (renamed from indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml)12
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_groups.xml27
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_groups_view.xml (renamed from indra/newview/skins/default/xui/en/menu_people_groups_view_sort.xml)9
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_nearby.xml99
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml14
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_nearby_view.xml53
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml57
-rw-r--r--indra/newview/skins/default/xui/en/menu_people_recent_view.xml (renamed from indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml)4
-rw-r--r--indra/newview/skins/default/xui/en/menu_url_agent.xml20
-rw-r--r--indra/newview/skins/default/xui/en/menu_url_objectim.xml2
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml165
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml165
-rw-r--r--indra/newview/skins/default/xui/en/panel_activeim_row.xml97
-rw-r--r--indra/newview/skins/default/xui/en/panel_adhoc_control_panel.xml95
-rw-r--r--indra/newview/skins/default/xui/en/panel_avatar_list_item.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_block_list_sidetray.xml163
-rw-r--r--indra/newview/skins/default/xui/en/panel_blocked_list_item.xml71
-rw-r--r--indra/newview/skins/default/xui/en/panel_bottomtray_lite.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_chiclet_bar.xml48
-rw-r--r--indra/newview/skins/default/xui/en/panel_conversation_list_item.xml98
-rw-r--r--indra/newview/skins/default/xui/en/panel_conversation_log_list_item.xml107
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_control_panel.xml109
-rw-r--r--indra/newview/skins/default/xui/en/panel_group_list_item.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_im_control_panel.xml166
-rw-r--r--indra/newview/skins/default/xui/en/panel_inbox_inventory.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_landmarks.xml16
-rw-r--r--indra/newview/skins/default/xui/en/panel_nearby_chat.xml22
-rw-r--r--indra/newview/skins/default/xui/en/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/en/panel_outbox_inventory.xml25
-rw-r--r--indra/newview/skins/default/xui/en/panel_people.xml929
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_chat.xml693
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_colors.xml4
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_general.xml6
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_privacy.xml226
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml42
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chat_editor.xml4
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chiclet_im_adhoc.xml55
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chiclet_im_group.xml56
-rw-r--r--indra/newview/skins/default/xui/en/widgets/chiclet_im_p2p.xml56
-rwxr-xr-xindra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml42
-rw-r--r--indra/newview/skins/default/xui/en/widgets/conversation_view_session.xml16
-rw-r--r--indra/newview/skins/default/xui/en/widgets/folder_view_item.xml8
-rw-r--r--indra/newview/skins/default/xui/en/widgets/inbox_folder_view_folder.xml8
-rw-r--r--indra/newview/skins/default/xui/en/widgets/inbox_inventory_panel.xml3
-rw-r--r--indra/newview/skins/default/xui/en/widgets/outbox_folder_view_folder.xml9
-rw-r--r--indra/newview/skins/default/xui/en/widgets/outbox_inventory_panel.xml2
-rw-r--r--indra/newview/skins/default/xui/en/widgets/text.xml1
-rw-r--r--indra/newview/skins/default/xui/en/widgets/toolbar.xml12
-rw-r--r--indra/newview/skins/default/xui/es/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/es/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/es/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/fr/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/fr/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/it/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/it/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/it/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/ja/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/ja/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/ja/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/pl/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/pl/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/pt/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/pt/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/pt/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/ru/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/ru/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/ru/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/tr/floater_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/tr/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/tr/panel_nearby_chat_bar.xml2
-rw-r--r--indra/newview/skins/default/xui/zh/menu_im_well_button.xml4
-rw-r--r--indra/newview/skins/default/xui/zh/panel_nearby_chat_bar.xml2
396 files changed, 24840 insertions, 24421 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index e93d73ad0e..bd0169fb2f 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -114,12 +114,13 @@ set(viewer_SOURCE_FILES
llavatarlist.cpp
llavatarlistitem.cpp
llavatarpropertiesprocessor.cpp
+ llblockedlistitem.cpp
+ llblocklist.cpp
llbox.cpp
llbreadcrumbview.cpp
llbrowsernotification.cpp
llbuycurrencyhtml.cpp
llcallbacklist.cpp
- llcallfloater.cpp
llcallingcard.cpp
llcapabilitylistener.cpp
llcaphttpsender.cpp
@@ -137,16 +138,24 @@ set(viewer_SOURCE_FILES
llcommanddispatcherlistener.cpp
llcommandhandler.cpp
llcommandlineparser.cpp
+ llcommunicationchannel.cpp
llcompilequeue.cpp
llconfirmationmanager.cpp
+ llconversationlog.cpp
+ llconversationloglist.cpp
+ llconversationloglistitem.cpp
+ llconversationmodel.cpp
+ llconversationview.cpp
llcurrencyuimanager.cpp
llcylinder.cpp
lldateutil.cpp
lldaycyclemanager.cpp
lldebugmessagebox.cpp
lldebugview.cpp
+ lldeferredsounds.cpp
lldelayedgestureerror.cpp
lldirpicker.cpp
+ lldonotdisturbnotificationstorage.cpp
lldndbutton.cpp
lldrawable.cpp
lldrawpool.cpp
@@ -194,7 +203,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
@@ -262,13 +274,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
@@ -294,8 +306,9 @@ set(viewer_SOURCE_FILES
llhudrender.cpp
llhudtext.cpp
llhudview.cpp
- llimfloater.cpp
- llimfloatercontainer.cpp
+ llfloaterimsessiontab.cpp
+ llfloaterimsession.cpp
+ llfloaterimcontainer.cpp
llimhandler.cpp
llimview.cpp
llinspect.cpp
@@ -347,10 +360,9 @@ set(viewer_SOURCE_FILES
llnameeditor.cpp
llnamelistctrl.cpp
llnavigationbar.cpp
- llnearbychat.cpp
- llnearbychatbar.cpp
- llnearbychathandler.cpp
- llnearbychatbarlistener.cpp
+ llfloaterimnearbychat.cpp
+ llfloaterimnearbychathandler.cpp
+ llfloaterimnearbychatlistener.cpp
llnetmap.cpp
llnotificationalerthandler.cpp
llnotificationgrouphandler.cpp
@@ -380,7 +392,6 @@ set(viewer_SOURCE_FILES
llpanelgroupnotices.cpp
llpanelgrouproles.cpp
llpanelhome.cpp
- llpanelimcontrolpanel.cpp
llpanelland.cpp
llpanellandaudio.cpp
llpanellandmarkinfo.cpp
@@ -391,7 +402,6 @@ set(viewer_SOURCE_FILES
llpanelmaininventory.cpp
llpanelmarketplaceinbox.cpp
llpanelmarketplaceinboxinventory.cpp
- llpanelmarketplaceoutboxinventory.cpp
llpanelmediasettingsgeneral.cpp
llpanelmediasettingspermissions.cpp
llpanelmediasettingssecurity.cpp
@@ -441,10 +451,12 @@ set(viewer_SOURCE_FILES
llpathfindingobject.cpp
llpathfindingobjectlist.cpp
llpathfindingpathtool.cpp
+ llpersistentnotificationstorage.cpp
llphysicsmotion.cpp
llphysicsshapebuilderutil.cpp
llplacesinventorybridge.cpp
llplacesinventorypanel.cpp
+ llplacesfolderview.cpp
llpopupview.cpp
llpolymesh.cpp
llpolymorph.cpp
@@ -690,11 +702,12 @@ set(viewer_HEADER_FILES
llavatarlist.h
llavatarlistitem.h
llavatarpropertiesprocessor.h
+ llblockedlistitem.h
+ llblocklist.h
llbox.h
llbreadcrumbview.h
llbuycurrencyhtml.h
llcallbacklist.h
- llcallfloater.h
llcallingcard.h
llcapabilitylistener.h
llcapabilityprovider.h
@@ -713,16 +726,24 @@ set(viewer_HEADER_FILES
llcommanddispatcherlistener.h
llcommandhandler.h
llcommandlineparser.h
+ llcommunicationchannel.h
llcompilequeue.h
llconfirmationmanager.h
+ llconversationlog.h
+ llconversationloglist.h
+ llconversationloglistitem.h
+ llconversationmodel.h
+ llconversationview.h
llcurrencyuimanager.h
llcylinder.h
lldateutil.h
lldaycyclemanager.h
lldebugmessagebox.h
lldebugview.h
+ lldeferredsounds.h
lldelayedgestureerror.h
lldirpicker.h
+ lldonotdisturbnotificationstorage.h
lldndbutton.h
lldrawable.h
lldrawpool.h
@@ -770,7 +791,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
@@ -838,14 +862,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
@@ -870,8 +893,9 @@ set(viewer_HEADER_FILES
llhudrender.h
llhudtext.h
llhudview.h
- llimfloater.h
- llimfloatercontainer.h
+ llfloaterimsessiontab.h
+ llfloaterimsession.h
+ llfloaterimcontainer.h
llimview.h
llinspect.h
llinspectavatar.h
@@ -923,10 +947,9 @@ set(viewer_HEADER_FILES
llnameeditor.h
llnamelistctrl.h
llnavigationbar.h
- llnearbychat.h
- llnearbychatbar.h
- llnearbychathandler.h
- llnearbychatbarlistener.h
+ llfloaterimnearbychat.h
+ llfloaterimnearbychathandler.h
+ llfloaterimnearbychatlistener.h
llnetmap.h
llnotificationhandler.h
llnotificationmanager.h
@@ -950,7 +973,6 @@ set(viewer_HEADER_FILES
llpanelgroupnotices.h
llpanelgrouproles.h
llpanelhome.h
- llpanelimcontrolpanel.h
llpanelland.h
llpanellandaudio.h
llpanellandmarkinfo.h
@@ -961,7 +983,6 @@ set(viewer_HEADER_FILES
llpanelmaininventory.h
llpanelmarketplaceinbox.h
llpanelmarketplaceinboxinventory.h
- llpanelmarketplaceoutboxinventory.h
llpanelmediasettingsgeneral.h
llpanelmediasettingspermissions.h
llpanelmediasettingssecurity.h
@@ -1006,10 +1027,12 @@ set(viewer_HEADER_FILES
llpathfindingobject.h
llpathfindingobjectlist.h
llpathfindingpathtool.h
+ llpersistentnotificationstorage.h
llphysicsmotion.h
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..4659673333 100644
--- a/indra/newview/app_settings/commands.xml
+++ b/indra/newview/app_settings/commands.xml
@@ -44,13 +44,14 @@
/>
<command name="chat"
available_in_toybox="true"
+ is_flashing_allowed="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 +240,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 b302f5c9b9..89a8f74b0c 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>
@@ -1562,6 +1584,28 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>ChatLoadGroupMaxMembers</key>
+ <map>
+ <key>Comment</key>
+ <string>Max number of active members we'll show up for an unresponsive group</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <real>100</real>
+ </map>
+ <key>ChatLoadGroupTimeout</key>
+ <map>
+ <key>Comment</key>
+ <string>Time we give the server to send group participants before we hit the server for group info (seconds)</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>F32</string>
+ <key>Value</key>
+ <real>10.0</real>
+ </map>
<key>ChatOnlineNotification</key>
<map>
<key>Comment</key>
@@ -1595,17 +1639,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 +1661,61 @@
<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>CloseChatOnReturn</key>
<map>
<key>Comment</key>
@@ -4183,6 +4271,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>IMShowContentPanel</key>
+ <map>
+ <key>Comment</key>
+ <string>Show Toolbar and Body Panels</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>IgnoreAllNotifications</key>
<map>
<key>Comment</key>
@@ -4225,7 +4324,7 @@
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>0.65</real>
+ <real>0.95</real>
</map>
<key>InBandwidth</key>
<map>
@@ -6208,6 +6307,61 @@
<key>Value</key>
<integer>305</integer>
</map>
+ <key>NotificationConferenceIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies how the UI responds to Conference IM Notifications.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>toast</string>
+ </map>
+ <key>NotificationFriendIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies how the UI responds to Friend IM Notifications.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>toast</string>
+ </map>
+ <key>NotificationGroupChatOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies how the UI responds to Group Chat Notifications.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>toast</string>
+ </map>
+ <key>NotificationNearbyChatOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies how the UI responds to Nearby Chat Notifications.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>toast</string>
+ </map>
+ <key>NotificationNonFriendIMOptions</key>
+ <map>
+ <key>Comment</key>
+ <string>Specifies how the UI responds to Non Friend IM Notifications.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string>toast</string>
+ </map>
<key>NotificationToastLifeTime</key>
<map>
<key>Comment</key>
@@ -6724,6 +6878,50 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>PlaySoundIncomingVoiceCall</key>
+ <map>
+ <key>Comment</key>
+ <string>Plays a sound when have an incoming voice call.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>PlaySoundInventoryOffer</key>
+ <map>
+ <key>Comment</key>
+ <string>Plays a sound when have an inventory offer.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>PlaySoundNewConversation</key>
+ <map>
+ <key>Comment</key>
+ <string>Plays a sound when have a new conversation.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
+ <key>PlaySoundTeleportOffer</key>
+ <map>
+ <key>Comment</key>
+ <string>Plays a sound when have a teleport offer.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
<key>PluginAttachDebuggerToPlugins</key>
<map>
<key>Comment</key>
@@ -9847,7 +10045,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>
@@ -10031,6 +10229,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>0</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>
@@ -12396,17 +12627,6 @@
<key>Value</key>
<string />
</map>
- <key>SpeakerParticipantDefaultOrder</key>
- <map>
- <key>Comment</key>
- <string>Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent.</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>U32</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
<key>SpeakerParticipantRemoveDelay</key>
<map>
<key>Comment</key>
@@ -12451,6 +12671,17 @@
<key>Value</key>
<integer>1</integer>
</map>
+ <key>UsePeopleAPI</key>
+ <map>
+ <key>Comment</key>
+ <string>Use the people API cap for avatar name fetching, use old legacy protocol if false. Requires restart.</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>UseStartScreen</key>
<map>
<key>Comment</key>
@@ -12945,10 +13176,10 @@
<key>Value</key>
<real>50.0</real>
</map>
- <key>WellIconFlashCount</key>
+ <key>FlashCount</key>
<map>
<key>Comment</key>
- <string>Number of flashes of IM Well and Notification Well icons after which flashing buttons stay lit up. Requires restart.</string>
+ <string>Number of flashes of item. Requires restart.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -12956,16 +13187,16 @@
<key>Value</key>
<integer>3</integer>
</map>
- <key>WellIconFlashPeriod</key>
+ <key>FlashPeriod</key>
<map>
<key>Comment</key>
- <string>Period at which IM Well and Notification Well icons flash (seconds). Requires restart.</string>
+ <string>Period at which item flash (seconds). Requires restart.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>F32</string>
<key>Value</key>
- <real>0.25</real>
+ <real>0.5</real>
</map>
<key>WindLightUseAtmosShaders</key>
<map>
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 143126b334..ada374f892 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -1,9 +1,9 @@
<llsd>
<map>
- <key>BusyResponseChanged</key>
+ <key>DoNotDisturbResponseChanged</key>
<map>
<key>Comment</key>
- <string>Does user's busy mode message differ from default?</string>
+ <string>Does user's do not disturb mode message differ from default?</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
@@ -11,17 +11,72 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>BusyModeResponse</key>
+ <key>DoNotDisturbModeResponse</key>
<map>
<key>Comment</key>
- <string>Auto response to instant messages while in busy mode.</string>
+ <string>Auto response to instant messages while in do not disturb mode.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>String</string>
<key>Value</key>
- <string>The Resident you messaged is in &apos;busy mode&apos; which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing.</string>
+ <string>This resident has turned on &apos;Do Not Disturb&apos; and will see your message later.</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>205</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>
@@ -121,17 +176,6 @@
<key>Value</key>
<integer>1</integer>
</map>
- <key>LogInstantMessages</key>
- <map>
- <key>Comment</key>
- <string>Log Instant Messages</string>
- <key>Persist</key>
- <integer>1</integer>
- <key>Type</key>
- <string>Boolean</string>
- <key>Value</key>
- <integer>1</integer>
- </map>
<key>LogShowHistory</key>
<map>
<key>Comment</key>
@@ -215,7 +259,40 @@
<key>Value</key>
<integer>0</integer>
</map>
- <key>ShowFavoritesOnLogin</key>
+ <key>TranslatingEnabled</key>
+ <map>
+ <key>Comment</key>
+ <string>Translation prefs are set</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>0</integer>
+ </map>
+ <key>KeepConversationLogTranscripts</key>
+ <map>
+ <key>Comment</key>
+ <string>Keep a conversation log and transcripts</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>S32</string>
+ <key>Value</key>
+ <integer>2</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>ShowFavoritesOnLogin</key>
<map>
<key>Comment</key>
<string>Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen</string>
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
index b6fd7bc9c2..094d502078 100755
--- a/indra/newview/llagent.cpp
+++ b/indra/newview/llagent.cpp
@@ -41,6 +41,7 @@
#include "llchannelmanager.h"
#include "llchicletbar.h"
#include "llconsole.h"
+#include "lldonotdisturbnotificationstorage.h"
#include "llenvmanager.h"
#include "llfirstuse.h"
#include "llfloatercamera.h"
@@ -54,7 +55,7 @@
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychat.h"
#include "llnotificationsutil.h"
#include "llpaneltopinfobar.h"
#include "llparcel.h"
@@ -375,7 +376,7 @@ LLAgent::LLAgent() :
mShowAvatar(TRUE),
mFrameAgent(),
- mIsBusy(FALSE),
+ mIsDoNotDisturb(false),
mControlFlags(0x00000000),
mbFlagsDirty(FALSE),
@@ -1355,12 +1356,7 @@ void LLAgent::setAFK()
{
sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START);
setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP);
- LL_INFOS("AFK") << "Setting Away" << LL_ENDL;
gAwayTimer.start();
- if (gAFKMenu)
- {
- gAFKMenu->setLabel(LLTrans::getString("AvatarSetNotAway"));
- }
}
}
@@ -1379,11 +1375,6 @@ void LLAgent::clearAFK()
{
sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
clearControlFlags(AGENT_CONTROL_AWAY);
- LL_INFOS("AFK") << "Clearing Away" << LL_ENDL;
- if (gAFKMenu)
- {
- gAFKMenu->setLabel(LLTrans::getString("AvatarSetAway"));
- }
}
}
@@ -1396,39 +1387,26 @@ BOOL LLAgent::getAFK() const
}
//-----------------------------------------------------------------------------
-// setBusy()
-//-----------------------------------------------------------------------------
-void LLAgent::setBusy()
-{
- sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START);
- mIsBusy = TRUE;
- if (gBusyMenu)
- {
- gBusyMenu->setLabel(LLTrans::getString("AvatarSetNotBusy"));
- }
- LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(true);
-}
-
-//-----------------------------------------------------------------------------
-// clearBusy()
+// setDoNotDisturb()
//-----------------------------------------------------------------------------
-void LLAgent::clearBusy()
+void LLAgent::setDoNotDisturb(bool pIsDoNotDisturb)
{
- mIsBusy = FALSE;
- sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP);
- if (gBusyMenu)
+ bool isDoNotDisturbSwitchedOff = (mIsDoNotDisturb && !pIsDoNotDisturb);
+ mIsDoNotDisturb = pIsDoNotDisturb;
+ sendAnimationRequest(ANIM_AGENT_DO_NOT_DISTURB, (pIsDoNotDisturb ? ANIM_REQUEST_START : ANIM_REQUEST_STOP));
+ LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(pIsDoNotDisturb);
+ if (isDoNotDisturbSwitchedOff)
{
- gBusyMenu->setLabel(LLTrans::getString("AvatarSetBusy"));
+ LLDoNotDisturbNotificationStorage::getInstance()->updateNotifications();
}
- LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(false);
}
//-----------------------------------------------------------------------------
-// getBusy()
+// isDoNotDisturb()
//-----------------------------------------------------------------------------
-BOOL LLAgent::getBusy() const
+bool LLAgent::isDoNotDisturb() const
{
- return mIsBusy;
+ return mIsDoNotDisturb;
}
@@ -1910,7 +1888,8 @@ void LLAgent::startTyping()
{
sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
}
- LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE);
+ (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
+ sendChatFromViewer("", CHAT_TYPE_START, FALSE);
}
//-----------------------------------------------------------------------------
@@ -1922,7 +1901,8 @@ void LLAgent::stopTyping()
{
clearRenderState(AGENT_STATE_TYPING);
sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
- LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
+ (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
+ sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
}
}
@@ -2564,51 +2544,21 @@ void LLMaturityPreferencesResponder::error(U32 pStatus, const std::string& pReas
U8 LLMaturityPreferencesResponder::parseMaturityFromServerResponse(const LLSD &pContent)
{
- // stinson 05/24/2012 Pathfinding regions have re-defined the response behavior. In the old server code,
- // if you attempted to change the preferred maturity to the same value, the response content would be an
- // undefined LLSD block. In the new server code with pathfinding, the response content should always be
- // defined. Thus, the check for isUndefined() can be replaced with an assert after pathfinding is merged
- // into server trunk and fully deployed.
U8 maturity = SIM_ACCESS_MIN;
- if (pContent.isUndefined())
- {
- maturity = mPreferredMaturity;
- }
- else
- {
- llassert(!pContent.isUndefined());
- llassert(pContent.isMap());
-
- if (!pContent.isUndefined() && pContent.isMap())
- {
- // stinson 05/24/2012 Pathfinding regions have re-defined the response syntax. The if statement catches
- // the new syntax, and the else statement catches the old syntax. After pathfinding is merged into
- // server trunk and fully deployed, we can remove the else statement.
- if (pContent.has("access_prefs"))
- {
- llassert(pContent.has("access_prefs"));
- llassert(pContent.get("access_prefs").isMap());
- llassert(pContent.get("access_prefs").has("max"));
- llassert(pContent.get("access_prefs").get("max").isString());
- if (pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max") &&
- pContent.get("access_prefs").get("max").isString())
- {
- LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString();
- LLStringUtil::trim(actualPreference);
- maturity = LLViewerRegion::shortStringToAccess(actualPreference);
- }
- }
- else if (pContent.has("max"))
- {
- llassert(pContent.get("max").isString());
- if (pContent.get("max").isString())
- {
- LLSD::String actualPreference = pContent.get("max").asString();
- LLStringUtil::trim(actualPreference);
- maturity = LLViewerRegion::shortStringToAccess(actualPreference);
- }
- }
- }
+
+ llassert(!pContent.isUndefined());
+ llassert(pContent.isMap());
+ llassert(pContent.has("access_prefs"));
+ llassert(pContent.get("access_prefs").isMap());
+ llassert(pContent.get("access_prefs").has("max"));
+ llassert(pContent.get("access_prefs").get("max").isString());
+ if (!pContent.isUndefined() && pContent.isMap() && pContent.has("access_prefs")
+ && pContent.get("access_prefs").isMap() && pContent.get("access_prefs").has("max")
+ && pContent.get("access_prefs").get("max").isString())
+ {
+ LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString();
+ LLStringUtil::trim(actualPreference);
+ maturity = LLViewerRegion::shortStringToAccess(actualPreference);
}
return maturity;
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
index 99904e118c..daa15b0c1a 100644
--- a/indra/newview/llagent.h
+++ b/indra/newview/llagent.h
@@ -378,14 +378,13 @@ public:
void sitDown();
//--------------------------------------------------------------------
- // Busy
+ // Do Not Disturb
//--------------------------------------------------------------------
public:
- void setBusy();
- void clearBusy();
- BOOL getBusy() const;
+ void setDoNotDisturb(bool pIsDoNotDisturb);
+ bool isDoNotDisturb() const;
private:
- BOOL mIsBusy;
+ bool mIsDoNotDisturb;
//--------------------------------------------------------------------
// Grab
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 06a9892c7e..769b4eafe1 100644
--- a/indra/newview/llappearancemgr.cpp
+++ b/indra/newview/llappearancemgr.cpp
@@ -1603,8 +1603,6 @@ void LLAppearanceMgr::updateAgentWearables(LLWearableHoldingPattern* holder, boo
{
gAgentWearables.setWearableOutfit(items, wearables, !append);
}
-
-// dec_busy_count();
}
static void remove_non_link_items(LLInventoryModel::item_array_t &items)
@@ -2005,7 +2003,6 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego
void LLAppearanceMgr::wearOutfitByName(const std::string& name)
{
LL_INFOS("Avatar") << self_av_string() << "Wearing category " << name << LL_ENDL;
- //inc_busy_count();
LLInventoryModel::cat_array_t cat_array;
LLInventoryModel::item_array_t item_array;
@@ -2045,8 +2042,6 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name)
llwarns << "Couldn't find outfit " <<name<< " in wearOutfitByName()"
<< llendl;
}
-
- //dec_busy_count();
}
bool areMatchingWearables(const LLViewerInventoryItem *a, const LLViewerInventoryItem *b)
@@ -2331,7 +2326,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;
@@ -2968,7 +2963,6 @@ public:
{
llwarns << "Nothing fetched in category " << mComplete.front()
<< llendl;
- //dec_busy_count();
gInventory.removeObserver(this);
// lets notify observers that loading is finished.
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 7331b93810..d5c5921ea3 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 "llfloaterimcontainer.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"
@@ -93,7 +95,6 @@
#include "llweb.h"
#include "llsecondlifeurls.h"
#include "llupdaterservice.h"
-#include "llcallfloater.h"
#include "llfloatertexturefetchdebugger.h"
#include "llspellcheck.h"
@@ -200,6 +201,7 @@
#include "llviewercontrol.h"
#include "lleventnotifier.h"
#include "llcallbacklist.h"
+#include "lldeferredsounds.h"
#include "pipeline.h"
#include "llgesturemgr.h"
#include "llsky.h"
@@ -219,7 +221,6 @@
#include "llmachineid.h"
#include "llmainlooprepeater.h"
-
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
// this app, or another 'component' of the viewer. App globals should be
@@ -459,7 +460,18 @@ static void ui_audio_callback(const LLUUID& uuid)
{
if (gAudiop)
{
- gAudiop->triggerSound(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
+ SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
+ gAudiop->triggerSound(soundData);
+ }
+}
+
+// A callback set in LLAppViewer::init()
+static void deferred_ui_audio_callback(const LLUUID& uuid)
+{
+ if (gAudiop)
+ {
+ SoundData soundData(uuid, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI);
+ LLDeferredSounds::instance().deferSound(soundData);
}
}
@@ -773,6 +785,7 @@ bool LLAppViewer::init()
LLUI::initClass(settings_map,
LLUIImageList::getInstance(),
ui_audio_callback,
+ deferred_ui_audio_callback,
&LLUI::sGLScaleFactor);
LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ;
@@ -1217,7 +1230,7 @@ bool LLAppViewer::mainLoop()
LLVoiceChannel::initClass();
LLVoiceClient::getInstance()->init(gServicePump);
- LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true);
+ LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
LLTimer frameTimer,idleTimer;
LLTimer debugTime;
LLViewerJoystick* joystick(LLViewerJoystick::getInstance());
@@ -1844,6 +1857,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/llautoreplace.cpp b/indra/newview/llautoreplace.cpp
index d71cf290d6..1d72397cbc 100644
--- a/indra/newview/llautoreplace.cpp
+++ b/indra/newview/llautoreplace.cpp
@@ -30,68 +30,60 @@
#include "llviewercontrol.h"
#include "llnotificationsutil.h"
-LLAutoReplace* LLAutoReplace::sInstance;
-
const char* LLAutoReplace::SETTINGS_FILE_NAME = "autoreplace.xml";
-LLAutoReplace::LLAutoReplace()
-{
-}
-
-LLAutoReplace::~LLAutoReplace()
+void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text)
{
- sInstance = NULL;
-}
+ // make sure these returned values are cleared in case there is no replacement
+ replacement_start = 0;
+ replacement_length = 0;
+ replacement_string.clear();
-void LLAutoReplace::autoreplaceCallback(LLUIString& inputText, S32& cursorPos)
-{
static LLCachedControl<bool> perform_autoreplace(gSavedSettings, "AutoReplace");
- if(perform_autoreplace)
+ if (perform_autoreplace)
{
- S32 wordEnd = cursorPos-1;
- LLWString text = inputText.getWString();
+ S32 word_end = cursor_pos - 1;
- bool atSpace = (text[wordEnd] == ' ');
- bool haveWord = (LLWStringUtil::isPartOfWord(text[wordEnd]));
+ bool at_space = (input_text[word_end] == ' ');
+ bool have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
- if (atSpace || haveWord)
+ if (at_space || have_word)
{
- if (atSpace && wordEnd > 0)
+ if (at_space && word_end > 0)
{
// find out if this space immediately follows a word
- wordEnd--;
- haveWord = (LLWStringUtil::isPartOfWord(text[wordEnd]));
+ word_end--;
+ have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
}
- if (haveWord)
+ if (have_word)
{
- // wordEnd points to the end of a word, now find the start of the word
+ // word_end points to the end of a word, now find the start of the word
std::string word;
- S32 wordStart = wordEnd;
- for ( S32 backOne = wordStart - 1;
- backOne >= 0 && LLWStringUtil::isPartOfWord(text[backOne]);
- backOne--
- )
+ S32 word_start = word_end;
+ for (S32 back_one = word_start - 1;
+ back_one >= 0 && LLWStringUtil::isPartOfWord(input_text[back_one]);
+ back_one--
+ )
{
- wordStart--; // walk wordStart back to the beginning of the word
+ word_start--; // walk word_start back to the beginning of the word
}
- LL_DEBUGS("AutoReplace")<<"wordStart: "<<wordStart<<" wordEnd: "<<wordEnd<<LL_ENDL;
- std::string strText = std::string(text.begin(), text.end());
- std::string lastWord = strText.substr(wordStart, wordEnd-wordStart+1);
- std::string replacementWord( mSettings.replaceWord( lastWord ) );
+ LL_DEBUGS("AutoReplace") << "word_start: " << word_start << " word_end: " << word_end << LL_ENDL;
+ std::string str_text = std::string(input_text.begin(), input_text.end());
+ std::string last_word = str_text.substr(word_start, word_end - word_start + 1);
+ std::string replacement_word(mSettings.replaceWord(last_word));
- if ( replacementWord != lastWord )
+ if (replacement_word != last_word)
{
// The last word is one for which we have a replacement
- if (atSpace)
+ if (at_space)
{
- // replace the last word in the input
- LLWString strNew = utf8str_to_wstring(replacementWord);
- LLWString strOld = utf8str_to_wstring(lastWord);
- int size_change = strNew.size() - strOld.size();
-
- text.replace(wordStart,lastWord.length(),strNew);
- inputText = wstring_to_utf8str(text);
- cursorPos+=size_change;
+ // return the replacement string
+ replacement_start = word_start;
+ replacement_length = last_word.length();
+ replacement_string = utf8str_to_wstring(replacement_word);
+ LLWString old_string = utf8str_to_wstring(last_word);
+ S32 size_change = replacement_string.size() - old_string.size();
+ cursor_pos += size_change;
}
}
}
@@ -99,16 +91,6 @@ void LLAutoReplace::autoreplaceCallback(LLUIString& inputText, S32& cursorPos)
}
}
-LLAutoReplace* LLAutoReplace::getInstance()
-{
- if(!sInstance)
- {
- sInstance = new LLAutoReplace();
- sInstance->loadFromSettings();
- }
- return sInstance;
-}
-
std::string LLAutoReplace::getUserSettingsFileName()
{
std::string path=gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "");
@@ -147,6 +129,15 @@ void LLAutoReplace::setSettings(const LLAutoReplaceSettings& newSettings)
saveToUserSettings();
}
+LLAutoReplace::LLAutoReplace()
+{
+}
+
+void LLAutoReplace::initSingleton()
+{
+ loadFromSettings();
+}
+
void LLAutoReplace::loadFromSettings()
{
std::string filename=getUserSettingsFileName();
@@ -220,7 +211,7 @@ void LLAutoReplace::saveToUserSettings()
std::string filename=getUserSettingsFileName();
llofstream file;
file.open(filename.c_str());
- LLSDSerialize::toPrettyXML(mSettings.getAsLLSD(), file);
+ LLSDSerialize::toPrettyXML(mSettings.asLLSD(), file);
file.close();
LL_INFOS("AutoReplace") << "settings saved to '" << filename << "'" << LL_ENDL;
}
@@ -801,7 +792,7 @@ LLSD LLAutoReplaceSettings::getExampleLLSD()
return example;
}
-const LLSD& LLAutoReplaceSettings::getAsLLSD()
+const LLSD& LLAutoReplaceSettings::asLLSD()
{
return mLists;
}
diff --git a/indra/newview/llautoreplace.h b/indra/newview/llautoreplace.h
index f720cc4eda..9eecc2d981 100644
--- a/indra/newview/llautoreplace.h
+++ b/indra/newview/llautoreplace.h
@@ -132,7 +132,7 @@ class LLAutoReplaceSettings
LLSD getExampleLLSD();
/// Get the actual settings as LLSD
- const LLSD& getAsLLSD();
+ const LLSD& asLLSD();
///< @note for use only in AutoReplace::saveToUserSettings
private:
@@ -183,49 +183,45 @@ class LLAutoReplaceSettings
* When the end of a word is detected (defined as any punctuation character,
* or any whitespace except newline or return), the preceding word is used
* as a lookup key in an ordered list of maps. If a match is found in any
- * map, the keyword is replaced by the associated value from the map.
+ * map, the replacement start index and length are returned along with the
+ * new replacement string.
*
* See the autoreplaceCallback method for how to add autoreplace functionality
* to a text entry tool.
*/
class LLAutoReplace : public LLSingleton<LLAutoReplace>
{
- public:
- LLAutoReplace();
- ~LLAutoReplace();
-
- /// @return a pointer to the active instance
- static LLAutoReplace* getInstance();
+public:
+ /// Callback that provides the hook for use in text entry methods
+ void autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text);
- /// Callback that provides the hook for use in text entry methods
- void autoreplaceCallback(LLUIString& inputText, S32& cursorPos);
+ /// Get a copy of the current settings
+ LLAutoReplaceSettings getSettings();
- /// Get a copy of the current settings
- LLAutoReplaceSettings getSettings();
+ /// Commit new settings after making changes
+ void setSettings(const LLAutoReplaceSettings& settings);
- /// Commit new settings after making changes
- void setSettings(const LLAutoReplaceSettings& settings);
-
- private:
- friend class LLSingleton<LLAutoReplace>;
- static LLAutoReplace* sInstance; ///< the active settings instance
+private:
+ friend class LLSingleton<LLAutoReplace>;
+ LLAutoReplace();
+ /*virtual*/ void initSingleton();
- LLAutoReplaceSettings mSettings; ///< configuration information
+ LLAutoReplaceSettings mSettings; ///< configuration information
- /// Read settings from persistent storage
- void loadFromSettings();
+ /// Read settings from persistent storage
+ void loadFromSettings();
- /// Make the newSettings active and write them to user storage
- void saveToUserSettings();
+ /// Make the newSettings active and write them to user storage
+ void saveToUserSettings();
- /// Compute the user settings file name
- std::string getUserSettingsFileName();
+ /// Compute the user settings file name
+ std::string getUserSettingsFileName();
- /// Compute the (read-ony) application settings file name
- std::string getAppSettingsFileName();
+ /// Compute the (read-ony) application settings file name
+ std::string getAppSettingsFileName();
- /// basename for the settings files
- static const char* SETTINGS_FILE_NAME;
+ /// basename for the settings files
+ static const char* SETTINGS_FILE_NAME;
};
#endif /* LLAUTOREPLACE_H */
diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp
index fdd4565e50..b513a52ff7 100755
--- a/indra/newview/llavataractions.cpp
+++ b/indra/newview/llavataractions.cpp
@@ -42,7 +42,9 @@
#include "llappviewer.h" // for gLastVersionChannel
#include "llcachename.h"
#include "llcallingcard.h" // for LLAvatarTracker
+#include "llconversationlog.h"
#include "llfloateravatarpicker.h" // for LLFloaterAvatarPicker
+#include "llfloaterconversationpreview.h"
#include "llfloatergroupinvite.h"
#include "llfloatergroups.h"
#include "llfloaterreg.h"
@@ -55,6 +57,7 @@
#include "llinventorybridge.h"
#include "llinventorymodel.h" // for gInventory.findCategoryUUIDForType
#include "llinventorypanel.h"
+#include "llfloaterimcontainer.h"
#include "llimview.h" // for gIMMgr
#include "llmutelist.h"
#include "llnotificationsutil.h" // for LLNotificationsUtil
@@ -66,7 +69,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
@@ -93,7 +95,7 @@ void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::strin
LLRecentPeople::instance().add(id);
}
-void on_avatar_name_friendship(const LLUUID& id, const LLAvatarName av_name)
+static void on_avatar_name_friendship(const LLUUID& id, const LLAvatarName av_name)
{
LLAvatarActions::requestFriendshipDialog(id, av_name.getCompleteName());
}
@@ -134,7 +136,7 @@ void LLAvatarActions::removeFriendsDialog(const uuid_vec_t& ids)
LLAvatarName av_name;
if(LLAvatarNameCache::get(agent_id, &av_name))
{
- args["NAME"] = av_name.mDisplayName;
+ args["NAME"] = av_name.getDisplayName();
}
msgType = "RemoveFromFriends";
@@ -179,11 +181,11 @@ void LLAvatarActions::offerTeleport(const uuid_vec_t& ids)
static void on_avatar_name_cache_start_im(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
- std::string name = av_name.getCompleteName();
+ std::string name = av_name.getDisplayName();
LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id);
if (session_id != LLUUID::null)
{
- LLIMFloater::show(session_id);
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
}
make_ui_sound("UISndStartIM");
}
@@ -191,11 +193,10 @@ static void on_avatar_name_cache_start_im(const LLUUID& agent_id,
// static
void LLAvatarActions::startIM(const LLUUID& id)
{
- if (id.isNull())
+ if (id.isNull() || gAgent.getID() == id)
return;
- LLAvatarNameCache::get(id,
- boost::bind(&on_avatar_name_cache_start_im, _1, _2));
+ LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_cache_start_im, _1, _2));
}
// static
@@ -214,7 +215,7 @@ void LLAvatarActions::endIM(const LLUUID& id)
static void on_avatar_name_cache_start_call(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
- std::string name = av_name.getCompleteName();
+ std::string name = av_name.getDisplayName();
LLUUID session_id = gIMMgr->addSession(name, IM_NOTHING_SPECIAL, agent_id, true);
if (session_id != LLUUID::null)
{
@@ -230,12 +231,11 @@ void LLAvatarActions::startCall(const LLUUID& id)
{
return;
}
- LLAvatarNameCache::get(id,
- boost::bind(&on_avatar_name_cache_start_call, _1, _2));
+ LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_cache_start_call, _1, _2));
}
// static
-void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids)
+void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids, const LLUUID& floater_id)
{
if (ids.size() == 0)
{
@@ -252,7 +252,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids)
// create the new ad hoc voice session
const std::string title = LLTrans::getString("conference-title");
LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START,
- ids[0], id_array, true);
+ ids[0], id_array, true, floater_id);
if (session_id == LLUUID::null)
{
return;
@@ -285,7 +285,7 @@ bool LLAvatarActions::canCall()
}
// static
-void LLAvatarActions::startConference(const uuid_vec_t& ids)
+void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& floater_id)
{
// *HACK: Copy into dynamic array
LLDynamicArray<LLUUID> id_array;
@@ -294,11 +294,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;
}
+
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
+
make_ui_sound("UISndStartIM");
}
@@ -310,19 +314,11 @@ static const char* get_profile_floater_name(const LLUUID& avatar_id)
static void on_avatar_name_show_profile(const LLUUID& agent_id, const LLAvatarName& av_name)
{
- std::string username = av_name.mUsername;
- if (username.empty())
- {
- username = LLCacheName::buildUsername(av_name.mDisplayName);
- }
-
- llinfos << "opening web profile for " << username << llendl;
- std::string url = getProfileURL(username);
+ std::string url = getProfileURL(av_name.getAccountName());
// PROFILES: open in webkit window
LLFloaterWebContent::Params p;
- p.url(url).
- id(agent_id.asString());
+ p.url(url).id(agent_id.asString());
LLFloaterReg::showInstance(get_profile_floater_name(agent_id), p);
}
@@ -374,19 +370,19 @@ void LLAvatarActions::showOnMap(const LLUUID& id)
return;
}
- gFloaterWorldMap->trackAvatar(id, av_name.mDisplayName);
+ gFloaterWorldMap->trackAvatar(id, av_name.getDisplayName());
LLFloaterReg::showInstance("world_map");
}
// static
void LLAvatarActions::pay(const LLUUID& id)
{
- LLNotification::Params params("BusyModePay");
+ LLNotification::Params params("DoNotDisturbModePay");
params.functor.function(boost::bind(&LLAvatarActions::handlePay, _1, _2, id));
- if (gAgent.getBusy())
+ if (gAgent.isDoNotDisturb())
{
- // warn users of being in busy mode during a transaction
+ // warn users of being in do not disturb mode during a transaction
LLNotifications::instance().add(params);
}
else
@@ -448,6 +444,7 @@ void LLAvatarActions::share(const LLUUID& id)
{
LLSD key;
LLFloaterSidePanelContainer::showPanel("inventory", key);
+ LLFloaterReg::showInstance("im_container");
LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL,id);
@@ -529,23 +526,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 +661,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 +692,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).getDisplayName());
+ 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 +777,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 +800,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 +816,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 +851,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 +916,33 @@ 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);
+ return;
+ }
+ }
+
+ if (LLLogChat::isTranscriptExist(id))
+ {
+ LLAvatarName avatar_name;
+ LLSD extended_id(id);
+
+ LLAvatarNameCache::get(id, &avatar_name);
+ extended_id[LL_FCP_COMPLETE_NAME] = avatar_name.getCompleteName();
+ extended_id[LL_FCP_ACCOUNT_NAME] = avatar_name.getAccountName();
+ LLFloaterReg::showInstance("preview_conversation", extended_id, true);
+ }
+}
+
//== private methods ========================================================================================
// static
@@ -907,7 +985,7 @@ bool LLAvatarActions::handlePay(const LLSD& notification, const LLSD& response,
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0)
{
- gAgent.clearBusy();
+ gAgent.setDoNotDisturb(false);
}
LLFloaterPayUtil::payDirectly(&give_money, avatar_id, /*is_group=*/false);
@@ -1015,7 +1093,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 +1111,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..714bde6f37 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"
@@ -36,7 +38,7 @@
#include "llmenugl.h"
#include "lluictrlfactory.h"
#include "llagentdata.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
// library includes
#include "llavatarnamecache.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,19 @@ void LLAvatarIconCtrl::setValue(const LLSD& value)
LLIconCtrl::setValue(value);
}
- LLAvatarNameCache::get(mAvatarId,
- boost::bind(&LLAvatarIconCtrl::onAvatarNameCache,
- this, _1, _2));
+ fetchAvatarName();
+}
+
+void LLAvatarIconCtrl::fetchAvatarName()
+{
+ if (mAvatarId.notNull())
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarId, boost::bind(&LLAvatarIconCtrl::onAvatarNameCache, this, _1, _2));
+ }
}
bool LLAvatarIconCtrl::updateFromCache()
@@ -292,11 +313,13 @@ void LLAvatarIconCtrl::processProperties(void* data, EAvatarProcessorType type)
void LLAvatarIconCtrl::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
if (agent_id == mAvatarId)
{
// Most avatar icon controls are next to a UI element that shows
// a display name, so only show username.
- mFullName = av_name.mUsername;
+ mFullName = av_name.getUserName();
if (mDrawTooltip)
{
diff --git a/indra/newview/llavatariconctrl.h b/indra/newview/llavatariconctrl.h
index 7f568fc5b8..4929efb7d0 100644
--- a/indra/newview/llavatariconctrl.h
+++ b/indra/newview/llavatariconctrl.h
@@ -27,7 +27,9 @@
#ifndef LL_LLAVATARICONCTRL_H
#define LL_LLAVATARICONCTRL_H
-#include "lliconctrl.h"
+#include <boost/signals2.hpp>
+
+#include "../llui/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/llavatarlist.cpp b/indra/newview/llavatarlist.cpp
index 771419f60a..9f02f301a1 100644
--- a/indra/newview/llavatarlist.cpp
+++ b/indra/newview/llavatarlist.cpp
@@ -46,6 +46,7 @@
#include "lluuid.h"
#include "llvoiceclient.h"
#include "llviewercontrol.h" // for gSavedSettings
+#include "lltooldraganddrop.h"
static LLDefaultChildRegistry::Register<LLAvatarList> r("avatar_list");
@@ -278,7 +279,7 @@ void LLAvatarList::refresh()
LLAvatarName av_name;
have_names &= LLAvatarNameCache::get(buddy_id, &av_name);
- if (!have_filter || findInsensitive(av_name.mDisplayName, mNameFilter))
+ if (!have_filter || findInsensitive(av_name.getDisplayName(), mNameFilter))
{
if (nadded >= ADD_LIMIT)
{
@@ -296,8 +297,9 @@ void LLAvatarList::refresh()
}
else
{
+ std::string display_name = av_name.getDisplayName();
addNewItem(buddy_id,
- av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName,
+ display_name.empty() ? waiting_str : display_name,
LLAvatarTracker::instance().isBuddyOnline(buddy_id));
}
@@ -325,7 +327,7 @@ void LLAvatarList::refresh()
const LLUUID& buddy_id = it->asUUID();
LLAvatarName av_name;
have_names &= LLAvatarNameCache::get(buddy_id, &av_name);
- if (!findInsensitive(av_name.mDisplayName, mNameFilter))
+ if (!findInsensitive(av_name.getDisplayName(), mNameFilter))
{
removeItemByUUID(buddy_id);
modified = true;
@@ -398,7 +400,7 @@ bool LLAvatarList::filterHasMatches()
// If name has not been loaded yet we consider it as a match.
// When the name will be loaded the filter will be applied again(in refresh()).
- if (have_name && !findInsensitive(av_name.mDisplayName, mNameFilter))
+ if (have_name && !findInsensitive(av_name.getDisplayName(), mNameFilter))
{
continue;
}
@@ -461,6 +463,57 @@ BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask)
return handled;
}
+BOOL LLAvatarList::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gFocusMgr.setMouseCapture(this);
+
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y);
+
+ return LLFlatListViewEx::handleMouseDown(x, y, mask);
+}
+
+BOOL LLAvatarList::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if(hasMouseCapture())
+ {
+ gFocusMgr.setMouseCapture(NULL);
+ }
+
+ return LLFlatListViewEx::handleMouseUp(x, y, mask);
+}
+
+BOOL LLAvatarList::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;
+ getSelectedUUIDs(cargo_ids);
+ types.resize(cargo_ids.size(), DAD_PERSON);
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_PEOPLE;
+ LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, src);
+ }
+ }
+
+ if(!handled)
+ {
+ handled = LLFlatListViewEx::handleHover(x, y, mask);
+ }
+
+ return handled;
+}
+
bool LLAvatarList::isAvalineItemSelected()
{
std::vector<LLPanel*> selected_items;
diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h
index 4814a88a79..3542577ae3 100644
--- a/indra/newview/llavatarlist.h
+++ b/indra/newview/llavatarlist.h
@@ -84,6 +84,9 @@ public:
bool getIconsVisible() const { return mShowIcons; }
const std::string getIconParamName() const{return mIconParamName;}
virtual BOOL handleRightMouseDown(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);
// Return true if filter has at least one match.
bool filterHasMatches();
diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp
index 30eecfe323..3e6c817dd6 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,29 @@ BOOL LLAvatarListItem::postBuild()
return TRUE;
}
+void LLAvatarListItem::handleVisibilityChange ( BOOL new_visibility )
+{
+ //Adjust positions of icons (info button etc) when
+ //speaking indicator visibility was changed/toggled while panel was closed (not visible)
+ if(new_visibility && mSpeakingIndicator->getIndicatorToggled())
+ {
+ updateChildren();
+ mSpeakingIndicator->setIndicatorToggled(false);
+ }
+}
+
+void LLAvatarListItem::fetchAvatarName()
+{
+ if (mAvatarId.notNull())
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2));
+ }
+}
+
S32 LLAvatarListItem::notifyParent(const LLSD& info)
{
if (info.has("visibility_changed"))
@@ -259,8 +294,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();
}
}
@@ -358,8 +392,7 @@ std::string LLAvatarListItem::getAvatarToolTip() const
void LLAvatarListItem::updateAvatarName()
{
- LLAvatarNameCache::get(getAvatarId(),
- boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2));
+ fetchAvatarName();
}
//== PRIVATE SECITON ==========================================================
@@ -371,8 +404,10 @@ void LLAvatarListItem::setNameInternal(const std::string& name, const std::strin
void LLAvatarListItem::onAvatarNameCache(const LLAvatarName& av_name)
{
- setAvatarName(av_name.mDisplayName);
- setAvatarToolTip(av_name.mUsername);
+ mAvatarNameCacheConnection.disconnect();
+
+ setAvatarName(av_name.getDisplayName());
+ setAvatarToolTip(av_name.getUserName());
//requesting the list to resort
notifyParent(LLSD().with("sort", LLSD()));
diff --git a/indra/newview/llavatarlistitem.h b/indra/newview/llavatarlistitem.h
index c95ac39696..7ef35a746e 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"
@@ -82,6 +84,7 @@ public:
/**
* Processes notification from speaker indicator to update children when indicator's visibility is changed.
*/
+ virtual void handleVisibilityChange ( BOOL new_visibility );
virtual S32 notifyParent(const LLSD& info);
virtual void onMouseLeave(S32 x, S32 y, MASK mask);
virtual void onMouseEnter(S32 x, S32 y, MASK mask);
@@ -214,6 +217,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..19747757db 100644
--- a/indra/newview/llbrowsernotification.cpp
+++ b/indra/newview/llbrowsernotification.cpp
@@ -35,11 +35,13 @@
using namespace LLNotificationsUI;
-bool LLBrowserNotification::processNotification(const LLSD& notify)
+LLBrowserNotification::LLBrowserNotification()
+ : LLSystemNotificationHandler("Browser", "browser")
{
- LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
- if (!notification) return false;
+}
+bool LLBrowserNotification::processNotification(const LLNotificationPtr& notification)
+{
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
deleted file mode 100644
index f2375bfa4f..0000000000
--- a/indra/newview/llcallfloater.cpp
+++ /dev/null
@@ -1,821 +0,0 @@
-/**
- * @file llcallfloater.cpp
- * @author Mike Antipov
- * @brief Voice Control Panel in a Voice Chats (P2P, Group, Nearby...).
- *
- * $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 "llcallfloater.h"
-
-#include "llnotificationsutil.h"
-#include "lltrans.h"
-
-#include "llagent.h"
-#include "llagentdata.h" // for gAgentID
-#include "llavatarnamecache.h"
-#include "llavatariconctrl.h"
-#include "llavatarlist.h"
-#include "lldraghandle.h"
-#include "llimfloater.h"
-#include "llimview.h"
-#include "llfloaterreg.h"
-#include "llparticipantlist.h"
-#include "llspeakers.h"
-#include "lltextutil.h"
-#include "lltransientfloatermgr.h"
-#include "llviewercontrol.h"
-#include "llviewerdisplayname.h"
-#include "llviewerwindow.h"
-#include "llvoicechannel.h"
-#include "llviewerparcelmgr.h"
-#include "llfirstuse.h"
-
-static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids);
-void reshape_floater(LLCallFloater* floater, S32 delta_height);
-
-class LLNonAvatarCaller : public LLAvatarListItem
-{
-public:
- LLNonAvatarCaller() : LLAvatarListItem(false)
- {
-
- }
- BOOL postBuild()
- {
- BOOL rv = LLAvatarListItem::postBuild();
-
- if (rv)
- {
- setOnline(true);
- showLastInteractionTime(false);
- setShowProfileBtn(false);
- setShowInfoBtn(false);
- mAvatarIcon->setValue("Avaline_Icon");
- mAvatarIcon->setToolTip(std::string(""));
- }
- return rv;
- }
-
- void setName(const std::string& name)
- {
- const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name);
- LLAvatarListItem::setAvatarName(formatted_phone);
- LLAvatarListItem::setAvatarToolTip(formatted_phone);
- }
-
- void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); }
-};
-
-
-static void* create_non_avatar_caller(void*)
-{
- return new LLNonAvatarCaller;
-}
-
-LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL;
-
-LLCallFloater::LLCallFloater(const LLSD& key)
-: LLTransientDockableFloater(NULL, false, key)
-, mSpeakerManager(NULL)
-, mParticipants(NULL)
-, mAvatarList(NULL)
-, mNonAvatarCaller(NULL)
-, mVoiceType(VC_LOCAL_CHAT)
-, mAgentPanel(NULL)
-, mSpeakingIndicator(NULL)
-, mIsModeratorMutedVoice(false)
-, mInitParticipantsVoiceState(false)
-{
- static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10);
- mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay);
-
- mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
- LLVoiceClient::instance().addObserver(this);
- LLTransientFloaterMgr::getInstance()->addControlView(this);
-
- // update the agent's name if display name setting change
- LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLCallFloater::updateAgentModeratorState, this));
- LLViewerDisplayName::addNameChangedCallback(boost::bind(&LLCallFloater::updateAgentModeratorState, this));
-
-}
-
-LLCallFloater::~LLCallFloater()
-{
- resetVoiceRemoveTimers();
- delete mSpeakerDelayRemover;
-
- delete mParticipants;
- mParticipants = NULL;
-
- mAvatarListRefreshConnection.disconnect();
- mVoiceChannelStateChangeConnection.disconnect();
-
- if(LLVoiceClient::instanceExists())
- {
- LLVoiceClient::getInstance()->removeObserver(this);
- }
- LLTransientFloaterMgr::getInstance()->removeControlView(this);
-}
-
-// virtual
-BOOL LLCallFloater::postBuild()
-{
- mAvatarList = getChild<LLAvatarList>("speakers_list");
- mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this));
-
- childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
-
- mNonAvatarCaller = findChild<LLNonAvatarCaller>("non_avatar_caller");
- mNonAvatarCaller->setVisible(FALSE);
-
- initAgentData();
-
- connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
-
- updateTransparency(TT_ACTIVE); // force using active floater transparency (STORM-730)
-
- updateSession();
- return TRUE;
-}
-
-// virtual
-void LLCallFloater::onOpen(const LLSD& /*key*/)
-{
- LLFirstUse::speak(false);
-}
-
-// virtual
-void LLCallFloater::draw()
-{
- // we have to refresh participants to display ones not in voice as disabled.
- // It should be done only when she joins or leaves voice chat.
- // But seems that LLVoiceClientParticipantObserver is not enough to satisfy this requirement.
- // *TODO: mantipov: remove from draw()
-
- // NOTE: it looks like calling onChange() here is not necessary,
- // but sometime it is not called properly from the observable object.
- // Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent)
-// onChange();
-
- bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID);
-
- if (mIsModeratorMutedVoice != is_moderator_muted)
- {
- setModeratorMutedVoice(is_moderator_muted);
- }
-
- // Need to resort the participant list if it's in sort by recent speaker order.
- if (mParticipants)
- mParticipants->update();
-
- LLFloater::draw();
-}
-
-// virtual
-void LLCallFloater::setFocus( BOOL b )
-{
- LLFloater::setFocus(b);
-
- // Force using active floater transparency (STORM-730).
- // We have to override setFocus() for LLCallFloater because selecting an item
- // of the voice morphing combobox causes the floater to lose focus and thus become transparent.
- updateTransparency(TT_ACTIVE);
-}
-
-// virtual
-void LLCallFloater::onParticipantsChanged()
-{
- if (NULL == mParticipants) return;
- updateParticipantsVoiceState();
-
- // Add newly joined participants.
- uuid_vec_t speakers_uuids;
- get_voice_participants_uuids(speakers_uuids);
- for (uuid_vec_t::const_iterator it = speakers_uuids.begin(); it != speakers_uuids.end(); it++)
- {
- mParticipants->addAvatarIDExceptAgent(*it);
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-/// PRIVATE SECTION
-//////////////////////////////////////////////////////////////////////////
-
-void LLCallFloater::leaveCall()
-{
- LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
- if (voice_channel)
- {
- gIMMgr->endCall(voice_channel->getSessionID());
- }
-}
-
-void LLCallFloater::updateSession()
-{
- LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
- if (voice_channel)
- {
- LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL;
-
- if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID())
- {
- LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL;
- return;
- }
- else
- {
- mSpeakerManager = NULL;
- }
- }
-
- const LLUUID& session_id = voice_channel ? voice_channel->getSessionID() : LLUUID::null;
-
- LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
- if (im_session)
- {
- mSpeakerManager = LLIMModel::getInstance()->getSpeakerManager(session_id);
- switch (im_session->mType)
- {
- case IM_NOTHING_SPECIAL:
- case IM_SESSION_P2P_INVITE:
- mVoiceType = VC_PEER_TO_PEER;
-
- if (!im_session->mOtherParticipantIsAvatar)
- {
- mVoiceType = VC_PEER_TO_PEER_AVALINE;
- }
- break;
- case IM_SESSION_CONFERENCE_START:
- case IM_SESSION_GROUP_START:
- case IM_SESSION_INVITE:
- if (gAgent.isInGroup(session_id))
- {
- mVoiceType = VC_GROUP_CHAT;
- }
- else
- {
- mVoiceType = VC_AD_HOC_CHAT;
- }
- break;
- default:
- llwarning("Failed to determine voice call IM type", 0);
- mVoiceType = VC_GROUP_CHAT;
- break;
- }
- }
-
- if (NULL == mSpeakerManager)
- {
- // By default show nearby chat participants
- mSpeakerManager = LLLocalSpeakerMgr::getInstance();
- LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;
- mVoiceType = VC_LOCAL_CHAT;
- }
-
- updateTitle();
-
- // Hide "Leave Call" button for nearby chat
- bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
- getChildView("leave_call_btn_panel")->setVisible( !is_local_chat);
-
- refreshParticipantList();
- updateAgentModeratorState();
-
- // Show floater for voice calls & only in CONNECTED to voice channel state
- if (!is_local_chat &&
- voice_channel &&
- LLVoiceChannel::STATE_CONNECTED == voice_channel->getState())
- {
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
- bool show_me = !(im_floater && im_floater->getVisible());
- if (show_me)
- {
- setVisible(true);
- }
- }
-}
-
-void LLCallFloater::refreshParticipantList()
-{
- bool non_avatar_caller = VC_PEER_TO_PEER_AVALINE == mVoiceType;
-
- if (non_avatar_caller)
- {
- LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID());
- mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
- mNonAvatarCaller->setName(session->mName);
- }
-
- mNonAvatarCaller->setVisible(non_avatar_caller);
- mAvatarList->setVisible(!non_avatar_caller);
-
- 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->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1));
- const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
- mParticipants->setSortOrder(LLParticipantList::EParticipantSortOrder(speaker_sort_order));
-
- if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager)
- {
- mAvatarList->setNoItemsCommentText(getString("no_one_near"));
- }
-
- // we have to made delayed initialization of voice state of participant list.
- // it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed().
- mInitParticipantsVoiceState = true;
- }
-}
-
-void LLCallFloater::onAvatarListRefreshed()
-{
- if (mInitParticipantsVoiceState)
- {
- initParticipantsVoiceState();
- mInitParticipantsVoiceState = false;
- }
- else
- {
- updateParticipantsVoiceState();
- }
-}
-
-// static
-void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
-{
- LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
-
- // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO
- // it sill be sent for the same channel again (when state is changed).
- // So, lets ignore this call.
- if (channel == sCurrentVoiceChannel) return;
-
- LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls");
-
- call_floater->connectToChannel(channel);
-}
-
-void LLCallFloater::onAvatarNameCache(const LLUUID& agent_id,
- const LLAvatarName& av_name)
-{
- LLStringUtil::format_map_t args;
- args["[NAME]"] = av_name.getCompleteName();
- std::string title = getString("title_peer_2_peer", args);
- setTitle(title);
-}
-
-void LLCallFloater::updateTitle()
-{
- LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
- if (mVoiceType == VC_PEER_TO_PEER)
- {
- LLUUID session_id = voice_channel->getSessionID();
- LLIMModel::LLIMSession* im_session =
- LLIMModel::getInstance()->findIMSession(session_id);
- if (im_session)
- {
- LLAvatarNameCache::get(im_session->mOtherParticipantID,
- boost::bind(&LLCallFloater::onAvatarNameCache,
- this, _1, _2));
- return;
- }
- }
- std::string title;
- switch (mVoiceType)
- {
- case VC_LOCAL_CHAT:
- title = getString("title_nearby");
- break;
- case VC_PEER_TO_PEER:
- case VC_PEER_TO_PEER_AVALINE:
- {
- title = voice_channel->getSessionName();
-
- if (VC_PEER_TO_PEER_AVALINE == mVoiceType)
- {
- title = LLTextUtil::formatPhoneNumber(title);
- }
-
- LLStringUtil::format_map_t args;
- args["[NAME]"] = title;
- title = getString("title_peer_2_peer", args);
- }
- break;
- case VC_AD_HOC_CHAT:
- title = getString("title_adhoc");
- break;
- case VC_GROUP_CHAT:
- {
- LLStringUtil::format_map_t args;
- args["[GROUP]"] = voice_channel->getSessionName();
- title = getString("title_group", args);
- }
- break;
- }
-
- setTitle(title);
-}
-
-void LLCallFloater::initAgentData()
-{
- mAgentPanel = getChild<LLPanel> ("my_panel");
-
- if ( mAgentPanel )
- {
- mAgentPanel->getChild<LLUICtrl>("user_icon")->setValue(gAgentID);
-
- // Just use display name, because it's you
- LLAvatarName av_name;
- LLAvatarNameCache::get( gAgentID, &av_name );
- mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(av_name.mDisplayName);
-
- mSpeakingIndicator = mAgentPanel->getChild<LLOutputMonitorCtrl>("speaking_indicator");
- mSpeakingIndicator->setSpeakerId(gAgentID);
- }
-}
-
-void LLCallFloater::setModeratorMutedVoice(bool moderator_muted)
-{
- mIsModeratorMutedVoice = moderator_muted;
-
- if (moderator_muted)
- {
- LLNotificationsUtil::add("VoiceIsMutedByModerator");
- }
- mSpeakingIndicator->setIsMuted(moderator_muted);
-}
-
-void LLCallFloater::onModeratorNameCache(const LLAvatarName& av_name)
-{
- std::string name;
- name = av_name.mDisplayName;
-
- if(mSpeakerManager && gAgent.isInGroup(mSpeakerManager->getSessionID()))
- {
- // This method can be called when LLVoiceChannel.mState == STATE_NO_CHANNEL_INFO
- // in this case there are not any speakers yet.
- if (mSpeakerManager->findSpeaker(gAgentID))
- {
- // Agent is Moderator
- if (mSpeakerManager->findSpeaker(gAgentID)->mIsModerator)
-
- {
- const std::string moderator_indicator(LLTrans::getString("IM_moderator_label"));
- name += " " + moderator_indicator;
- }
- }
- }
- mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(name);
-}
-
-void LLCallFloater::updateAgentModeratorState()
-{
- LLAvatarNameCache::get(gAgentID, boost::bind(&LLCallFloater::onModeratorNameCache, this, _2));
-}
-
-static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids)
-{
- // Get a list of participants from VoiceClient
- std::set<LLUUID> participants;
- LLVoiceClient::getInstance()->getParticipantList(participants);
-
- for (std::set<LLUUID>::const_iterator iter = participants.begin();
- iter != participants.end(); ++iter)
- {
- speakers_uuids.push_back(*iter);
- }
-
-}
-
-void LLCallFloater::initParticipantsVoiceState()
-{
- // Set initial status for each participant in the list.
- std::vector<LLPanel*> items;
- mAvatarList->getItems(items);
- std::vector<LLPanel*>::const_iterator
- it = items.begin(),
- it_end = items.end();
-
-
- uuid_vec_t speakers_uuids;
- get_voice_participants_uuids(speakers_uuids);
-
- for(; it != it_end; ++it)
- {
- LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
-
- if (!item) continue;
-
- LLUUID speaker_id = item->getAvatarId();
-
- uuid_vec_t::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id);
-
- // If an avatarID assigned to a panel is found in a speakers list
- // obtained from VoiceClient we assign the JOINED status to the owner
- // of this avatarID.
- if (speaker_iter != speakers_uuids.end())
- {
- setState(item, STATE_JOINED);
- }
- else
- {
- LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id);
- // If someone has already left the call before, we create his
- // avatar row panel with HAS_LEFT status and remove it after
- // the timeout, otherwise we create a panel with INVITED status
- if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall)
- {
- setState(item, STATE_LEFT);
- }
- else
- {
- setState(item, STATE_INVITED);
- }
- }
- }
-}
-
-void LLCallFloater::updateParticipantsVoiceState()
-{
- uuid_vec_t speakers_list;
-
- // Get a list of participants from VoiceClient
- uuid_vec_t speakers_uuids;
- get_voice_participants_uuids(speakers_uuids);
-
- // Updating the status for each participant already in list.
- std::vector<LLPanel*> items;
- mAvatarList->getItems(items);
- std::vector<LLPanel*>::const_iterator
- it = items.begin(),
- it_end = items.end();
-
- for(; it != it_end; ++it)
- {
- LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
- if (!item) continue;
-
- const LLUUID participant_id = item->getAvatarId();
- bool found = false;
-
- uuid_vec_t::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id);
-
- LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL;
-
- // If an avatarID assigned to a panel is found in a speakers list
- // obtained from VoiceClient we assign the JOINED status to the owner
- // of this avatarID.
- if (speakers_iter != speakers_uuids.end())
- {
- setState(item, STATE_JOINED);
-
- LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
- if (speaker.isNull())
- continue;
- speaker->mHasLeftCurrentCall = FALSE;
-
- speakers_uuids.erase(speakers_iter);
- found = true;
- }
-
- if (!found)
- {
- updateNotInVoiceParticipantState(item);
- }
- }
-}
-
-void LLCallFloater::updateNotInVoiceParticipantState(LLAvatarListItem* item)
-{
- LLUUID participant_id = item->getAvatarId();
- ESpeakerState current_state = getState(participant_id);
-
- switch (current_state)
- {
- case STATE_JOINED:
- // If an avatarID is not found in a speakers list from VoiceClient and
- // a panel with this ID has a JOINED status this means that this person
- // HAS LEFT the call.
- setState(item, STATE_LEFT);
-
- {
- LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
- if (speaker.notNull())
- {
- speaker->mHasLeftCurrentCall = TRUE;
- }
- }
- break;
- case STATE_LEFT:
- // nothing to do. These states should not be changed.
- break;
- case STATE_INVITED:
- // If avatar was invited into group chat and went offline it is still exists in mSpeakerStateMap
- // If it goes online it will be rendered as JOINED via LAvatarListItem.
- // Lets update its visual representation. See EXT-6660
- case STATE_UNKNOWN:
- // If an avatarID is not found in a speakers list from VoiceClient and
- // a panel with this ID has an UNKNOWN status this means that this person
- // HAS ENTERED session but it is not in voice chat yet. So, set INVITED status
- setState(item, STATE_INVITED);
- break;
- default:
- // for possible new future states.
- llwarns << "Unsupported (" << getState(participant_id) << ") state for: " << item->getAvatarName() << llendl;
- break;
- }
-}
-
-void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)
-{
- // *HACK: mantipov: sometimes such situation is possible while switching to voice channel:
-/*
- - voice channel is switched to the one user is joining
- - participant list is initialized with voice states: agent is in voice
- - than such log messages were found (with agent UUID)
- - LLVivoxProtocolParser::process_impl: parsing: <Response requestId="22" action="Session.MediaDisconnect.1"><ReturnCode>0</ReturnCode><Results><StatusCode>0</StatusCode><StatusString /></Results><InputXml><Request requestId="22" action="Session.MediaDisconnect.1"><SessionGroupHandle>9</SessionGroupHandle><SessionHandle>12</SessionHandle><Media>Audio</Media></Request></InputXml></Response>
- - LLVoiceClient::sessionState::removeParticipant: participant "sip:x2pwNkMbpR_mK4rtB_awASA==@bhr.vivox.com" (da9c0d90-c6e9-47f9-8ae2-bb41fdac0048) removed.
- - and than while updating participants voice states agent is marked as HAS LEFT
- - next updating of LLVoiceClient state makes agent JOINED
- So, lets skip HAS LEFT state for agent's avatar
-*/
- if (STATE_LEFT == state && item->getAvatarId() == gAgentID) return;
-
- setState(item->getAvatarId(), state);
-
- switch (state)
- {
- case STATE_INVITED:
- item->setState(LLAvatarListItem::IS_VOICE_INVITED);
- break;
- case STATE_JOINED:
- removeVoiceRemoveTimer(item->getAvatarId());
- item->setState(LLAvatarListItem::IS_VOICE_JOINED);
- break;
- case STATE_LEFT:
- {
- setVoiceRemoveTimer(item->getAvatarId());
- item->setState(LLAvatarListItem::IS_VOICE_LEFT);
- }
- break;
- default:
- llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl;
- break;
- }
-}
-
-void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id)
-{
- mSpeakerDelayRemover->setActionTimer(voice_speaker_id);
-}
-
-bool LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id)
-{
- uuid_vec_t& speaker_uuids = mAvatarList->getIDs();
- uuid_vec_t::iterator pos = std::find(speaker_uuids.begin(), speaker_uuids.end(), voice_speaker_id);
- if(pos != speaker_uuids.end())
- {
- speaker_uuids.erase(pos);
- mAvatarList->setDirty();
- }
-
- return false;
-}
-
-
-void LLCallFloater::resetVoiceRemoveTimers()
-{
- mSpeakerDelayRemover->removeAllTimers();
-}
-
-void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id)
-{
- mSpeakerDelayRemover->unsetActionTimer(voice_speaker_id);
-}
-
-bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id)
-{
- bool is_valid = true;
- switch (mVoiceType)
- {
- case VC_LOCAL_CHAT:
- {
- // A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice).
- uuid_vec_t speakers;
- get_voice_participants_uuids(speakers);
- is_valid = std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end();
- }
- break;
- case VC_GROUP_CHAT:
- // if participant had left this call before do not allow add her again. See EXT-4216.
- // but if she Join she will be added into the list from the LLCallFloater::onChange()
- is_valid = STATE_LEFT != getState(speaker_id);
- break;
- default:
- // do nothing. required for Linux build
- break;
- }
-
- return is_valid;
-}
-
-void LLCallFloater::connectToChannel(LLVoiceChannel* channel)
-{
- mVoiceChannelStateChangeConnection.disconnect();
-
- sCurrentVoiceChannel = channel;
-
- mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
-
- updateState(channel->getState());
-}
-
-void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
-{
- // check is voice operational and if it doesn't work hide VCP (EXT-4397)
- if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())
- {
- updateState(new_state);
- }
- else
- {
- closeFloater();
- }
-}
-
-void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
-{
- LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL;
- if (LLVoiceChannel::STATE_CONNECTED == new_state)
- {
- updateSession();
- }
- else
- {
- reset(new_state);
- }
-}
-
-void LLCallFloater::reset(const LLVoiceChannel::EState& new_state)
-{
- // lets forget states from the previous session
- // for timers...
- resetVoiceRemoveTimers();
-
- // ...and for speaker state
- mSpeakerStateMap.clear();
-
- delete mParticipants;
- mParticipants = NULL;
- mAvatarList->clear();
-
- // These ifs were added instead of simply showing "loading" to make VCP work correctly in parcels
- // with disabled voice (EXT-4648 and EXT-4649)
- if (!LLViewerParcelMgr::getInstance()->allowAgentVoice() && LLVoiceChannel::STATE_HUNG_UP == new_state)
- {
- // hides "Leave Call" when call is ended in parcel with disabled voice- hiding usually happens in
- // updateSession() which won't be called here because connect to nearby voice never happens
- getChildView("leave_call_btn_panel")->setVisible( false);
- // setting title to nearby chat an "no one near..." text- because in region with disabled
- // voice we won't have chance to really connect to nearby, so VCP is changed here manually
- setTitle(getString("title_nearby"));
- mAvatarList->setNoItemsCommentText(getString("no_one_near"));
- }
- // "loading" is shown only when state is "ringing" to avoid showing it in nearby chat vcp
- // of parcels with disabled voice all the time- "no_one_near" is now shown there (EXT-4648)
- else if (new_state == LLVoiceChannel::STATE_RINGING)
- {
- // update floater to show Loading while waiting for data.
- mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
- }
-
- mAvatarList->setVisible(TRUE);
- mNonAvatarCaller->setVisible(FALSE);
-
- mSpeakerManager = NULL;
-}
-
-//EOF
diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h
deleted file mode 100644
index 00a3f76e56..0000000000
--- a/indra/newview/llcallfloater.h
+++ /dev/null
@@ -1,273 +0,0 @@
-/**
- * @file llcallfloater.h
- * @author Mike Antipov
- * @brief Voice Control Panel in a Voice Chats (P2P, Group, Nearby...).
- *
- * $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_LLCALLFLOATER_H
-#define LL_LLCALLFLOATER_H
-
-#include "lltransientdockablefloater.h"
-#include "llvoicechannel.h"
-#include "llvoiceclient.h"
-
-class LLAvatarList;
-class LLAvatarListItem;
-class LLAvatarName;
-class LLNonAvatarCaller;
-class LLOutputMonitorCtrl;
-class LLParticipantList;
-class LLSpeakerMgr;
-class LLSpeakersDelayActionsStorage;
-
-/**
- * The Voice Control Panel is an ambient window summoned by clicking the flyout chevron
- * on the Speak button. It can be torn-off and freely positioned onscreen.
- *
- * When the Resident is engaged in Voice Chat, the Voice Control Panel provides control
- * over the audible volume of each of the other participants, the Resident's own Voice
- * Morphing settings (if she has subscribed to enable the feature), and Voice Recording.
- *
- * When the Resident is engaged in any chat except Nearby Chat, the Voice Control Panel
- * also provides a 'Leave Call' button to allow the Resident to leave that voice channel.
- */
-class LLCallFloater : public LLTransientDockableFloater, LLVoiceClientParticipantObserver
-{
-public:
-
- LOG_CLASS(LLCallFloater);
-
- LLCallFloater(const LLSD& key);
- ~LLCallFloater();
-
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void onOpen(const LLSD& key);
- /*virtual*/ void draw();
- /*virtual*/ void setFocus( BOOL b );
-
- /**
- * Is called by LLVoiceClient::notifyParticipantObservers when voice participant list is changed.
- *
- * Refreshes list to display participants not in voice as disabled.
- */
- /*virtual*/ void onParticipantsChanged();
-
- static void sOnCurrentChannelChanged(const LLUUID& session_id);
-
-private:
- typedef enum e_voice_controls_type
- {
- VC_LOCAL_CHAT,
- VC_GROUP_CHAT,
- VC_AD_HOC_CHAT,
- VC_PEER_TO_PEER,
- VC_PEER_TO_PEER_AVALINE
- }EVoiceControls;
-
- typedef enum e_speaker_state
- {
- STATE_UNKNOWN,
- STATE_INVITED,
- STATE_JOINED,
- STATE_LEFT,
- } ESpeakerState;
-
- typedef std::map<LLUUID, ESpeakerState> speaker_state_map_t;
-
- void leaveCall();
-
- /**
- * Updates mSpeakerManager and list according to current Voice Channel
- *
- * It compares mSpeakerManager & current Voice Channel session IDs.
- * If they are different gets Speaker manager related to current channel and updates channel participant list.
- */
- void updateSession();
-
- /**
- * Refreshes participant list according to current Voice Channel
- */
- void refreshParticipantList();
-
- /**
- * Handles event on avatar list is refreshed after it was marked dirty.
- *
- * It sets initial participants voice states (once after the first refreshing)
- * and updates voice states each time anybody is joined/left voice chat in session.
- */
- void onAvatarListRefreshed();
-
- /**
- * Updates window title with an avatar name
- */
- void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
-
- void updateTitle();
- void initAgentData();
- void setModeratorMutedVoice(bool moderator_muted);
- void updateAgentModeratorState();
- void onModeratorNameCache(const LLAvatarName& av_name);
-
- /**
- * Sets initial participants voice states in avatar list (Invited, Joined, Has Left).
- *
- * @see refreshParticipantList()
- * @see onAvatarListRefreshed()
- * @see mInitParticipantsVoiceState
- */
- void initParticipantsVoiceState();
-
- /**
- * Updates participants voice states in avatar list (Invited, Joined, Has Left).
- *
- * @see onAvatarListRefreshed()
- * @see onChanged()
- */
- void updateParticipantsVoiceState();
-
- /**
- * Updates voice state of participant not in current voice channel depend on its current state.
- */
- void updateNotInVoiceParticipantState(LLAvatarListItem* item);
- void setState(LLAvatarListItem* item, ESpeakerState state);
- void setState(const LLUUID& speaker_id, ESpeakerState state)
- {
- lldebugs << "Storing state: " << speaker_id << ", " << state << llendl;
- mSpeakerStateMap[speaker_id] = state;
- }
-
- ESpeakerState getState(const LLUUID& speaker_id)
- {
- lldebugs << "Getting state: " << speaker_id << ", " << mSpeakerStateMap[speaker_id] << llendl;
-
- return mSpeakerStateMap[speaker_id];
- }
-
- /**
- * Instantiates new LLAvatarListItemRemoveTimer and adds it into the map if it is not already created.
- *
- * @param voice_speaker_id LLUUID of Avatar List item to be removed from the list when timer expires.
- */
- void setVoiceRemoveTimer(const LLUUID& voice_speaker_id);
-
- /**
- * Removes specified by UUID Avatar List item.
- *
- * @param voice_speaker_id LLUUID of Avatar List item to be removed from the list.
- */
- bool removeVoiceLeftParticipant(const LLUUID& voice_speaker_id);
-
- /**
- * Deletes all timers from the list to prevent started timers from ticking after destruction
- * and after switching on another voice channel.
- */
- void resetVoiceRemoveTimers();
-
- /**
- * Removes specified by UUID timer from the map.
- *
- * @param voice_speaker_id LLUUID of Avatar List item whose timer should be removed from the map.
- */
- void removeVoiceRemoveTimer(const LLUUID& voice_speaker_id);
-
- /**
- * Called by LLParticipantList before adding a speaker to the participant list.
- *
- * If false is returned, the speaker will not be added to the list.
- *
- * @param speaker_id Speaker to validate.
- * @return true if this is a valid speaker, false otherwise.
- */
- bool validateSpeaker(const LLUUID& speaker_id);
-
- /**
- * Connects to passed channel to be updated according to channel's voice states.
- */
- void connectToChannel(LLVoiceChannel* channel);
-
- /**
- * Callback to process changing of voice channel's states.
- */
- void onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state);
-
- /**
- * Updates floater according to passed channel's voice state.
- */
- void updateState(const LLVoiceChannel::EState& new_state);
-
- /**
- * Resets floater to be ready to show voice participants.
- *
- * Clears all data from the latest voice session.
- */
- void reset(const LLVoiceChannel::EState& new_state);
-
-private:
- speaker_state_map_t mSpeakerStateMap;
- LLSpeakerMgr* mSpeakerManager;
- LLParticipantList* mParticipants;
- LLAvatarList* mAvatarList;
- LLNonAvatarCaller* mNonAvatarCaller;
- EVoiceControls mVoiceType;
- LLPanel* mAgentPanel;
- LLOutputMonitorCtrl* mSpeakingIndicator;
- bool mIsModeratorMutedVoice;
-
- /**
- * Flag indicated that participants voice states should be initialized.
- *
- * It is used due to Avatar List has delayed refreshing after it content is changed.
- * Real initializing is performed when Avatar List is first time refreshed.
- *
- * @see onAvatarListRefreshed()
- * @see initParticipantsVoiceState()
- */
- bool mInitParticipantsVoiceState;
-
- boost::signals2::connection mAvatarListRefreshConnection;
-
-
- /**
- * time out speakers when they are not part of current session
- */
- LLSpeakersDelayActionsStorage* mSpeakerDelayRemover;
-
- /**
- * Stores reference to current voice channel.
- *
- * Is used to ignore voice channel changed callback for the same channel.
- *
- * @see sOnCurrentChannelChanged()
- */
- static LLVoiceChannel* sCurrentVoiceChannel;
-
- /* virtual */
- LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }
-
- boost::signals2::connection mVoiceChannelStateChangeConnection;
-};
-
-
-#endif //LL_LLCALLFLOATER_H
-
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
index 0d55c4429a..30306b230f 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"
@@ -703,9 +704,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
if(chat_notify)
{
// Look up the name of this agent for the notification
- LLAvatarNameCache::get(agent_id,
- boost::bind(&on_avatar_name_cache_notify,
- _1, _2, online, payload));
+ LLAvatarNameCache::get(agent_id,boost::bind(&on_avatar_name_cache_notify,_1, _2, online, payload));
}
mModifyMask |= LLFriendObserver::ONLINE;
@@ -722,13 +721,14 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id,
// Popup a notify box with online status of this agent
// Use display name only because this user is your friend
LLSD args;
- args["NAME"] = av_name.mDisplayName;
+ args["NAME"] = av_name.getDisplayName();
+ 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 +736,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.
@@ -867,7 +867,7 @@ bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship
{
LLAvatarName av_name;
LLAvatarNameCache::get( buddy_id, &av_name);
- buddy_map_t::value_type value(av_name.mDisplayName, buddy_id);
+ buddy_map_t::value_type value(av_name.getDisplayName(), buddy_id);
if(buddy->isOnline() && buddy->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
{
mMappable.insert(value);
@@ -890,7 +890,7 @@ bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* bud
{
LLAvatarName av_name;
LLAvatarNameCache::get(buddy_id, &av_name);
- mFullName = av_name.mDisplayName;
+ mFullName = av_name.getDisplayName();
buddy_map_t::value_type value(mFullName, buddy_id);
if(buddy->isOnline())
{
diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp
index 987651fc80..43757d0174 100644
--- a/indra/newview/llchannelmanager.cpp
+++ b/indra/newview/llchannelmanager.cpp
@@ -29,7 +29,8 @@
#include "llchannelmanager.h"
#include "llappviewer.h"
-#include "llnotificationstorage.h"
+#include "lldonotdisturbnotificationstorage.h"
+#include "llpersistentnotificationstorage.h"
#include "llviewercontrol.h"
#include "llviewerwindow.h"
#include "llrootview.h"
@@ -138,6 +139,9 @@ void LLChannelManager::onLoginCompleted()
}
LLPersistentNotificationStorage::getInstance()->loadNotifications();
+
+ LLDoNotDisturbNotificationStorage::getInstance()->initialize();
+ LLDoNotDisturbNotificationStorage::getInstance()->loadNotifications();
}
//--------------------------------------------------------------------------
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
index d6095cce07..7d0331757b 100644
--- a/indra/newview/llchatbar.cpp
+++ b/indra/newview/llchatbar.cpp
@@ -669,47 +669,3 @@ void LLChatBar::onCommitGesture(LLUICtrl* ctrl)
mGestureCombo->setFocus(FALSE);
}
}
-
-
-/* Cruft - global gChatHandler declared below has been commented out,
- so this class is never used. See similar code in llnearbychatbar.cpp
-class LLChatHandler : public LLCommandHandler
-{
-public:
- // not allowed from outside the app
- LLChatHandler() : 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;
- // Say mesg on channel
- std::string mesg = tokens[1].asString();
- send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
- }
- else
- {
- retval = false;
- // Tell us this is an unsupported SLurl.
- }
- }
- return retval;
- }
-};
-
-// Creating the object registers with the dispatcher.
-//LLChatHandler gChatHandler;
-cruft */
diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp
index 3e1fa1e49b..53926c1fef 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"
@@ -56,7 +58,7 @@
#include "llworld.h"
#include "lluiconstants.h"
#include "llstring.h"
-
+#include "llurlaction.h"
#include "llviewercontrol.h"
static LLDefaultChildRegistry::Register<LLChatHistory> r("chat_history");
@@ -112,7 +114,8 @@ public:
mMinUserNameWidth(0),
mUserNameFont(NULL),
mUserNameTextBox(NULL),
- mTimeBoxTextBox(NULL)
+ mTimeBoxTextBox(NULL),
+ mAvatarNameCacheConnection()
{}
static LLChatHistoryHeader* createInstance(const std::string& file_name)
@@ -126,6 +129,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)
@@ -145,8 +153,20 @@ 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()));
+ }
+ else if (level == "map")
+ {
+ std::string url = "secondlife://" + mObjectData["slurl"].asString();
+ LLUrlAction::showLocationOnMap(url);
+ }
+ else if (level == "teleport")
+ {
+ std::string url = "secondlife://" + mObjectData["slurl"].asString();
+ LLUrlAction::teleportToLocation(url);
}
+
}
void onAvatarIconContextMenuItemClicked(const LLSD& userdata)
@@ -287,8 +307,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)
@@ -420,31 +439,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;
@@ -559,6 +553,46 @@ private:
user_name->reshape(user_rect.getWidth() + delta_pos_x, user_rect.getHeight());
}
+ void fetchAvatarName()
+ {
+ if (mAvatarID.notNull())
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID,
+ boost::bind(&LLChatHistoryHeader::onAvatarNameCache, this, _1, _2));
+ }
+ }
+
+ void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
+ {
+ mAvatarNameCacheConnection.disconnect();
+
+ mFrom = av_name.getDisplayName();
+
+ LLTextBox* user_name = getChild<LLTextBox>("user_name");
+ user_name->setValue( LLSD(av_name.getDisplayName() ) );
+ user_name->setToolTip( av_name.getUserName() );
+
+ if (gSavedSettings.getBOOL("NameTagShowUsernames") &&
+ av_name.useDisplayNames() &&
+ !av_name.isDisplayNameDefault())
+ {
+ 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.getUserName(), FALSE, style_params_name);
+ }
+ setToolTip( av_name.getUserName() );
+ // name might have changed, update width
+ updateMinUserNameWidth();
+ }
+
protected:
LLHandle<LLView> mPopupMenuHandleAvatar;
LLHandle<LLView> mPopupMenuHandleObject;
@@ -575,6 +609,9 @@ protected:
const LLFontGL* mUserNameFont;
LLTextBox* mUserNameTextBox;
LLTextBox* mTimeBoxTextBox;
+
+private:
+ boost::signals2::connection mAvatarNameCacheConnection;
};
LLUICtrl* LLChatHistoryHeader::sInfoCtrl = NULL;
@@ -591,7 +628,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();
@@ -601,6 +639,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();
@@ -702,6 +748,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)
@@ -709,9 +756,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);
@@ -741,16 +789,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);
@@ -773,29 +828,60 @@ 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";
+ body_message_params.font.style = "ITALIC";
+ }
+
+ if(chat.mChatType == CHAT_TYPE_WHISPER)
+ {
+ body_message_params.font.style = "ITALIC";
+ }
+ else if(chat.mChatType == CHAT_TYPE_SHOUT)
+ {
+ body_message_params.font.style = "BOLD";
}
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;
+ }
- if (utf8str_trim(chat.mFromName).size() != 0)
+ // out the opening square bracket (if need)
+ if (square_brackets)
+ {
+ mEditor->appendText("[", prependNewLineState, body_message_params);
+ prependNewLineState = false;
+ }
+
+ // 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())
@@ -805,32 +891,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;
@@ -851,7 +952,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
@@ -880,41 +981,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();
@@ -931,6 +1007,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;
@@ -938,7 +1016,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() &&
@@ -953,13 +1031,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..a1a9463d43 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 "llfloaterimnearbychat.h"
#include "llviewercontrol.h"
#include "llagentdata.h"
@@ -81,23 +81,30 @@ public:
LLObjectHandler gObjectHandler;
//*******************************************************************************************************************
-//LLNearbyChatToastPanel
+//LLFloaterIMNearbyChatToastPanel
//*******************************************************************************************************************
-LLNearbyChatToastPanel* LLNearbyChatToastPanel::createInstance()
+LLFloaterIMNearbyChatToastPanel* LLFloaterIMNearbyChatToastPanel::createInstance()
{
- LLNearbyChatToastPanel* item = new LLNearbyChatToastPanel();
+ LLFloaterIMNearbyChatToastPanel* item = new LLFloaterIMNearbyChatToastPanel();
item->buildFromFile("panel_chat_item.xml");
item->setFollows(FOLLOWS_NONE);
return item;
}
-void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_parent )
+void LLFloaterIMNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_parent )
{
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();
@@ -114,12 +121,12 @@ void LLNearbyChatToastPanel::reshape (S32 width, S32 height, BOOL called_from_p
msg_text->setRect(msg_text_rect);
}
-BOOL LLNearbyChatToastPanel::postBuild()
+BOOL LLFloaterIMNearbyChatToastPanel::postBuild()
{
return LLPanel::postBuild();
}
-void LLNearbyChatToastPanel::addMessage(LLSD& notification)
+void LLFloaterIMNearbyChatToastPanel::addMessage(LLSD& notification)
{
std::string messageText = notification["message"].asString(); // UTF-8 line of text
@@ -171,7 +178,7 @@ void LLNearbyChatToastPanel::addMessage(LLSD& notification)
}
-void LLNearbyChatToastPanel::init(LLSD& notification)
+void LLFloaterIMNearbyChatToastPanel::init(LLSD& notification)
{
std::string messageText = notification["message"].asString(); // UTF-8 line of text
std::string fromName = notification["from"].asString(); // agent or object name
@@ -266,7 +273,7 @@ void LLNearbyChatToastPanel::init(LLSD& notification)
mIsDirty = true;//will set Avatar Icon in draw
}
-void LLNearbyChatToastPanel::snapToMessageHeight ()
+void LLFloaterIMNearbyChatToastPanel::snapToMessageHeight ()
{
LLChatMsgBox* text_box = getChild<LLChatMsgBox>("msg_text", false);
S32 new_height = llmax (text_box->getTextPixelHeight() + 2*text_box->getVPad() + 2*msg_height_pad, 25);
@@ -281,22 +288,22 @@ void LLNearbyChatToastPanel::snapToMessageHeight ()
}
-void LLNearbyChatToastPanel::onMouseLeave (S32 x, S32 y, MASK mask)
+void LLFloaterIMNearbyChatToastPanel::onMouseLeave (S32 x, S32 y, MASK mask)
{
}
-void LLNearbyChatToastPanel::onMouseEnter (S32 x, S32 y, MASK mask)
+void LLFloaterIMNearbyChatToastPanel::onMouseEnter (S32 x, S32 y, MASK mask)
{
if(mSourceType != CHAT_SOURCE_AGENT)
return;
}
-BOOL LLNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask)
+BOOL LLFloaterIMNearbyChatToastPanel::handleMouseDown (S32 x, S32 y, MASK mask)
{
return LLPanel::handleMouseDown(x,y,mask);
}
-BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask)
+BOOL LLFloaterIMNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask)
{
/*
fix for request EXT-4780
@@ -316,16 +323,16 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask)
return TRUE;
else
{
- LLNearbyChatBar::getInstance()->showHistory();
+ (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->showHistory();
return FALSE;
}
}
- LLNearbyChatBar::getInstance()->showHistory();
+ (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->showHistory();
return LLPanel::handleMouseUp(x,y,mask);
}
-void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e)
+void LLFloaterIMNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e)
{
LLUICtrl* icon = getChild<LLUICtrl>("avatar_icon", false);
if(icon)
@@ -333,7 +340,7 @@ void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e)
}
-bool LLNearbyChatToastPanel::canAddText ()
+bool LLFloaterIMNearbyChatToastPanel::canAddText ()
{
LLChatMsgBox* msg_text = findChild<LLChatMsgBox>("msg_text");
if(!msg_text)
@@ -341,7 +348,7 @@ bool LLNearbyChatToastPanel::canAddText ()
return msg_text->getLineCount()<10;
}
-BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask)
+BOOL LLFloaterIMNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
LLUICtrl* avatar_icon = getChild<LLUICtrl>("avatar_icon", false);
@@ -353,8 +360,10 @@ BOOL LLNearbyChatToastPanel::handleRightMouseDown(S32 x, S32 y, MASK mask)
return TRUE;
return LLPanel::handleRightMouseDown(x,y,mask);
}
-void LLNearbyChatToastPanel::draw()
+void LLFloaterIMNearbyChatToastPanel::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..54b6499d52 100644
--- a/indra/newview/llchatitemscontainerctrl.h
+++ b/indra/newview/llchatitemscontainerctrl.h
@@ -40,18 +40,18 @@ typedef enum e_show_item_header
CHATITEMHEADER_SHOW_BOTH
} EShowItemHeader;
-class LLNearbyChatToastPanel: public LLToastPanelBase
+class LLFloaterIMNearbyChatToastPanel : public LLPanel
{
protected:
- LLNearbyChatToastPanel()
+ LLFloaterIMNearbyChatToastPanel()
:
mIsDirty(false),
mSourceType(CHAT_SOURCE_OBJECT)
{};
public:
- ~LLNearbyChatToastPanel(){}
+ ~LLFloaterIMNearbyChatToastPanel(){}
- static LLNearbyChatToastPanel* createInstance();
+ static LLFloaterIMNearbyChatToastPanel* createInstance();
const LLUUID& getFromID() const { return mFromID;}
const std::string& getFromName() const { return mFromName; }
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index a661808d1f..b221daf936 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -27,35 +27,17 @@
#include "llviewerprecompiledheaders.h" // must be first include
#include "llchiclet.h"
-#include "llagent.h"
-#include "llavataractions.h"
#include "llchicletbar.h"
-#include "lleventtimer.h"
-#include "llgroupactions.h"
-#include "lliconctrl.h"
-#include "llimfloater.h"
-#include "llimview.h"
+#include "llfloaterimsession.h"
+#include "llfloaterimcontainer.h"
#include "llfloaterreg.h"
#include "lllocalcliprect.h"
-#include "llmenugl.h"
-#include "llnotifications.h"
-#include "llnotificationsutil.h"
-#include "lloutputmonitorctrl.h"
#include "llscriptfloater.h"
-#include "llspeakers.h"
-#include "lltextbox.h"
-#include "llvoiceclient.h"
-#include "llgroupmgr.h"
-#include "llnotificationmanager.h"
-#include "lltransientfloatermgr.h"
+#include "llsingleton.h"
#include "llsyswellwindow.h"
static LLDefaultChildRegistry::Register<LLChicletPanel> t1("chiclet_panel");
-static LLDefaultChildRegistry::Register<LLIMWellChiclet> t2_0("chiclet_im_well");
static LLDefaultChildRegistry::Register<LLNotificationChiclet> t2("chiclet_notification");
-static LLDefaultChildRegistry::Register<LLIMP2PChiclet> t3("chiclet_im_p2p");
-static LLDefaultChildRegistry::Register<LLIMGroupChiclet> t4("chiclet_im_group");
-static LLDefaultChildRegistry::Register<LLAdHocChiclet> t5("chiclet_im_adhoc");
static LLDefaultChildRegistry::Register<LLScriptChiclet> t6("chiclet_script");
static LLDefaultChildRegistry::Register<LLInvOfferChiclet> t7("chiclet_offer");
@@ -66,65 +48,10 @@ boost::signals2::signal<LLChiclet* (const LLUUID&),
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-/**
- * Updates the Well's 'Lit' state to flash it when "new messages" are come.
- *
- * It gets callback which will be called 2*N times with passed period. See EXT-3147
- */
-class LLSysWellChiclet::FlashToLitTimer : public LLEventTimer
-{
-public:
- typedef boost::function<void()> callback_t;
-
- /**
- * Constructor.
- *
- * @param count - how many times callback should be called (twice to not change original state)
- * @param period - how frequently callback should be called
- * @param cb - callback to be called each tick
- */
- FlashToLitTimer(S32 count, F32 period, callback_t cb)
- : LLEventTimer(period)
- , mCallback(cb)
- , mFlashCount(2 * count)
- , mCurrentFlashCount(0)
- {
- mEventTimer.stop();
- }
-
- BOOL tick()
- {
- mCallback();
-
- if (++mCurrentFlashCount == mFlashCount) mEventTimer.stop();
- return FALSE;
- }
-
- void flash()
- {
- mCurrentFlashCount = 0;
- mEventTimer.start();
- }
-
- void stopFlashing()
- {
- mEventTimer.stop();
- }
-
-private:
- callback_t mCallback;
-
- /**
- * How many times Well will blink.
- */
- S32 mFlashCount;
- S32 mCurrentFlashCount;
-};
-
LLSysWellChiclet::Params::Params()
-: button("button")
-, unread_notifications("unread_notifications")
-, max_displayed_count("max_displayed_count", 99)
+ : button("button")
+ , unread_notifications("unread_notifications")
+ , max_displayed_count("max_displayed_count", 99)
{
button.name = "button";
button.tab_stop = FALSE;
@@ -132,30 +59,24 @@ LLSysWellChiclet::Params::Params()
}
LLSysWellChiclet::LLSysWellChiclet(const Params& p)
-: LLChiclet(p)
-, mButton(NULL)
-, mCounter(0)
-, mMaxDisplayedCount(p.max_displayed_count)
-, mIsNewMessagesState(false)
-, mFlashToLitTimer(NULL)
-, mContextMenu(NULL)
+ : LLChiclet(p)
+ , mButton(NULL)
+ , mCounter(0)
+ , mMaxDisplayedCount(p.max_displayed_count)
+ , mIsNewMessagesState(false)
+ , mFlashToLitTimer(NULL)
+ , mContextMenu(NULL)
{
LLButton::Params button_params = p.button;
mButton = LLUICtrlFactory::create<LLButton>(button_params);
addChild(mButton);
- // use settings from settings.xml to be able change them via Debug settings. See EXT-5973.
- // Due to Timer is implemented as derived class from EventTimer it is impossible to change period
- // in runtime. So, both settings are made as required restart.
- static S32 flash_to_lit_count = gSavedSettings.getS32("WellIconFlashCount");
- static F32 flash_period = gSavedSettings.getF32("WellIconFlashPeriod");
-
- mFlashToLitTimer = new FlashToLitTimer(flash_to_lit_count, flash_period, boost::bind(&LLSysWellChiclet::changeLitState, this));
+ mFlashToLitTimer = new LLFlashTimer(boost::bind(&LLSysWellChiclet::changeLitState, this, _1));
}
LLSysWellChiclet::~LLSysWellChiclet()
{
- delete mFlashToLitTimer;
+ mFlashToLitTimer->unset();
}
void LLSysWellChiclet::setCounter(S32 counter)
@@ -190,7 +111,7 @@ void LLSysWellChiclet::setToggleState(BOOL toggled) {
mButton->setToggleState(toggled);
}
-void LLSysWellChiclet::changeLitState()
+void LLSysWellChiclet::changeLitState(bool blink)
{
setNewMessagesState(!mIsNewMessagesState);
}
@@ -235,130 +156,18 @@ BOOL LLSysWellChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
}
/************************************************************************/
-/* LLIMWellChiclet implementation */
-/************************************************************************/
-LLIMWellChiclet::LLIMWellChiclet(const Params& p)
-: LLSysWellChiclet(p)
-{
- LLIMModel::instance().addNewMsgCallback(boost::bind(&LLIMWellChiclet::messageCountChanged, this, _1));
- LLIMModel::instance().addNoUnreadMsgsCallback(boost::bind(&LLIMWellChiclet::messageCountChanged, this, _1));
-
- LLIMMgr::getInstance()->addSessionObserver(this);
-
- LLIMWellWindow::getInstance()->setSysWellChiclet(this);
-}
-
-LLIMWellChiclet::~LLIMWellChiclet()
-{
- LLIMWellWindow* im_well_window = LLIMWellWindow::findInstance();
- if (im_well_window)
- {
- im_well_window->setSysWellChiclet(NULL);
- }
-
- LLIMMgr::getInstance()->removeSessionObserver(this);
-}
-
-void LLIMWellChiclet::onMenuItemClicked(const LLSD& user_data)
-{
- std::string action = user_data.asString();
- if("close all" == action)
- {
- LLIMWellWindow::getInstance()->closeAll();
- }
-}
-
-bool LLIMWellChiclet::enableMenuItem(const LLSD& user_data)
-{
- std::string item = user_data.asString();
- if (item == "can close all")
- {
- return !LLIMWellWindow::getInstance()->isWindowEmpty();
- }
- return true;
-}
-
-void LLIMWellChiclet::createMenu()
-{
- if(mContextMenu)
- {
- llwarns << "Menu already exists" << llendl;
- return;
- }
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("IMWellChicletMenu.Action",
- boost::bind(&LLIMWellChiclet::onMenuItemClicked, this, _2));
-
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
- enable_registrar.add("IMWellChicletMenu.EnableItem",
- boost::bind(&LLIMWellChiclet::enableMenuItem, this, _2));
-
- mContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>
- ("menu_im_well_button.xml",
- LLMenuGL::sMenuContainer,
- LLViewerMenuHolderGL::child_registry_t::instance());
-}
-
-void LLIMWellChiclet::messageCountChanged(const LLSD& session_data)
-{
- // The singleton class LLChicletBar instance might be already deleted
- // so don't create a new one.
- if (!LLChicletBar::instanceExists())
- {
- return;
- }
-
- const LLUUID& session_id = session_data["session_id"];
- const S32 counter = LLChicletBar::getInstance()->getTotalUnreadIMCount();
- const bool im_not_visible = !LLFloaterReg::instanceVisible("im_container")
- && !LLFloaterReg::instanceVisible("impanel", session_id);
-
- setNewMessagesState(counter > mCounter && im_not_visible);
-
- // we have to flash to 'Lit' state each time new unread message is coming.
- if (counter > mCounter && im_not_visible)
- {
- mFlashToLitTimer->flash();
- }
- else if (counter == 0)
- {
- // if notification is resolved while well is flashing it can leave in the 'Lit' state
- // when flashing finishes itself. Let break flashing here.
- mFlashToLitTimer->stopFlashing();
- }
-
- setCounter(counter);
-}
-
-/************************************************************************/
/* 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 +216,23 @@ void LLNotificationChiclet::setCounter(S32 counter)
updateWidget(getCounter() == 0);
}
+
+bool LLNotificationChiclet::ChicletNotificationChannel::filterNotification( LLNotificationPtr notification )
+{
+ if (notification->getName() == "ScriptDialog")
+ {
+ return false;
+ }
+
+ if( !(notification->canLogToIM() && notification->hasFormElements())
+ && (!notification->getPayload().has("give_inventory_notification")
+ || notification->getPayload()["give_inventory_notification"]))
+ {
+ return true;
+ }
+ return false;
+}
+
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -422,12 +248,6 @@ LLChiclet::LLChiclet(const Params& p)
, mSessionId(LLUUID::null)
, mShowCounter(p.show_counter)
{
-
-}
-
-LLChiclet::~LLChiclet()
-{
-
}
boost::signals2::connection LLChiclet::setLeftButtonClickCallback(
@@ -462,7 +282,9 @@ LLSD LLChiclet::getValue() const
void LLChiclet::setValue(const LLSD& value)
{
if(value.isUUID())
+ {
setSessionId(value.asUUID());
+ }
}
//////////////////////////////////////////////////////////////////////////
@@ -474,12 +296,9 @@ LLIMChiclet::LLIMChiclet(const LLIMChiclet::Params& p)
, mShowSpeaker(false)
, mDefaultWidth(p.rect().getWidth())
, mNewMessagesIcon(NULL)
-, mSpeakerCtrl(NULL)
-, mCounterCtrl(NULL)
, mChicletButton(NULL)
, mPopupMenu(NULL)
{
- enableCounterControl(p.enable_counter);
}
/* virtual*/
@@ -490,16 +309,6 @@ BOOL LLIMChiclet::postBuild()
mChicletButton->setDoubleClickCallback(boost::bind(&LLIMChiclet::onMouseDown, this));
return TRUE;
}
-void LLIMChiclet::setShowSpeaker(bool show)
-{
- bool needs_resize = getShowSpeaker() != show;
- if(needs_resize)
- {
- mShowSpeaker = show;
- }
-
- toggleSpeakerControl();
-}
void LLIMChiclet::enableCounterControl(bool enable)
{
@@ -510,87 +319,13 @@ void LLIMChiclet::enableCounterControl(bool enable)
}
}
-void LLIMChiclet::setShowCounter(bool show)
-{
- if(!mCounterEnabled)
- {
- return;
- }
-
- bool needs_resize = getShowCounter() != show;
- if(needs_resize)
- {
- LLChiclet::setShowCounter(show);
- toggleCounterControl();
- }
-}
-
-void LLIMChiclet::initSpeakerControl()
-{
- // virtual
-}
-
void LLIMChiclet::setRequiredWidth()
{
- bool show_speaker = getShowSpeaker();
- bool show_counter = getShowCounter();
S32 required_width = mDefaultWidth;
-
- if (show_counter)
- {
- required_width += mCounterCtrl->getRect().getWidth();
- }
- if (show_speaker)
- {
- required_width += mSpeakerCtrl->getRect().getWidth();
- }
-
reshape(required_width, getRect().getHeight());
-
onChicletSizeChanged();
}
-void LLIMChiclet::toggleSpeakerControl()
-{
- if(getShowSpeaker())
- {
- // move speaker to the right of chiclet icon
- LLRect speaker_rc = mSpeakerCtrl->getRect();
- speaker_rc.setLeftTopAndSize(mDefaultWidth, speaker_rc.mTop, speaker_rc.getWidth(), speaker_rc.getHeight());
- mSpeakerCtrl->setRect(speaker_rc);
-
- if(getShowCounter())
- {
- // move speaker to the right of counter
- mSpeakerCtrl->translate(mCounterCtrl->getRect().getWidth(), 0);
- }
-
- initSpeakerControl();
- }
-
- setRequiredWidth();
- mSpeakerCtrl->setSpeakerId(LLUUID::null);
- mSpeakerCtrl->setVisible(getShowSpeaker());
-}
-
-void LLIMChiclet::setCounter(S32 counter)
-{
- if (mCounterCtrl->getCounter() == counter)
- {
- return;
- }
-
- mCounterCtrl->setCounter(counter);
- setShowCounter(counter);
- setShowNewMessagesIcon(counter);
-}
-
-void LLIMChiclet::toggleCounterControl()
-{
- setRequiredWidth();
- mCounterCtrl->setVisible(getShowCounter());
-}
-
void LLIMChiclet::setShowNewMessagesIcon(bool show)
{
if(mNewMessagesIcon)
@@ -607,8 +342,7 @@ bool LLIMChiclet::getShowNewMessagesIcon()
void LLIMChiclet::onMouseDown()
{
- LLIMFloater::toggle(getSessionId());
- setCounter(0);
+ LLFloaterIMSession::toggle(getSessionId());
}
void LLIMChiclet::setToggleState(bool toggle)
@@ -616,52 +350,6 @@ void LLIMChiclet::setToggleState(bool toggle)
mChicletButton->setToggleState(toggle);
}
-void LLIMChiclet::draw()
-{
- LLUICtrl::draw();
-}
-
-// static
-LLIMChiclet::EType LLIMChiclet::getIMSessionType(const LLUUID& session_id)
-{
- EType type = TYPE_UNKNOWN;
-
- if(session_id.isNull())
- return type;
-
- EInstantMessage im_type = LLIMModel::getInstance()->getType(session_id);
- if (IM_COUNT == im_type)
- {
- llassert_always(0 && "IM session not found"); // should never happen
- return type;
- }
-
- switch(im_type)
- {
- case IM_NOTHING_SPECIAL:
- case IM_SESSION_P2P_INVITE:
- type = TYPE_IM;
- break;
- case IM_SESSION_GROUP_START:
- case IM_SESSION_INVITE:
- if (gAgent.isInGroup(session_id))
- {
- type = TYPE_GROUP;
- }
- else
- {
- type = TYPE_AD_HOC;
- }
- break;
- case IM_SESSION_CONFERENCE_START:
- type = TYPE_AD_HOC;
- default:
- break;
- }
-
- return type;
-}
-
BOOL LLIMChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
if(!mPopupMenu)
@@ -697,382 +385,6 @@ bool LLIMChiclet::canCreateMenu()
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-LLIMP2PChiclet::Params::Params()
-: avatar_icon("avatar_icon")
-, chiclet_button("chiclet_button")
-, unread_notifications("unread_notifications")
-, speaker("speaker")
-, new_message_icon("new_message_icon")
-, show_speaker("show_speaker")
-{
-}
-
-LLIMP2PChiclet::LLIMP2PChiclet(const Params& p)
-: LLIMChiclet(p)
-, mChicletIconCtrl(NULL)
-{
- LLButton::Params button_params = p.chiclet_button;
- mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
- addChild(mChicletButton);
-
- LLIconCtrl::Params new_msg_params = p.new_message_icon;
- mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
- addChild(mNewMessagesIcon);
-
- LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
- mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
- addChild(mChicletIconCtrl);
-
- LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
- mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
- addChild(mCounterCtrl);
-
- setCounter(getCounter());
- setShowCounter(getShowCounter());
-
- LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
- mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
- addChild(mSpeakerCtrl);
-
- sendChildToFront(mNewMessagesIcon);
- setShowSpeaker(p.show_speaker);
-}
-
-void LLIMP2PChiclet::initSpeakerControl()
-{
- mSpeakerCtrl->setSpeakerId(getOtherParticipantId());
-}
-
-void LLIMP2PChiclet::setOtherParticipantId(const LLUUID& other_participant_id)
-{
- LLIMChiclet::setOtherParticipantId(other_participant_id);
- mChicletIconCtrl->setValue(getOtherParticipantId());
-}
-
-void LLIMP2PChiclet::updateMenuItems()
-{
- if(!mPopupMenu)
- return;
- if(getSessionId().isNull())
- return;
-
- LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId());
- bool open_window_exists = open_im_floater && open_im_floater->getVisible();
- mPopupMenu->getChild<LLUICtrl>("Send IM")->setEnabled(!open_window_exists);
-
- bool is_friend = LLAvatarActions::isFriend(getOtherParticipantId());
- mPopupMenu->getChild<LLUICtrl>("Add Friend")->setEnabled(!is_friend);
-}
-
-void LLIMP2PChiclet::createPopupMenu()
-{
- if(!canCreateMenu())
- return;
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("IMChicletMenu.Action", boost::bind(&LLIMP2PChiclet::onMenuItemClicked, this, _2));
-
- mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
- ("menu_imchiclet_p2p.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-}
-
-void LLIMP2PChiclet::onMenuItemClicked(const LLSD& user_data)
-{
- std::string level = user_data.asString();
- LLUUID other_participant_id = getOtherParticipantId();
-
- if("profile" == level)
- {
- LLAvatarActions::showProfile(other_participant_id);
- }
- else if("im" == level)
- {
- LLAvatarActions::startIM(other_participant_id);
- }
- else if("add" == level)
- {
- LLAvatarActions::requestFriendshipDialog(other_participant_id);
- }
- else if("end" == level)
- {
- LLAvatarActions::endIM(other_participant_id);
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-LLAdHocChiclet::Params::Params()
-: avatar_icon("avatar_icon")
-, chiclet_button("chiclet_button")
-, unread_notifications("unread_notifications")
-, speaker("speaker")
-, new_message_icon("new_message_icon")
-, show_speaker("show_speaker")
-, avatar_icon_color("avatar_icon_color", LLColor4::green)
-{
-}
-
-LLAdHocChiclet::LLAdHocChiclet(const Params& p)
-: LLIMChiclet(p)
-, mChicletIconCtrl(NULL)
-{
- LLButton::Params button_params = p.chiclet_button;
- mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
- addChild(mChicletButton);
-
- LLIconCtrl::Params new_msg_params = p.new_message_icon;
- mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
- addChild(mNewMessagesIcon);
-
- LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
- mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
- //Make the avatar modified
- mChicletIconCtrl->setColor(p.avatar_icon_color);
- addChild(mChicletIconCtrl);
-
- LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
- mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
- addChild(mCounterCtrl);
-
- setCounter(getCounter());
- setShowCounter(getShowCounter());
-
- LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
- mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
- addChild(mSpeakerCtrl);
-
- sendChildToFront(mNewMessagesIcon);
- setShowSpeaker(p.show_speaker);
-}
-
-void LLAdHocChiclet::setSessionId(const LLUUID& session_id)
-{
- LLChiclet::setSessionId(session_id);
- LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
- mChicletIconCtrl->setValue(im_session->mOtherParticipantID);
-}
-
-void LLAdHocChiclet::draw()
-{
- switchToCurrentSpeaker();
- LLIMChiclet::draw();
-}
-
-void LLAdHocChiclet::initSpeakerControl()
-{
- switchToCurrentSpeaker();
-}
-
-void LLAdHocChiclet::switchToCurrentSpeaker()
-{
- LLUUID speaker_id;
- LLSpeakerMgr::speaker_list_t speaker_list;
-
- LLIMModel::getInstance()->findIMSession(getSessionId())->mSpeakers->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)
- {
- speaker_id = s->mID;
- break;
- }
- }
-
- mSpeakerCtrl->setSpeakerId(speaker_id);
-}
-
-void LLAdHocChiclet::createPopupMenu()
-{
- if(!canCreateMenu())
- return;
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("IMChicletMenu.Action", boost::bind(&LLAdHocChiclet::onMenuItemClicked, this, _2));
-
- mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
- ("menu_imchiclet_adhoc.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-}
-
-void LLAdHocChiclet::onMenuItemClicked(const LLSD& user_data)
-{
- std::string level = user_data.asString();
- LLUUID group_id = getSessionId();
-
- if("end" == level)
- {
- LLGroupActions::endIM(group_id);
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-LLIMGroupChiclet::Params::Params()
-: group_icon("group_icon")
-, chiclet_button("chiclet_button")
-, unread_notifications("unread_notifications")
-, speaker("speaker")
-, new_message_icon("new_message_icon")
-, show_speaker("show_speaker")
-{
-}
-
-LLIMGroupChiclet::LLIMGroupChiclet(const Params& p)
-: LLIMChiclet(p)
-, LLGroupMgrObserver(LLUUID::null)
-, mChicletIconCtrl(NULL)
-{
- LLButton::Params button_params = p.chiclet_button;
- mChicletButton = LLUICtrlFactory::create<LLButton>(button_params);
- addChild(mChicletButton);
-
- LLIconCtrl::Params new_msg_params = p.new_message_icon;
- mNewMessagesIcon = LLUICtrlFactory::create<LLIconCtrl>(new_msg_params);
- addChild(mNewMessagesIcon);
-
- LLChicletGroupIconCtrl::Params avatar_params = p.group_icon;
- mChicletIconCtrl = LLUICtrlFactory::create<LLChicletGroupIconCtrl>(avatar_params);
- addChild(mChicletIconCtrl);
-
- LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
- mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
- addChild(mCounterCtrl);
-
- setCounter(getCounter());
- setShowCounter(getShowCounter());
-
- LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
- mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
- addChild(mSpeakerCtrl);
-
- sendChildToFront(mNewMessagesIcon);
- setShowSpeaker(p.show_speaker);
-}
-
-LLIMGroupChiclet::~LLIMGroupChiclet()
-{
- LLGroupMgr::getInstance()->removeObserver(this);
-}
-
-void LLIMGroupChiclet::draw()
-{
- if(getShowSpeaker())
- {
- switchToCurrentSpeaker();
- }
- LLIMChiclet::draw();
-}
-
-void LLIMGroupChiclet::initSpeakerControl()
-{
- switchToCurrentSpeaker();
-}
-
-void LLIMGroupChiclet::switchToCurrentSpeaker()
-{
- LLUUID speaker_id;
- LLSpeakerMgr::speaker_list_t speaker_list;
-
- LLIMModel::getInstance()->findIMSession(getSessionId())->mSpeakers->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)
- {
- speaker_id = s->mID;
- break;
- }
- }
-
- mSpeakerCtrl->setSpeakerId(speaker_id);
-}
-
-void LLIMGroupChiclet::setSessionId(const LLUUID& session_id)
-{
- LLChiclet::setSessionId(session_id);
-
- LLGroupMgr* grp_mgr = LLGroupMgr::getInstance();
- LLGroupMgrGroupData* group_data = grp_mgr->getGroupData(session_id);
- if (group_data && group_data->mInsigniaID.notNull())
- {
- mChicletIconCtrl->setValue(group_data->mInsigniaID);
- }
- else
- {
- if(getSessionId() != mID)
- {
- grp_mgr->removeObserver(this);
- mID = getSessionId();
- grp_mgr->addObserver(this);
- }
- grp_mgr->sendGroupPropertiesRequest(session_id);
- }
-}
-
-void LLIMGroupChiclet::changed(LLGroupChange gc)
-{
- if (GC_PROPERTIES == gc)
- {
- LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(getSessionId());
- if (group_data)
- {
- mChicletIconCtrl->setValue(group_data->mInsigniaID);
- }
- }
-}
-
-void LLIMGroupChiclet::updateMenuItems()
-{
- if(!mPopupMenu)
- return;
- if(getSessionId().isNull())
- return;
-
- LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId());
- bool open_window_exists = open_im_floater && open_im_floater->getVisible();
- mPopupMenu->getChild<LLUICtrl>("Chat")->setEnabled(!open_window_exists);
-}
-
-void LLIMGroupChiclet::createPopupMenu()
-{
- if(!canCreateMenu())
- return;
-
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- registrar.add("IMChicletMenu.Action", boost::bind(&LLIMGroupChiclet::onMenuItemClicked, this, _2));
-
- mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>
- ("menu_imchiclet_group.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
-}
-
-void LLIMGroupChiclet::onMenuItemClicked(const LLSD& user_data)
-{
- std::string level = user_data.asString();
- LLUUID group_id = getSessionId();
-
- if("group chat" == level)
- {
- LLGroupActions::startIM(group_id);
- }
- else if("info" == level)
- {
- LLGroupActions::show(group_id);
- }
- else if("end" == level)
- {
- LLGroupActions::endIM(group_id);
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
LLChicletPanel::Params::Params()
: chiclet_padding("chiclet_padding")
, scrolling_offset("scrolling_offset")
@@ -1115,28 +427,17 @@ LLChicletPanel::~LLChicletPanel()
void LLChicletPanel::onMessageCountChanged(const LLSD& data)
{
+ // *TODO : we either suppress this method or return a value. Right now, it servers no purpose.
+ /*
LLUUID session_id = data["session_id"].asUUID();
S32 unread = data["participant_unread"].asInteger();
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if (im_floater && im_floater->getVisible() && im_floater->hasFocus())
{
unread = 0;
}
-
- std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(session_id);
- std::list<LLChiclet *>::iterator iter;
- for (iter = chiclets.begin(); iter != chiclets.end(); iter++) {
- LLChiclet* chiclet = *iter;
- if (chiclet != NULL)
- {
- chiclet->setCounter(unread);
- }
- else
- {
- llwarns << "Unable to set counter for chiclet " << session_id << llendl;
- }
- }
+ */
}
void LLChicletPanel::objectChicletCallback(const LLSD& data)
@@ -1151,10 +452,6 @@ void LLChicletPanel::objectChicletCallback(const LLSD& data)
LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*iter);
if (chiclet != NULL)
{
- if(data.has("unread"))
- {
- chiclet->setCounter(data["unread"]);
- }
chiclet->setShowNewMessagesIcon(new_message);
}
}
@@ -1196,28 +493,13 @@ void LLChicletPanel::onCurrentVoiceChannelChanged(const LLUUID& session_id)
LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
if(chiclet)
{
- chiclet->setShowSpeaker(true);
if (gSavedSettings.getBOOL("OpenIMOnVoice"))
{
- LLIMFloater::show(chiclet->getSessionId());
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
}
}
}
- if(!s_previous_active_voice_session_id.isNull() && s_previous_active_voice_session_id != session_id)
- {
- chiclets = LLIMChiclet::sFindChicletsSignal(s_previous_active_voice_session_id);
-
- for(std::list<LLChiclet *>::iterator it = chiclets.begin(); it != chiclets.end(); ++it)
- {
- LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
- if(chiclet)
- {
- chiclet->setShowSpeaker(false);
- }
- }
- }
-
s_previous_active_voice_session_id = session_id;
}
@@ -1690,7 +972,7 @@ bool LLChicletPanel::isAnyIMFloaterDoked()
for (chiclet_list_t::iterator it = mChicletList.begin(); it
!= mChicletList.end(); it++)
{
- LLIMFloater* im_floater = LLFloaterReg::findTypedInstance<LLIMFloater>(
+ LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance<LLFloaterIMSession>(
"impanel", (*it)->getSessionId());
if (im_floater != NULL && im_floater->getVisible()
&& !im_floater->isMinimized() && im_floater->isDocked())
@@ -1703,89 +985,17 @@ bool LLChicletPanel::isAnyIMFloaterDoked()
return res;
}
-S32 LLChicletPanel::getTotalUnreadIMCount()
-{
- S32 count = 0;
- chiclet_list_t::const_iterator it = mChicletList.begin();
- for( ; mChicletList.end() != it; ++it)
- {
- LLIMChiclet* chiclet = dynamic_cast<LLIMChiclet*>(*it);
- if(chiclet)
- {
- count += chiclet->getCounter();
- }
- }
- return count;
-}
-
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
LLChicletNotificationCounterCtrl::Params::Params()
-: max_displayed_count("max_displayed_count", 99)
-{
-}
-
-LLChicletNotificationCounterCtrl::LLChicletNotificationCounterCtrl(const Params& p)
- : LLTextBox(p)
- , mCounter(0)
- , mInitialWidth(0)
- , mMaxDisplayedCount(p.max_displayed_count)
-{
- mInitialWidth = getRect().getWidth();
-}
-
-void LLChicletNotificationCounterCtrl::setCounter(S32 counter)
-{
- mCounter = counter;
-
- // note same code in LLSysWellChiclet::setCounter(S32 counter)
- std::string s_count;
- if(counter != 0)
- {
- static std::string more_messages_exist("+");
- std::string more_messages(counter > mMaxDisplayedCount ? more_messages_exist : "");
- s_count = llformat("%d%s"
- , llmin(counter, mMaxDisplayedCount)
- , more_messages.c_str()
- );
- }
-
- if(mCounter != 0)
- {
- setText(s_count);
- }
- else
- {
- setText(std::string(""));
- }
-}
-
-LLRect LLChicletNotificationCounterCtrl::getRequiredRect()
-{
- LLRect rc;
- S32 text_width = getTextPixelWidth();
-
- rc.mRight = rc.mLeft + llmax(text_width, mInitialWidth);
-
- return rc;
-}
-
-void LLChicletNotificationCounterCtrl::setValue(const LLSD& value)
+ : max_displayed_count("max_displayed_count", 99)
{
- if(value.isInteger())
- setCounter(value.asInteger());
-}
-
-LLSD LLChicletNotificationCounterCtrl::getValue() const
-{
- return LLSD(getCounter());
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-
LLChicletAvatarIconCtrl::LLChicletAvatarIconCtrl(const Params& p)
: LLAvatarIconCtrl(p)
{
@@ -1795,29 +1005,6 @@ LLChicletAvatarIconCtrl::LLChicletAvatarIconCtrl(const Params& p)
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-LLChicletGroupIconCtrl::LLChicletGroupIconCtrl(const Params& p)
-: LLIconCtrl(p)
-, mDefaultIcon(p.default_icon)
-{
- setValue(LLUUID::null);
-}
-
-void LLChicletGroupIconCtrl::setValue(const LLSD& value )
-{
- if(value.asUUID().isNull())
- {
- LLIconCtrl::setValue(mDefaultIcon);
- }
- else
- {
- LLIconCtrl::setValue(value);
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
LLChicletInvOfferIconCtrl::LLChicletInvOfferIconCtrl(const Params& p)
: LLChicletAvatarIconCtrl(p)
, mDefaultIcon(p.default_icon)
@@ -1840,15 +1027,6 @@ void LLChicletInvOfferIconCtrl::setValue(const LLSD& value )
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
-LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p)
- : LLOutputMonitorCtrl(p)
-{
-}
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
LLScriptChiclet::Params::Params()
: icon("icon")
, chiclet_button("chiclet_button")
@@ -1884,11 +1062,6 @@ void LLScriptChiclet::setSessionId(const LLUUID& session_id)
setToolTip(LLScriptFloaterManager::getObjectName(session_id));
}
-void LLScriptChiclet::setCounter(S32 counter)
-{
- setShowNewMessagesIcon( counter > 0 );
-}
-
void LLScriptChiclet::onMouseDown()
{
LLScriptFloaterManager::getInstance()->toggleScriptFloater(getSessionId());
@@ -1967,11 +1140,6 @@ void LLInvOfferChiclet::setSessionId(const LLUUID& session_id)
}
}
-void LLInvOfferChiclet::setCounter(S32 counter)
-{
- setShowNewMessagesIcon( counter > 0 );
-}
-
void LLInvOfferChiclet::onMouseDown()
{
LLScriptFloaterManager::instance().toggleScriptFloater(getSessionId());
diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h
index 19683492c2..efaf03384a 100644
--- a/indra/newview/llchiclet.h
+++ b/indra/newview/llchiclet.h
@@ -29,14 +29,11 @@
#include "llavatariconctrl.h"
#include "llbutton.h"
-#include "llpanel.h"
+#include "llnotifications.h"
#include "lltextbox.h"
-#include "lloutputmonitorctrl.h"
-#include "llgroupmgr.h"
-#include "llimview.h"
class LLMenuGL;
-class LLIMFloater;
+class LLFloaterIMSession;
/**
* Class for displaying amount of messages/notifications(unread).
@@ -48,11 +45,11 @@ public:
struct Params : public LLInitParam::Block<Params, LLTextBox::Params>
{
/**
- * Contains maximum displayed count of unread messages. Default value is 9.
- *
- * If count is less than "max_unread_count" will be displayed as is.
- * Otherwise 9+ will be shown (for default value).
- */
+ * Contains maximum displayed count of unread messages. Default value is 9.
+ *
+ * If count is less than "max_unread_count" will be displayed as is.
+ * Otherwise 9+ will be shown (for default value).
+ */
Optional<S32> max_displayed_count;
Params();
@@ -120,35 +117,6 @@ protected:
};
/**
- * Class for displaying group's icon in Group chiclet.
- */
-class LLChicletGroupIconCtrl : public LLIconCtrl
-{
-public:
-
- struct Params : public LLInitParam::Block<Params, LLIconCtrl::Params>
- {
- Optional<std::string> default_icon;
-
- Params()
- : default_icon("default_icon", "Generic_Group")
- {}
- };
-
- /**
- * Sets icon, if value is LLUUID::null - default icon will be set.
- */
- virtual void setValue(const LLSD& value );
-
-protected:
-
- LLChicletGroupIconCtrl(const Params& p);
- friend class LLUICtrlFactory;
-
- std::string mDefaultIcon;
-};
-
-/**
* Class for displaying icon in inventory offer chiclet.
*/
class LLChicletInvOfferIconCtrl : public LLChicletAvatarIconCtrl
@@ -182,23 +150,6 @@ private:
};
/**
- * Class for displaying of speaker's voice indicator
- */
-class LLChicletSpeakerCtrl : public LLOutputMonitorCtrl
-{
-public:
-
- struct Params : public LLInitParam::Block<Params, LLOutputMonitorCtrl::Params>
- {
- Params(){};
- };
-protected:
-
- LLChicletSpeakerCtrl(const Params&p);
- friend class LLUICtrlFactory;
-};
-
-/**
* Base class for all chiclets.
*/
class LLChiclet : public LLUICtrl
@@ -213,7 +164,7 @@ public:
Params();
};
- /*virtual*/ ~LLChiclet();
+ virtual ~LLChiclet() {}
/**
* Associates chat session id with chiclet.
@@ -226,26 +177,11 @@ public:
virtual const LLUUID& getSessionId() const { return mSessionId; }
/**
- * Sets number of unread notifications.
- */
- virtual void setCounter(S32 counter) = 0;
-
- /**
- * Returns number of unread notifications.
- */
- virtual S32 getCounter() = 0;
-
- /**
* Sets show counter state.
*/
virtual void setShowCounter(bool show) { mShowCounter = show; }
/**
- * Returns show counter state.
- */
- virtual bool getShowCounter() {return mShowCounter;};
-
- /**
* Connects chiclet clicked event with callback.
*/
/*virtual*/ boost::signals2::connection setLeftButtonClickCallback(
@@ -322,6 +258,7 @@ public:
* It is used for default setting up of chicklet:click handler, etc.
*/
BOOL postBuild();
+
/**
* Sets IM session name. This name will be displayed in chiclet tooltip.
*/
@@ -334,52 +271,11 @@ public:
virtual void setOtherParticipantId(const LLUUID& other_participant_id) { mOtherParticipantId = other_participant_id; }
/**
- * Gets id of person/group user is chatting with.
+ * Enables/disables the counter control for a chiclet.
*/
- virtual LLUUID getOtherParticipantId() { return mOtherParticipantId; }
-
- /**
- * Init Speaker Control with speaker's ID
- */
- virtual void initSpeakerControl();
-
- /**
- * set status (Shows/Hide) for voice control.
- */
- virtual void setShowSpeaker(bool show);
-
- /**
- * Returns voice chat status control visibility.
- */
- virtual bool getShowSpeaker() {return mShowSpeaker;};
-
- /**
- * Shows/Hides for voice control for a chiclet.
- */
- virtual void toggleSpeakerControl();
-
- /**
- * Sets number of unread messages. Will update chiclet's width if number text
- * exceeds size of counter and notify it's parent about size change.
- */
- virtual void setCounter(S32);
-
- /**
- * Enables/disables the counter control for a chiclet.
- */
virtual void enableCounterControl(bool enable);
/**
- * Sets show counter state.
- */
- virtual void setShowCounter(bool show);
-
- /**
- * Shows/Hides for counter control for a chiclet.
- */
- virtual void toggleCounterControl();
-
- /**
* Sets required width for a chiclet according to visible controls.
*/
virtual void setRequiredWidth();
@@ -394,21 +290,6 @@ public:
*/
virtual bool getShowNewMessagesIcon();
- virtual void draw();
-
- /**
- * Determine whether given ID refers to a group or an IM chat session.
- *
- * This is used when we need to chose what IM chiclet (P2P/group)
- * class to instantiate.
- *
- * @param session_id session ID.
- * @return TYPE_GROUP in case of group chat session,
- * TYPE_IM in case of P2P session,
- * TYPE_UNKNOWN otherwise.
- */
- static EType getIMSessionType(const LLUUID& session_id);
-
/**
* The action taken on mouse down event.
*
@@ -450,8 +331,6 @@ protected:
S32 mDefaultWidth;
LLIconCtrl* mNewMessagesIcon;
- LLChicletNotificationCounterCtrl* mCounterCtrl;
- LLChicletSpeakerCtrl* mSpeakerCtrl;
LLButton* mChicletButton;
/** the id of another participant, either an avatar id or a group id*/
@@ -479,137 +358,6 @@ public:
sFindChicletsSignal;
};
-/**
- * Implements P2P chiclet.
- */
-class LLIMP2PChiclet : public LLIMChiclet
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params>
- {
- Optional<LLButton::Params> chiclet_button;
-
- Optional<LLChicletAvatarIconCtrl::Params> avatar_icon;
-
- Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
-
- Optional<LLChicletSpeakerCtrl::Params> speaker;
-
- Optional<LLIconCtrl::Params> new_message_icon;
-
- Optional<bool> show_speaker;
-
- Params();
- };
-
- /* virtual */ void setOtherParticipantId(const LLUUID& other_participant_id);
-
- /**
- * Init Speaker Control with speaker's ID
- */
- /*virtual*/ void initSpeakerControl();
-
- /**
- * Returns number of unread messages.
- */
- /*virtual*/ S32 getCounter() { return mCounterCtrl->getCounter(); }
-
-protected:
- LLIMP2PChiclet(const Params& p);
- friend class LLUICtrlFactory;
-
- /**
- * Creates chiclet popup menu. Will create P2P or Group IM Chat menu
- * based on other participant's id.
- */
- virtual void createPopupMenu();
-
- /**
- * Processes clicks on chiclet popup menu.
- */
- virtual void onMenuItemClicked(const LLSD& user_data);
-
- /**
- * Enables/disables menus based on relationship with other participant.
- * Enables/disables "show session" menu item depending on visible IM floater existence.
- */
- virtual void updateMenuItems();
-
-private:
-
- LLChicletAvatarIconCtrl* mChicletIconCtrl;
-};
-
-/**
- * Implements AD-HOC chiclet.
- */
-class LLAdHocChiclet : public LLIMChiclet
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params>
- {
- Optional<LLButton::Params> chiclet_button;
-
- Optional<LLChicletAvatarIconCtrl::Params> avatar_icon;
-
- Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
-
- Optional<LLChicletSpeakerCtrl::Params> speaker;
-
- Optional<LLIconCtrl::Params> new_message_icon;
-
- Optional<bool> show_speaker;
-
- Optional<LLColor4> avatar_icon_color;
-
- Params();
- };
-
- /**
- * Sets session id.
- * Session ID for group chat is actually Group ID.
- */
- /*virtual*/ void setSessionId(const LLUUID& session_id);
-
- /**
- * Keep Speaker Control with actual speaker's ID
- */
- /*virtual*/ void draw();
-
- /**
- * Init Speaker Control with speaker's ID
- */
- /*virtual*/ void initSpeakerControl();
-
- /**
- * Returns number of unread messages.
- */
- /*virtual*/ S32 getCounter() { return mCounterCtrl->getCounter(); }
-
-protected:
- LLAdHocChiclet(const Params& p);
- friend class LLUICtrlFactory;
-
- /**
- * Creates chiclet popup menu. Will create AdHoc Chat menu
- * based on other participant's id.
- */
- virtual void createPopupMenu();
-
- /**
- * Processes clicks on chiclet popup menu.
- */
- virtual void onMenuItemClicked(const LLSD& user_data);
-
- /**
- * Finds a current speaker and resets the SpeakerControl with speaker's ID
- */
- /*virtual*/ void switchToCurrentSpeaker();
-
-private:
-
- LLChicletAvatarIconCtrl* mChicletIconCtrl;
-};
/**
* Chiclet for script floaters.
@@ -631,10 +379,6 @@ public:
/*virtual*/ void setSessionId(const LLUUID& session_id);
- /*virtual*/ void setCounter(S32 counter);
-
- /*virtual*/ S32 getCounter() { return 0; }
-
/**
* Toggle script floater
*/
@@ -680,10 +424,6 @@ public:
/*virtual*/ void setSessionId(const LLUUID& session_id);
- /*virtual*/ void setCounter(S32 counter);
-
- /*virtual*/ S32 getCounter() { return 0; }
-
/**
* Toggle script floater
*/
@@ -708,96 +448,13 @@ private:
};
/**
- * Implements Group chat chiclet.
- */
-class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver
-{
-public:
-
- struct Params : public LLInitParam::Block<Params, LLIMChiclet::Params>
- {
- Optional<LLButton::Params> chiclet_button;
-
- Optional<LLChicletGroupIconCtrl::Params> group_icon;
-
- Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
-
- Optional<LLChicletSpeakerCtrl::Params> speaker;
-
- Optional<LLIconCtrl::Params> new_message_icon;
-
- Optional<bool> show_speaker;
-
- Params();
- };
-
- /**
- * Sets session id.
- * Session ID for group chat is actually Group ID.
- */
- /*virtual*/ void setSessionId(const LLUUID& session_id);
-
- /**
- * Keep Speaker Control with actual speaker's ID
- */
- /*virtual*/ void draw();
-
- /**
- * Callback for LLGroupMgrObserver, we get this when group data is available or changed.
- * Sets group icon.
- */
- /*virtual*/ void changed(LLGroupChange gc);
-
- /**
- * Init Speaker Control with speaker's ID
- */
- /*virtual*/ void initSpeakerControl();
-
- /**
- * Returns number of unread messages.
- */
- /*virtual*/ S32 getCounter() { return mCounterCtrl->getCounter(); }
-
- ~LLIMGroupChiclet();
-
-protected:
- LLIMGroupChiclet(const Params& p);
- friend class LLUICtrlFactory;
-
- /**
- * Finds a current speaker and resets the SpeakerControl with speaker's ID
- */
- /*virtual*/ void switchToCurrentSpeaker();
-
- /**
- * Creates chiclet popup menu. Will create P2P or Group IM Chat menu
- * based on other participant's id.
- */
- virtual void createPopupMenu();
-
- /**
- * Processes clicks on chiclet popup menu.
- */
- virtual void onMenuItemClicked(const LLSD& user_data);
-
- /**
- * Enables/disables "show session" menu item depending on visible IM floater existence.
- */
- virtual void updateMenuItems();
-
-private:
-
- LLChicletGroupIconCtrl* mChicletIconCtrl;
-};
-
-/**
* Implements notification chiclet. Used to display total amount of unread messages
* across all IM sessions, total amount of system notifications. See EXT-3147 for details
*/
class LLSysWellChiclet : public LLChiclet
{
public:
-
+
struct Params : public LLInitParam::Block<Params, LLChiclet::Params>
{
Optional<LLButton::Params> button;
@@ -843,7 +500,7 @@ protected:
* There is an assumption that it will be called 2*N times to do not change its start state.
* @see FlashToLitTimer
*/
- void changeLitState();
+ void changeLitState(bool blink);
/**
* Displays menu.
@@ -859,86 +516,58 @@ protected:
S32 mMaxDisplayedCount;
bool mIsNewMessagesState;
- FlashToLitTimer* mFlashToLitTimer;
+ LLFlashTimer* mFlashToLitTimer;
LLContextMenu* mContextMenu;
};
-/**
- * Class represented a chiclet for IM Well Icon.
- *
- * It displays a count of unread messages from other participants in all IM sessions.
- */
-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) {}
-
- ~LLIMWellChiclet();
-protected:
- LLIMWellChiclet(const Params& p);
-
- /**
- * Processes clicks on chiclet popup menu.
- */
- virtual void onMenuItemClicked(const LLSD& user_data);
-
- /**
- * Enables chiclet menu items.
- */
- bool enableMenuItem(const LLSD& user_data);
-
- /**
- * Creates menu.
- */
- /*virtual*/ void createMenu();
-
- /**
- * Handles changes in a session (message was added, messages were read, etc.)
- *
- * It get total count of unread messages from a LLIMMgr in all opened sessions and display it.
- *
- * @param[in] session_data contains session related data, is not used now
- * ["session_id"] - id of an appropriate session
- * ["participant_unread"] - count of unread messages from "real" participants.
- *
- * @see LLIMMgr::getNumberOfUnreadParticipantMessages()
- */
- void messageCountChanged(const LLSD& session_data);
-};
-
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);
-
+
/**
* Processes clicks on chiclet menu.
*/
void onMenuItemClicked(const LLSD& user_data);
-
+
/**
* Enables chiclet menu items.
*/
bool enableMenuItem(const LLSD& user_data);
-
+
/**
* Creates menu.
*/
/*virtual*/ void createMenu();
- // 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;
};
@@ -1044,9 +673,7 @@ public:
S32 getMinWidth() const { return mMinWidth; }
- S32 getTotalUnreadIMCount();
-
- S32 notifyParent(const LLSD& info);
+ /*virtual*/ S32 notifyParent(const LLSD& info);
/**
* Toggle chiclet by session id ON and toggle OFF all other chiclets.
diff --git a/indra/newview/llchicletbar.cpp b/indra/newview/llchicletbar.cpp
index f1bc51fbe7..a51c844775 100644
--- a/indra/newview/llchicletbar.cpp
+++ b/indra/newview/llchicletbar.cpp
@@ -25,16 +25,10 @@
*/
#include "llviewerprecompiledheaders.h" // must be first include
-
#include "llchicletbar.h"
-// library includes
-#include "llfloaterreg.h"
-#include "lllayoutstack.h"
-
-// newview includes
#include "llchiclet.h"
-#include "llimfloater.h" // for LLIMFloater
+#include "lllayoutstack.h"
#include "llpaneltopinfobar.h"
#include "llsyswellwindow.h"
@@ -57,107 +51,14 @@ 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)
-{
- LLIMChiclet::EType im_chiclet_type = LLIMChiclet::getIMSessionType(session_id);
-
- switch (im_chiclet_type)
- {
- case LLIMChiclet::TYPE_IM:
- return getChicletPanel()->createChiclet<LLIMP2PChiclet>(session_id);
- case LLIMChiclet::TYPE_GROUP:
- return getChicletPanel()->createChiclet<LLIMGroupChiclet>(session_id);
- case LLIMChiclet::TYPE_AD_HOC:
- return getChicletPanel()->createChiclet<LLAdHocChiclet>(session_id);
- case LLIMChiclet::TYPE_UNKNOWN:
- break;
- }
-
- return NULL;
-}
-
-//virtual
-void LLChicletBar::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
-{
- if (!getChicletPanel()) return;
-
- LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
- if (!session) return;
-
- // no need to spawn chiclets for participants in P2P calls called through Avaline
- if (session->isP2P() && session->isOtherParticipantAvaline()) return;
-
- if (getChicletPanel()->findChiclet<LLChiclet>(session_id)) return;
-
- LLIMChiclet* chiclet = createIMChiclet(session_id);
- if(chiclet)
- {
- chiclet->setIMSessionName(name);
- chiclet->setOtherParticipantId(other_participant_id);
-
- LLIMFloater::onIMChicletCreated(session_id);
-
- }
- else
- {
- llwarns << "Could not create chiclet" << llendl;
- }
-}
-
-//virtual
-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)
- {
- iMfloater->closeFloater();
- }
-
- getChicletPanel()->removeChiclet(session_id);
- }
-}
-
-void LLChicletBar::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id)
-{
- //this is only needed in case of outgoing ad-hoc/group chat sessions
- LLChicletPanel* chiclet_panel = getChicletPanel();
- if (chiclet_panel)
- {
- //it should be ad-hoc im chiclet or group im chiclet
- LLChiclet* chiclet = chiclet_panel->findChiclet<LLChiclet>(old_session_id);
- if (chiclet) chiclet->setSessionId(new_session_id);
- }
-}
-
-S32 LLChicletBar::getTotalUnreadIMCount()
-{
- return getChicletPanel()->getTotalUnreadIMCount();
-}
-
BOOL LLChicletBar::postBuild()
{
mToolbarStack = getChild<LLLayoutStack>("toolbar_stack");
mChicletPanel = getChild<LLChicletPanel>("chiclet_list");
- showWellButton("im_well", !LLIMWellWindow::getInstance()->isWindowEmpty());
showWellButton("notification_well", !LLNotificationWellWindow::getInstance()->isWindowEmpty());
LLPanelTopInfoBar::instance().setResizeCallback(boost::bind(&LLChicletBar::fitWithTopInfoBar, this));
diff --git a/indra/newview/llchicletbar.h b/indra/newview/llchicletbar.h
index 1427bf95e0..956c82cb77 100644
--- a/indra/newview/llchicletbar.h
+++ b/indra/newview/llchicletbar.h
@@ -28,7 +28,6 @@
#define LL_LLCHICLETBAR_H
#include "llpanel.h"
-#include "llimview.h"
class LLChicletPanel;
class LLIMChiclet;
@@ -38,30 +37,17 @@ class LLLayoutStack;
class LLChicletBar
: public LLSingleton<LLChicletBar>
, public LLPanel
- , public LLIMSessionObserver
{
LOG_CLASS(LLChicletBar);
friend class LLSingleton<LLChicletBar>;
public:
- ~LLChicletBar();
BOOL postBuild();
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);
-
- S32 getTotalUnreadIMCount();
-
/*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
- /**
- * Creates IM Chiclet based on session type (IM chat or Group chat)
- */
- LLIMChiclet* createIMChiclet(const LLUUID& session_id);
/**
* Shows/hides panel with specified well button (IM or Notification)
diff --git a/indra/newview/llcommunicationchannel.cpp b/indra/newview/llcommunicationchannel.cpp
new file mode 100644
index 0000000000..0821510645
--- /dev/null
+++ b/indra/newview/llcommunicationchannel.cpp
@@ -0,0 +1,113 @@
+/**
+* @file llcommunicationchannel.cpp
+* @brief Implementation of llcommunicationchannel
+* @author Stinson@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" // must be first include
+
+#include "llcommunicationchannel.h"
+
+#include <string>
+#include <map>
+
+#include "llagent.h"
+#include "lldate.h"
+#include "llnotifications.h"
+
+
+LLCommunicationChannel::LLCommunicationChannel(const std::string& pName, const std::string& pParentName)
+ : LLNotificationChannel(pName, pParentName, filterByDoNotDisturbStatus)
+ , mHistory()
+{
+}
+
+LLCommunicationChannel::~LLCommunicationChannel()
+{
+}
+
+bool LLCommunicationChannel::filterByDoNotDisturbStatus(LLNotificationPtr)
+{
+ return !gAgent.isDoNotDisturb();
+}
+
+S32 LLCommunicationChannel::getHistorySize() const
+{
+ return mHistory.size();
+}
+
+LLCommunicationChannel::history_list_t::const_iterator LLCommunicationChannel::beginHistory() const
+{
+ return mHistory.begin();
+}
+
+LLCommunicationChannel::history_list_t::const_iterator LLCommunicationChannel::endHistory() const
+{
+ return mHistory.end();
+}
+
+LLCommunicationChannel::history_list_t::iterator LLCommunicationChannel::beginHistory()
+{
+ return mHistory.begin();
+}
+
+LLCommunicationChannel::history_list_t::iterator LLCommunicationChannel::endHistory()
+{
+ return mHistory.end();
+}
+
+void LLCommunicationChannel::clearHistory()
+{
+ mHistory.clear();
+}
+
+void LLCommunicationChannel::removeItemFromHistory(LLNotificationPtr p)
+{
+ //Find the notification and removes it from mHistory
+ for(history_list_t::iterator it = beginHistory(); it != endHistory(); ++it)
+ {
+ if(it->second == p)
+ {
+ mHistory.erase(it);
+ break;
+ }
+ }
+}
+
+void LLCommunicationChannel::onDelete(LLNotificationPtr p)
+{
+ removeItemFromHistory(p);
+}
+
+void LLCommunicationChannel::onFilterFail(LLNotificationPtr pNotificationPtr)
+{
+ std::string notificationType = pNotificationPtr->getType();
+ if ((notificationType == "groupnotify")
+ || (notificationType == "offer")
+ || (notificationType == "notifytoast")
+ && !pNotificationPtr->isCancelled())
+ {
+ mHistory.insert(std::make_pair<LLDate, LLNotificationPtr>(pNotificationPtr->getDate(), pNotificationPtr));
+ }
+}
diff --git a/indra/newview/llcommunicationchannel.h b/indra/newview/llcommunicationchannel.h
new file mode 100644
index 0000000000..0d8f7f4387
--- /dev/null
+++ b/indra/newview/llcommunicationchannel.h
@@ -0,0 +1,66 @@
+/**
+* @file llcommunicationchannel.h
+* @brief Header file for llcommunicationchannel
+* @author Stinson@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_LLCOMMUNICATIONCHANNEL_H
+#define LL_LLCOMMUNICATIONCHANNEL_H
+
+#include <string>
+#include <map>
+
+#include "lldate.h"
+#include "llerror.h"
+#include "llnotifications.h"
+
+class LLCommunicationChannel : public LLNotificationChannel
+{
+ LOG_CLASS(LLCommunicationChannel);
+public:
+ LLCommunicationChannel(const std::string& pName, const std::string& pParentName);
+ virtual ~LLCommunicationChannel();
+
+ static bool filterByDoNotDisturbStatus(LLNotificationPtr);
+
+ typedef std::multimap<LLDate, LLNotificationPtr> history_list_t;
+ S32 getHistorySize() const;
+ history_list_t::const_iterator beginHistory() const;
+ history_list_t::const_iterator endHistory() const;
+ history_list_t::iterator beginHistory();
+ history_list_t::iterator endHistory();
+
+ void clearHistory();
+ void removeItemFromHistory(LLNotificationPtr p);
+
+protected:
+ virtual void onDelete(LLNotificationPtr p);
+ virtual void onFilterFail(LLNotificationPtr pNotificationPtr);
+
+private:
+
+ history_list_t mHistory;
+};
+
+#endif // LL_LLCOMMUNICATIONCHANNEL_H
+
diff --git a/indra/newview/llconversationlog.cpp b/indra/newview/llconversationlog.cpp
new file mode 100644
index 0000000000..7883e4cb89
--- /dev/null
+++ b/indra/newview/llconversationlog.cpp
@@ -0,0 +1,615 @@
+/**
+ * @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 "lldiriterator.h"
+#include "llnotificationsutil.h"
+#include "lltrans.h"
+
+#include <boost/foreach.hpp>
+#include "boost/lexical_cast.hpp"
+
+const int CONVERSATION_LIFETIME = 30; // lifetime of LLConversation is 30 days by spec
+
+struct ConversationParams
+{
+ ConversationParams(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 ConversationParams& 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()
+{
+ LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(mSessionID);
+
+ bool offline_ims_visible = LLFloaterIMSession::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 = LLFloaterIMSession::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() :
+ mAvatarNameCacheConnection(),
+ mLoggingEnabled(false)
+{
+ if(gSavedPerAccountSettings.controlExists("KeepConversationLogTranscripts"))
+ {
+ LLControlVariable * keep_log_ctrlp = gSavedPerAccountSettings.getControl("KeepConversationLogTranscripts").get();
+ S32 log_mode = keep_log_ctrlp->getValue();
+ keep_log_ctrlp->getSignal()->connect(boost::bind(&LLConversationLog::enableLogging, this, _2));
+ if (log_mode > 0)
+ {
+ loadFromFile(getFileName());
+
+ enableLogging(log_mode);
+ }
+ }
+}
+
+void LLConversationLog::enableLogging(S32 log_mode)
+{
+ mLoggingEnabled = log_mode > 0;
+ if (log_mode > 0)
+ {
+ mConversations.clear();
+ loadFromFile(getFileName());
+ LLIMMgr::instance().addSessionObserver(this);
+ mNewMessageSignalConnection = LLIMModel::instance().addNewMsgCallback(boost::bind(&LLConversationLog::onNewMessageReceived, this, _1));
+
+ mFriendObserver = new LLConversationLogFriendObserver;
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+ }
+ else
+ {
+ saveToFile(getFileName());
+
+ LLIMMgr::instance().removeSessionObserver(this);
+ mNewMessageSignalConnection.disconnect();
+ LLAvatarTracker::instance().removeObserver(mFriendObserver);
+ }
+
+ notifyObservers();
+}
+
+void LLConversationLog::logConversation(const LLUUID& session_id, BOOL has_offline_msg)
+{
+ const LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
+ LLConversation* conversation = findConversation(session);
+
+ if (session && session->mOtherParticipantID != gAgentID)
+ {
+ if (conversation)
+ {
+ if(has_offline_msg)
+ {
+ updateOfflineIMs(session, has_offline_msg);
+ }
+ updateConversationTimestamp(conversation);
+ }
+ else
+ {
+ 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)
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = 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->setConversationName(name);
+ notifyParticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_NAME);
+ }
+}
+
+void LLConversationLog::updateOfflineIMs(const LLIMModel::LLIMSession* session, BOOL new_messages)
+{
+ if (!session)
+ {
+ return;
+ }
+
+ LLConversation* conversation = findConversation(session);
+ if (conversation)
+ {
+ conversation->setOfflineMessages(new_messages);
+ notifyParticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_OfflineIMs);
+ }
+}
+
+void LLConversationLog::updateConversationTimestamp(LLConversation* conversation)
+{
+ if (conversation)
+ {
+ conversation->updateTimestamp();
+ notifyParticularConversationObservers(conversation->getSessionID(), LLConversationLogObserver::CHANGED_TIME);
+ }
+}
+
+LLConversation* LLConversationLog::findConversation(const LLIMModel::LLIMSession* session)
+{
+ if (session)
+ {
+ 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();
+ cache();
+ 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, BOOL has_offline_msg)
+{
+ logConversation(session_id, has_offline_msg);
+}
+
+void LLConversationLog::cache()
+{
+ if (gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0)
+ {
+ saveToFile(getFileName());
+ }
+}
+
+void LLConversationLog::getListOfBackupLogs(std::vector<std::string>& list_of_backup_logs)
+{
+ // get Users log directory
+ std::string dirname = gDirUtilp->getPerAccountChatLogsDir();
+
+ // add final OS dependent delimiter
+ dirname += gDirUtilp->getDirDelimiter();
+
+ // create search pattern
+ std::string pattern = "conversation.log.backup*";
+
+ LLDirIterator iter(dirname, pattern);
+ std::string filename;
+ while (iter.next(filename))
+ {
+ list_of_backup_logs.push_back(gDirUtilp->add(dirname, filename));
+ }
+}
+
+void LLConversationLog::deleteBackupLogs()
+{
+ std::vector<std::string> backup_logs;
+ getListOfBackupLogs(backup_logs);
+
+ BOOST_FOREACH(const std::string& fullpath, backup_logs)
+ {
+ LLFile::remove(fullpath);
+ }
+}
+
+bool LLConversationLog::moveLog(const std::string &originDirectory, const std::string &targetDirectory)
+{
+
+ std::string backupFileName;
+ unsigned backupFileCount = 0;
+
+ //Does the file exist in the current path, if it does lets move it
+ if(LLFile::isfile(originDirectory))
+ {
+ //The target directory contains that file already, so lets store it
+ if(LLFile::isfile(targetDirectory))
+ {
+ backupFileName = targetDirectory + ".backup";
+
+ //If needed store backup file as .backup1 etc.
+ while(LLFile::isfile(backupFileName))
+ {
+ ++backupFileCount;
+ backupFileName = targetDirectory + ".backup" + boost::lexical_cast<std::string>(backupFileCount);
+ }
+
+ //Rename the file to its backup name so it is not overwritten
+ LLFile::rename(targetDirectory, backupFileName);
+ }
+
+ //Move the file from the current path to target path
+ if(LLFile::rename(originDirectory, targetDirectory) != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+std::string LLConversationLog::getFileName()
+{
+ std::string filename = "conversation";
+ return gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, 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, "[%lld] %d %d %d %s| %s %s %s|\n",
+ (S64)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];
+ S32 has_offline_ims;
+ S32 stype;
+ S64 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, "[%lld] %d %d %d %[^|]| %s %s %[^|]|",
+ &time,
+ &stype,
+ &prereserved_unused,
+ &has_offline_ims,
+ conv_name_buffer,
+ part_id_buffer,
+ conv_id_buffer,
+ history_file_name);
+
+ ConversationParams params((time_t)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::notifyParticularConversationObservers(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, false);
+}
+
+void LLConversationLog::onAvatarNameCache(const LLUUID& participant_id, const LLAvatarName& av_name, const LLIMModel::LLIMSession* session)
+{
+ mAvatarNameCacheConnection.disconnect();
+ updateConversationName(session, av_name.getCompleteName());
+}
+
+void LLConversationLog::onClearLog()
+{
+ LLNotificationsUtil::add("PreferenceChatClearLog", LLSD(), LLSD(), boost::bind(&LLConversationLog::onClearLogResponse, this, _1, _2));
+}
+
+void LLConversationLog::onClearLogResponse(const LLSD& notification, const LLSD& response)
+{
+ if (0 == LLNotificationsUtil::getSelectedOption(notification, response))
+ {
+ mConversations.clear();
+ notifyObservers();
+ cache();
+ deleteBackupLogs();
+ }
+}
diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h
new file mode 100644
index 0000000000..265b1f0ef0
--- /dev/null
+++ b/indra/newview/llconversationlog.h
@@ -0,0 +1,216 @@
+/**
+ * @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 "llfloaterimsession.h"
+#include "llimview.h"
+
+class LLConversationLogObserver;
+struct ConversationParams;
+
+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 ConversationParams& 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 setConversationName(std::string conv_name) { mConversationName = conv_name; }
+ void setOfflineMessages(bool new_messages) { mHasOfflineIMs = new_messages; }
+ 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 LLFloaterIMSession
+ * 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, BOOL has_offline_msg);
+ 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();
+ bool moveLog(const std::string &originDirectory, const std::string &targetDirectory);
+ void getListOfBackupLogs(std::vector<std::string>& list_of_backup_logs);
+ void deleteBackupLogs();
+
+ void onClearLog();
+ void onClearLogResponse(const LLSD& notification, const LLSD& response);
+
+ bool getIsLoggingEnabled() { return mLoggingEnabled; }
+ bool isLogEmpty() { return mConversations.empty(); }
+
+ /**
+ * constructs file name in which conversations log will be saved
+ * file name is conversation.log
+ */
+ std::string getFileName();
+
+private:
+
+ LLConversationLog();
+ virtual ~LLConversationLog()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
+
+ void enableLogging(S32 log_mode);
+
+ /**
+ * adds conversation to the conversation list and notifies observers
+ */
+ void logConversation(const LLUUID& session_id, BOOL has_offline_msg);
+
+ void notifyParticularConversationObservers(const LLUUID& session_id, U32 mask);
+
+ 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);
+ void updateOfflineIMs(const LLIMModel::LLIMSession* session, BOOL new_messages);
+
+ 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 mNewMessageSignalConnection;
+ boost::signals2::connection mAvatarNameCacheConnection;
+
+ bool mLoggingEnabled;
+};
+
+class LLConversationLogObserver
+{
+public:
+
+ enum EConversationChange
+ {
+ CHANGED_TIME = 1, // last interaction time changed
+ CHANGED_NAME = 2, // conversation name changed
+ CHANGED_OfflineIMs = 3
+ };
+
+ 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..5ab108b39f
--- /dev/null
+++ b/indra/newview/llconversationloglist.cpp
@@ -0,0 +1,533 @@
+/**
+ * @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"
+#include "lltrans.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;
+ }
+ }
+ else if (mask & LLConversationLogObserver::CHANGED_OfflineIMs)
+ {
+ item->updateOfflineIMs();
+ }
+}
+
+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()
+{
+ const LLConversation * selected_conversationp = getSelectedConversation();
+
+ clear();
+
+ bool have_filter = !mNameFilter.empty();
+ LLConversationLog &log_instance = LLConversationLog::instance();
+
+ const std::vector<LLConversation>& conversations = log_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);
+ }
+
+ // try to restore selection of item
+ if (NULL != selected_conversationp)
+ {
+ selectItemByUUID(selected_conversationp->getSessionID());
+ }
+
+ bool logging_enabled = log_instance.getIsLoggingEnabled();
+ bool log_empty = log_instance.isLogEmpty();
+ if (!logging_enabled && log_empty)
+ {
+ setNoItemsCommentText(LLTrans::getString("logging_calls_disabled_log_empty"));
+ }
+ else if (!logging_enabled && !log_empty)
+ {
+ setNoItemsCommentText(LLTrans::getString("logging_calls_disabled_log_not_empty"));
+ }
+ else if (logging_enabled && log_empty)
+ {
+ setNoItemsCommentText(LLTrans::getString("logging_calls_enabled_log_empty"));
+ }
+ else if (logging_enabled && !log_empty)
+ {
+ setNoItemsCommentText("");
+ }
+}
+
+void LLConversationLogList::onCustomAction(const LLSD& userdata)
+{
+ const LLConversation * selected_conversationp = getSelectedConversation();
+
+ if (NULL == selected_conversationp)
+ {
+ return;
+ }
+
+ const std::string command_name = userdata.asString();
+ const LLUUID& selected_conversation_participant_id = selected_conversationp->getParticipantID();
+ const LLUUID& selected_conversation_session_id = selected_conversationp->getSessionID();
+ LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
+
+ if ("im" == command_name)
+ {
+ switch (stype)
+ {
+ case LLIMModel::LLIMSession::P2P_SESSION:
+ LLAvatarActions::startIM(selected_conversation_participant_id);
+ break;
+
+ case LLIMModel::LLIMSession::GROUP_SESSION:
+ LLGroupActions::startIM(selected_conversation_session_id);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ("call" == command_name)
+ {
+ switch (stype)
+ {
+ case LLIMModel::LLIMSession::P2P_SESSION:
+ LLAvatarActions::startCall(selected_conversation_participant_id);
+ break;
+
+ case LLIMModel::LLIMSession::GROUP_SESSION:
+ LLGroupActions::startCall(selected_conversation_session_id);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ("view_profile" == command_name)
+ {
+ switch (stype)
+ {
+ case LLIMModel::LLIMSession::P2P_SESSION:
+ LLAvatarActions::showProfile(selected_conversation_participant_id);
+ break;
+
+ case LLIMModel::LLIMSession::GROUP_SESSION:
+ LLGroupActions::show(selected_conversation_session_id);
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ("chat_history" == command_name)
+ {
+ LLFloaterReg::showInstance("preview_conversation", selected_conversation_session_id, true);
+ }
+ else if ("offer_teleport" == command_name)
+ {
+ LLAvatarActions::offerTeleport(selected_conversation_participant_id);
+ }
+ else if("add_friend" == command_name)
+ {
+ if (!LLAvatarActions::isFriend(selected_conversation_participant_id))
+ {
+ LLAvatarActions::requestFriendshipDialog(selected_conversation_participant_id);
+ }
+ }
+ else if("remove_friend" == command_name)
+ {
+ if (LLAvatarActions::isFriend(selected_conversation_participant_id))
+ {
+ LLAvatarActions::removeFriendDialog(selected_conversation_participant_id);
+ }
+ }
+ else if ("invite_to_group" == command_name)
+ {
+ LLAvatarActions::inviteToGroup(selected_conversation_participant_id);
+ }
+ else if ("show_on_map" == command_name)
+ {
+ LLAvatarActions::showOnMap(selected_conversation_participant_id);
+ }
+ else if ("share" == command_name)
+ {
+ LLAvatarActions::share(selected_conversation_participant_id);
+ }
+ else if ("pay" == command_name)
+ {
+ LLAvatarActions::pay(selected_conversation_participant_id);
+ }
+ else if ("block" == command_name)
+ {
+ LLAvatarActions::toggleBlock(selected_conversation_participant_id);
+ }
+}
+
+bool LLConversationLogList::isActionEnabled(const LLSD& userdata)
+{
+ const LLConversation * selected_conversationp = getSelectedConversation();
+
+ if (NULL == selected_conversationp || numSelected() > 1)
+ {
+ return false;
+ }
+
+ const std::string command_name = userdata.asString();
+
+ LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
+ const LLUUID& selected_id = selected_conversationp->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" == command_name)
+ {
+ return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike());
+ }
+
+ return false;
+}
+
+bool LLConversationLogList::isActionChecked(const LLSD& userdata)
+{
+ const LLConversation * selected_conversationp = getSelectedConversation();
+
+ if (NULL == selected_conversationp)
+ {
+ return false;
+ }
+
+ const std::string command_name = userdata.asString();
+
+ const LLUUID& selected_id = selected_conversationp->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..4e984d603b
--- /dev/null
+++ b/indra/newview/llconversationloglistitem.cpp
@@ -0,0 +1,184 @@
+/**
+ * @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");
+
+ LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(mConversation->getSessionID());
+
+ bool ims_are_read = LLFloaterIMSession::isVisible(floater) && floater->hasFocus();
+
+ if (mConversation->hasOfflineMessages() && !ims_are_read)
+ {
+ mIMFloaterShowedConnection = LLFloaterIMSession::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::updateOfflineIMs()
+{
+ getChild<LLIconCtrl>("unread_ims_icon")->setVisible(mConversation->hasOfflineMessages());
+}
+
+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..ee28456bbb
--- /dev/null
+++ b/indra/newview/llconversationloglistitem.h
@@ -0,0 +1,86 @@
+/**
+ * @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 "llfloaterimsession.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();
+ void updateOfflineIMs();
+
+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..c74ce24872
--- /dev/null
+++ b/indra/newview/llconversationmodel.cpp
@@ -0,0 +1,702 @@
+/**
+ * @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 "llfloaterimsession.h"
+#include "llsdutil.h"
+#include "llconversationmodel.h"
+#include "llimview.h" //For LLIMModel
+#include "lltrans.h"
+
+#include <boost/foreach.hpp>
+
+//
+// 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),
+ mDisplayModeratorOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::LLConversationItem(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mName(""),
+ mUUID(uuid),
+ mNeedsRefresh(true),
+ mConvType(CONV_UNKNOWN),
+ mLastActiveTime(0.0),
+ mDisplayModeratorOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::LLConversationItem(LLFolderViewModelInterface& root_view_model) :
+ LLFolderViewModelItemCommon(root_view_model),
+ mName(""),
+ mUUID(),
+ mNeedsRefresh(true),
+ mConvType(CONV_UNKNOWN),
+ mLastActiveTime(0.0),
+ mDisplayModeratorOptions(false),
+ mAvatarNameCacheConnection()
+{
+}
+
+LLConversationItem::~LLConversationItem()
+{
+ // Disconnect any previous avatar name cache connection to ensure
+ // that the callback method is not called after destruction
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+}
+
+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, U32 flags)
+{
+ if (flags & ITEM_IN_MULTI_SELECTION)
+ {
+ 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("remove_friends"));
+ }
+ else
+ {
+ 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"));
+ if (static_cast<LLConversationItem*>(mParent)->getType() == CONV_SESSION_NEARBY)
+ items.push_back(std::string("zoom_in"));
+ 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"));
+ items.push_back(std::string("MuteText"));
+
+ if ((getType() != CONV_SESSION_1_ON_1) && mDisplayModeratorOptions)
+ {
+ 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"));
+ }
+ }
+}
+
+// method does subscription to changes in avatar name cache for current session/participant conversation item.
+void LLConversationItem::fetchAvatarName(bool isParticipant /*= true*/)
+{
+ LLUUID item_id = getUUID();
+
+ // item should not be null for participants
+ if (isParticipant)
+ {
+ llassert(item_id.notNull());
+ }
+
+ // disconnect any previous avatar name cache connection
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ // exclude nearby chat item
+ if (item_id.notNull())
+ {
+ // for P2P session item, override it as item of called agent
+ if (CONV_SESSION_1_ON_1 == getType())
+ {
+ item_id = LLIMModel::getInstance()->getOtherParticipantID(item_id);
+ }
+
+ // subscribe on avatar name cache changes for participant and session items
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(item_id, boost::bind(&LLConversationItem::onAvatarNameCache, this, _2));
+ }
+}
+
+//
+// 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;
+ updateName(participant);
+ postEvent("add_participant", this, participant);
+}
+
+void LLConversationItemSession::updateName(LLConversationItemParticipant* participant)
+{
+ EConversationType conversation_type = getType();
+ // We modify the session name only in the case of an ad-hoc session or P2P session, exit otherwise (nothing to do)
+ if ((conversation_type != CONV_SESSION_AD_HOC) && (conversation_type != CONV_SESSION_1_ON_1))
+ {
+ return;
+ }
+
+ // Avoid changing the default name if no participant present yet
+ if (mChildren.size() == 0)
+ {
+ return;
+ }
+
+ uuid_vec_t temp_uuids; // uuids vector for building the added participants' names string
+ if (conversation_type == CONV_SESSION_AD_HOC || conversation_type == CONV_SESSION_1_ON_1)
+ {
+ // Build a string containing the participants UUIDs (minus own agent) and check if ready for display (we don't want "(waiting)" in there)
+ // Note: we don't bind ourselves to the LLAvatarNameCache event as updateParticipantName() is called by
+ // onAvatarNameCache() which is itself attached to the same event.
+
+ // In the case of a P2P conversation, we need to grab the name of the other participant in the session instance itself
+ // as we do not create participants for such a session.
+
+ LLFolderViewModelItem * itemp;
+ BOOST_FOREACH(itemp, mChildren)
+ {
+ LLConversationItem* current_participant = dynamic_cast<LLConversationItem*>(itemp);
+ // Add the avatar uuid to the list (except if it's the own agent uuid)
+ if (current_participant->getUUID() != gAgentID)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(current_participant->getUUID(), &av_name))
+ {
+ temp_uuids.push_back(current_participant->getUUID());
+
+ if (conversation_type == CONV_SESSION_1_ON_1)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (temp_uuids.size() != 0)
+ {
+ 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;
+ updateName(participant);
+ 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->muteVoice(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, flags);
+ }
+ 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(bool dump_children)
+{
+ // Session info
+ llinfos << "Merov debug : session " << this << ", uuid = " << mUUID << ", name = " << mName << ", is loaded = " << mIsLoaded << llendl;
+ // Children info
+ if (dump_children)
+ {
+ for (child_list_t::iterator iter = mChildren.begin(); iter != mChildren.end(); iter++)
+ {
+ LLConversationItemParticipant* participant = dynamic_cast<LLConversationItemParticipant*>(*iter);
+ if (participant)
+ {
+ participant->dumpDebugData();
+ }
+ }
+ }
+}
+
+// should be invoked only for P2P sessions
+void LLConversationItemSession::onAvatarNameCache(const LLAvatarName& av_name)
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ renameItem(av_name.getDisplayName());
+ postEvent("update_session", this, NULL);
+}
+
+//
+// LLConversationItemParticipant
+//
+
+LLConversationItemParticipant::LLConversationItemParticipant(std::string display_name, const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(display_name,uuid,root_view_model),
+ mIsModerator(false),
+ mDisplayModeratorLabel(false),
+ mDistToAgent(-1.0)
+{
+ mDisplayName = display_name;
+ mConvType = CONV_PARTICIPANT;
+}
+
+LLConversationItemParticipant::LLConversationItemParticipant(const LLUUID& uuid, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItem(uuid,root_view_model),
+ mIsModerator(false),
+ mDisplayModeratorLabel(false),
+ mDistToAgent(-1.0)
+{
+ mConvType = CONV_PARTICIPANT;
+}
+
+void LLConversationItemParticipant::updateName()
+{
+ llassert(getUUID().notNull());
+ if (getUUID().notNull())
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(getUUID(),&av_name))
+ {
+ updateName(av_name);
+ }
+ }
+}
+
+void LLConversationItemParticipant::onAvatarNameCache(const LLAvatarName& av_name)
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
+ updateName(av_name);
+}
+
+void LLConversationItemParticipant::updateName(const LLAvatarName& av_name)
+{
+ mName = av_name.getUserName();
+ mDisplayName = av_name.getDisplayName();
+
+ if (mDisplayModeratorLabel)
+ {
+ mDisplayName += " " + LLTrans::getString("IM_moderator_label");
+ }
+
+ renameItem(mDisplayName);
+ if (mParent != NULL)
+ {
+ LLConversationItemSession* parent_session = dynamic_cast<LLConversationItemSession*>(mParent);
+ if (parent_session != NULL)
+ {
+ parent_session->requestSort();
+ parent_session->updateName(this);
+ postEvent("update_participant", parent_session, this);
+ }
+ }
+}
+
+void LLConversationItemParticipant::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ buildParticipantMenuOptions(items, flags);
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
+LLConversationItemSession* LLConversationItemParticipant::getParentSession()
+{
+ LLConversationItemSession* parent_session = NULL;
+ if (hasParent())
+ {
+ parent_session = dynamic_cast<LLConversationItemSession*>(mParent);
+ }
+ return parent_session;
+}
+
+void LLConversationItemParticipant::dumpDebugData()
+{
+ llinfos << "Merov debug : participant, uuid = " << mUUID << ", name = " << mName << ", display name = " << mDisplayName << ", muted = " << isVoiceMuted() << ", moderator = " << mIsModerator << llendl;
+}
+
+void LLConversationItemParticipant::setDisplayModeratorRole(bool displayRole)
+{
+ if (displayRole != mDisplayModeratorLabel)
+ {
+ mDisplayModeratorLabel = displayRole;
+ updateName();
+ }
+}
+
+bool LLConversationItemParticipant::isVoiceMuted()
+{
+ return LLMuteList::getInstance()->isMuted(mUUID, LLMute::flagVoiceChat);
+}
+
+void LLConversationItemParticipant::muteVoice(bool mute_voice)
+{
+ std::string name;
+ gCacheName->getFullName(mUUID, name);
+ LLMuteList * mute_listp = LLMuteList::getInstance();
+ bool voice_already_muted = mute_listp->isMuted(mUUID, name);
+
+ LLMute mute(mUUID, name, LLMute::AGENT);
+ if (voice_already_muted && !mute_voice)
+ {
+ mute_listp->remove(mute);
+ }
+ else if (!voice_already_muted && mute_voice)
+ {
+ mute_listp->add(mute);
+ }
+}
+
+//
+// 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 (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 ((type_a == LLConversationItem::CONV_SESSION_NEARBY) || (type_b == LLConversationItem::CONV_SESSION_NEARBY))
+ {
+ // If one is the nearby session, put nearby session *always* last
+ return (type_b == LLConversationItem::CONV_SESSION_NEARBY);
+ }
+ 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..8766585049
--- /dev/null
+++ b/indra/newview/llconversationmodel.h
@@ -0,0 +1,314 @@
+/**
+ * @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 "../llui/llfolderviewitem.h"
+#include "../llui/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, U32 flags);
+
+ void fetchAvatarName(bool isParticipant = true); // fetch and update the avatar name
+
+protected:
+ virtual void onAvatarNameCache(const LLAvatarName& av_name) {}
+
+ 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;
+ bool mDisplayModeratorOptions;
+ boost::signals2::connection mAvatarNameCacheConnection;
+};
+
+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*/ 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 updateName(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(bool dump_children = false);
+
+private:
+ /*virtual*/ void onAvatarNameCache(const LLAvatarName& av_name);
+
+ 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 const std::string& getDisplayName() const { return mDisplayName; }
+
+ bool isVoiceMuted();
+ bool isModerator() const { return mIsModerator; }
+ void muteVoice(bool mute_voice);
+ 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 updateName(); // get from the cache (do *not* fetch) and update the avatar name
+ LLConversationItemSession* getParentSession();
+
+ void dumpDebugData();
+ void setModeratorOptionsVisible(bool visible) { mDisplayModeratorOptions = visible; }
+ void setDisplayModeratorRole(bool displayRole);
+
+private:
+ void onAvatarNameCache(const LLAvatarName& av_name); // callback used by fetchAvatarName
+ void updateName(const LLAvatarName& av_name);
+
+ bool mIsMuted; // default is false
+ bool mIsModerator; // default is false
+ bool mDisplayModeratorLabel; // 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..956abcd586
--- /dev/null
+++ b/indra/newview/llconversationview.cpp
@@ -0,0 +1,696 @@
+/**
+ * @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 "llfloaterimsession.h"
+#include "llfloaterimnearbychat.h"
+#include "llfloaterimsessiontab.h"
+#include "llfloaterimcontainer.h"
+#include "llfloaterreg.h"
+#include "llgroupiconctrl.h"
+#include "lluictrlfactory.h"
+#include "lltoolbarview.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)
+ {
+ conversation->showVoiceIndicator(conversation
+ && status != STATUS_JOINING
+ && status != STATUS_LEFT_CHANNEL
+ && LLVoiceClient::getInstance()->voiceEnabled()
+ && LLVoiceClient::getInstance()->isVoiceWorking());
+ }
+
+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),
+ mCollapsedMode(false),
+ mHasArrow(true),
+ mIsInActiveVoiceChannel(false),
+ mFlashStateOn(false),
+ mFlashStarted(false)
+{
+ mFlashTimer = new LLFlashTimer();
+}
+
+LLConversationViewSession::~LLConversationViewSession()
+{
+ mActiveVoiceChannelConnection.disconnect();
+
+ if(LLVoiceClient::instanceExists() && mVoiceClientObserver)
+ {
+ LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver);
+ }
+
+ mFlashTimer->unset();
+}
+
+void LLConversationViewSession::setFlashState(bool flash_state)
+{
+ if (flash_state && !mFlashStateOn)
+ {
+ // flash chat toolbar button if scrolled out of sight (because flashing will not be visible)
+ if (mContainer->isScrolledOutOfSight(this))
+ {
+ gToolBarView->flashCommand(LLCommandId("chat"), true);
+ }
+ }
+
+ mFlashStateOn = flash_state;
+ mFlashStarted = false;
+ mFlashTimer->stopFlashing();
+}
+
+void LLConversationViewSession::startFlashing()
+{
+ if (mFlashStateOn && !mFlashStarted)
+ {
+ mFlashStarted = true;
+ mFlashTimer->startFlashing();
+ }
+}
+
+bool LLConversationViewSession::isHighlightAllowed()
+{
+ return mFlashStateOn || mIsSelected;
+}
+
+bool LLConversationViewSession::isHighlightActive()
+{
+ return (mFlashStateOn ? (mFlashTimer->isFlashingInProgress() ? mFlashTimer->isCurrentlyHighlighted() : true) : mIsCurSelection);
+}
+
+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_indicator");
+
+ 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);
+ mHasArrow = false;
+ }
+ break;
+ }
+ case LLConversationItem::CONV_SESSION_AD_HOC:
+ {
+ LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon");
+ icon->setVisible(true);
+ mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true);
+ break;
+ }
+ 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);
+ mIsInActiveVoiceChannel = 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);
+
+ // Indicate that flash can start (moot operation if already started, done or not flashing)
+ startFlashing();
+
+ // draw highlight for selected items
+ drawHighlight(show_context, true, sHighlightBgColor, sFlashBgColor, 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);
+ }
+
+ // we don't draw the open folder arrow in minimized mode
+ if (mHasArrow && !mCollapsedMode)
+ {
+ // update the rotation angle of open folder arrow
+ updateLabelRotation();
+ drawOpenFolderArrow(default_params, sFgColor);
+ }
+
+ refresh();
+
+ LLView::draw();
+}
+
+BOOL LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ //Will try to select a child node and then itself (if a child was not selected)
+ BOOL result = LLFolderViewFolder::handleMouseDown(x, y, mask);
+
+ //This node (conversation) was selected and a child (participant) was not
+ if(result && getRoot())
+ {
+ selectConversationItem();
+ }
+
+ return result;
+}
+
+BOOL LLConversationViewSession::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL result = LLFolderViewFolder::handleRightMouseDown(x, y, mask);
+
+ if(result)
+ {
+ selectConversationItem();
+ }
+
+ return result;
+}
+
+void LLConversationViewSession::selectConversationItem()
+{
+ if(getRoot()->getCurSelectedItem() == this)
+ {
+ LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem());
+ LLUUID session_id = item? item->getUUID() : LLUUID();
+
+ LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ im_container->flashConversationItemWidget(session_id,false);
+ im_container->selectConversationPair(session_id, false);
+ im_container->collapseMessagesPane(false);
+ }
+}
+
+// virtual
+S32 LLConversationViewSession::arrange(S32* width, S32* height)
+{
+ //LLFolderViewFolder::arrange computes value for getIndentation() function below
+ S32 arranged = LLFolderViewFolder::arrange(width, height);
+
+ S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation();
+
+ LLRect rect(mCollapsedMode ? getLocalRect().mLeft : h_pad,
+ getLocalRect().mTop,
+ getLocalRect().mRight,
+ getLocalRect().mTop - getItemHeight());
+ mItemPanel->setShape(rect);
+
+ return arranged;
+}
+
+// virtual
+void LLConversationViewSession::toggleOpen()
+{
+ // conversations should not be opened while in minimized mode
+ if (!mCollapsedMode)
+ {
+ LLFolderViewFolder::toggleOpen();
+
+ // do item's selection when opened
+ if (LLFolderViewFolder::isOpen())
+ {
+ getParentFolder()->setSelection(this, true);
+ }
+ mContainer->reSelectConversation();
+ }
+}
+
+void LLConversationViewSession::toggleCollapsedMode(bool is_collapsed)
+{
+ mCollapsedMode = is_collapsed;
+
+ // 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(!mCollapsedMode);
+
+ S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation();
+
+ mItemPanel->translate(mCollapsedMode ? -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
+ LLFolderViewModelItem* item = mViewModelItem;
+ LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID();
+ LLFloater* session_floater = LLFloaterIMSessionTab::getConversation(session_uuid);
+
+ 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(bool visible)
+{
+ mCallIconLayoutPanel->setVisible(visible && LLVoiceChannel::getCurrentVoiceChannel()->getSessionID().isNull());
+ requestArrange();
+}
+
+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());
+ }
+
+ // Update all speaking indicators
+ LLSpeakingIndicatorManager::updateSpeakingIndicators();
+
+ // we should show indicator for specified voice session only if this is current channel. EXT-5562.
+ if (!mIsInActiveVoiceChannel)
+ {
+ if (mSpeakingIndicator)
+ {
+ mSpeakingIndicator->setVisible(false);
+ }
+ LLConversationViewParticipant* participant = NULL;
+ items_t::const_iterator iter;
+ for (iter = getItemsBegin(); iter != getItemsEnd(); iter++)
+ {
+ participant = dynamic_cast<LLConversationViewParticipant*>(*iter);
+ if (participant)
+ {
+ participant->hideSpeakingIndicator();
+ }
+ }
+ }
+ requestArrange();
+ // Do the regular upstream refresh
+ LLFolderViewFolder::refresh();
+}
+
+void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id)
+{
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem());
+
+ if (vmi)
+ {
+ mIsInActiveVoiceChannel = vmi->getUUID() == session_id;
+ mCallIconLayoutPanel->setVisible(mIsInActiveVoiceChannel);
+ }
+}
+
+//
+// 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)
+{
+}
+
+LLConversationViewParticipant::~LLConversationViewParticipant()
+{
+ mActiveVoiceChannelConnection.disconnect();
+}
+
+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 sFgDisabledColor = LLUIColorTable::instance().getColor("MenuItemDisabledColor", DEFAULT_WHITE);
+ static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
+ static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
+ static LLUIColor sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", 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;
+ LLLocalSpeakerMgr *speakerMgr = LLLocalSpeakerMgr::getInstance();
+
+ if (speakerMgr && speakerMgr->isSpeakerToBeRemoved(mUUID))
+ {
+ color = sFgDisabledColor;
+ }
+ else
+ {
+ color = mIsSelected ? sHighlightFgColor : sFgColor;
+ }
+
+ drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
+ drawLabel(font, text_left, y, color, right_x);
+ refresh();
+
+ LLView::draw();
+}
+
+// virtual
+S32 LLConversationViewParticipant::arrange(S32* width, S32* height)
+{
+ //Need to call arrange first since it computes value used in getIndentation()
+ S32 arranged = LLFolderViewItem::arrange(width, height);
+
+ //Adjusts the avatar icon based upon the indentation
+ LLRect avatarRect(getIndentation(),
+ mAvatarIcon->getRect().mTop,
+ getIndentation() + mAvatarIcon->getRect().getWidth(),
+ mAvatarIcon->getRect().mBottom);
+ mAvatarIcon->setShape(avatarRect);
+
+ //Since dimensions changed, adjust the children (info button, speaker indicator)
+ updateChildren();
+
+ return arranged;
+}
+
+void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder)
+{
+ // Add the item to the folder (conversation)
+ LLFolderViewItem::addToFolder(folder);
+
+ // Retrieve the folder (conversation) UUID, which is also the speaker session UUID
+ LLConversationItem* vmi = getParentFolder() ? dynamic_cast<LLConversationItem*>(getParentFolder()->getViewModelItem()) : NULL;
+ if (vmi)
+ {
+ addToSession(vmi->getUUID());
+ }
+}
+
+void LLConversationViewParticipant::addToSession(const LLUUID& session_id)
+{
+ //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, session_id);
+}
+
+void LLConversationViewParticipant::onInfoBtnClick()
+{
+ LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mUUID));
+}
+
+BOOL LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL result = LLFolderViewItem::handleMouseDown(x, y, mask);
+
+ if(result && getRoot())
+ {
+ if(getRoot()->getCurSelectedItem() == this)
+ {
+ LLConversationItem* vmi = getParentFolder() ? dynamic_cast<LLConversationItem*>(getParentFolder()->getViewModelItem()) : NULL;
+ LLUUID session_id = vmi? vmi->getUUID() : LLUUID();
+
+ LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+ im_container->setSelectedSession(session_id);
+ im_container->flashConversationItemWidget(session_id,false);
+ im_container->selectFloater(session_floater);
+ im_container->collapseMessagesPane(false);
+ }
+ }
+ return result;
+}
+
+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 getIndentation() + mAvatarIcon->getRect().getWidth() + 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;
+}
+
+void LLConversationViewParticipant::hideSpeakingIndicator()
+{
+ mSpeakingIndicator->setVisible(false);
+}
+
+// EOF
+
diff --git a/indra/newview/llconversationview.h b/indra/newview/llconversationview.h
new file mode 100755
index 0000000000..3eb2e63792
--- /dev/null
+++ b/indra/newview/llconversationview.h
@@ -0,0 +1,177 @@
+/**
+ * @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 "../llui/llfolderviewitem.h"
+
+#include "llavatariconctrl.h"
+#include "../llui/llbutton.h"
+#include "lloutputmonitorctrl.h"
+
+class LLTextBox;
+class LLFloaterIMContainer;
+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<LLFloaterIMContainer*> container;
+
+ Params();
+ };
+
+protected:
+ friend class LLUICtrlFactory;
+ LLConversationViewSession( const Params& p );
+
+ /*virtual*/ bool isHighlightAllowed();
+ /*virtual*/ bool isHighlightActive();
+ /*virtual*/ bool isFlashing() { return mFlashStateOn; }
+
+ LLFloaterIMContainer* mContainer;
+
+public:
+ virtual ~LLConversationViewSession();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+
+ /*virtual*/ S32 arrange(S32* width, S32* height);
+
+ /*virtual*/ void toggleOpen();
+
+ /*virtual*/ bool isCollapsed() { return mCollapsedMode; }
+
+ void toggleCollapsedMode(bool is_collapsed);
+
+ void setVisibleIfDetached(BOOL visible);
+ LLConversationViewParticipant* findParticipant(const LLUUID& participant_id);
+
+ void showVoiceIndicator(bool visible);
+
+ virtual void refresh();
+
+ /*virtual*/ void setFlashState(bool flash_state);
+
+private:
+
+ void onCurrentVoiceSessionChanged(const LLUUID& session_id);
+ void startFlashing();
+ void selectConversationItem();
+
+ LLPanel* mItemPanel;
+ LLPanel* mCallIconLayoutPanel;
+ LLTextBox* mSessionTitle;
+ LLOutputMonitorCtrl* mSpeakingIndicator;
+ LLFlashTimer* mFlashTimer;
+ bool mFlashStateOn;
+ bool mFlashStarted;
+
+ bool mCollapsedMode;
+ bool mHasArrow;
+
+ bool mIsInActiveVoiceChannel;
+
+ 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<LLFloaterIMContainer*> container;
+ Optional<LLUUID> participant_id;
+ Optional<LLAvatarIconCtrl::Params> avatar_icon;
+ Optional<LLButton::Params> info_button;
+ Optional<LLOutputMonitorCtrl::Params> output_monitor;
+
+ Params();
+ };
+
+ virtual ~LLConversationViewParticipant( void );
+
+ bool hasSameValue(const LLUUID& uuid) { return (uuid == mUUID); }
+ void addToFolder(LLFolderViewFolder* folder);
+ void addToSession(const LLUUID& session_id);
+
+ void onMouseEnter(S32 x, S32 y, MASK mask);
+ void onMouseLeave(S32 x, S32 y, MASK mask);
+
+ /*virtual*/ S32 getLabelXPos();
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ void hideSpeakingIndicator();
+
+protected:
+ friend class LLUICtrlFactory;
+ LLConversationViewParticipant( const Params& p );
+ void initFromParams(const Params& params);
+ BOOL postBuild();
+ /*virtual*/ void draw();
+ /*virtual*/ S32 arrange(S32* width, S32* height);
+
+ 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);
+
+ boost::signals2::connection mActiveVoiceChannelConnection;
+};
+
+#endif // LL_LLCONVERSATIONVIEW_H
diff --git a/indra/newview/lldeferredsounds.cpp b/indra/newview/lldeferredsounds.cpp
new file mode 100644
index 0000000000..9416e7cd29
--- /dev/null
+++ b/indra/newview/lldeferredsounds.cpp
@@ -0,0 +1,45 @@
+/**
+* @file lldeferredsounds.cpp
+* @brief Implementation of lldeferredsounds
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, 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 "lldeferredsounds.h"
+
+#include "llaudioengine.h"
+
+void LLDeferredSounds::deferSound(SoundData& sound)
+{
+ soundVector.push_back(sound);
+}
+void LLDeferredSounds::playdeferredSounds()
+{
+ while(soundVector.size())
+ {
+ gAudiop->triggerSound(soundVector.back());
+ soundVector.pop_back();
+ }
+}
diff --git a/indra/newview/lldeferredsounds.h b/indra/newview/lldeferredsounds.h
new file mode 100644
index 0000000000..bf1eb62957
--- /dev/null
+++ b/indra/newview/lldeferredsounds.h
@@ -0,0 +1,46 @@
+/**
+* @file lldeferredsounds.h
+* @brief Header file for lldeferredsounds
+* @author Gilbert@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, 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_LLDEFERREDSOUNDS_H
+#define LL_LLDEFERREDSOUNDS_H
+
+#include "llsingleton.h"
+
+struct SoundData;
+
+class LLDeferredSounds : public LLSingleton<LLDeferredSounds>
+{
+private:
+ std::vector<SoundData> soundVector;
+public:
+ //Add sounds to be played once progress bar is hidden (such as after teleport or loading screen)
+ void deferSound(SoundData& sound);
+
+ void playdeferredSounds();
+};
+
+#endif // LL_LLDEFERREDSOUNDS_H
+
diff --git a/indra/newview/lldelayedgestureerror.cpp b/indra/newview/lldelayedgestureerror.cpp
index 80e7c9f1b2..ef1b644ad4 100644
--- a/indra/newview/lldelayedgestureerror.cpp
+++ b/indra/newview/lldelayedgestureerror.cpp
@@ -113,9 +113,11 @@ bool LLDelayedGestureError::doDialog(const LLErrorEntry &ent, bool uuid_ok)
}
}
-
- LLNotificationsUtil::add(ent.mNotifyName, args);
-
+ if(!LLApp::isQuitting())
+ {
+ LLNotificationsUtil::add(ent.mNotifyName, args);
+ }
+
return true;
}
diff --git a/indra/newview/lldonotdisturbnotificationstorage.cpp b/indra/newview/lldonotdisturbnotificationstorage.cpp
new file mode 100644
index 0000000000..be20adeb8a
--- /dev/null
+++ b/indra/newview/lldonotdisturbnotificationstorage.cpp
@@ -0,0 +1,346 @@
+/**
+* @file lldonotdisturbnotificationstorage.cpp
+* @brief Implementation of lldonotdisturbnotificationstorage
+* @author Stinson@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 "lldonotdisturbnotificationstorage.h"
+
+#include "llcommunicationchannel.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "llfloaterreg.h"
+#include "llimview.h"
+#include "llnotifications.h"
+#include "llnotificationhandler.h"
+#include "llnotificationstorage.h"
+#include "llscriptfloater.h"
+#include "llsd.h"
+#include "llsingleton.h"
+#include "lluuid.h"
+
+static const F32 DND_TIMER = 3.0;
+const char * LLDoNotDisturbNotificationStorage::toastName = "IMToast";
+const char * LLDoNotDisturbNotificationStorage::offerName = "UserGiveItem";
+
+LLDoNotDisturbNotificationStorageTimer::LLDoNotDisturbNotificationStorageTimer() : LLEventTimer(DND_TIMER)
+{
+
+}
+
+LLDoNotDisturbNotificationStorageTimer::~LLDoNotDisturbNotificationStorageTimer()
+{
+
+}
+
+BOOL LLDoNotDisturbNotificationStorageTimer::tick()
+{
+ LLDoNotDisturbNotificationStorage * doNotDisturbNotificationStorage = LLDoNotDisturbNotificationStorage::getInstance();
+
+ if(doNotDisturbNotificationStorage
+ && doNotDisturbNotificationStorage->getDirty())
+ {
+ doNotDisturbNotificationStorage->saveNotifications();
+ }
+ return FALSE;
+}
+
+LLDoNotDisturbNotificationStorage::LLDoNotDisturbNotificationStorage()
+ : LLSingleton<LLDoNotDisturbNotificationStorage>()
+ , LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml"))
+ , mDirty(false)
+{
+ nameToPayloadParameterMap[toastName] = "SESSION_ID";
+ nameToPayloadParameterMap[offerName] = "object_id";
+}
+
+LLDoNotDisturbNotificationStorage::~LLDoNotDisturbNotificationStorage()
+{
+}
+
+void LLDoNotDisturbNotificationStorage::initialize()
+{
+ getCommunicationChannel()->connectFailedFilter(boost::bind(&LLDoNotDisturbNotificationStorage::onChannelChanged, this, _1));
+}
+
+bool LLDoNotDisturbNotificationStorage::getDirty()
+{
+ return mDirty;
+}
+
+void LLDoNotDisturbNotificationStorage::resetDirty()
+{
+ mDirty = false;
+}
+
+static LLFastTimer::DeclareTimer FTM_SAVE_DND_NOTIFICATIONS("Save DND Notifications");
+
+void LLDoNotDisturbNotificationStorage::saveNotifications()
+{
+ LLFastTimer _(FTM_SAVE_DND_NOTIFICATIONS);
+
+ LLNotificationChannelPtr channelPtr = getCommunicationChannel();
+ const LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get());
+ llassert(commChannel != NULL);
+
+ LLSD output = LLSD::emptyMap();
+ LLSD& data = output["data"];
+ data = LLSD::emptyArray();
+
+ for (LLCommunicationChannel::history_list_t::const_iterator historyIter = commChannel->beginHistory();
+ historyIter != commChannel->endHistory(); ++historyIter)
+ {
+ LLNotificationPtr notificationPtr = historyIter->second;
+
+ if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired())
+ {
+ data.append(notificationPtr->asLLSD(true));
+ }
+ }
+
+ writeNotifications(output);
+
+ resetDirty();
+}
+
+static LLFastTimer::DeclareTimer FTM_LOAD_DND_NOTIFICATIONS("Load DND Notifications");
+
+void LLDoNotDisturbNotificationStorage::loadNotifications()
+{
+ LLFastTimer _(FTM_LOAD_DND_NOTIFICATIONS);
+
+ LLSD input;
+ if (!readNotifications(input) ||input.isUndefined())
+ {
+ return;
+ }
+
+ LLSD& data = input["data"];
+ if (data.isUndefined())
+ {
+ return;
+ }
+
+ LLNotifications& instance = LLNotifications::instance();
+ bool imToastExists = false;
+ bool group_ad_hoc_toast_exists = false;
+ S32 toastSessionType;
+ bool offerExists = false;
+
+ for (LLSD::array_const_iterator notification_it = data.beginArray();
+ notification_it != data.endArray();
+ ++notification_it)
+ {
+ LLSD notification_params = *notification_it;
+ const LLUUID& notificationID = notification_params["id"];
+ std::string notificationName = notification_params["name"];
+ LLNotificationPtr notification = instance.find(notificationID);
+
+ if(notificationName == toastName)
+ {
+ toastSessionType = notification_params["payload"]["SESSION_TYPE"];
+ if(toastSessionType == LLIMModel::LLIMSession::P2P_SESSION)
+ {
+ imToastExists = true;
+ }
+ //Don't add group/ad-hoc messages to the notification system because
+ //this means the group/ad-hoc session has to be re-created
+ else if(toastSessionType == LLIMModel::LLIMSession::GROUP_SESSION
+ || toastSessionType == LLIMModel::LLIMSession::ADHOC_SESSION)
+ {
+ //Just allows opening the conversation log for group/ad-hoc messages upon startup
+ group_ad_hoc_toast_exists = true;
+ continue;
+ }
+ }
+ else if(notificationName == offerName)
+ {
+ offerExists = true;
+ }
+
+ //Notification already exists due to persistent storage adding it first into the notification system
+ if(notification)
+ {
+ notification->setDND(true);
+ instance.update(instance.find(notificationID));
+ }
+ //New notification needs to be added
+ else
+ {
+ notification = (LLNotificationPtr) new LLNotification(notification_params.with("is_dnd", true));
+ LLNotificationResponderInterface* responder = createResponder(notification_params["responder_sd"]["responder_type"], notification_params["responder_sd"]);
+ if (responder == NULL)
+ {
+ LL_WARNS("LLDoNotDisturbNotificationStorage") << "cannot create responder for notification of type '"
+ << notification->getType() << "'" << LL_ENDL;
+ }
+ else
+ {
+ LLNotificationResponderPtr responderPtr(responder);
+ notification->setResponseFunctor(responderPtr);
+ }
+
+ instance.add(notification);
+ }
+
+ }
+
+ if(imToastExists)
+ {
+ LLFloaterReg::showInstance("im_container");
+ }
+
+ if(group_ad_hoc_toast_exists)
+ {
+ LLFloaterReg::showInstance("conversation");
+ }
+
+ if(imToastExists || group_ad_hoc_toast_exists || offerExists)
+ {
+ make_ui_sound_deferred("UISndNewIncomingIMSession");
+ }
+
+ //writes out empty .xml file (since LLCommunicationChannel::mHistory is empty)
+ saveNotifications();
+}
+
+void LLDoNotDisturbNotificationStorage::updateNotifications()
+{
+
+ LLNotificationChannelPtr channelPtr = getCommunicationChannel();
+ LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get());
+ llassert(commChannel != NULL);
+
+ LLNotifications& instance = LLNotifications::instance();
+ bool imToastExists = false;
+ bool offerExists = false;
+
+ for (LLCommunicationChannel::history_list_t::const_iterator it = commChannel->beginHistory();
+ it != commChannel->endHistory();
+ ++it)
+ {
+ LLNotificationPtr notification = it->second;
+ std::string notificationName = notification->getName();
+
+ if(notificationName == toastName)
+ {
+ imToastExists = true;
+ }
+ else if(notificationName == offerName)
+ {
+ offerExists = true;
+ }
+
+ //Notification already exists in notification pipeline (same instance of app running)
+ if (notification)
+ {
+ notification->setDND(true);
+ instance.update(notification);
+ }
+ }
+
+ if(imToastExists)
+ {
+ LLFloaterReg::showInstance("im_container");
+ }
+
+ if(imToastExists || offerExists)
+ {
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
+
+ //When exit DND mode, write empty notifications file
+ if(commChannel->getHistorySize())
+ {
+ commChannel->clearHistory();
+ saveNotifications();
+ }
+}
+
+LLNotificationChannelPtr LLDoNotDisturbNotificationStorage::getCommunicationChannel() const
+{
+ LLNotificationChannelPtr channelPtr = LLNotifications::getInstance()->getChannel("Communication");
+ llassert(channelPtr);
+ return channelPtr;
+}
+
+void LLDoNotDisturbNotificationStorage::removeNotification(const char * name, const LLUUID& id)
+{
+ LLNotifications& instance = LLNotifications::instance();
+ LLNotificationChannelPtr channelPtr = getCommunicationChannel();
+ LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get());
+ LLNotificationPtr notification;
+ LLSD payload;
+ LLUUID notificationObjectID;
+ std::string notificationName;
+ std::string payloadVariable = nameToPayloadParameterMap[name];
+ LLCommunicationChannel::history_list_t::iterator it;
+ std::vector<LLCommunicationChannel::history_list_t::iterator> itemsToRemove;
+
+ //Find notification with the matching session id
+ for (it = commChannel->beginHistory();
+ it != commChannel->endHistory();
+ ++it)
+ {
+ notification = it->second;
+ payload = notification->getPayload();
+ notificationObjectID = payload[payloadVariable].asUUID();
+ notificationName = notification->getName();
+
+ if((notificationName == name)
+ && id == notificationObjectID)
+ {
+ itemsToRemove.push_back(it);
+ }
+ }
+
+
+ //Remove the notifications
+ if(itemsToRemove.size())
+ {
+ while(itemsToRemove.size())
+ {
+ it = itemsToRemove.back();
+ notification = it->second;
+ commChannel->removeItemFromHistory(notification);
+ instance.cancel(notification);
+ itemsToRemove.pop_back();
+ }
+ //Trigger saving of notifications to xml once all have been removed
+ saveNotifications();
+ }
+}
+
+
+bool LLDoNotDisturbNotificationStorage::onChannelChanged(const LLSD& pPayload)
+{
+ if (pPayload["sigtype"].asString() != "load")
+ {
+ mDirty = true;
+ }
+
+ return false;
+}
diff --git a/indra/newview/lldonotdisturbnotificationstorage.h b/indra/newview/lldonotdisturbnotificationstorage.h
new file mode 100644
index 0000000000..6e68b0d1be
--- /dev/null
+++ b/indra/newview/lldonotdisturbnotificationstorage.h
@@ -0,0 +1,78 @@
+/**
+* @file lldonotdisturbnotificationstorage.h
+* @brief Header file for lldonotdisturbnotificationstorage
+* @author Stinson@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_LLDONOTDISTURBNOTIFICATIONSTORAGE_H
+#define LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H
+
+#include "llerror.h"
+#include "lleventtimer.h"
+#include "llnotifications.h"
+#include "llnotificationstorage.h"
+#include "llsingleton.h"
+
+class LLSD;
+
+class LLDoNotDisturbNotificationStorageTimer : public LLEventTimer
+{
+public:
+ LLDoNotDisturbNotificationStorageTimer();
+ ~LLDoNotDisturbNotificationStorageTimer();
+
+public:
+ BOOL tick();
+};
+
+class LLDoNotDisturbNotificationStorage : public LLSingleton<LLDoNotDisturbNotificationStorage>, public LLNotificationStorage
+{
+ LOG_CLASS(LLDoNotDisturbNotificationStorage);
+public:
+ static const char * toastName;
+ static const char * offerName;
+
+ LLDoNotDisturbNotificationStorage();
+ ~LLDoNotDisturbNotificationStorage();
+
+ void initialize();
+ bool getDirty();
+ void resetDirty();
+ void saveNotifications();
+ void loadNotifications();
+ void updateNotifications();
+ void removeNotification(const char * name, const LLUUID& id);
+
+protected:
+
+private:
+ bool mDirty;
+ LLDoNotDisturbNotificationStorageTimer mTimer;
+
+ LLNotificationChannelPtr getCommunicationChannel() const;
+ bool onChannelChanged(const LLSD& pPayload);
+ std::map<std::string, std::string> nameToPayloadParameterMap;
+};
+
+#endif // LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H
+
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
index d041baea90..4b0d3b361d 100644
--- a/indra/newview/lldrawable.cpp
+++ b/indra/newview/lldrawable.cpp
@@ -441,7 +441,7 @@ void LLDrawable::makeActive()
}
llassert(isAvatar() || isRoot() || mParent->isActive());
-}
+ }
void LLDrawable::makeStatic(BOOL warning_enabled)
@@ -455,7 +455,7 @@ void LLDrawable::makeStatic(BOOL warning_enabled)
//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++)
@@ -577,6 +577,12 @@ F32 LLDrawable::updateXform(BOOL undamped)
mVObjp->dirtySpatialGroup();
}
}
+ else if (!isRoot() &&
+ ((dist_vec_squared(old_pos, target_pos) > 0.f)
+ || (1.f - dot(old_rot, target_rot)) > 0.f))
+ { //fix for BUG-840, MAINT-2275, MAINT-1742, MAINT-2247
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_POSITION, TRUE);
+ }
else if (!getVOVolume() && !isAvatar())
{
movePartition();
@@ -642,20 +648,10 @@ BOOL LLDrawable::updateMove()
{
return FALSE;
}
-
+
makeActive();
- BOOL done;
-
- if (isState(MOVE_UNDAMPED))
- {
- done = updateMoveUndamped();
- }
- else
- {
- done = updateMoveDamped();
- }
- return done;
+ return isState(MOVE_UNDAMPED) ? updateMoveUndamped() : updateMoveDamped();
}
BOOL LLDrawable::updateMoveUndamped()
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..e30dd51acb 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,295 @@ 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 );
+ // Note : use the "John Doe" and not the "john.doe" version of the name
+ // as we'll compare it with the stored credentials in the login panel.
+ lldebugs << "Saved favorites for " << av_name.getUserName() << llendl;
+ fav_llsd[av_name.getUserName()] = 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 );
+ // Note : use the "John Doe" and not the "john.doe" version of the name.
+ // See saveFavoritesSLURLs() here above for the reason why.
+ lldebugs << "Removed favorites for " << av_name.getUserName() << llendl;
+ if (fav_llsd.has(av_name.getUserName()))
+ {
+ fav_llsd.erase(av_name.getUserName());
+ }
+
+ 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 77a0cdffce..a5ee4607a1 100644
--- a/indra/newview/llflexibleobject.cpp
+++ b/indra/newview/llflexibleobject.cpp
@@ -342,10 +342,10 @@ void LLVolumeImplFlexible::doIdleUpdate()
if (drawablep)
{
//LLFastTimer ftm(FTM_FLEXIBLE_UPDATE);
-
+
//ensure drawable is active
drawablep->makeActive();
-
+
if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
{
bool visible = drawablep->isVisible();
@@ -381,21 +381,31 @@ void LLVolumeImplFlexible::doIdleUpdate()
id = parent->getVolumeInterfaceID();
}
- if ((LLDrawable::getCurrentFrame()+id)%update_period == 0)
- {
+ if (mVO->isRootEdit())
+ {
+ id = mID;
+ }
+ else
+ {
+ LLVOVolume* parent = (LLVOVolume*) mVO->getParent();
+ id = parent->getVolumeInterfaceID();
+ }
+
+ if ((LLDrawable::getCurrentFrame()+id)%update_period == 0)
+ {
sUpdateDelay[mInstanceIndex] = (S32) update_period-1;
- updateRenderRes();
+ updateRenderRes();
- gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE);
- }
- }
+ gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE);
}
+ }
+ }
else
{
sUpdateDelay[mInstanceIndex] = (S32) update_period;
- }
- }
+ }
+}
}
}
@@ -420,7 +430,6 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
if ((mSimulateRes == 0 || !mInitialized) && mVO->mDrawable->isVisible())
{
BOOL force_update = mSimulateRes == 0 ? TRUE : FALSE;
-
doIdleUpdate();
if (!force_update || !gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
@@ -435,7 +444,7 @@ void LLVolumeImplFlexible::doFlexibleUpdate()
return ;
}
- // stinson 11/12/2012: Need to check with davep on the following.
+ // Fix for MAINT-1894
// Skipping the flexible update if render res is negative. If we were to continue with a negative value,
// the subsequent S32 num_render_sections = 1<<mRenderRes; code will specify a really large number of
// render sections which will then create a length exception in the std::vector::resize() method.
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
index 0290e7cdf0..f7dd4a4a6b 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()
@@ -288,9 +307,9 @@ void LLFloaterAvatarPicker::populateNearMe()
else
{
element["columns"][0]["column"] = "name";
- element["columns"][0]["value"] = av_name.mDisplayName;
+ element["columns"][0]["value"] = av_name.getDisplayName();
element["columns"][1]["column"] = "username";
- element["columns"][1]["value"] = av_name.mUsername;
+ element["columns"][1]["value"] = av_name.getUserName();
sAvatarNameMap[av] = av_name;
}
@@ -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);
@@ -426,9 +505,7 @@ void LLFloaterAvatarPicker::find()
LLViewerRegion* region = gAgent.getRegion();
url = region->getCapability("AvatarPickerSearch");
// Prefer use of capabilities to search on both SLID and display name
- // but allow display name search to be manually turned off for test
- if (!url.empty()
- && LLAvatarNameCache::useDisplayNames())
+ if (!url.empty())
{
// capability urls don't end in '/', but we need one to parse
// query parameters correctly
@@ -439,7 +516,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 +658,36 @@ 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.fromString(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 +702,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 d6ebe44daa..a03425649f 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..4c910c5655
--- /dev/null
+++ b/indra/newview/llfloaterconversationlog.cpp
@@ -0,0 +1,134 @@
+/**
+ * @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));
+
+ return LLFloater::postBuild();
+}
+
+void LLFloaterConversationLog::draw()
+{
+ getChild<LLMenuButton>("conversations_gear_btn")->setEnabled(mConversationLogList->getSelectedItem() != NULL);
+ 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;
+}
+
diff --git a/indra/newview/llfloaterconversationlog.h b/indra/newview/llfloaterconversationlog.h
new file mode 100644
index 0000000000..e971330f3d
--- /dev/null
+++ b/indra/newview/llfloaterconversationlog.h
@@ -0,0 +1,56 @@
+/**
+ * @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);
+
+ LLConversationLogList* mConversationLogList;
+};
+
+
+#endif /* LLFLOATERCONVERSATIONLOG_H_ */
diff --git a/indra/newview/llfloaterconversationpreview.cpp b/indra/newview/llfloaterconversationpreview.cpp
new file mode 100644
index 0000000000..a3d715530d
--- /dev/null
+++ b/indra/newview/llfloaterconversationpreview.cpp
@@ -0,0 +1,186 @@
+/**
+ * @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 "llfloaterimnearbychat.h"
+#include "llspinctrl.h"
+#include "lltrans.h"
+
+const std::string LL_FCP_COMPLETE_NAME("complete_name");
+const std::string LL_FCP_ACCOUNT_NAME("user_name");
+
+LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id)
+: LLFloater(session_id),
+ mChatHistory(NULL),
+ mSessionID(session_id.asUUID()),
+ mCurrentPage(0),
+ mPageSize(gSavedSettings.getS32("ConversationHistoryPageSize")),
+ mAccountName(session_id[LL_FCP_ACCOUNT_NAME]),
+ mCompleteName(session_id[LL_FCP_COMPLETE_NAME])
+{
+}
+
+BOOL LLFloaterConversationPreview::postBuild()
+{
+ mChatHistory = getChild<LLChatHistory>("chat_history");
+
+ const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID);
+ std::string name;
+ std::string file;
+
+ if (mAccountName != "")
+ {
+ name = mCompleteName;
+ file = mAccountName;
+ }
+ else 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 = LLFloaterIMNearbyChat::isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT;
+ }
+
+ LLSD chat_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"] = gSavedSettings.getBOOL("IMShowNamesForP2PConv");
+
+ mChatHistory->appendMessage(chat,chat_args);
+ }
+
+}
+
+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..b17ae84b63
--- /dev/null
+++ b/indra/newview/llfloaterconversationpreview.h
@@ -0,0 +1,64 @@
+/**
+ * @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"
+
+extern const std::string LL_FCP_COMPLETE_NAME; //"complete_name"
+extern const std::string LL_FCP_ACCOUNT_NAME; //"user_name"
+
+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;
+ std::string mAccountName;
+ std::string mCompleteName;
+};
+
+#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */
diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp
index ac8f107928..e2cef5630b 100644
--- a/indra/newview/llfloaterdisplayname.cpp
+++ b/indra/newview/llfloaterdisplayname.cpp
@@ -44,7 +44,7 @@ class LLFloaterDisplayName : public LLFloater
{
public:
LLFloaterDisplayName(const LLSD& key);
- virtual ~LLFloaterDisplayName() {};
+ virtual ~LLFloaterDisplayName() { }
/*virtual*/ BOOL postBuild();
void onSave();
void onReset();
@@ -58,8 +58,8 @@ private:
const LLSD& content);
};
-LLFloaterDisplayName::LLFloaterDisplayName(const LLSD& key)
- : LLFloater(key)
+LLFloaterDisplayName::LLFloaterDisplayName(const LLSD& key) :
+ LLFloater(key)
{
}
@@ -122,10 +122,6 @@ void LLFloaterDisplayName::onCacheSetName(bool success,
LLSD args;
args["DISPLAY_NAME"] = content["display_name"];
LLNotificationsUtil::add("SetDisplayNameSuccess", args);
-
- // Re-fetch my name, as it may have been sanitized by the service
- //LLAvatarNameCache::get(getAvatarId(),
- // boost::bind(&LLPanelMyProfileEdit::onNameCache, this, _1, _2));
return;
}
@@ -164,10 +160,9 @@ void LLFloaterDisplayName::onCancel()
void LLFloaterDisplayName::onReset()
{
- if (LLAvatarNameCache::useDisplayNames())
+ if (LLAvatarNameCache::hasNameLookupURL())
{
- LLViewerDisplayName::set("",
- boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3));
+ LLViewerDisplayName::set("",boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3));
}
else
{
@@ -199,10 +194,9 @@ void LLFloaterDisplayName::onSave()
return;
}
- if (LLAvatarNameCache::useDisplayNames())
+ if (LLAvatarNameCache::hasNameLookupURL())
{
- LLViewerDisplayName::set(display_name_utf8,
- boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3));
+ LLViewerDisplayName::set(display_name_utf8,boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3));
}
else
{
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/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp
new file mode 100644
index 0000000000..5e0cd8ef78
--- /dev/null
+++ b/indra/newview/llfloaterimcontainer.cpp
@@ -0,0 +1,1982 @@
+/**
+ * @file llfloaterimcontainer.cpp
+ * @brief Multifloater containing active IM sessions in separate tab container tabs
+ *
+ * $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 "llfloaterimsession.h"
+#include "llfloaterimcontainer.h"
+
+#include "llfloaterreg.h"
+#include "lllayoutstack.h"
+#include "llfloaterimnearbychat.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llavatariconctrl.h"
+#include "llavatarnamecache.h"
+#include "llcallbacklist.h"
+#include "lldonotdisturbnotificationstorage.h"
+#include "llgroupactions.h"
+#include "llgroupiconctrl.h"
+#include "llflashtimer.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"
+#include "llviewerobjectlist.h"
+
+//
+// LLFloaterIMContainer
+//
+LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& params /*= getDefaultParams()*/)
+: LLMultiFloater(seed, params),
+ mExpandCollapseBtn(NULL),
+ mConversationsRoot(NULL),
+ mConversationsEventStream("ConversationsEvents"),
+ mInitialized(false)
+{
+ mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2));
+ mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2));
+
+ mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMContainer::checkContextMenuItem, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMContainer::enableContextMenuItem, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.VisibleItem", boost::bind(&LLFloaterIMContainer::visibleContextMenuItem, this, _2));
+ mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelected, this, _2));
+
+ mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLFloaterIMContainer::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);
+}
+
+LLFloaterIMContainer::~LLFloaterIMContainer()
+{
+ mConversationsEventStream.stopListening("ConversationsRefresh");
+
+ gIdleCallbacks.deleteFunction(idle, this);
+
+ mNewMessageConnection.disconnect();
+ LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
+
+ if (mMicroChangedSignal.connected())
+ {
+ mMicroChangedSignal.disconnect();
+ }
+
+ gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed());
+ gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());
+
+ if (!LLSingleton<LLIMMgr>::destroyed())
+ {
+ LLIMMgr::getInstance()->removeSessionObserver(this);
+ }
+}
+
+void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg)
+{
+ addConversationListItem(session_id);
+ LLFloaterIMSessionTab::addToHost(session_id);
+}
+
+void LLFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
+{
+ selectConversationPair(session_id, true);
+ collapseMessagesPane(false);
+}
+
+void LLFloaterIMContainer::sessionVoiceOrIMStarted(const LLUUID& session_id)
+{
+ addConversationListItem(session_id);
+ LLFloaterIMSessionTab::addToHost(session_id);
+}
+
+void LLFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id)
+{
+ // The general strategy when a session id is modified is to delete all related objects and create them anew.
+
+ // Note however that the LLFloaterIMSession has its session id updated through a call to sessionInitReplyReceived()
+ // and do not need to be deleted and recreated (trying this creates loads of problems). We do need however to suppress
+ // its related mSessions record as it's indexed with the wrong id.
+ // Grabbing the updated LLFloaterIMSession and readding it in mSessions will eventually be done by addConversationListItem().
+ mSessions.erase(old_session_id);
+
+ // Delete the model and participants related to the old session
+ bool change_focus = removeConversationListItem(old_session_id);
+
+ // Create a new conversation with the new id
+ addConversationListItem(new_session_id, change_focus);
+ LLFloaterIMSessionTab::addToHost(new_session_id);
+}
+
+void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id)
+{
+ removeConversationListItem(session_id);
+}
+
+// static
+void LLFloaterIMContainer::onCurrentChannelChanged(const LLUUID& session_id)
+{
+ if (session_id != LLUUID::null)
+ {
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
+ }
+}
+
+BOOL LLFloaterIMContainer::postBuild()
+{
+ mOrigMinWidth = getMinWidth();
+ mOrigMinHeight = getMinHeight();
+
+ mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLFloaterIMContainer::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"));
+ mStubPanel = getChild<LLPanel>("stub_panel");
+ mStubTextBox = getChild<LLTextBox>("stub_textbox");
+ mStubTextBox->setURLClickedCallback(boost::bind(&LLFloaterIMContainer::returnFloaterToHost, this));
+
+ 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(&LLFloaterIMContainer::doToSelected, this, LLSD("im")));
+
+ // The resize limits for LLFloaterIMContainer should be updated, based on current values of width of conversation and message panels
+ mConversationsPane->getResizeBar()->setResizeListener(boost::bind(&LLFloaterIMContainer::assignResizeLimits, this));
+
+ // 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(&LLFloaterIMContainer::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(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this));
+ mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn");
+ mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this));
+ getChild<LLButton>("speak_btn")->setClickedCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonClicked, this));
+
+ childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this));
+
+ collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed"));
+ collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed"), false);
+ LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false));
+ mMicroChangedSignal = LLVoiceClient::getInstance()->MicroChangedCallback(boost::bind(&LLFloaterIMContainer::updateSpeakBtnState, this));
+
+ if (! mMessagesPane->isCollapsed() && ! mConversationsPane->isCollapsed())
+ {
+ S32 conversations_panel_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth");
+ LLRect conversations_panel_rect = mConversationsPane->getRect();
+ conversations_panel_rect.mRight = conversations_panel_rect.mLeft + conversations_panel_width;
+ mConversationsPane->handleReshape(conversations_panel_rect, TRUE);
+ }
+
+ // Init the sort order now that the root had been created
+ setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder")));
+
+ // Keep the xml set title around for when we have to overwrite it
+ mGeneralTitle = getTitle();
+
+ 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(&LLFloaterIMContainer::processParticipantsStyleUpdate, this));
+
+ return TRUE;
+}
+
+void LLFloaterIMContainer::onOpen(const LLSD& key)
+{
+ LLMultiFloater::onOpen(key);
+ openNearbyChat();
+ reSelectConversation();
+ assignResizeLimits();
+}
+
+// virtual
+void LLFloaterIMContainer::addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainer::eInsertionPoint insertion_point)
+{
+ if(!floaterp) return;
+
+ // already here
+ if (floaterp->getHost() == this)
+ {
+ openFloater(floaterp->getKey());
+ return;
+ }
+
+ LLUUID session_id = floaterp->getKey();
+
+ // Add the floater
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+
+
+
+ LLIconCtrl* icon = 0;
+
+ if(gAgent.isInGroup(session_id, TRUE))
+ {
+ LLGroupIconCtrl::Params icon_params;
+ icon_params.group_id = session_id;
+ icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params);
+
+ mSessions[session_id] = floaterp;
+ floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id));
+ }
+ else
+ { LLUUID avatar_id = session_id.notNull()?
+ LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID();
+
+ LLAvatarIconCtrl::Params icon_params;
+ icon_params.avatar_id = avatar_id;
+ icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params);
+
+ mSessions[session_id] = floaterp;
+ floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id));
+ }
+
+ // forced resize of the floater
+ LLRect wrapper_rect = this->mTabContainer->getLocalRect();
+ floaterp->setRect(wrapper_rect);
+
+ mTabContainer->setTabImage(floaterp, icon);
+}
+
+
+void LLFloaterIMContainer::onCloseFloater(LLUUID& id)
+{
+ mSessions.erase(id);
+ setFocus(TRUE);
+}
+
+void LLFloaterIMContainer::onNewMessageReceived(const LLSD& data)
+{
+ LLUUID session_id = data["session_id"].asUUID();
+ LLFloater* floaterp = get_ptr_in_map(mSessions, session_id);
+ LLFloater* current_floater = LLMultiFloater::getActiveFloater();
+
+ if(floaterp && current_floater && floaterp != current_floater)
+ {
+ if(LLMultiFloater::isFloaterFlashing(floaterp))
+ LLMultiFloater::setFloaterFlashing(floaterp, FALSE);
+ LLMultiFloater::setFloaterFlashing(floaterp, TRUE);
+ }
+}
+
+void LLFloaterIMContainer::onStubCollapseButtonClicked()
+{
+ collapseMessagesPane(true);
+}
+
+void LLFloaterIMContainer::onSpeakButtonClicked()
+{
+ LLAgent::toggleMicrophone("speak");
+ updateSpeakBtnState();
+}
+void LLFloaterIMContainer::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());
+ }
+ reSelectConversation();
+}
+
+LLFloaterIMContainer* LLFloaterIMContainer::findInstance()
+{
+ return LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
+}
+
+LLFloaterIMContainer* LLFloaterIMContainer::getInstance()
+{
+ return LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+}
+
+// Update all participants in the conversation lists
+void LLFloaterIMContainer::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->updateName();
+ // Next participant
+ current_participant_model++;
+ }
+ }
+}
+
+// static
+void LLFloaterIMContainer::idle(void* user_data)
+{
+ LLFloaterIMContainer* self = static_cast<LLFloaterIMContainer*>(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 LLFloaterIMContainer::onConversationModelEvent(const LLSD& event)
+{
+ // For debug only
+ //std::ostringstream llsd_value;
+ //llsd_value << LLSDOStreamer<LLSDNotationFormatter>(event) << std::endl;
+ //llinfos << "LLFloaterIMContainer::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*>(get_ptr_in_map(mConversationsWidgets,session_id));
+ if (!session_view)
+ {
+ // We skip events that are not associated with a session
+ return false;
+ }
+ LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id);
+ LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ?
+ (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))
+ : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id)));
+
+ if (type == "remove_participant")
+ {
+ // Remove a participant view from the hierarchical conversation list
+ if (participant_view)
+ {
+ session_view->extractItem(participant_view);
+ delete participant_view;
+ session_view->refresh();
+ mConversationsRoot->arrangeAll();
+ }
+ // Remove a participant view from the conversation floater
+ if (conversation_floater)
+ {
+ conversation_floater->removeConversationViewParticipant(participant_id);
+ }
+ }
+ else if (type == "add_participant")
+ {
+ LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(mConversationsItems[session_id]);
+ LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL);
+ if (!participant_view && session_model && participant_model)
+ {
+ LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id);
+ if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType()))
+ {
+ participant_view = createConversationViewParticipant(participant_model);
+ participant_view->addToFolder(session_view);
+ participant_view->setVisible(TRUE);
+ }
+ }
+ // Add a participant view to the conversation floater
+ if (conversation_floater && participant_model)
+ {
+ conversation_floater->addConversationViewParticipant(participant_model);
+ }
+ }
+ else if (type == "update_participant")
+ {
+ // Update the participant view in the hierarchical conversation list
+ if (participant_view)
+ {
+ participant_view->refresh();
+ }
+ // Update the participant view in the conversation floater
+ if (conversation_floater)
+ {
+ conversation_floater->updateConversationViewParticipant(participant_id);
+ }
+ }
+ else if (type == "update_session")
+ {
+ session_view->refresh();
+ }
+
+ mConversationViewModel.requestSortAll();
+ mConversationsRoot->arrangeAll();
+ if (conversation_floater)
+ {
+ conversation_floater->refreshConversation();
+ }
+
+ return false;
+}
+
+void LLFloaterIMContainer::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);
+ }
+
+ const LLConversationItem *current_session = getCurSelectedViewModelItem();
+ if (current_session)
+ {
+ // Update moderator options visibility
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd();
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model);
+ participant_model->setModeratorOptionsVisible(isGroupModerator() && participant_model->getUUID() != gAgentID);
+
+ current_participant_model++;
+ }
+ // Update floater's title as required by the currently selected session or use the default title
+ LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(current_session->getUUID());
+ setTitle(conversation_floaterp && conversation_floaterp->needsTitleOverwrite() ? conversation_floaterp->getTitle() : mGeneralTitle);
+ }
+
+ // "Manually" resize of mConversationsPane: same as temporarity cancellation of the flag "auto_resize=false" for it
+ if (!mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed())
+ {
+ LLRect stack_rect = mConversationsStack->getRect();
+ mConversationsPane->reshape(stack_rect.getWidth(), stack_rect.getHeight(), true);
+ }
+
+ LLFloater::draw();
+}
+
+void LLFloaterIMContainer::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);
+ }
+}
+
+//Shows/hides the stub panel when a conversation floater is torn off
+void LLFloaterIMContainer::showStub(bool stub_is_visible)
+{
+ S32 tabCount = 0;
+ LLPanel * tabPanel = NULL;
+
+ if(stub_is_visible)
+ {
+ tabCount = mTabContainer->getTabCount();
+
+ //Hide all tabs even stub
+ for(S32 i = 0; i < tabCount; ++i)
+ {
+ tabPanel = mTabContainer->getPanelByIndex(i);
+
+ if(tabPanel)
+ {
+ tabPanel->setVisible(false);
+ }
+ }
+
+ //Set the index to the stub panel since we will be showing the stub
+ mTabContainer->setCurrentPanelIndex(0);
+ }
+
+ //Now show/hide the stub
+ mStubPanel->setVisible(stub_is_visible);
+}
+
+// listener for click on mStubTextBox2
+void LLFloaterIMContainer::returnFloaterToHost()
+{
+ LLUUID session_id = this->getSelectedSession();
+ LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id);
+ floater->onTearOffClicked();
+}
+
+void LLFloaterIMContainer::setVisible(BOOL visible)
+{ LLFloaterIMNearbyChat* nearby_chat;
+ if (visible)
+ {
+ // Make sure we have the Nearby Chat present when showing the conversation container
+ nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("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);
+ setSelectedSession(LLUUID(NULL));
+ }
+ openNearbyChat();
+ selectConversationPair(getSelectedSession(), false, false);
+ }
+
+ nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
+ {
+ LLFloaterIMSessionTab::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 LLFloaterIMContainer::updateResizeLimits()
+{
+ LLMultiFloater::updateResizeLimits();
+ assignResizeLimits();
+}
+
+void LLFloaterIMContainer::collapseMessagesPane(bool collapse)
+{
+ if (mMessagesPane->isCollapsed() == collapse)
+ {
+ return;
+ }
+
+ // Save current width of panels before collapsing/expanding right pane.
+ S32 conv_pane_width = mConversationsPane->getRect().getWidth();
+ S32 msg_pane_width = mMessagesPane->getRect().getWidth();
+
+ if (collapse)
+ {
+ // Save the messages pane width before collapsing it.
+ gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", msg_pane_width);
+
+ // Save the order in which the panels are closed to reverse user's last action.
+ gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed());
+ }
+
+ mConversationsPane->setIgnoreReshape(collapse);
+
+ // Show/hide the messages pane.
+ mConversationsStack->collapsePanel(mMessagesPane, collapse);
+
+ // Make sure layout is updated before resizing conversation pane.
+ mConversationsStack->updateLayout();
+
+ reshapeFloaterAndSetResizeLimits(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth"));
+
+ if (!collapse)
+ {
+ // Restore conversation's pane previous width after expanding messages pane.
+ mConversationsPane->setTargetDim(conv_pane_width);
+ }
+}
+
+void LLFloaterIMContainer::collapseConversationsPane(bool collapse, bool save_is_allowed /*=true*/)
+{
+ 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"));
+
+ // Save current width of Conversation panel before collapsing/expanding right pane.
+ S32 conv_pane_width = mConversationsPane->getRect().getWidth();
+
+ if (collapse && save_is_allowed)
+ {
+ // Save the conversations pane width before collapsing it.
+ gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", conv_pane_width);
+
+ // Save the order in which the panels are closed to reverse user's last action.
+ gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed());
+ }
+
+ mConversationsStack->collapsePanel(mConversationsPane, collapse);
+ if (!collapse)
+ {
+ // Make sure layout is updated before resizing conversation pane.
+ mConversationsStack->updateLayout();
+ // Restore conversation's pane previous width.
+ mConversationsPane->setTargetDim(gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"));
+ }
+
+ S32 delta_width =
+ gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - mConversationsPane->getMinDim();
+
+ reshapeFloaterAndSetResizeLimits(collapse, delta_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->toggleCollapsedMode(collapse);
+
+ // force closing all open conversations when collapsing to minimized state
+ if (collapse)
+ {
+ widget->setOpen(false);
+ }
+ widget->requestArrange();
+ }
+ }
+}
+
+void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(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 at_least_one_panel_is_expanded =
+ ! (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed());
+
+ setCanResize(at_least_one_panel_is_expanded);
+ setCanMinimize(at_least_one_panel_is_expanded);
+
+ assignResizeLimits();
+
+ // 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);
+}
+
+void LLFloaterIMContainer::assignResizeLimits()
+{
+ bool is_conv_pane_expanded = !mConversationsPane->isCollapsed();
+ bool is_msg_pane_expanded = !mMessagesPane->isCollapsed();
+
+ // With two panels visible number of borders is three, because the borders
+ // between the panels are merged into one
+ S32 number_of_visible_borders = llmin((is_conv_pane_expanded? 2 : 0) + (is_msg_pane_expanded? 2 : 0), 3);
+ S32 summary_width_of_visible_borders = number_of_visible_borders * LLPANEL_BORDER_WIDTH;
+ S32 conv_pane_target_width = is_conv_pane_expanded?
+ (is_msg_pane_expanded?
+ mConversationsPane->getRect().getWidth()
+ : mConversationsPane->getExpandedMinDim())
+ : mConversationsPane->getMinDim();
+ S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0;
+ S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders;
+
+ setResizeLimits(new_min_width, getMinHeight());
+
+ mConversationsStack->updateLayout();
+}
+
+void LLFloaterIMContainer::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(&LLFloaterIMContainer::onAvatarPicked, this, _1), TRUE, TRUE, TRUE, root_floater->getName(), button);
+
+ if (picker && root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
+}
+
+void LLFloaterIMContainer::onAvatarPicked(const uuid_vec_t& ids)
+{
+ if (ids.size() == 1)
+ {
+ LLAvatarActions::startIM(ids.back());
+ }
+ else
+ {
+ LLAvatarActions::startConference(ids);
+ }
+}
+
+void LLFloaterIMContainer::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_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences");
+ if (floater_prefp)
+ {
+ floater_prefp->selectChatPanel();
+ }
+ }
+ if ("privacy_preferences" == command)
+ {
+ LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences");
+ if (floater_prefp)
+ {
+ floater_prefp->selectPrivacyPanel();
+ }
+ }
+ if ("Translating.Toggle" == command)
+ {
+ gSavedSettings.setBOOL("TranslateChat", !gSavedSettings.getBOOL("TranslateChat"));
+ }
+}
+
+BOOL LLFloaterIMContainer::isActionChecked(const LLSD& userdata)
+{
+ 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);
+ }
+ if ("Translating.Enabled" == command)
+ {
+ return gSavedPerAccountSettings.getBOOL("TranslatingEnabled");
+ }
+ if ("Translating.On" == command)
+ {
+ return gSavedSettings.getBOOL("TranslateChat");
+ }
+ return FALSE;
+}
+
+void LLFloaterIMContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order)
+{
+ LLConversationSort old_order = mConversationViewModel.getSorter();
+ if (order != old_order.getSortOrderSessions())
+ {
+ old_order.setSortOrderSessions(order);
+ setSortOrder(old_order);
+ }
+}
+
+void LLFloaterIMContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order)
+{
+ LLConversationSort old_order = mConversationViewModel.getSorter();
+ if (order != old_order.getSortOrderParticipants())
+ {
+ old_order.setSortOrderParticipants(order);
+ setSortOrder(old_order);
+ }
+}
+
+void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order)
+{
+ mConversationViewModel.setSorter(order);
+ mConversationsRoot->arrangeAll();
+ // try to keep selection onscreen, even if it wasn't to start with
+ mConversationsRoot->scrollToShowSelection();
+
+ // Notify all conversation (torn off or not) of the change to the sort order
+ // Note: For the moment, the sort order is *unique* across all conversations. That might change in the future.
+ for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++)
+ {
+ LLUUID session_id = it_session->first;
+ LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id)));
+ if (conversation_floater)
+ {
+ conversation_floater->setSortOrder(order);
+ }
+ }
+
+ gSavedSettings.setU32("ConversationSortOrder", (U32)order);
+}
+
+void LLFloaterIMContainer::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());
+
+ //When a one-on-one conversation exists, retrieve the participant id from the conversation floater
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(conversationItem->getUUID());
+ LLUUID participant_id = conversation_floaterp->getOtherParticipantUUID();
+ selected_uuids.push_back(participant_id);
+ }
+ else
+ {
+ selected_uuids.push_back(conversationItem->getUUID());
+ }
+ }
+}
+
+const LLConversationItem * LLFloaterIMContainer::getCurSelectedViewModelItem()
+{
+ LLConversationItem * conversation_item = NULL;
+
+ if(mConversationsRoot &&
+ mConversationsRoot->getCurSelectedItem() &&
+ mConversationsRoot->getCurSelectedItem()->getViewModelItem())
+ {
+ LLFloaterIMSessionTab *selected_session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ if (selected_session_floater && !selected_session_floater->getHost() && selected_session_floater->getCurSelectedViewModelItem())
+ {
+ conversation_item = selected_session_floater->getCurSelectedViewModelItem();
+ }
+ else
+ {
+ conversation_item = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem());
+ }
+ }
+
+ return conversation_item;
+}
+
+void LLFloaterIMContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids)
+{
+ //Find the conversation floater associated with the selected id
+ const LLConversationItem * conversation_item = getCurSelectedViewModelItem();
+
+ if (NULL == conversation_item)
+ {
+ return;
+ }
+
+ getSelectedUUIDs(selected_uuids);
+}
+
+void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS)
+{
+ if (selectedIDS.size() == 1)
+ {
+ const LLUUID& userID = selectedIDS.front();
+ if ("view_profile" == command)
+ {
+ LLAvatarActions::showProfile(userID);
+ }
+ else if ("im" == command)
+ {
+ if (gAgent.getID() != userID)
+ {
+ 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 ("zoom_in" == command)
+ {
+ handle_zoom_to_object(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)
+ {
+ toggleMute(userID, LLMute::flagVoiceChat);
+ }
+ else if ("mute_unmute" == command)
+ {
+ toggleMute(userID, LLMute::flagTextChat);
+ }
+ else if ("selected" == command || "mute_all" == command || "unmute_all" == command)
+ {
+ moderateVoice(command, userID);
+ }
+ else if ("toggle_allow_text_chat" == command)
+ {
+ toggleAllowTextChat(userID);
+ }
+ }
+ else if (selectedIDS.size() > 1)
+ {
+ if ("im" == command)
+ {
+ LLAvatarActions::startConference(selectedIDS);
+ }
+ else if ("offer_teleport" == command)
+ {
+ LLAvatarActions::offerTeleport(selectedIDS);
+ }
+ else if ("voice_call" == command)
+ {
+ LLAvatarActions::startAdhocCall(selectedIDS);
+ }
+ else if ("remove_friend" == command)
+ {
+ LLAvatarActions::removeFriendsDialog(selectedIDS);
+ }
+ }
+}
+
+void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS)
+{
+ //Find the conversation floater associated with the selected id
+ const LLConversationItem * conversationItem = getCurSelectedViewModelItem();
+ LLFloaterIMSession *conversationFloater = LLFloaterIMSession::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)
+ {
+ if (selectedIDS.size() > 0)
+ {
+ LLAvatarActions::viewChatHistory(selectedIDS.front());
+ }
+ }
+ else
+ {
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ doToParticipants(command, selectedIDS);
+ }
+ }
+ }
+}
+
+void LLFloaterIMContainer::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 LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata)
+{
+ std::string action = userdata.asString();
+
+ if (action == "group_profile")
+ {
+ LLGroupActions::show(mSelectedSession);
+ }
+ else if (action == "activate_group")
+ {
+ LLGroupActions::activate(mSelectedSession);
+ }
+ else if (action == "leave_group")
+ {
+ LLGroupActions::leave(mSelectedSession);
+ }
+}
+
+bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
+{
+ const std::string& item = userdata.asString();
+ uuid_vec_t uuids;
+ getParticipantUUIDs(uuids);
+
+ if ("conversation_log" == item)
+ {
+ return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
+ }
+
+ //Enable Chat history item for ad-hoc and group conversations
+ if ("can_chat_history" == item && uuids.size() > 0)
+ {
+ return LLLogChat::isTranscriptExist(uuids.front());
+ }
+
+ // If nothing is selected(and selected item is not group chat), everything needs to be disabled
+ if (uuids.size() <= 0)
+ {
+ if(getCurSelectedViewModelItem())
+ {
+ return getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP;
+ }
+ return false;
+ }
+
+ if("can_activate_group" == item)
+ {
+ LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID();
+ return gAgent.getGroupID() != selected_group_id;
+ }
+
+ return enableContextMenuItem(item, uuids);
+}
+
+bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_vec_t& uuids)
+{
+ // Extract the single select info
+ bool is_single_select = (uuids.size() == 1);
+ const LLUUID& single_id = uuids.front();
+
+ // Handle options that are applicable to all including the user agent
+ if ("can_view_profile" == item)
+ {
+ return is_single_select;
+ }
+
+ // Beyond that point, if only the user agent is selected, everything is disabled
+ if (is_single_select && (single_id == gAgentID))
+ {
+ return false;
+ }
+
+ // If the user agent is selected with others, everything is disabled
+ for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id)
+ {
+ if (gAgent.getID() == *id)
+ {
+ return false;
+ }
+ }
+
+ // Handle all other options
+ if (("can_invite" == item) || ("can_chat_history" == item) || ("can_share" == item) || ("can_pay" == item))
+ {
+ // Those menu items are enable only if a single avatar is selected
+ return is_single_select;
+ }
+ else if ("can_block" == item)
+ {
+ return (is_single_select ? LLAvatarActions::canBlock(single_id) : false);
+ }
+ else if ("can_add" == item)
+ {
+ // We can add friends if:
+ // - there is only 1 selected avatar (EXT-7389)
+ // - this avatar is not already a friend
+ return (is_single_select ? !LLAvatarActions::isFriend(single_id) : false);
+ }
+ else if ("can_delete" == item)
+ {
+ // We can remove friends if there are only friends among the selection
+ bool result = true;
+ for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id)
+ {
+ result &= LLAvatarActions::isFriend(*id);
+ }
+ return result;
+ }
+ else if ("can_call" == item)
+ {
+ return LLAvatarActions::canCall();
+ }
+ else if ("can_zoom_in" == item)
+ {
+ return is_single_select && gObjectList.findObject(single_id);
+ }
+ else if ("can_show_on_map" == item)
+ {
+ return (is_single_select ? (LLAvatarTracker::instance().isBuddyOnline(single_id) && is_agent_mappable(single_id)) || gAgent.isGodlike() : false);
+ }
+ else if ("can_offer_teleport" == item)
+ {
+ return LLAvatarActions::canOfferTeleport(uuids);
+ }
+ else if (("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item))
+ {
+ // *TODO : get that out of here...
+ return enableModerateContextMenuItem(item);
+ }
+
+ // By default, options that not explicitely disabled are enabled
+ return true;
+}
+
+bool LLFloaterIMContainer::checkContextMenuItem(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ uuid_vec_t uuids;
+ getParticipantUUIDs(uuids);
+
+ return checkContextMenuItem(item, uuids);
+}
+
+bool LLFloaterIMContainer::checkContextMenuItem(const std::string& item, uuid_vec_t& uuids)
+{
+ if (uuids.size() == 1)
+ {
+ if ("is_blocked" == item)
+ {
+ return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagVoiceChat);
+ }
+ else if (item == "is_muted")
+ {
+ return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagTextChat);
+ }
+ else if ("is_allowed_text_chat" == item)
+ {
+ const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant());
+
+ if (NULL != speakerp)
+ {
+ return !speakerp->mModeratorMutedText;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool LLFloaterIMContainer::visibleContextMenuItem(const LLSD& userdata)
+{
+ const std::string& item = userdata.asString();
+
+ if ("show_mute" == item)
+ {
+ return !isMuted(getCurSelectedViewModelItem()->getUUID());
+ }
+ else if ("show_unmute" == item)
+ {
+ return isMuted(getCurSelectedViewModelItem()->getUUID());
+ }
+
+ return true;
+}
+
+void LLFloaterIMContainer::showConversation(const LLUUID& session_id)
+{
+ setVisibleAndFrontmost(false);
+ selectConversationPair(session_id, true);
+
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+ session_floater->restoreFloater();
+}
+
+void LLFloaterIMContainer::clearAllFlashStates()
+{
+ 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->setFlashState(false);
+ }
+ }
+}
+
+void LLFloaterIMContainer::selectConversation(const LLUUID& session_id)
+{
+ selectConversationPair(session_id, true);
+}
+
+// Select the conversation *after* (or before if none after) the passed uuid conversation
+// Used to change the selection on key hits
+void LLFloaterIMContainer::selectNextConversationByID(const LLUUID& uuid)
+{
+ bool new_selection = false;
+ selectConversation(uuid);
+ new_selection = selectNextorPreviousConversation(true);
+ if (!new_selection)
+ {
+ selectNextorPreviousConversation(false);
+ }
+}
+
+// Synchronous select the conversation item and the conversation floater
+BOOL LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool select_widget, bool focus_floater/*=true*/)
+{
+ BOOL handled = TRUE;
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+
+ /* widget processing */
+ if (select_widget && mConversationsRoot->getSelectedCount() <= 1)
+ {
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,session_id);
+ if (widget && widget->getParentFolder())
+ {
+ widget->getParentFolder()->setSelection(widget, FALSE, FALSE);
+ mConversationsRoot->scrollToShowSelection();
+ }
+
+ //When in DND mode, remove stored IM notifications
+ //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal
+ if(gAgent.isDoNotDisturb() && session_id.notNull())
+ {
+ LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, session_id);
+ }
+ }
+
+ /* floater processing */
+
+ if (NULL != session_floater)
+ {
+ if (session_id != getSelectedSession())
+ {
+ // Store the active session
+ setSelectedSession(session_id);
+
+
+
+ if (session_floater->getHost())
+ {
+ // Always expand the message pane if the panel is hosted by the container
+ collapseMessagesPane(false);
+ // Switch to the conversation floater that is being selected
+ selectFloater(session_floater);
+ }
+ else
+ {
+ showStub(true);
+ }
+ }
+
+ // Set the focus on the selected floater
+ if (!session_floater->hasFocus())
+ {
+ BOOL is_minimized = session_floater->isMinimized();
+ session_floater->setFocus(focus_floater);
+ session_floater->setMinimized(is_minimized);
+ }
+ }
+ flashConversationItemWidget(session_id,false);
+ return handled;
+}
+
+void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id)
+{
+ LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(get_ptr_in_map(mConversationsItems,session_id));
+ if (item)
+ {
+ item->setTimeNow(participant_id);
+ mConversationViewModel.requestSortAll();
+ mConversationsRoot->arrangeAll();
+ }
+}
+
+void LLFloaterIMContainer::setNearbyDistances()
+{
+ // Get the nearby chat session: that's the one with uuid nul
+ LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(get_ptr_in_map(mConversationsItems,LLUUID()));
+ 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();
+ }
+}
+
+LLConversationItem* LLFloaterIMContainer::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 (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 item_it->second;
+ }
+
+ // 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, getRootViewModel());
+ }
+ if (!item)
+ {
+ llwarns << "Couldn't create conversation session item : " << display_name << llendl;
+ return NULL;
+ }
+ item->renameItem(display_name);
+ item->updateName(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();
+
+ LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(uuid);
+
+ // Create the participants widgets now
+ // Note: usually, we do not get an updated avatar list at that point
+ if (uuid.isNull() || im_sessionp && !im_sessionp->isP2PSessionType())
+ {
+ 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++;
+ }
+ }
+
+ if (uuid.notNull() && im_sessionp->isP2PSessionType())
+ {
+ item->fetchAvatarName(false);
+ }
+
+ // Do that too for the conversation dialog
+ LLFloaterIMSessionTab *conversation_floater = (uuid.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(uuid)));
+ if (conversation_floater)
+ {
+ conversation_floater->buildConversationViewParticipant();
+ }
+
+ // set the widget to minimized mode if conversations pane is collapsed
+ widget->toggleCollapsedMode(mConversationsPane->isCollapsed());
+
+ if (isWidgetSelected || 0 == mConversationsRoot->getSelectedCount())
+ {
+ selectConversationPair(uuid, true);
+ widget->requestArrange();
+
+ // scroll to newly added item
+ mConversationsRoot->scrollToShowSelection();
+ }
+
+ return item;
+}
+
+bool LLFloaterIMContainer::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 is_widget_selected = false;
+ LLFolderViewItem* new_selection = NULL;
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid);
+ if (widget)
+ {
+ is_widget_selected = widget->isSelected();
+ new_selection = mConversationsRoot->getNextFromChild(widget, FALSE);
+ if (!new_selection)
+ {
+ new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE);
+ }
+ 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)
+ {
+ if (mConversationsWidgets.size() == 1)
+ {
+ // If only one widget is left, it has to be the Nearby Chat. Select it directly.
+ selectConversationPair(LLUUID(NULL), true);
+ }
+ else
+ {
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
+ if (vmi)
+ {
+ selectConversationPair(vmi->getUUID(), true);
+ }
+ }
+ }
+ }
+ return is_widget_selected;
+}
+
+LLConversationViewSession* LLFloaterIMContainer::createConversationItemWidget(LLConversationItem* item)
+{
+ LLConversationViewSession::Params params;
+
+ params.name = item->getDisplayName();
+ params.root = mConversationsRoot;
+ params.listener = item;
+ params.tool_tip = params.name;
+ params.container = this;
+
+ //Indentation for aligning the p2p converstation image with the nearby chat arrow
+ if(item->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ params.folder_indentation = 3;
+ }
+
+ return LLUICtrlFactory::create<LLConversationViewSession>(params);
+}
+
+LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParticipant(LLConversationItem* item)
+{
+ LLConversationViewParticipant::Params params;
+ LLRect panel_rect = mConversationsListPanel->getRect();
+
+ params.name = item->getDisplayName();
+ 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();
+ params.folder_indentation = 27;
+
+ return LLUICtrlFactory::create<LLConversationViewParticipant>(params);
+}
+
+bool LLFloaterIMContainer::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 LLFloaterIMContainer::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 LLFloaterIMContainer::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 LLFloaterIMContainer::isMuted(const LLUUID& avatar_id)
+{
+ const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant());
+ return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED;
+}
+
+void LLFloaterIMContainer::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 LLFloaterIMContainer::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 LLFloaterIMContainer::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 * LLFloaterIMContainer::getSpeakerMgrForSelectedParticipant()
+{
+ LLFolderViewItem *selectedItem = mConversationsRoot->getCurSelectedItem();
+ if (NULL == selectedItem)
+ {
+ llwarns << "Current selected item is null" << llendl;
+ return NULL;
+ }
+
+ 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 == selectedItem || iter->second == selectedItem->getParentFolder())
+ {
+ 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 * LLFloaterIMContainer::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 LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid)
+{
+ LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant());
+ if (NULL != speaker_managerp)
+ {
+ speaker_managerp->toggleAllowTextChat(participant_uuid);
+ }
+}
+
+void LLFloaterIMContainer::toggleMute(const LLUUID& participant_id, U32 flags)
+{
+ BOOL is_muted = LLMuteList::getInstance()->isMuted(participant_id, flags);
+ std::string name;
+ gCacheName->getFullName(participant_id, name);
+ LLMute mute(participant_id, name, LLMute::AGENT);
+
+ if (!is_muted)
+ {
+ LLMuteList::getInstance()->add(mute, flags);
+ }
+ else
+ {
+ LLMuteList::getInstance()->remove(mute, flags);
+ }
+}
+
+void LLFloaterIMContainer::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)&&(!mConversationsPane->isCollapsed()))
+ {
+ LLConversationViewSession* nearby_chat = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,LLUUID()));
+ if (nearby_chat)
+ {
+ reSelectConversation();
+ nearby_chat->setOpen(TRUE);
+ }
+ }
+}
+
+void LLFloaterIMContainer::onNearbyChatClosed()
+{
+ // If nearby chat is the only remaining conversation and it is closed, close whole conversation floater as well
+ if (mConversationsItems.size() == 1)
+ closeFloater();
+}
+
+void LLFloaterIMContainer::reSelectConversation()
+{
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ if (session_floater->getHost())
+ {
+ selectFloater(session_floater);
+ }
+}
+
+void LLFloaterIMContainer::updateSpeakBtnState()
+{
+ LLButton* mSpeakBtn = getChild<LLButton>("speak_btn");
+ mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState());
+ mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak"));
+}
+
+bool LLFloaterIMContainer::isConversationLoggingAllowed()
+{
+ return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
+}
+
+void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, bool is_flashes)
+{
+ //Finds the conversation line item to flash using the session_id
+ LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id));
+
+ if (widget)
+ {
+ widget->setFlashState(is_flashes);
+ }
+}
+
+bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget)
+{
+ llassert(conversation_item_widget != NULL);
+
+ // make sure the widget is actually in the right spot first
+ mConversationsRoot->arrange(NULL, NULL);
+
+ // check whether the widget is in the visible portion of the scroll container
+ LLRect widget_rect;
+ conversation_item_widget->localRectToOtherView(conversation_item_widget->getLocalRect(), &widget_rect, mConversationsRoot);
+ return !mConversationsRoot->getVisibleRect().overlaps(widget_rect);
+}
+
+BOOL LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask )
+{
+ if(mask == MASK_ALT)
+ {
+ if (KEY_RETURN == key )
+ {
+ expandConversation();
+ }
+
+ if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
+ {
+ selectNextorPreviousConversation(true);
+ }
+ if ((KEY_UP == key) || (KEY_LEFT == key))
+ {
+ selectNextorPreviousConversation(false);
+ }
+ }
+ return TRUE;
+}
+
+bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected)
+{
+ bool selectedAdjacentConversation = selectNextorPreviousConversation(true, focus_selected);
+
+ if(!selectedAdjacentConversation)
+ {
+ selectedAdjacentConversation = selectNextorPreviousConversation(false, focus_selected);
+ }
+
+ return selectedAdjacentConversation;
+}
+
+bool LLFloaterIMContainer::selectNextorPreviousConversation(bool select_next, bool focus_selected)
+{
+ if (mConversationsWidgets.size() > 1)
+ {
+ LLFolderViewItem* new_selection = NULL;
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,getSelectedSession());
+ if (widget)
+ {
+ if(select_next)
+ {
+ new_selection = mConversationsRoot->getNextFromChild(widget, FALSE);
+ }
+ else
+ {
+ new_selection = mConversationsRoot->getPreviousFromChild(widget, FALSE);
+ }
+ if (new_selection)
+ {
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
+ if (vmi)
+ {
+ selectConversationPair(vmi->getUUID(), true, focus_selected);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void LLFloaterIMContainer::expandConversation()
+{
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,getSelectedSession()));
+ if (widget)
+ {
+ widget->setOpen(!widget->isOpen());
+ }
+}
+
+void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/)
+{
+ // Always unminimize before trying to close.
+ // Most of the time the user will never see this state.
+ setMinimized(FALSE);
+
+ LLFloater::closeFloater(app_quitting);
+}
+
+// EOF
diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h
new file mode 100644
index 0000000000..2cbc1e99f9
--- /dev/null
+++ b/indra/newview/llfloaterimcontainer.h
@@ -0,0 +1,209 @@
+/**
+ * @file llfloaterimcontainer.h
+ * @brief Multifloater containing active IM sessions in separate tab container tabs
+ *
+ * $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_LLFLOATERIMCONTAINER_H
+#define LL_LLFLOATERIMCONTAINER_H
+
+#include <map>
+#include <vector>
+
+#include "llimview.h"
+#include "llevents.h"
+#include "../llui/llfloater.h"
+#include "../llui/llmultifloater.h"
+#include "llavatarpropertiesprocessor.h"
+#include "llgroupmgr.h"
+#include "../llui/lltrans.h"
+#include "llconversationmodel.h"
+#include "llconversationview.h"
+
+class LLButton;
+class LLLayoutPanel;
+class LLLayoutStack;
+class LLTabContainer;
+class LLFloaterIMContainer;
+class LLSpeaker;
+class LLSpeakerMgr;
+
+class LLFloaterIMContainer
+ : public LLMultiFloater
+ , public LLIMSessionObserver
+{
+public:
+ LLFloaterIMContainer(const LLSD& seed, const Params& params = getDefaultParams());
+ virtual ~LLFloaterIMContainer();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onOpen(const LLSD& key);
+ /*virtual*/ void draw();
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void updateResizeLimits();
+ void onCloseFloater(LLUUID& id);
+
+ /*virtual*/ void addFloater(LLFloater* floaterp,
+ BOOL select_added_floater,
+ LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
+ void returnFloaterToHost();
+ void showConversation(const LLUUID& session_id);
+ void selectConversation(const LLUUID& session_id);
+ void selectNextConversationByID(const LLUUID& session_id);
+ BOOL selectConversationPair(const LLUUID& session_id, bool select_widget, bool focus_floater = true);
+ void clearAllFlashStates();
+ bool selectAdjacentConversation(bool focus_selected);
+ bool selectNextorPreviousConversation(bool select_next, bool focus_selected = true);
+ void expandConversation();
+
+ /*virtual*/ void tabClose();
+ void showStub(bool visible);
+
+ static LLFloater* getCurrentVoiceFloater();
+ static LLFloaterIMContainer* findInstance();
+ static LLFloaterIMContainer* getInstance();
+
+ 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, BOOL has_offline_msg);
+ /*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; }
+ LLConversationItem* getSessionModel(const LLUUID& session_id) { return get_ptr_in_map(mConversationsItems,session_id); }
+ LLConversationSort& getSortOrder() { return mConversationViewModel.getSorter(); }
+
+ void onNearbyChatClosed();
+
+ // Handling of lists of participants is public so to be common with llfloatersessiontab
+ // *TODO : Find a better place for this.
+ bool checkContextMenuItem(const std::string& item, uuid_vec_t& selectedIDS);
+ bool enableContextMenuItem(const std::string& item, uuid_vec_t& selectedIDS);
+ void doToParticipants(const std::string& item, uuid_vec_t& selectedIDS);
+
+ void assignResizeLimits();
+ virtual BOOL handleKeyHere(KEY key, MASK mask );
+ /*virtual*/ void closeFloater(bool app_quitting = false);
+
+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 onStubCollapseButtonClicked();
+ void processParticipantsStyleUpdate();
+ void onSpeakButtonClicked();
+
+ void collapseConversationsPane(bool collapse, bool save_is_allowed=true);
+
+ void reshapeFloaterAndSetResizeLimits(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);
+ bool checkContextMenuItem(const LLSD& userdata);
+ bool enableContextMenuItem(const LLSD& userdata);
+ bool visibleContextMenuItem(const LLSD& userdata);
+ void doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS);
+ void doToSelectedGroup(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 toggleMute(const LLUUID& participant_id, U32 flags);
+ void openNearbyChat();
+
+ LLButton* mExpandCollapseBtn;
+ LLButton* mStubCollapseBtn;
+ LLPanel* mStubPanel;
+ LLTextBox* mStubTextBox;
+ LLLayoutPanel* mMessagesPane;
+ LLLayoutPanel* mConversationsPane;
+ LLLayoutStack* mConversationsStack;
+
+ bool mInitialized;
+
+ LLUUID mSelectedSession;
+ std::string mGeneralTitle;
+
+ // Conversation list implementation
+public:
+ bool removeConversationListItem(const LLUUID& uuid, bool change_focus = true);
+ LLConversationItem* addConversationListItem(const LLUUID& uuid, bool isWidgetSelected = false);
+ void setTimeNow(const LLUUID& session_id, const LLUUID& participant_id);
+ void setNearbyDistances();
+ void reSelectConversation();
+ void updateSpeakBtnState();
+ static bool isConversationLoggingAllowed();
+ void flashConversationItemWidget(const LLUUID& session_id, bool is_flashes);
+ bool isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget);
+ boost::signals2::connection mMicroChangedSignal;
+ S32 getConversationListItemSize() { return mConversationsWidgets.size(); }
+
+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_LLFLOATERIMCONTAINER_H
diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp
new file mode 100644
index 0000000000..b287950c21
--- /dev/null
+++ b/indra/newview/llfloaterimnearbychat.cpp
@@ -0,0 +1,881 @@
+/**
+ * @file LLFloaterIMNearbyChat.cpp
+ * @brief LLFloaterIMNearbyChat 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 "lliconctrl.h"
+#include "llappviewer.h"
+#include "llchatentry.h"
+#include "llfloaterreg.h"
+#include "lltrans.h"
+#include "llfloaterimcontainer.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 "llfloaterimnearbychathandler.h"
+#include "llchannelmanager.h"
+#include "llchathistory.h"
+#include "llstylemap.h"
+#include "llavatarnamecache.h"
+#include "llfloaterreg.h"
+#include "lltrans.h"
+
+#include "llfirstuse.h"
+#include "llfloaterimnearbychat.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"
+#include "llautoreplace.h"
+
+S32 LLFloaterIMNearbyChat::sLastSpecialChatChannel = 0;
+
+const S32 EXPANDED_HEIGHT = 266;
+const S32 COLLAPSED_HEIGHT = 60;
+const S32 EXPANDED_MIN_HEIGHT = 150;
+
+// legacy callback glue
+void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel);
+
+struct LLChatTypeTrigger {
+ std::string name;
+ EChatType type;
+};
+
+static LLChatTypeTrigger sChatTypeTriggers[] = {
+ { "/whisper" , CHAT_TYPE_WHISPER},
+ { "/shout" , CHAT_TYPE_SHOUT}
+};
+
+
+LLFloaterIMNearbyChat::LLFloaterIMNearbyChat(const LLSD& llsd)
+: LLFloaterIMSessionTab(llsd),
+ //mOutputMonitor(NULL),
+ mSpeakerMgr(NULL),
+ mExpandedHeight(COLLAPSED_HEIGHT + EXPANDED_HEIGHT)
+{
+ mIsP2PChat = false;
+ mIsNearbyChat = true;
+ mSpeakerMgr = LLLocalSpeakerMgr::getInstance();
+ mSessionID = LLUUID();
+}
+
+//static
+LLFloaterIMNearbyChat* LLFloaterIMNearbyChat::buildFloater(const LLSD& key)
+{
+ LLFloaterReg::getInstance("im_container");
+ return new LLFloaterIMNearbyChat(key);
+}
+
+//virtual
+BOOL LLFloaterIMNearbyChat::postBuild()
+{
+ setIsSingleInstance(TRUE);
+ BOOL result = LLFloaterIMSessionTab::postBuild();
+
+ mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2, _3, _4, _5));
+ mInputEditor->setCommitCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxCommit, this));
+ mInputEditor->setKeystrokeCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxKeystroke, this));
+ mInputEditor->setFocusLostCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusLost, this));
+ mInputEditor->setFocusReceivedCallback(boost::bind(&LLFloaterIMNearbyChat::onChatBoxFocusReceived, this));
+ mInputEditor->setLabel(LLTrans::getString("NearbyChatTitle"));
+
+ // Title must be defined BEFORE call to addConversationListItem() because
+ // it is used to show the item's name in the conversations list
+ setTitle(LLTrans::getString("NearbyChatTitle"));
+
+ // obsolete, but may be needed for backward compatibility?
+ gSavedSettings.declareS32("nearbychat_showicons_and_names", 2, "NearByChat header settings", true);
+
+ if (gSavedPerAccountSettings.getBOOL("LogShowHistory"))
+ {
+ loadHistory();
+ }
+
+ return result;
+}
+
+// virtual
+void LLFloaterIMNearbyChat::closeHostedFloater()
+{
+ // Should check how many conversations are ongoing. Close all if 1 only (the Nearby Chat), select next one otherwise
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ if (floater_container->getConversationListItemSize() == 1)
+ {
+ floater_container->closeFloater();
+ }
+ else
+ {
+ if (!getHost())
+ {
+ setVisible(FALSE);
+ }
+ floater_container->selectNextConversationByID(LLUUID());
+ }
+}
+
+// virtual
+void LLFloaterIMNearbyChat::refresh()
+{
+ displaySpeakingIndicator();
+ updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState());
+
+ // *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);
+ }
+}
+
+void LLFloaterIMNearbyChat::reloadMessages(bool clean_messages/* = false*/)
+{
+ if (clean_messages)
+ {
+ mMessageArchive.clear();
+ loadHistory();
+ }
+
+ mChatHistory->clear();
+
+ LLSD do_not_log;
+ do_not_log["do_not_log"] = true;
+ for(std::vector<LLChat>::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it)
+ {
+ // Update the messages without re-writing them to a log file.
+ addMessage(*it,false, do_not_log);
+ }
+}
+
+void LLFloaterIMNearbyChat::loadHistory()
+{
+ LLSD do_not_log;
+ do_not_log["do_not_log"] = true;
+
+ std::list<LLSD> history;
+ LLLogChat::loadChatHistory("chat", history);
+
+ std::list<LLSD>::const_iterator it = history.begin();
+ while (it != history.end())
+ {
+ const LLSD& msg = *it;
+
+ std::string from = msg[LL_IM_FROM];
+ LLUUID from_id;
+ if (msg[LL_IM_FROM_ID].isDefined())
+ {
+ from_id = msg[LL_IM_FROM_ID].asUUID();
+ }
+ else
+ {
+ std::string legacy_name = gCacheName->buildLegacyName(from);
+ gCacheName->getUUID(legacy_name, from_id);
+ }
+
+ LLChat chat;
+ chat.mFromName = from;
+ chat.mFromID = from_id;
+ chat.mText = msg[LL_IM_TEXT].asString();
+ chat.mTimeStr = msg[LL_IM_TIME].asString();
+ chat.mChatStyle = CHAT_STYLE_HISTORY;
+
+ chat.mSourceType = CHAT_SOURCE_AGENT;
+ if (from_id.isNull() && SYSTEM_FROM == from)
+ {
+ chat.mSourceType = CHAT_SOURCE_SYSTEM;
+
+ }
+ else if (from_id.isNull())
+ {
+ chat.mSourceType = isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT;
+ }
+
+ addMessage(chat, true, do_not_log);
+
+ it++;
+ }
+}
+
+void LLFloaterIMNearbyChat::removeScreenChat()
+{
+ LLNotificationsUI::LLScreenChannelBase* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->findChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID")));
+ if(chat_channel)
+ {
+ chat_channel->removeToastsFromChannel();
+ }
+}
+
+
+void LLFloaterIMNearbyChat::setVisible(BOOL visible)
+{
+ LLFloaterIMSessionTab::setVisible(visible);
+
+ if(visible)
+ {
+ removeScreenChat();
+ }
+}
+
+
+void LLFloaterIMNearbyChat::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key)
+{
+ LLFloaterIMSessionTab::setVisibleAndFrontmost(take_focus, key);
+
+ if(!isTornOff() && matchesKey(key))
+ {
+ LLFloaterIMContainer::getInstance()->selectConversationPair(mSessionID, true, false);
+ }
+}
+
+// virtual
+void LLFloaterIMNearbyChat::onTearOffClicked()
+{
+ LLFloaterIMSessionTab::onTearOffClicked();
+
+ // see CHUI-170: Save torn-off state of the nearby chat between sessions
+ BOOL in_the_multifloater = (BOOL)getHost();
+ gSavedPerAccountSettings.setBOOL("NearbyChatIsNotTornOff", in_the_multifloater);
+}
+
+
+// virtual
+void LLFloaterIMNearbyChat::onOpen(const LLSD& key)
+{
+ LLFloaterIMSessionTab::onOpen(key);
+ showTranslationCheckbox(LLTranslate::isTranslationConfigured());
+}
+
+// virtual
+void LLFloaterIMNearbyChat::onClose(bool app_quitting)
+{
+ // Override LLFloaterIMSessionTab::onClose() so that Nearby Chat is not removed from the conversation floater
+ LLFloaterIMSessionTab::restoreFloater();
+ onClickCloseBtn();
+}
+
+// virtual
+void LLFloaterIMNearbyChat::onClickCloseBtn()
+{
+ if (!isTornOff())
+ {
+ return;
+ }
+ LLFloaterIMSessionTab::onTearOffClicked();
+
+ LLFloaterIMContainer *im_box = LLFloaterIMContainer::findInstance();
+ if (im_box)
+ {
+ im_box->onNearbyChatClosed();
+ }
+}
+
+void LLFloaterIMNearbyChat::onChatFontChange(LLFontGL* fontp)
+{
+ // Update things with the new font whohoo
+ if (mInputEditor)
+ {
+ mInputEditor->setFont(fontp);
+ }
+}
+
+
+void LLFloaterIMNearbyChat::show()
+{
+ if (isChatMultiTab())
+ {
+ openFloater(getKey());
+ }
+}
+
+bool LLFloaterIMNearbyChat::isChatVisible() const
+{
+ bool isVisible = false;
+ LLFloaterIMContainer* im_box = LLFloaterIMContainer::getInstance();
+ // Is the IM floater container ever null?
+ llassert(im_box != NULL);
+ if (im_box != NULL)
+ {
+ isVisible =
+ isChatMultiTab() && gSavedPerAccountSettings.getBOOL("NearbyChatIsNotTornOff")?
+ im_box->getVisible() && !im_box->isMinimized() :
+ getVisible() && !isMinimized();
+ }
+
+ return isVisible;
+}
+
+void LLFloaterIMNearbyChat::showHistory()
+{
+ openFloater();
+ if(!isMessagePaneExpanded())
+ {
+ restoreFloater();
+ setFocus(true);
+ }
+ setResizeLimits(getMinWidth(), EXPANDED_MIN_HEIGHT);
+}
+
+std::string LLFloaterIMNearbyChat::getCurrentChat()
+{
+ return mInputEditor ? mInputEditor->getText() : LLStringUtil::null;
+}
+
+// virtual
+BOOL LLFloaterIMNearbyChat::handleKeyHere( KEY key, MASK mask )
+{
+ BOOL handled = FALSE;
+
+ if( KEY_RETURN == key && mask == MASK_CONTROL)
+ {
+ // shout
+ sendChat(CHAT_TYPE_SHOUT);
+ handled = TRUE;
+ }
+ else if (KEY_RETURN == key && mask == MASK_SHIFT)
+ {
+ // whisper
+ sendChat(CHAT_TYPE_WHISPER);
+ handled = TRUE;
+ }
+
+
+ if((mask == MASK_ALT) && isTornOff())
+ {
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ if ((KEY_UP == key) || (KEY_LEFT == key))
+ {
+ floater_container->selectNextorPreviousConversation(false);
+ handled = TRUE;
+ }
+ if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
+ {
+ floater_container->selectNextorPreviousConversation(true);
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLFloaterIMNearbyChat::matchChatTypeTrigger(const std::string& in_str, std::string* out_str)
+{
+ U32 in_len = in_str.length();
+ S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers);
+
+ 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 LLFloaterIMNearbyChat::onChatBoxKeystroke()
+{
+ LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();
+ if (im_box)
+ {
+ im_box->flashConversationItemWidget(mSessionID,false);
+ }
+
+ LLFirstUse::otherAvatarChatFirst(false);
+
+ LLWString raw_text = 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
+ {
+ 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);
+ mInputEditor->setText( new_text );
+ 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());
+ 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.
+ 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());
+ mInputEditor->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part
+ mInputEditor->endOfDoc();
+ }
+
+ //llinfos << "GESTUREDEBUG " << trigger
+ // << " len " << length
+ // << " outlen " << out_str.getLength()
+ // << llendl;
+ }
+}
+
+// static
+void LLFloaterIMNearbyChat::onChatBoxFocusLost()
+{
+ // stop typing animation
+ gAgent.stopTyping();
+}
+
+void LLFloaterIMNearbyChat::onChatBoxFocusReceived()
+{
+ mInputEditor->setEnabled(!gDisconnected);
+}
+
+EChatType LLFloaterIMNearbyChat::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 LLFloaterIMNearbyChat::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 LLFloaterIMNearbyChat::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.getS32("KeepConversationLogTranscripts") > 1)
+ {
+ 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.isDisplayNameDefault())
+ {
+ from_name = av_name.getCompleteName();
+ }
+ }
+
+ LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText);
+ }
+}
+
+
+void LLFloaterIMNearbyChat::onChatBoxCommit()
+{
+ if (mInputEditor->getText().length() > 0)
+ {
+ sendChat(CHAT_TYPE_NORMAL);
+ }
+
+ gAgent.stopTyping();
+}
+
+void LLFloaterIMNearbyChat::displaySpeakingIndicator()
+{
+ LLSpeakerMgr::speaker_list_t speaker_list;
+ LLUUID id;
+
+ id.setNull();
+ mSpeakerMgr->update(FALSE);
+ 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;
+ }
+ }
+}
+
+void LLFloaterIMNearbyChat::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
+{
+ sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
+}
+
+void LLFloaterIMNearbyChat::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 LLFloaterIMNearbyChat::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 LLFloaterIMNearbyChat::startChat(const char* line)
+{
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
+ {
+ if(!nearby_chat->isTornOff())
+ {
+ nearby_chat->show();
+ }
+ if(nearby_chat->isMinimized())
+ {
+ nearby_chat->setMinimized(false);
+ }
+ 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 LLFloaterIMNearbyChat::stopChat()
+{
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("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 LLFloaterIMNearbyChat::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/llfloaterimnearbychat.h
index 662496d338..05b48cccb0 100644
--- a/indra/newview/llnearbychatbar.h
+++ b/indra/newview/llfloaterimnearbychat.h
@@ -1,6 +1,6 @@
/**
- * @file llnearbychatbar.h
- * @brief LLNearbyChatBar class definition
+ * @file llfloaterimnearbychat.h
+ * @brief LLFloaterIMNearbyChat class definition
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -24,38 +24,54 @@
* $/LicenseInfo$
*/
-#ifndef LL_LLNEARBYCHATBAR_H
-#define LL_LLNEARBYCHATBAR_H
+#ifndef LL_LLFLOATERIMNEARBYCHAT_H
+#define LL_LLFLOATERIMNEARBYCHAT_H
-#include "llfloater.h"
+#include "llfloaterimsessiontab.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 "llpanel.h"
-class LLNearbyChatBarListener;
+class LLResizeBar;
-class LLNearbyChatBar : public LLFloater
+class LLFloaterIMNearbyChat
+ : public LLFloaterIMSessionTab
{
- LOG_CLASS(LLNearbyChatBar);
-
public:
// constructor for inline chat-bars (e.g. hosted in chat history window)
- LLNearbyChatBar(const LLSD& key);
- ~LLNearbyChatBar() {}
+ LLFloaterIMNearbyChat(const LLSD& key = LLSD(LLUUID()));
+ ~LLFloaterIMNearbyChat() {}
+
+ static LLFloaterIMNearbyChat* buildFloater(const LLSD& key);
- virtual BOOL postBuild();
+ /*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD());
+ /*virtual*/ void closeHostedFloater();
- static LLNearbyChatBar* getInstance();
+ void loadHistory();
+ void reloadMessages(bool clean_messages = false);
+ void removeScreenChat();
- LLLineEditor* getChatBox() { return mChatBox; }
+ void show();
+ bool isChatVisible() const;
- virtual void draw();
+ /** @param archive true - to save a message to the chat history log */
+ void addMessage (const LLChat& message,bool archive = true, const LLSD &args = LLSD());
+
+ LLChatEntry* getChatBox() { return mInputEditor; }
std::string getCurrentChat();
+ S32 getMessageArchiveLength() {return mMessageArchive.size();}
+
virtual BOOL handleKeyHere( KEY key, MASK mask );
static void startChat(const char* line);
@@ -64,24 +80,22 @@ public:
static void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate);
static void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate);
+ static bool isWordsName(const std::string& name);
+
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 onChatBoxKeystroke();
+ void onChatBoxFocusLost();
void onChatBoxFocusReceived();
void sendChat( EChatType type );
void onChatBoxCommit();
void onChatFontChange(LLFontGL* fontp);
- /* virtual */ bool applyRectControl();
-
- void showNearbyChatPanel(bool show);
- void onToggleNearbyChatPanel();
+ /*virtual*/ void onTearOffClicked();
+ /*virtual*/ void onClickCloseBtn();
static LLWString stripChannelNumber(const LLWString &mesg, S32* channel);
EChatType processChatTypeTriggers(EChatType type, std::string &str);
@@ -91,14 +105,15 @@ protected:
// 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;
+private:
+ /*virtual*/ void refresh();
+
+ std::vector<LLChat> mMessageArchive;
};
-#endif
+#endif // LL_LLFLOATERIMNEARBYCHAT_H
diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp
index 600fd395fb..7afcf288ce 100644
--- a/indra/newview/llnearbychathandler.cpp
+++ b/indra/newview/llfloaterimnearbychathandler.cpp
@@ -1,6 +1,6 @@
/**
- * @file LLNearbyChatHandler.cpp
- * @brief Nearby chat notification managment
+ * @file LLFloaterIMNearbyChatHandler.cpp
+ * @brief Nearby chat chat managment
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -27,43 +27,46 @@
#include "llviewerprecompiledheaders.h"
#include "llagentdata.h" // for gAgentID
-#include "llnearbychathandler.h"
+#include "llfloaterimnearbychathandler.h"
#include "llchatitemscontainerctrl.h"
#include "llfirstuse.h"
#include "llfloaterscriptdebug.h"
#include "llhints.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llrecentpeople.h"
#include "llviewercontrol.h"
#include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance
#include "llviewerwindow.h"//for screen channel position
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychat.h"
+#include "llfloaterimcontainer.h"
#include "llrootview.h"
#include "lllayoutstack.h"
-//add LLNearbyChatHandler to LLNotificationsUI namespace
+//add LLFloaterIMNearbyChatHandler to LLNotificationsUI namespace
using namespace LLNotificationsUI;
-//-----------------------------------------------------------------------------------------------
-//LLNearbyChatScreenChannel
-//-----------------------------------------------------------------------------------------------
-LLToastPanelBase* createToastPanel()
+static LLFloaterIMNearbyChatToastPanel* createToastPanel()
{
- LLNearbyChatToastPanel* item = LLNearbyChatToastPanel::createInstance();
+ LLFloaterIMNearbyChatToastPanel* item = LLFloaterIMNearbyChatToastPanel::createInstance();
return item;
}
-class LLNearbyChatScreenChannel: public LLScreenChannelBase
+
+//-----------------------------------------------------------------------------------------------
+//LLFloaterIMNearbyChatScreenChannel
+//-----------------------------------------------------------------------------------------------
+
+class LLFloaterIMNearbyChatScreenChannel: public LLScreenChannelBase
{
- LOG_CLASS(LLNearbyChatScreenChannel);
+ LOG_CLASS(LLFloaterIMNearbyChatScreenChannel);
public:
typedef std::vector<LLHandle<LLToast> > toast_vec_t;
typedef std::list<LLHandle<LLToast> > toast_list_t;
- LLNearbyChatScreenChannel(const Params& p)
+ LLFloaterIMNearbyChatScreenChannel(const Params& p)
: LLScreenChannelBase(p)
{
mStopProcessing = false;
@@ -71,20 +74,20 @@ public:
LLControlVariable* ctrl = gSavedSettings.getControl("NearbyToastLifeTime").get();
if (ctrl)
{
- ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastsLifetime, this));
+ ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::updateToastsLifetime, this));
}
ctrl = gSavedSettings.getControl("NearbyToastFadingTime").get();
if (ctrl)
{
- ctrl->getSignal()->connect(boost::bind(&LLNearbyChatScreenChannel::updateToastFadingTime, this));
+ ctrl->getSignal()->connect(boost::bind(&LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime, this));
}
}
- void addNotification (LLSD& notification);
+ void addChat (LLSD& chat);
void arrangeToasts ();
- typedef boost::function<LLToastPanelBase* (void )> create_toast_panel_callback_t;
+ typedef boost::function<LLFloaterIMNearbyChatToastPanel* (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,17 +155,19 @@ protected:
bool mChannelRect;
};
+
+
//-----------------------------------------------------------------------------------------------
-// LLNearbyChatToast
+// LLFloaterIMNearbyChatToast
//-----------------------------------------------------------------------------------------------
// We're deriving from LLToast to be able to override onClose()
// in order to handle closing nearby chat toasts properly.
-class LLNearbyChatToast : public LLToast
+class LLFloaterIMNearbyChatToast : public LLToast
{
- LOG_CLASS(LLNearbyChatToast);
+ LOG_CLASS(LLFloaterIMNearbyChatToast);
public:
- LLNearbyChatToast(const LLToast::Params& p, LLNearbyChatScreenChannel* nc_channelp)
+ LLFloaterIMNearbyChatToast(const LLToast::Params& p, LLFloaterIMNearbyChatScreenChannel* nc_channelp)
: LLToast(p),
mNearbyChatScreenChannelp(nc_channelp)
{
@@ -171,14 +176,14 @@ public:
/*virtual*/ void onClose(bool app_quitting);
private:
- LLNearbyChatScreenChannel* mNearbyChatScreenChannelp;
+ LLFloaterIMNearbyChatScreenChannel* mNearbyChatScreenChannelp;
};
//-----------------------------------------------------------------------------------------------
-// LLNearbyChatScreenChannel
+// LLFloaterIMNearbyChatScreenChannel
//-----------------------------------------------------------------------------------------------
-void LLNearbyChatScreenChannel::deactivateToast(LLToast* toast)
+void LLFloaterIMNearbyChatScreenChannel::deactivateToast(LLToast* toast)
{
toast_vec_t::iterator pos = std::find(m_active_toasts.begin(), m_active_toasts.end(), toast->getHandle());
@@ -192,12 +197,12 @@ void LLNearbyChatScreenChannel::deactivateToast(LLToast* toast)
m_active_toasts.erase(pos);
}
-void LLNearbyChatScreenChannel::createOverflowToast(S32 bottom, F32 timer)
+void LLFloaterIMNearbyChatScreenChannel::createOverflowToast(S32 bottom, F32 timer)
{
//we don't need overflow toast in nearby chat
}
-void LLNearbyChatScreenChannel::onToastDestroyed(LLToast* toast, bool app_quitting)
+void LLFloaterIMNearbyChatScreenChannel::onToastDestroyed(LLToast* toast, bool app_quitting)
{
LL_DEBUGS("NearbyChat") << "Toast destroyed (app_quitting=" << app_quitting << ")" << llendl;
@@ -216,7 +221,7 @@ void LLNearbyChatScreenChannel::onToastDestroyed(LLToast* toast, bool app_quitti
}
}
-void LLNearbyChatScreenChannel::onToastFade(LLToast* toast)
+void LLFloaterIMNearbyChatScreenChannel::onToastFade(LLToast* toast)
{
LL_DEBUGS("NearbyChat") << "Toast fading" << llendl;
@@ -231,7 +236,7 @@ void LLNearbyChatScreenChannel::onToastFade(LLToast* toast)
arrangeToasts();
}
-void LLNearbyChatScreenChannel::updateToastsLifetime()
+void LLFloaterIMNearbyChatScreenChannel::updateToastsLifetime()
{
S32 seconds = gSavedSettings.getS32("NearbyToastLifeTime");
toast_list_t::iterator it;
@@ -242,7 +247,7 @@ void LLNearbyChatScreenChannel::updateToastsLifetime()
}
}
-void LLNearbyChatScreenChannel::updateToastFadingTime()
+void LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime()
{
S32 seconds = gSavedSettings.getS32("NearbyToastFadingTime");
toast_list_t::iterator it;
@@ -253,9 +258,9 @@ void LLNearbyChatScreenChannel::updateToastFadingTime()
}
}
-bool LLNearbyChatScreenChannel::createPoolToast()
+bool LLFloaterIMNearbyChatScreenChannel::createPoolToast()
{
- LLToastPanelBase* panel= m_create_toast_panel_callback_t();
+ LLFloaterIMNearbyChatToastPanel* panel= m_create_toast_panel_callback_t();
if(!panel)
return false;
@@ -264,20 +269,20 @@ bool LLNearbyChatScreenChannel::createPoolToast()
p.lifetime_secs = gSavedSettings.getS32("NearbyToastLifeTime");
p.fading_time_secs = gSavedSettings.getS32("NearbyToastFadingTime");
- LLToast* toast = new LLNearbyChatToast(p, this);
+ LLToast* toast = new LLFloaterIMNearbyChatToast(p, this);
- toast->setOnFadeCallback(boost::bind(&LLNearbyChatScreenChannel::onToastFade, this, _1));
+ toast->setOnFadeCallback(boost::bind(&LLFloaterIMNearbyChatScreenChannel::onToastFade, this, _1));
// If the toast gets somehow prematurely destroyed, deactivate it to prevent crash (STORM-1352).
- toast->setOnToastDestroyedCallback(boost::bind(&LLNearbyChatScreenChannel::onToastDestroyed, this, _1, false));
+ toast->setOnToastDestroyedCallback(boost::bind(&LLFloaterIMNearbyChatScreenChannel::onToastDestroyed, this, _1, false));
LL_DEBUGS("NearbyChat") << "Creating and pooling toast" << llendl;
m_toast_pool.push_back(toast->getHandle());
return true;
}
-void LLNearbyChatScreenChannel::addNotification(LLSD& notification)
+void LLFloaterIMNearbyChatScreenChannel::addChat(LLSD& chat)
{
//look in pool. if there is any message
if(mStopProcessing)
@@ -289,16 +294,16 @@ 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)
{
- LLNearbyChatToastPanel* panel = dynamic_cast<LLNearbyChatToastPanel*>(toast->getPanel());
+ LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast<LLFloaterIMNearbyChatToastPanel*>(toast->getPanel());
if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText())
{
- panel->addMessage(notification);
+ panel->addMessage(chat);
toast->reshapeToPanel();
toast->startTimer();
@@ -316,11 +321,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 +344,10 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification)
m_toast_pool.pop_back();
- LLToastPanelBase* panel = dynamic_cast<LLToastPanelBase*>(toast->getPanel());
+ LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast<LLFloaterIMNearbyChatToastPanel*>(toast->getPanel());
if(!panel)
return;
- panel->init(notification);
+ panel->init(chat);
toast->reshapeToPanel();
toast->startTimer();
@@ -361,7 +366,7 @@ static bool sort_toasts_predicate(LLHandle<LLToast> first, LLHandle<LLToast> sec
return v1 > v2;
}
-void LLNearbyChatScreenChannel::arrangeToasts()
+void LLFloaterIMNearbyChatScreenChannel::arrangeToasts()
{
if(mStopProcessing || isHovering())
return;
@@ -441,20 +446,18 @@ void LLNearbyChatScreenChannel::arrangeToasts()
//-----------------------------------------------------------------------------------------------
-//LLNearbyChatHandler
+//LLFloaterIMNearbyChatHandler
//-----------------------------------------------------------------------------------------------
-boost::scoped_ptr<LLEventPump> LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat"));
+boost::scoped_ptr<LLEventPump> LLFloaterIMNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat"));
-LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& id)
+LLFloaterIMNearbyChatHandler::LLFloaterIMNearbyChatHandler()
{
- mType = type;
-
// Getting a Channel for our notifications
- LLNearbyChatScreenChannel::Params p;
+ LLFloaterIMNearbyChatScreenChannel::Params p;
p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID"));
- LLNearbyChatScreenChannel* channel = new LLNearbyChatScreenChannel(p);
+ LLFloaterIMNearbyChatScreenChannel* channel = new LLFloaterIMNearbyChatScreenChannel(p);
- LLNearbyChatScreenChannel::create_toast_panel_callback_t callback = createToastPanel;
+ LLFloaterIMNearbyChatScreenChannel::create_toast_panel_callback_t callback = createToastPanel;
channel->setCreatePanelCallback(callback);
@@ -463,12 +466,12 @@ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& i
mChannel = channel->getHandle();
}
-LLNearbyChatHandler::~LLNearbyChatHandler()
+LLFloaterIMNearbyChatHandler::~LLFloaterIMNearbyChatHandler()
{
}
-void LLNearbyChatHandler::initChannel()
+void LLFloaterIMNearbyChatHandler::initChannel()
{
//LLRect snap_rect = gFloaterView->getSnapRect();
//mChannel->init(snap_rect.mLeft, snap_rect.mLeft + 200);
@@ -476,7 +479,7 @@ void LLNearbyChatHandler::initChannel()
-void LLNearbyChatHandler::processChat(const LLChat& chat_msg,
+void LLFloaterIMNearbyChatHandler::processChat(const LLChat& chat_msg,
const LLSD &args)
{
if(chat_msg.mMuted == TRUE)
@@ -485,28 +488,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");
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("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,15 +555,18 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg,
}
// Send event on to LLEventStream
- sChatWatcher->post(notification);
+ sChatWatcher->post(chat);
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
- if( !chat_bar->isMinimized()
- && nearby_chat->isInVisibleChain()
+ if(( nearby_chat->hasFocus()
+ || im_box->hasFocus()
|| ( chat_msg.mSourceType == CHAT_SOURCE_AGENT
&& gSavedSettings.getBOOL("UseChatBubbles") )
|| mChannel.isDead()
- || !mChannel.get()->getShowToasts() ) // to prevent toasts in Busy mode
+ || !mChannel.get()->getShowToasts() )
+ && nearby_chat->isMessagePaneExpanded())
+ // to prevent toasts in Do Not Disturb mode
return;//no need in toast if chat is visible or if bubble chat is enabled
// arrange a channel on a screen
@@ -582,7 +587,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg,
}
*/
- LLNearbyChatScreenChannel* channel = dynamic_cast<LLNearbyChatScreenChannel*>(mChannel.get());
+ LLFloaterIMNearbyChatScreenChannel* channel = dynamic_cast<LLFloaterIMNearbyChatScreenChannel*>(mChannel.get());
if(channel)
{
@@ -601,33 +606,43 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg,
toast_msg = chat_msg.mText;
}
- // Add a nearby chat toast.
- LLUUID id;
- id.generate();
- notification["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);
- }
-}
+ //Don't show nearby toast, if conversation is visible but not focused
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(LLUUID());
+ if (session_floater && session_floater->isMessagePaneExpanded()
+ && session_floater->isInVisibleChain() && !session_floater->isMinimized()
+ && !(session_floater->getHost() && session_floater->getHost()->isMinimized()))
+ {
+ return;
+ }
-void LLNearbyChatHandler::onDeleteToast(LLToast* toast)
-{
+ //Will show toast when chat preference is set
+ if((gSavedSettings.getString("NotificationNearbyChatOptions") == "toast") || !session_floater->isMessagePaneExpanded())
+ {
+ // Add a nearby chat toast.
+ LLUUID id;
+ id.generate();
+ 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);
+
+ 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);
+ }
+
+ }
}
//-----------------------------------------------------------------------------------------------
-// LLNearbyChatToast
+// LLFloaterIMNearbyChatToast
//-----------------------------------------------------------------------------------------------
// virtual
-void LLNearbyChatToast::onClose(bool app_quitting)
+void LLFloaterIMNearbyChatToast::onClose(bool app_quitting)
{
mNearbyChatScreenChannelp->onToastDestroyed(this, app_quitting);
}
diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llfloaterimnearbychathandler.h
index b0e4f62d51..5e6f8cde30 100644
--- a/indra/newview/llnearbychathandler.h
+++ b/indra/newview/llfloaterimnearbychathandler.h
@@ -1,5 +1,5 @@
/**
- * @file llnearbychathandler.h
+ * @file llfloaterimnearbychathandler.h
* @brief nearby chat notify
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
@@ -24,27 +24,26 @@
* $/LicenseInfo$
*/
-#ifndef LL_LLNEARBYCHATHANDLER_H
-#define LL_LLNEARBYCHATHANDLER_H
+#ifndef LL_LLFLOATERIMNEARBYCHATHANDLER_H
+#define LL_LLFLOATERIMNEARBYCHATHANDLER_H
#include "llnotificationhandler.h"
class LLEventPump;
-//add LLNearbyChatHandler to LLNotificationsUI namespace
+//add LLFloaterIMNearbyChatHandler to LLNotificationsUI namespace
namespace LLNotificationsUI{
-class LLNearbyChatHandler : public LLChatHandler
+class LLFloaterIMNearbyChatHandler : public LLChatHandler
{
public:
- LLNearbyChatHandler(e_notification_type type,const LLSD& id);
- virtual ~LLNearbyChatHandler();
+ LLFloaterIMNearbyChatHandler();
+ virtual ~LLFloaterIMNearbyChatHandler();
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;
@@ -52,4 +51,4 @@ protected:
}
-#endif /* LL_LLNEARBYCHATHANDLER_H */
+#endif /* LL_LLFLOATERIMNEARBYCHATHANDLER_H */
diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp
index a63e1fb76e..14a22bcd84 100644
--- a/indra/newview/llnearbychatbarlistener.cpp
+++ b/indra/newview/llfloaterimnearbychatlistener.cpp
@@ -1,8 +1,8 @@
/**
- * @file llnearbychatbarlistener.cpp
+ * @file llfloaterimnearbychatlistener.cpp
* @author Dave Simmons
* @date 2011-03-15
- * @brief Implementation for LLNearbyChatBarListener.
+ * @brief Implementation for LLFloaterIMNearbyChatListener.
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -28,15 +28,15 @@
#include "llviewerprecompiledheaders.h"
-#include "llnearbychatbarlistener.h"
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychatlistener.h"
+#include "llfloaterimnearbychat.h"
#include "llagent.h"
#include "llchat.h"
-LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar)
+LLFloaterIMNearbyChatListener::LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar)
: LLEventAPI("LLChatBar",
"LLChatBar listener to (e.g.) sendChat, etc."),
mChatbar(chatbar)
@@ -46,12 +46,12 @@ LLNearbyChatBarListener::LLNearbyChatBarListener(LLNearbyChatBar & chatbar)
"[\"message\"] chat message text [required]\n"
"[\"channel\"] chat channel number [default = 0]\n"
"[\"type\"] chat type \"whisper\", \"normal\", \"shout\" [default = \"normal\"]",
- &LLNearbyChatBarListener::sendChat);
+ &LLFloaterIMNearbyChatListener::sendChat);
}
// "sendChat" command
-void LLNearbyChatBarListener::sendChat(LLSD const & chat_data) const
+void LLFloaterIMNearbyChatListener::sendChat(LLSD const & chat_data) const
{
// Extract the data
std::string chat_text = chat_data["message"].asString();
diff --git a/indra/newview/llnearbychatbarlistener.h b/indra/newview/llfloaterimnearbychatlistener.h
index 9af9bc1f7b..1470a6dc1e 100644
--- a/indra/newview/llnearbychatbarlistener.h
+++ b/indra/newview/llfloaterimnearbychatlistener.h
@@ -1,8 +1,8 @@
/**
- * @file llnearbychatbarlistener.h
+ * @file llfloaterimnearbychatlistener.h
* @author Dave Simmons
* @date 2011-03-15
- * @brief Class definition for LLNearbyChatBarListener.
+ * @brief Class definition for LLFloaterIMNearbyChatListener.
*
* $LicenseInfo:firstyear=2011&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -27,24 +27,24 @@
*/
-#ifndef LL_LLNEARBYCHATBARLISTENER_H
-#define LL_LLNEARBYCHATBARLISTENER_H
+#ifndef LL_LLFLOATERIMNEARBYCHATLISTENER_H
+#define LL_LLFLOATERIMNEARBYCHATLISTENER_H
#include "lleventapi.h"
class LLSD;
-class LLNearbyChatBar;
+class LLFloaterIMNearbyChat;
-class LLNearbyChatBarListener : public LLEventAPI
+class LLFloaterIMNearbyChatListener : public LLEventAPI
{
public:
- LLNearbyChatBarListener(LLNearbyChatBar & chatbar);
+ LLFloaterIMNearbyChatListener(LLFloaterIMNearbyChat & chatbar);
private:
void sendChat(LLSD const & chat_data) const;
- LLNearbyChatBar & mChatbar;
+ LLFloaterIMNearbyChat & mChatbar;
};
-#endif // LL_LLNEARBYCHATBARLISTENER_H
+#endif // LL_LLFLOATERIMNEARBYCHATLISTENER_H
diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp
new file mode 100644
index 0000000000..73adfd0eda
--- /dev/null
+++ b/indra/newview/llfloaterimsession.cpp
@@ -0,0 +1,1274 @@
+/**
+ * @file llfloaterimsession.cpp
+ * @brief LLFloaterIMSession 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 "llfloaterimsession.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 "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container
+#include "llinventoryfunctions.h"
+//#include "lllayoutstack.h"
+#include "llchatentry.h"
+#include "lllogchat.h"
+#include "llscreenchannel.h"
+#include "llsyswellwindow.h"
+#include "lltrans.h"
+#include "llchathistory.h"
+#include "llnotifications.h"
+#include "llviewerwindow.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 LLFloaterIMSession::sIMFloaterShowedSignal;
+
+LLFloaterIMSession::LLFloaterIMSession(const LLUUID& session_id)
+ : LLFloaterIMSessionTab(session_id),
+ mLastMessageIndex(-1),
+ mDialog(IM_NOTHING_SPECIAL),
+ mTypingStart(),
+ mShouldSendTypingState(false),
+ mMeTyping(false),
+ mOtherTyping(false),
+ mSessionNameUpdatedForTyping(false),
+ mTypingTimer(),
+ mTypingTimeoutTimer(),
+ mPositioned(false),
+ mSessionInitialized(false)
+{
+ mIsNearbyChat = false;
+
+ initIMSession(session_id);
+
+ setOverlapsScreenChannel(true);
+
+ LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
+ mEnableCallbackRegistrar.add("Avatar.EnableGearItem", boost::bind(&LLFloaterIMSession::enableGearMenuItem, this, _2));
+ mCommitCallbackRegistrar.add("Avatar.GearDoToSelected", boost::bind(&LLFloaterIMSession::GearDoToSelected, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.CheckGearItem", boost::bind(&LLFloaterIMSession::checkGearMenuItem, this, _2));
+
+ setDocked(true);
+}
+
+
+// virtual
+void LLFloaterIMSession::refresh()
+{
+ if (mMeTyping)
+{
+ // Time out if user hasn't typed for a while.
+ if (mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
+ {
+ setTyping(false);
+ }
+ }
+}
+
+// virtual
+void LLFloaterIMSession::onTearOffClicked()
+{
+ LLFloaterIMSessionTab::onTearOffClicked();
+
+ if(mIsP2PChat)
+ {
+ if(isTornOff())
+ {
+ mSpeakingIndicator->setSpeakerId(mOtherParticipantUUID, mSessionID);
+ }
+ else
+ {
+ mSpeakingIndicator->setSpeakerId(LLUUID::null);
+ }
+ }
+}
+
+// virtual
+void LLFloaterIMSession::onClickCloseBtn()
+{
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID);
+
+ if (session != NULL)
+ {
+ 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;
+ }
+ }
+ else
+ {
+ llwarns << "Empty session with id: " << (mSessionID.asString()) << llendl;
+ return;
+ }
+
+ LLFloaterIMSessionTab::onClickCloseBtn();
+}
+
+/* static */
+void LLFloaterIMSession::newIMCallback(const LLSD& data)
+{
+ if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull())
+ {
+ LLUUID session_id = data["session_id"].asUUID();
+
+ LLFloaterIMSession* floater = LLFloaterReg::findTypedInstance<LLFloaterIMSession>("impanel", session_id);
+
+ // update if visible, otherwise will be updated when opened
+ if (floater && floater->isInVisibleChain())
+ {
+ floater->updateMessages();
+ }
+ }
+}
+
+void LLFloaterIMSession::onVisibilityChange(const LLSD& new_visibility)
+{
+ bool visible = new_visibility.asBoolean();
+
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
+
+ if (visible && voice_channel &&
+ voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED)
+ {
+ LLFloaterReg::showInstance("voice_call", mSessionID);
+ }
+ else
+ {
+ LLFloaterReg::hideInstance("voice_call", mSessionID);
+ }
+}
+
+void LLFloaterIMSession::onSendMsg( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterIMSession* self = (LLFloaterIMSession*) userdata;
+ self->sendMsgFromInputEditor();
+ self->setTyping(false);
+}
+
+bool LLFloaterIMSession::enableGearMenuItem(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ selected_uuids.push_back(mOtherParticipantUUID);
+
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ return floater_container->enableContextMenuItem(command, selected_uuids);
+}
+
+void LLFloaterIMSession::GearDoToSelected(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ selected_uuids.push_back(mOtherParticipantUUID);
+
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ floater_container->doToParticipants(command, selected_uuids);
+}
+
+bool LLFloaterIMSession::checkGearMenuItem(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ selected_uuids.push_back(mOtherParticipantUUID);
+
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ return floater_container->checkContextMenuItem(command, selected_uuids);
+}
+
+void LLFloaterIMSession::sendMsgFromInputEditor()
+{
+ if (gAgent.isGodlike()
+ || (mDialog != IM_NOTHING_SPECIAL)
+ || !mOtherParticipantUUID.isNull())
+ {
+ if (mInputEditor)
+ {
+ LLWString text = mInputEditor->getWText();
+ LLWStringUtil::trim(text);
+ LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines.
+ if(!text.empty())
+ {
+ // Truncate and convert to UTF8 for transport
+ std::string utf8_text = wstring_to_utf8str(text);
+
+ sendMsg(utf8_text);
+
+ mInputEditor->setText(LLStringUtil::null);
+ }
+ }
+ }
+ else
+ {
+ llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
+ }
+}
+
+void LLFloaterIMSession::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);
+ }
+
+ updateMessages();
+}
+
+LLFloaterIMSession::~LLFloaterIMSession()
+{
+ mVoiceChannelStateChangeConnection.disconnect();
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver(this);
+ }
+
+ LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
+}
+
+
+void LLFloaterIMSession::initIMSession(const LLUUID& session_id)
+{
+ // Change the floater key to bind it to a new session.
+ setKey(session_id);
+
+ mSessionID = session_id;
+ mSession = LLIMModel::getInstance()->findIMSession(mSessionID);
+
+ if (mSession)
+ {
+ mIsP2PChat = mSession->isP2PSessionType();
+ mSessionInitialized = mSession->mSessionInitialized;
+ mDialog = mSession->mType;
+ }
+}
+
+void LLFloaterIMSession::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
+ if ( mSession && !mSession->mTextIMPossible )
+ {
+ mInputEditor->setEnabled(FALSE);
+ mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label"));
+ }
+
+ if (!mIsP2PChat)
+ {
+ std::string session_name(LLIMModel::instance().getName(mSessionID));
+ updateSessionName(session_name);
+ }
+}
+
+//virtual
+BOOL LLFloaterIMSession::postBuild()
+{
+ BOOL result = LLFloaterIMSessionTab::postBuild();
+
+ mInputEditor->setMaxTextLength(1023);
+ mInputEditor->setAutoreplaceCallback(boost::bind(&LLAutoReplace::autoreplaceCallback, LLAutoReplace::getInstance(), _1, _2, _3, _4, _5));
+ 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(&LLFloaterIMSession::onAddButtonClicked, this));
+
+ childSetAction("voice_call_btn", boost::bind(&LLFloaterIMSession::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)
+
+ initIMFloater();
+
+ return result;
+}
+
+void LLFloaterIMSession::onAddButtonClicked()
+{
+ LLView * button = findChild<LLView>("toolbar_panel")->findChild<LLButton>("add_btn");
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMSession::addSessionParticipants, this, _1), TRUE, TRUE, FALSE, root_floater->getName(), button);
+ if (!picker)
+ {
+ return;
+ }
+
+ // Need to disable 'ok' button when selected users are already in conversation.
+ picker->setOkBtnEnableCb(boost::bind(&LLFloaterIMSession::canAddSelectedToChat, this, _1));
+
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
+}
+
+bool LLFloaterIMSession::canAddSelectedToChat(const uuid_vec_t& uuids)
+{
+ if (!mSession
+ || mDialog == IM_SESSION_GROUP_START
+ || mDialog == IM_SESSION_INVITE && gAgent.isInGroup(mSessionID))
+ {
+ return false;
+ }
+
+ if (mIsP2PChat)
+ {
+ // For a P2P session just check if we are not adding the other participant.
+
+ for (uuid_vec_t::const_iterator id = uuids.begin();
+ id != uuids.end(); ++id)
+ {
+ if (*id == mOtherParticipantUUID)
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // 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 LLFloaterIMSession::addSessionParticipants(const uuid_vec_t& uuids)
+{
+ if (mIsP2PChat)
+ {
+ LLSD payload;
+ LLSD args;
+
+ LLNotificationsUtil::add("ConfirmAddingChatParticipants", args, payload,
+ boost::bind(&LLFloaterIMSession::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);
+ }
+}
+
+void LLFloaterIMSession::addP2PSessionParticipants(const LLSD& notification, const LLSD& response, const uuid_vec_t& uuids)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option != 0)
+ {
+ return;
+ }
+
+ 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 LLFloaterIMSession::sendParticipantsAddedNotification(const uuid_vec_t& uuids)
+{
+ std::string names_string;
+ LLAvatarActions::buildResidentsString(uuids, names_string);
+ LLStringUtil::format_map_t args;
+ args["[NAME]"] = names_string;
+
+ sendMsg(getString(uuids.size() > 1 ? "multiple_participants_added" : "participant_added", args));
+}
+
+void LLFloaterIMSession::boundVoiceChannel()
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
+ if(voice_channel)
+ {
+ mVoiceChannelStateChangeConnection = voice_channel->setStateChangedCallback(
+ boost::bind(&LLFloaterIMSession::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 LLFloaterIMSession::onCallButtonClicked()
+{
+ LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
+ if (voice_channel)
+ {
+ bool is_call_active = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED;
+ if (is_call_active)
+ {
+ gIMMgr->endCall(mSessionID);
+ }
+ else
+ {
+ gIMMgr->startCall(mSessionID);
+ }
+ }
+}
+
+void LLFloaterIMSession::onChange(EStatusType status, const std::string &channelURI, bool proximal)
+{
+ if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL)
+ {
+ enableDisableCallBtn();
+ }
+}
+
+void LLFloaterIMSession::onVoiceChannelStateChanged(
+ const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
+{
+ bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED;
+ updateCallBtnState(callIsActive);
+}
+
+void LLFloaterIMSession::updateSessionName(const std::string& name)
+{
+ if (!name.empty())
+ {
+ LLFloaterIMSessionTab::updateSessionName(name);
+ mTypingStart.setArg("[NAME]", name);
+ setTitle (mOtherTyping ? mTypingStart.getString() : name);
+ mSessionNameUpdatedForTyping = mOtherTyping;
+ }
+}
+
+//static
+LLFloaterIMSession* LLFloaterIMSession::show(const LLUUID& session_id)
+{
+ closeHiddenIMToasts();
+
+ 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
+ LLFloaterIMSession* floater = getInstance(session_id);
+ if (!floater)
+ return NULL;
+
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+
+ // Do not add again existing floaters
+ if (!exist)
+ {
+ // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
+ // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists
+ LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END;
+ if (floater_container)
+ {
+ floater_container->addFloater(floater, TRUE, i_pt);
+ }
+ }
+
+ floater->openFloater(floater->getKey());
+
+ floater->setVisible(TRUE);
+
+ return floater;
+}
+//static
+LLFloaterIMSession* LLFloaterIMSession::findInstance(const LLUUID& session_id)
+{
+ LLFloaterIMSession* conversation =
+ LLFloaterReg::findTypedInstance<LLFloaterIMSession>("impanel", session_id);
+
+ return conversation;
+}
+
+LLFloaterIMSession* LLFloaterIMSession::getInstance(const LLUUID& session_id)
+{
+ LLFloaterIMSession* conversation =
+ LLFloaterReg::getTypedInstance<LLFloaterIMSession>("impanel", session_id);
+
+ return conversation;
+}
+
+void LLFloaterIMSession::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);
+ LLFloaterIMSessionTab::restoreFloater();
+ // Clean up the conversation *after* the session has been ended
+ LLFloaterIMSessionTab::onClose(app_quitting);
+}
+
+void LLFloaterIMSession::setDocked(bool docked, bool pop_on_undock)
+{
+ // update notification channel state
+ LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
+ (LLNotificationsUI::LLChannelManager::getInstance()->
+ findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+
+ if(!isChatMultiTab())
+ {
+ LLTransientDockableFloater::setDocked(docked, pop_on_undock);
+ }
+
+ // update notification channel state
+ if(channel)
+ {
+ channel->updateShowToastsState();
+ channel->redrawToasts();
+ }
+}
+
+void LLFloaterIMSession::setVisible(BOOL visible)
+{
+ LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
+ (LLNotificationsUI::LLChannelManager::getInstance()->
+ findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+
+ LLFloaterIMSessionTab::setVisible(visible);
+
+ // update notification channel state
+ if(channel)
+ {
+ channel->updateShowToastsState();
+ channel->redrawToasts();
+ }
+
+ if(!visible)
+ {
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
+ {
+ LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(mSessionID);
+ if(NULL != chicletp)
+ {
+ chicletp->setToggleState(false);
+ }
+ }
+ }
+
+ if (visible && isInVisibleChain())
+ {
+ sIMFloaterShowedSignal(mSessionID);
+
+ }
+
+}
+
+BOOL LLFloaterIMSession::getVisible()
+{
+ bool visible;
+
+ if(isChatMultiTab())
+ {
+ LLFloaterIMContainer* im_container =
+ LLFloaterIMContainer::getInstance();
+
+ // Treat inactive floater as invisible.
+ bool is_active = im_container->getActiveFloater() == this;
+
+ //torn off floater is always inactive
+ if (!is_active && getHost() != im_container)
+ {
+ visible = LLTransientDockableFloater::getVisible();
+ }
+ else
+ {
+ // getVisible() returns TRUE when Tabbed IM window is minimized.
+ visible = is_active && !im_container->isMinimized()
+ && im_container->getVisible();
+ }
+ }
+ else
+ {
+ visible = LLTransientDockableFloater::getVisible();
+ }
+
+ return visible;
+}
+
+//static
+bool LLFloaterIMSession::toggle(const LLUUID& session_id)
+{
+ if(!isChatMultiTab())
+ {
+ LLFloaterIMSession* floater = LLFloaterReg::findTypedInstance<LLFloaterIMSession>(
+ "impanel", session_id);
+ if (floater && floater->getVisible() && floater->hasFocus())
+ {
+ // clicking on chiclet to close floater just hides it to maintain existing
+ // scroll/text entry state
+ floater->setVisible(false);
+ return false;
+ }
+ else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus()))
+ {
+ floater->setVisible(TRUE);
+ floater->setFocus(TRUE);
+ return true;
+ }
+ }
+
+ // ensure the list of messages is updated when floater is made visible
+ show(session_id);
+ return true;
+}
+
+void LLFloaterIMSession::sessionInitReplyReceived(const LLUUID& im_session_id)
+{
+ mSessionInitialized = true;
+
+ //will be different only for an ad-hoc im session
+ if (mSessionID != im_session_id)
+ {
+ initIMSession(im_session_id);
+ buildConversationViewParticipant();
+ }
+
+ initIMFloater();
+ LLFloaterIMSessionTab::updateGearBtn();
+ //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB)
+
+ //need to send delayed messages collected while waiting for session initialization
+ if (mQueuedMsgsForInit.size())
+ {
+ LLSD::array_iterator iter;
+ for ( iter = mQueuedMsgsForInit.beginArray();
+ iter != mQueuedMsgsForInit.endArray(); ++iter)
+ {
+ LLIMModel::sendMessage(iter->asString(), mSessionID,
+ mOtherParticipantUUID, mDialog);
+ }
+
+ mQueuedMsgsForInit.clear();
+ }
+}
+
+void LLFloaterIMSession::updateMessages()
+{
+ std::list<LLSD> messages;
+
+ // we shouldn't reset unread message counters if IM floater doesn't have focus
+ LLIMModel::instance().getMessages(
+ mSessionID, messages, mLastMessageIndex + 1, hasFocus());
+
+ if (messages.size())
+ {
+ std::ostringstream message;
+ std::list<LLSD>::const_reverse_iterator iter = messages.rbegin();
+ std::list<LLSD>::const_reverse_iterator iter_end = messages.rend();
+ for (; iter != iter_end; ++iter)
+ {
+ LLSD msg = *iter;
+
+ std::string time = msg["time"].asString();
+ LLUUID from_id = msg["from_id"].asUUID();
+ std::string from = msg["from"].asString();
+ std::string message = msg["message"].asString();
+ bool is_history = msg["is_history"].asBoolean();
+
+ LLChat chat;
+ chat.mFromID = from_id;
+ chat.mSessionID = mSessionID;
+ chat.mFromName = from;
+ chat.mTimeStr = time;
+ chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle;
+
+ // process offer notification
+ if (msg.has("notification_id"))
+ {
+ chat.mNotifId = msg["notification_id"].asUUID();
+ // if notification exists - embed it
+ if (LLNotificationsUtil::find(chat.mNotifId) != NULL)
+ {
+ // remove embedded notification from channel
+ LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
+ (LLNotificationsUI::LLChannelManager::getInstance()->
+ findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+ if (getVisible())
+ {
+ // toast will be automatically closed since it is not storable toast
+ channel->hideToast(chat.mNotifId);
+ }
+ }
+ // if notification doesn't exist - try to use next message which should be log entry
+ else
+ {
+ continue;
+ }
+ }
+ //process text message
+ else
+ {
+ chat.mText = message;
+ }
+
+ // 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
+ if (chat.mNotifId.notNull() && LLNotificationsUtil::find(chat.mNotifId) != NULL)
+ {
+ if (++iter == iter_end)
+ {
+ break;
+ }
+ else
+ {
+ mLastMessageIndex++;
+ }
+ }
+ }
+ }
+}
+
+void LLFloaterIMSession::reloadMessages(bool clean_messages/* = false*/)
+{
+ if (clean_messages)
+ {
+ LLIMModel::LLIMSession * sessionp = LLIMModel::instance().findIMSession(mSessionID);
+
+ if (NULL != sessionp)
+ {
+ sessionp->loadHistory();
+ }
+ }
+
+ mChatHistory->clear();
+ mLastMessageIndex = -1;
+ updateMessages();
+ mInputEditor->setFont(LLViewerChat::getChatFont());
+}
+
+// static
+void LLFloaterIMSession::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata )
+{
+ LLFloaterIMSession* self= (LLFloaterIMSession*) userdata;
+
+ // Allow enabling the LLFloaterIMSession input editor only if session can accept text
+ LLIMModel::LLIMSession* im_session =
+ LLIMModel::instance().findIMSession(self->mSessionID);
+ //TODO: While disabled lllineeditor can receive focus we need to check if it is enabled (EK)
+ if( im_session && im_session->mTextIMPossible && self->mInputEditor->getEnabled())
+ {
+ //in disconnected state IM input editor should be disabled
+ self->mInputEditor->setEnabled(!gDisconnected);
+ }
+}
+
+// static
+void LLFloaterIMSession::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata)
+{
+ LLFloaterIMSession* self = (LLFloaterIMSession*) userdata;
+ self->setTyping(false);
+}
+
+// static
+void LLFloaterIMSession::onInputEditorKeystroke(LLTextEditor* caller, void* userdata)
+{
+ LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
+ LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();
+ if (im_box)
+ {
+ im_box->flashConversationItemWidget(self->mSessionID,false);
+ }
+ std::string text = self->mInputEditor->getText();
+
+ // Deleting all text counts as stopping typing.
+ self->setTyping(!text.empty());
+}
+
+void LLFloaterIMSession::setTyping(bool typing)
+{
+ if ( typing )
+ {
+ // Started or proceeded typing, reset the typing timeout timer
+ mTypingTimeoutTimer.reset();
+ }
+
+ if ( mMeTyping != typing )
+ {
+ // Typing state is changed
+ mMeTyping = typing;
+ // So, should send current state
+ mShouldSendTypingState = true;
+ // In case typing is started, send state after some delay
+ mTypingTimer.reset();
+ }
+
+ // Don't want to send typing indicators to multiple people, potentially too
+ // much network traffic. Only send in person-to-person IMs.
+ if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL )
+ {
+ // Still typing, send 'start typing' notification or
+ // send 'stop typing' notification immediately
+ if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f)
+ {
+ LLIMModel::instance().sendTypingState(mSessionID,
+ mOtherParticipantUUID, mMeTyping);
+ mShouldSendTypingState = false;
+ }
+ }
+
+ if (!mIsNearbyChat)
+ {
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ if (speaker_mgr)
+ {
+ speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE);
+ }
+ }
+}
+
+void LLFloaterIMSession::processIMTyping(const LLIMInfo* im_info, BOOL typing)
+{
+ if ( typing )
+ {
+ // other user started typing
+ addTypingIndicator(im_info);
+ }
+ else
+ {
+ // other user stopped typing
+ removeTypingIndicator(im_info);
+ }
+}
+
+void LLFloaterIMSession::processAgentListUpdates(const LLSD& body)
+{
+ uuid_vec_t joined_uuids;
+
+ if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap())
+ {
+ LLSD::map_const_iterator update_it;
+ for(update_it = body["agent_updates"].beginMap();
+ update_it != body["agent_updates"].endMap();
+ ++update_it)
+ {
+ LLUUID agent_id(update_it->first);
+ LLSD agent_data = update_it->second;
+
+ if (agent_data.isMap())
+ {
+ // store the new participants in joined_uuids
+ if (agent_data.has("transition") && agent_data["transition"].asString() == "ENTER")
+ {
+ joined_uuids.push_back(agent_id);
+ }
+
+ // 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");
+ }
+ }
+ }
+ }
+
+ // 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());
+
+ 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)
+ {
+ 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 LLFloaterIMSession::processSessionUpdate(const LLSD& session_update)
+{
+ // *TODO : verify following code when moderated mode will be implemented
+ if ( false && session_update.has("moderated_mode") &&
+ session_update["moderated_mode"].has("voice") )
+ {
+ BOOL voice_moderated = session_update["moderated_mode"]["voice"];
+ const std::string session_label = LLIMModel::instance().getName(mSessionID);
+
+ if (voice_moderated)
+ {
+ setTitle(session_label + std::string(" ")
+ + LLTrans::getString("IM_moderated_chat_label"));
+ }
+ else
+ {
+ setTitle(session_label);
+ }
+
+ // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added
+ //update the speakers dropdown too
+ //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated);
+ }
+}
+
+// virtual
+void LLFloaterIMSession::draw()
+{
+ // add people who were added via dropPerson()
+ if (!mPendingParticipants.empty())
+ {
+ addSessionParticipants(mPendingParticipants);
+ mPendingParticipants.clear();
+ }
+
+ LLFloaterIMSessionTab::draw();
+}
+
+// virtual
+BOOL LLFloaterIMSession::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ std::string& tooltip_msg)
+{
+ if (cargo_type == DAD_PERSON)
+ {
+ if (dropPerson(static_cast<LLUUID*>(cargo_data), drop))
+ {
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ else if (mDialog == IM_NOTHING_SPECIAL)
+ {
+ LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop,
+ cargo_type, cargo_data, accept);
+ }
+
+ return TRUE;
+}
+
+bool LLFloaterIMSession::dropPerson(LLUUID* person_id, bool drop)
+{
+ bool res = person_id && person_id->notNull();
+ if(res)
+ {
+ uuid_vec_t ids;
+ ids.push_back(*person_id);
+
+ res = canAddSelectedToChat(ids);
+ if(res && drop)
+ {
+ // these people will be added during the next draw() call
+ // (so they can be added all at once)
+ mPendingParticipants.push_back(*person_id);
+ }
+ }
+
+ return res;
+}
+
+BOOL LLFloaterIMSession::isInviteAllowed() const
+{
+ return ( (IM_SESSION_CONFERENCE_START == mDialog)
+ || (IM_SESSION_INVITE == mDialog && !gAgent.isInGroup(mSessionID))
+ || mIsP2PChat);
+}
+
+class LLSessionInviteResponder : public LLHTTPClient::Responder
+{
+public:
+ LLSessionInviteResponder(const LLUUID& session_id)
+ {
+ mSessionID = session_id;
+ }
+
+ void error(U32 statusNum, const std::string& reason)
+ {
+ llinfos << "Error inviting all agents to session" << llendl;
+ //throw something back to the viewer here?
+ }
+
+private:
+ LLUUID mSessionID;
+};
+
+BOOL LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ bool is_region_exist = region != NULL;
+
+ if (is_region_exist)
+ {
+ S32 count = ids.size();
+
+ if( isInviteAllowed() && (count > 0) )
+ {
+ llinfos << "LLFloaterIMSession::inviteToSession() - inviting participants" << llendl;
+
+ std::string url = region->getCapability("ChatSessionRequest");
+
+ 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
+ {
+ llinfos << "LLFloaterIMSession::inviteToSession -"
+ << " no need to invite agents for "
+ << mDialog << llendl;
+ // successful add, because everyone that needed to get added
+ // was added.
+ }
+ }
+
+ return is_region_exist;
+}
+
+void LLFloaterIMSession::addTypingIndicator(const LLIMInfo* im_info)
+{
+ // We may have lost a "stop-typing" packet, don't add it twice
+ if (im_info && !mOtherTyping)
+ {
+ mOtherTyping = true;
+
+ // Update speaker
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ if ( speaker_mgr )
+ {
+ speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE);
+ }
+ }
+}
+
+void LLFloaterIMSession::removeTypingIndicator(const LLIMInfo* im_info)
+{
+ if (mOtherTyping)
+ {
+ mOtherTyping = false;
+
+ if (im_info)
+ {
+ // Update speaker
+ LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ if (speaker_mgr)
+ {
+ speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE);
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterIMSession::closeHiddenIMToasts()
+{
+ class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher
+ {
+ public:
+ bool matches(const LLNotificationPtr notification) const
+ {
+ // "notifytoast" type of notifications is reserved for IM notifications
+ return "notifytoast" == notification->getType();
+ }
+ };
+
+ LLNotificationsUI::LLScreenChannel* channel =
+ LLNotificationsUI::LLChannelManager::getNotificationScreenChannel();
+ if (channel != NULL)
+ {
+ channel->closeHiddenToasts(IMToastMatcher());
+ }
+}
+// static
+void LLFloaterIMSession::confirmLeaveCallCallback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ const LLSD& payload = notification["payload"];
+ LLUUID session_id = payload["session_id"];
+
+ LLFloater* im_floater = findInstance(session_id);
+ if (option == 0 && im_floater != NULL)
+ {
+ im_floater->closeFloater();
+ }
+
+ return;
+}
+
+// static
+void LLFloaterIMSession::sRemoveTypingIndicator(const LLSD& data)
+{
+ LLUUID session_id = data["session_id"];
+ if (session_id.isNull())
+ return;
+
+ LLUUID from_id = data["from_id"];
+ if (gAgentID == from_id || LLUUID::null == from_id)
+ return;
+
+ LLFloaterIMSession* floater = LLFloaterIMSession::findInstance(session_id);
+ if (!floater)
+ return;
+
+ if (IM_NOTHING_SPECIAL != floater->mDialog)
+ return;
+
+ floater->removeTypingIndicator();
+}
+
+// static
+void LLFloaterIMSession::onIMChicletCreated( const LLUUID& session_id )
+{
+ LLFloaterIMSession::addToHost(session_id);
+}
+
+boost::signals2::connection LLFloaterIMSession::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb)
+{
+ return LLFloaterIMSession::sIMFloaterShowedSignal.connect(cb);
+}
diff --git a/indra/newview/llimfloater.h b/indra/newview/llfloaterimsession.h
index f7cd35b5eb..cb330bca0f 100644
--- a/indra/newview/llimfloater.h
+++ b/indra/newview/llfloaterimsession.h
@@ -1,6 +1,6 @@
/**
- * @file llimfloater.h
- * @brief LLIMFloater class definition
+ * @file llfloaterimsession.h
+ * @brief LLFloaterIMSession class definition
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -24,62 +24,79 @@
* $/LicenseInfo$
*/
-#ifndef LL_IMFLOATER_H
-#define LL_IMFLOATER_H
+#ifndef LL_FLOATERIMSESSION_H
+#define LL_FLOATERIMSESSION_H
+#include "llimview.h"
+#include "llfloaterimsessiontab.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 LLFloaterIMSession
+ : public LLVoiceClientStatusObserver
+ , public LLFloaterIMSessionTab
{
- LOG_CLASS(LLIMFloater);
+ LOG_CLASS(LLFloaterIMSession);
public:
- LLIMFloater(const LLUUID& session_id);
+ LLFloaterIMSession(const LLUUID& session_id);
+
+ virtual ~LLFloaterIMSession();
+
+ void initIMSession(const LLUUID& session_id);
+ void initIMFloater();
- virtual ~LLIMFloater();
-
// LLView overrides
/*virtual*/ BOOL postBuild();
/*virtual*/ void setVisible(BOOL visible);
/*virtual*/ BOOL getVisible();
// Check typing timeout timer.
+
/*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);
+
+ static LLFloaterIMSession* findInstance(const LLUUID& session_id);
+ static LLFloaterIMSession* 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);
+ static LLFloaterIMSession* show(const LLUUID& session_id);
// Toggle panel specified by session_id
// 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();
+ /*virtual*/ void updateMessages();
+ void reloadMessages(bool clean_messages = false);
+ 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 +106,61 @@ public:
void setPositioned(bool b) { mPositioned = b; };
void onVisibilityChange(const LLSD& new_visibility);
- void processIMTyping(const LLIMInfo* im_info, BOOL typing);
- void processAgentListUpdates(const LLSD& body);
- void processSessionUpdate(const LLSD& session_update);
+ bool enableGearMenuItem(const LLSD& userdata);
+ void GearDoToSelected(const LLSD& userdata);
+ bool checkGearMenuItem(const LLSD& userdata);
- void updateChatHistoryStyle();
- static void processChatHistoryStyleUpdate(const LLSD& newvalue);
+ // Implements LLVoiceClientStatusObserver::onChange() to enable the call
+ // button when voice is available
+ void onChange(EStatusType status, const std::string &channelURI,
+ bool proximal);
- BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
- BOOL drop, EDragAndDropType cargo_type,
- void *cargo_data, EAcceptance *accept,
- std::string& tooltip_msg);
-
- /**
- * Returns true if chat is displayed in multi tabbed floater
- * false if chat is displayed in multiple windows
- */
- static bool isChatMultiTab();
+ virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }
+ virtual void onVoiceChannelStateChanged(
+ const LLVoiceChannel::EState& old_state,
+ const LLVoiceChannel::EState& new_state);
- static void initIMFloater();
+ void processIMTyping(const LLIMInfo* im_info, BOOL typing);
+ void processAgentListUpdates(const LLSD& body);
+ void processSessionUpdate(const LLSD& session_update);
//used as a callback on receiving new IM message
static void sRemoveTypingIndicator(const LLSD& data);
-
static void onIMChicletCreated(const LLUUID& session_id);
+ const LLUUID& getOtherParticipantUUID() {return mOtherParticipantUUID;}
- virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; }
-
-protected:
- /* virtual */
- void onClickCloseBtn();
+ static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb);
+ static floater_showed_signal_t sIMFloaterShowedSignal;
+ bool needsTitleOverwrite() { return mSessionNameUpdatedForTyping && mOtherTyping; }
+ S32 getLastChatMessageIndex() {return mLastMessageIndex;}
private:
- // process focus events to set a currently active session
- /* virtual */ void onFocusLost();
- /* virtual */ void onFocusReceived();
-
- // 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);
+
+ /*virtual*/ void refresh();
+
+ /*virtual*/ void onTearOffClicked();
+ /*virtual*/ void onClickCloseBtn();
+
+ // Update the window title and input field help text
+ /*virtual*/ void updateSessionName(const std::string& name);
+
+ 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,27 +172,28 @@ 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;
LLUIString mTypingStart;
bool mMeTyping;
bool mOtherTyping;
bool mShouldSendTypingState;
LLFrameTimer mTypingTimer;
LLFrameTimer mTypingTimeoutTimer;
+ bool mSessionNameUpdatedForTyping;
bool mSessionInitialized;
LLSD mQueuedMsgsForInit;
-};
+ uuid_vec_t mInvitedParticipants;
+ uuid_vec_t mPendingParticipants;
+
+ // connection to voice channel state change signal
+ boost::signals2::connection mVoiceChannelStateChangeConnection;
+};
-#endif // LL_IMFLOATER_H
+#endif // LL_FLOATERIMSESSION_H
diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp
new file mode 100644
index 0000000000..eab2ce7798
--- /dev/null
+++ b/indra/newview/llfloaterimsessiontab.cpp
@@ -0,0 +1,1082 @@
+/**
+ * @file llfloaterimsessiontab.cpp
+ * @brief LLFloaterIMSessionTab class implements the common behavior of LNearbyChatBar
+ * @brief and LLFloaterIMSession 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 "llfloaterimsessiontab.h"
+
+#include "llagent.h"
+#include "llagentcamera.h"
+#include "llavataractions.h"
+#include "llchatentry.h"
+#include "llchathistory.h"
+#include "llchiclet.h"
+#include "llchicletbar.h"
+#include "lldraghandle.h"
+#include "llfloaterreg.h"
+#include "llfloaterimsession.h"
+#include "llfloaterimcontainer.h" // to replace separate IM Floaters with multifloater container
+#include "lllayoutstack.h"
+#include "lltoolbarview.h"
+#include "llfloaterimnearbychat.h"
+
+const F32 REFRESH_INTERVAL = 1.0f;
+
+LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id)
+ : LLTransientDockableFloater(NULL, true, session_id)
+ , mIsP2PChat(false)
+ , mExpandCollapseBtn(NULL)
+ , mTearOffBtn(NULL)
+ , mCloseBtn(NULL)
+ , mSessionID(session_id.asUUID())
+ , mConversationsRoot(NULL)
+ , mScroller(NULL)
+ , mSpeakingIndicator(NULL)
+ , mChatHistory(NULL)
+ , mInputEditor(NULL)
+ , mInputEditorPad(0)
+ , mRefreshTimer(new LLTimer())
+ , mIsHostAttached(false)
+ , mHasVisibleBeenInitialized(false)
+ , mIsParticipantListExpanded(true)
+ , mChatLayoutPanel(NULL)
+{
+ setAutoFocus(FALSE);
+ mSession = LLIMModel::getInstance()->findIMSession(mSessionID);
+
+ mCommitCallbackRegistrar.add("IMSession.Menu.Action",
+ boost::bind(&LLFloaterIMSessionTab::onIMSessionMenuItemClicked, this, _2));
+ mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem",
+ boost::bind(&LLFloaterIMSessionTab::onIMCompactExpandedMenuItemCheck, this, _2));
+ mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem",
+ boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemCheck, this, _2));
+ mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable",
+ boost::bind(&LLFloaterIMSessionTab::onIMShowModesMenuItemEnable, this, _2));
+
+ // Right click menu handling
+ mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMSessionTab::checkContextMenuItem, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2));
+ mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2));
+}
+
+LLFloaterIMSessionTab::~LLFloaterIMSessionTab()
+{
+ delete mRefreshTimer;
+}
+
+//static
+LLFloaterIMSessionTab* LLFloaterIMSessionTab::findConversation(const LLUUID& uuid)
+{
+ LLFloaterIMSessionTab* conv;
+
+ if (uuid.isNull())
+ {
+ conv = LLFloaterReg::findTypedInstance<LLFloaterIMSessionTab>("nearby_chat");
+ }
+ else
+ {
+ conv = LLFloaterReg::findTypedInstance<LLFloaterIMSessionTab>("impanel", LLSD(uuid));
+ }
+
+ return conv;
+};
+
+//static
+LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid)
+{
+ LLFloaterIMSessionTab* conv;
+
+ if (uuid.isNull())
+ {
+ conv = LLFloaterReg::getTypedInstance<LLFloaterIMSessionTab>("nearby_chat");
+ }
+ else
+ {
+ conv = LLFloaterReg::getTypedInstance<LLFloaterIMSessionTab>("impanel", LLSD(uuid));
+ }
+
+ return conv;
+};
+
+void LLFloaterIMSessionTab::setVisible(BOOL visible)
+{
+ if(visible && !mHasVisibleBeenInitialized)
+ {
+ mHasVisibleBeenInitialized = true;
+ if(!gAgentCamera.cameraMouselook())
+ {
+ LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->setVisible(true);
+ }
+ LLFloaterIMSessionTab::addToHost(mSessionID);
+ mInputButtonPanel->setVisible(isTornOff());
+ }
+
+ LLTransientDockableFloater::setVisible(visible);
+}
+
+/*virtual*/
+void LLFloaterIMSessionTab::setFocus(BOOL focus)
+{
+ LLTransientDockableFloater::setFocus(focus);
+
+ //Redirect focus to input editor
+ if (focus)
+ {
+ updateMessages();
+
+ if (mInputEditor)
+ {
+ mInputEditor->setFocus(TRUE);
+ }
+ }
+}
+
+
+void LLFloaterIMSessionTab::addToHost(const LLUUID& session_id)
+{
+ if ((session_id.notNull() && !gIMMgr->hasSession(session_id))
+ || !LLFloaterIMSessionTab::isChatMultiTab())
+ {
+ return;
+ }
+
+ // Get the floater: this will create the instance if it didn't exist
+ LLFloaterIMSessionTab* conversp = LLFloaterIMSessionTab::getConversation(session_id);
+ if (conversp)
+ {
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+
+ // Do not add again existing floaters
+ if (floater_container && !conversp->isHostAttached())
+ {
+ conversp->setHostAttached(true);
+
+ if (!conversp->isNearbyChat()
+ || gSavedPerAccountSettings.getBOOL("NearbyChatIsNotTornOff"))
+ {
+ floater_container->addFloater(conversp, false, LLTabContainer::RIGHT_OF_CURRENT);
+ }
+ 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);
+
+ conversp->forceReshape();
+ }
+ // Added floaters share some state (like sort order) with their host
+ conversp->setSortOrder(floater_container->getSortOrder());
+ }
+ }
+}
+
+void LLFloaterIMSessionTab::assignResizeLimits()
+{
+ bool is_participants_pane_collapsed = mParticipantListPanel->isCollapsed();
+
+ // disable a layoutstack's functionality when participant list panel is collapsed
+ mRightPartPanel->setIgnoreReshape(is_participants_pane_collapsed);
+
+ S32 participants_pane_target_width = is_participants_pane_collapsed?
+ 0 : (mParticipantListPanel->getRect().getWidth() + LLPANEL_BORDER_WIDTH);
+
+ S32 new_min_width = participants_pane_target_width + mRightPartPanel->getExpandedMinDim() + mFloaterExtraWidth;
+
+ setResizeLimits(new_min_width, getMinHeight());
+
+ this->mParticipantListAndHistoryStack->updateLayout();
+}
+
+BOOL LLFloaterIMSessionTab::postBuild()
+{
+ BOOL result;
+
+ mBodyStack = getChild<LLLayoutStack>("main_stack");
+ mParticipantListAndHistoryStack = getChild<LLLayoutStack>("im_panels");
+
+ mCloseBtn = getChild<LLButton>("close_btn");
+ mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this));
+
+ mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn");
+ mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this));
+
+ mExpandCollapseLineBtn = getChild<LLButton>("minz_btn");
+ mExpandCollapseLineBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onCollapseToLine, this));
+
+ mTearOffBtn = getChild<LLButton>("tear_off_btn");
+ mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this));
+
+ mGearBtn = getChild<LLButton>("gear_btn");
+
+ mParticipantListPanel = getChild<LLLayoutPanel>("speakers_list_panel");
+ mRightPartPanel = getChild<LLLayoutPanel>("right_part_holder");
+
+ mToolbarPanel = getChild<LLLayoutPanel>("toolbar_panel");
+ mContentPanel = getChild<LLLayoutPanel>("body_panel");
+ mInputButtonPanel = getChild<LLLayoutPanel>("input_button_layout_panel");
+ mInputButtonPanel->setVisible(false);
+ // Add a scroller for the folder (participant) view
+ LLRect scroller_view_rect = mParticipantListPanel->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);
+ mScroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
+ mScroller->setFollowsAll();
+
+ mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator");
+
+ // Insert that scroller into the panel widgets hierarchy
+ mParticipantListPanel->addChild(mScroller);
+
+ mChatHistory = getChild<LLChatHistory>("chat_history");
+
+ mInputEditor = getChild<LLChatEntry>("chat_editor");
+
+ mChatLayoutPanel = getChild<LLLayoutPanel>("chat_layout_panel");
+
+ mInputEditor->setTextExpandedCallback(boost::bind(&LLFloaterIMSessionTab::reshapeChatLayoutPanel, this));
+ mInputEditor->setCommitOnFocusLost( FALSE );
+ mInputEditor->setPassDelete(TRUE);
+ mInputEditor->setFont(LLViewerChat::getChatFont());
+
+ mInputEditorPad = mChatLayoutPanel->getRect().getHeight() - mInputEditor->getRect().getHeight();
+
+ setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE);
+
+ mSaveRect = isNearbyChat()
+ && !gSavedPerAccountSettings.getBOOL("NearbyChatIsNotTornOff");
+ initRectControl();
+
+ if (isChatMultiTab())
+ {
+ result = LLFloater::postBuild();
+ }
+ else
+ {
+ result = LLDockableFloater::postBuild();
+ }
+
+ // Create the root using an ad-hoc base item
+ LLConversationItem* base_item = new LLConversationItem(mSessionID, mConversationViewModel);
+ LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
+ p.rect = LLRect(0, 0, getRect().getWidth(), 0);
+ p.parent_panel = mParticipantListPanel;
+ p.listener = base_item;
+ p.view_model = &mConversationViewModel;
+ p.root = NULL;
+ p.use_ellipses = true;
+ p.options_menu = "menu_conversation.xml";
+ p.name = "root";
+ mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
+ mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+ // Attach that root to the scroller
+ mScroller->addChild(mConversationsRoot);
+ mConversationsRoot->setScrollContainer(mScroller);
+ mConversationsRoot->setFollowsAll();
+ mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox);
+
+ setMessagePaneExpanded(true);
+
+ buildConversationViewParticipant();
+ refreshConversation();
+
+ // Zero expiry time is set only once to allow initial update.
+ mRefreshTimer->setTimerExpirySec(0);
+ mRefreshTimer->start();
+ initBtns();
+
+ if (mIsParticipantListExpanded != (bool)gSavedSettings.getBOOL("IMShowControlPanel"))
+ {
+ LLFloaterIMSessionTab::onSlide(this);
+ }
+
+ // The resize limits for LLFloaterIMSessionTab should be updated, based on current values of width of conversation and message panels
+ mParticipantListPanel->getResizeBar()->setResizeListener(boost::bind(&LLFloaterIMSessionTab::assignResizeLimits, this));
+ mFloaterExtraWidth =
+ getRect().getWidth()
+ - mParticipantListAndHistoryStack->getRect().getWidth()
+ - (mParticipantListPanel->isCollapsed()? 0 : LLPANEL_BORDER_WIDTH);
+
+ assignResizeLimits();
+
+ return result;
+}
+
+LLParticipantList* LLFloaterIMSessionTab::getParticipantList()
+{
+ return dynamic_cast<LLParticipantList*>(LLFloaterIMContainer::getInstance()->getSessionModel(mSessionID));
+}
+
+void LLFloaterIMSessionTab::draw()
+{
+ if (mRefreshTimer->hasExpired())
+ {
+ LLParticipantList* item = getParticipantList();
+ if (item)
+ {
+ // Update all model items
+ item->update();
+ // If the model and view list diverge in count, rebuild
+ // Note: this happens sometimes right around init (add participant events fire but get dropped) and is the cause
+ // of missing participants, often, the user agent itself. As there will be no other event fired, there's
+ // no other choice but get those inconsistencies regularly (and lightly) checked and scrubbed.
+ if (item->getChildrenCount() != mConversationsWidgets.size())
+ {
+ buildConversationViewParticipant();
+ }
+ refreshConversation();
+ }
+
+ // Restart the refresh timer
+ mRefreshTimer->setTimerExpirySec(REFRESH_INTERVAL);
+ }
+
+ LLTransientDockableFloater::draw();
+}
+
+void LLFloaterIMSessionTab::enableDisableCallBtn()
+{
+ getChildView("voice_call_btn")->setEnabled(
+ mSessionID.notNull()
+ && mSession
+ && mSession->mSessionInitialized
+ && LLVoiceClient::getInstance()->voiceEnabled()
+ && LLVoiceClient::getInstance()->isVoiceWorking()
+ && mSession->mCallBackEnabled);
+}
+
+void LLFloaterIMSessionTab::onFocusReceived()
+{
+ setBackgroundOpaque(true);
+
+ if (mSessionID.notNull() && isInVisibleChain())
+ {
+ LLIMModel::instance().sendNoUnreadMessages(mSessionID);
+ }
+
+ LLTransientDockableFloater::onFocusReceived();
+}
+
+void LLFloaterIMSessionTab::onFocusLost()
+{
+ setBackgroundOpaque(false);
+ LLTransientDockableFloater::onFocusLost();
+}
+
+std::string LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::appendMessage(const LLChat& chat, const LLSD &args)
+{
+
+ // Update the participant activity time
+ LLFloaterIMContainer* im_box = LLFloaterIMContainer::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 LLFloaterIMSessionTab::buildConversationViewParticipant()
+{
+ // Clear the widget list since we are rebuilding afresh from the model
+ conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
+ while (widget_it != mConversationsWidgets.end())
+ {
+ removeConversationViewParticipant(widget_it->first);
+ // Iterators are invalidated by erase so we need to pick begin again
+ widget_it = mConversationsWidgets.begin();
+ }
+
+ // Get the model list
+ LLParticipantList* item = getParticipantList();
+ if (!item)
+ {
+ // Nothing to do if the model list is inexistent
+ return;
+ }
+
+ // Create the participants widgets now
+ 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);
+ addConversationViewParticipant(participant_model);
+ current_participant_model++;
+ }
+}
+
+void LLFloaterIMSessionTab::addConversationViewParticipant(LLConversationItem* participant_model)
+{
+ // Check if the model already has an associated view
+ LLUUID uuid = participant_model->getUUID();
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid);
+
+ // If not already present, create the participant view and attach it to the root, otherwise, just refresh it
+ if (widget)
+ {
+ updateConversationViewParticipant(uuid); // overkill?
+ }
+ else
+ {
+ LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model);
+ mConversationsWidgets[uuid] = participant_view;
+ participant_view->addToFolder(mConversationsRoot);
+ participant_view->addToSession(mSessionID);
+ participant_view->setVisible(TRUE);
+ }
+}
+
+void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& participant_id)
+{
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id);
+ if (widget)
+ {
+ mConversationsRoot->extractItem(widget);
+ delete widget;
+ mConversationsWidgets.erase(participant_id);
+ }
+}
+
+void LLFloaterIMSessionTab::updateConversationViewParticipant(const LLUUID& participant_id)
+{
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id);
+ if (widget)
+ {
+ widget->refresh();
+ }
+}
+
+void LLFloaterIMSessionTab::refreshConversation()
+{
+ // Note: We collect participants names to change the session name only in the case of ad-hoc conversations
+ bool is_ad_hoc = (mSession ? mSession->isAdHocSessionType() : false);
+ uuid_vec_t participants_uuids; // uuids vector for building the added participants name string
+ // For P2P chat, we still need to update the session name who may have changed (switch display name for instance)
+ if (mIsP2PChat && mSession)
+ {
+ participants_uuids.push_back(mSession->mOtherParticipantID);
+ }
+
+ conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
+ while (widget_it != mConversationsWidgets.end())
+ {
+ // Add the participant to the list except if it's the agent itself (redundant)
+ if (is_ad_hoc && (widget_it->first != gAgentID))
+ {
+ participants_uuids.push_back(widget_it->first);
+ }
+ widget_it->second->refresh();
+ widget_it->second->setVisible(TRUE);
+ ++widget_it;
+ }
+ if (is_ad_hoc || mIsP2PChat)
+ {
+ // Build the session name and update it
+ std::string session_name;
+ if (participants_uuids.size() != 0)
+ {
+ LLAvatarActions::buildResidentsString(participants_uuids, session_name);
+ }
+ else
+ {
+ session_name = LLIMModel::instance().getName(mSessionID);
+ }
+ updateSessionName(session_name);
+ }
+
+ if (mSessionID.notNull())
+ {
+ LLParticipantList* participant_list = getParticipantList();
+ if (participant_list)
+ {
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = participant_list->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = participant_list->getChildrenEnd();
+ LLIMSpeakerMgr *speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model);
+ if (speaker_mgr && participant_model)
+ {
+ LLSpeaker *participant_speaker = speaker_mgr->findSpeaker(participant_model->getUUID());
+ LLSpeaker *agent_speaker = speaker_mgr->findSpeaker(gAgentID);
+ if (participant_speaker && agent_speaker)
+ {
+ participant_model->setDisplayModeratorRole(agent_speaker->mIsModerator && participant_speaker->mIsModerator);
+ }
+ }
+ current_participant_model++;
+ }
+ }
+ }
+
+ mConversationViewModel.requestSortAll();
+ if(mConversationsRoot != NULL)
+ {
+ mConversationsRoot->arrangeAll();
+ mConversationsRoot->update();
+ }
+ updateHeaderAndToolbar();
+ refresh();
+}
+
+// Copied from LLFloaterIMContainer::createConversationViewParticipant(). Refactor opportunity!
+LLConversationViewParticipant* LLFloaterIMSessionTab::createConversationViewParticipant(LLConversationItem* item)
+{
+ LLRect panel_rect = mParticipantListPanel->getRect();
+
+ LLConversationViewParticipant::Params params;
+ params.name = item->getDisplayName();
+ params.root = mConversationsRoot;
+ params.listener = item;
+ params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); // *TODO: use conversation_view_participant.xml itemHeight value in lieu of 24
+ params.tool_tip = params.name;
+ params.participant_id = item->getUUID();
+
+ return LLUICtrlFactory::create<LLConversationViewParticipant>(params);
+}
+
+void LLFloaterIMSessionTab::setSortOrder(const LLConversationSort& order)
+{
+ mConversationViewModel.setSorter(order);
+ mConversationsRoot->arrangeAll();
+ refreshConversation();
+}
+
+void LLFloaterIMSessionTab::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);
+ }
+
+ LLFloaterIMSessionTab::processChatHistoryStyleUpdate();
+}
+
+bool LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::updateSessionName(const std::string& name)
+{
+ mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + name);
+}
+
+void LLFloaterIMSessionTab::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 LLFloaterIMSessionTab::updateHeaderAndToolbar()
+{
+ // prevent start conversation before its container
+ LLFloaterIMContainer::getInstance();
+
+ bool is_not_torn_off = !checkIfTornOff();
+ if (is_not_torn_off)
+ {
+ hideAllStandardButtons();
+ }
+
+ hideOrShowTitle();
+
+ // Participant list should be visible only in torn off floaters.
+ bool is_participant_list_visible =
+ !is_not_torn_off
+ && mIsParticipantListExpanded
+ && !mIsP2PChat;
+
+ mParticipantListAndHistoryStack->collapsePanel(mParticipantListPanel, !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_not_torn_off || is_participant_list_visible;
+ mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon"));
+ mExpandCollapseBtn->setToolTip(
+ is_not_torn_off?
+ getString("expcol_button_not_tearoff_tooltip") :
+ (is_expanded?
+ getString("expcol_button_tearoff_and_expanded_tooltip") :
+ getString("expcol_button_tearoff_and_collapsed_tooltip")));
+
+ // toggle floater's drag handle and title visibility
+ if (mDragHandle)
+ {
+ mDragHandle->setTitleVisible(!is_not_torn_off);
+ }
+
+ // The button (>>) should be disabled for torn off P2P conversations.
+ mExpandCollapseBtn->setEnabled(is_not_torn_off || !mIsP2PChat);
+
+ mTearOffBtn->setImageOverlay(getString(is_not_torn_off? "tear_off_icon" : "return_icon"));
+ mTearOffBtn->setToolTip(getString(is_not_torn_off? "tooltip_to_separate_window" : "tooltip_to_main_window"));
+
+
+ mCloseBtn->setVisible(is_not_torn_off && !mIsNearbyChat);
+
+ enableDisableCallBtn();
+
+ showTranslationCheckbox();
+}
+
+void LLFloaterIMSessionTab::forceReshape()
+{
+ LLRect floater_rect = getRect();
+ reshape(llmax(floater_rect.getWidth(), this->getMinWidth()),
+ llmax(floater_rect.getHeight(), this->getMinHeight()),
+ true);
+}
+
+
+void LLFloaterIMSessionTab::reshapeChatLayoutPanel()
+{
+ LLRect chat_layout_panel_rect = mChatLayoutPanel->getRect();
+ LLRect input_rect = mInputEditor->getRect();
+ mChatLayoutPanel->reshape(chat_layout_panel_rect.getWidth(), input_rect.getHeight() + mInputEditorPad, FALSE);
+}
+
+void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show)
+{
+ getChild<LLUICtrl>("translate_chat_checkbox_lp")->setVisible(mIsNearbyChat? show : FALSE);
+}
+
+// static
+void LLFloaterIMSessionTab::processChatHistoryStyleUpdate(bool clean_messages/* = false*/)
+{
+ 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)
+ {
+ LLFloaterIMSession* floater = dynamic_cast<LLFloaterIMSession*>(*iter);
+ if (floater)
+ {
+ floater->reloadMessages(clean_messages);
+ }
+ }
+
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
+ {
+ nearby_chat->reloadMessages(clean_messages);
+ }
+}
+
+// static
+void LLFloaterIMSessionTab::reloadEmptyFloaters()
+{
+ 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)
+ {
+ LLFloaterIMSession* floater = dynamic_cast<LLFloaterIMSession*>(*iter);
+ if (floater && floater->getLastChatMessageIndex() == -1)
+ {
+ floater->reloadMessages(true);
+ }
+ }
+
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat && nearby_chat->getMessageArchiveLength() == 0)
+ {
+ nearby_chat->reloadMessages(true);
+ }
+}
+
+void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive)
+{
+ LLButton* voiceButton = getChild<LLButton>("voice_call_btn");
+ voiceButton->setImageOverlay(
+ callIsActive? getString("call_btn_stop") : getString("call_btn_start"));
+
+ voiceButton->setToolTip(
+ callIsActive? getString("end_call_button_tooltip") : getString("start_call_button_tooltip"));
+
+ enableDisableCallBtn();
+
+}
+
+void LLFloaterIMSessionTab::onSlide(LLFloaterIMSessionTab* self)
+{
+ LLFloaterIMContainer* host_floater = dynamic_cast<LLFloaterIMContainer*>(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 should_be_expanded = self->mParticipantListPanel->isCollapsed();
+
+ // Expand/collapse the participant list panel
+ self->mParticipantListAndHistoryStack->collapsePanel(self->mParticipantListPanel, !should_be_expanded);
+ self->mParticipantListPanel->setVisible(should_be_expanded);
+ gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded);
+ self->mIsParticipantListExpanded = should_be_expanded;
+ self->mExpandCollapseBtn->setImageOverlay(self->getString(should_be_expanded ? "collapse_icon" : "expand_icon"));
+ }
+ }
+
+ self->assignResizeLimits();
+}
+
+void LLFloaterIMSessionTab::onCollapseToLine(LLFloaterIMSessionTab* self)
+{
+ LLFloaterIMContainer* host_floater = dynamic_cast<LLFloaterIMContainer*>(self->getHost());
+ if (!host_floater)
+ {
+ bool expand = self->isMessagePaneExpanded();
+ self->mExpandCollapseLineBtn->setImageOverlay(self->getString(expand ? "collapseline_icon" : "expandline_icon"));
+ self->mContentPanel->setVisible(!expand);
+ self->mToolbarPanel->setVisible(!expand);
+ self->reshapeFloater(expand);
+ self->setMessagePaneExpanded(!expand);
+ }
+}
+
+void LLFloaterIMSessionTab::reshapeFloater(bool collapse)
+{
+ LLRect floater_rect = getRect();
+
+ if(collapse)
+ {
+ mFloaterHeight = floater_rect.getHeight();
+ S32 height = mContentPanel->getRect().getHeight() + mToolbarPanel->getRect().getHeight();
+ floater_rect.mTop -= height;
+ enableResizeCtrls(true, true, false);
+ }
+ else
+ {
+ floater_rect.mTop = floater_rect.mBottom + mFloaterHeight;
+ enableResizeCtrls(true, true, true);
+
+ }
+
+ setShape(floater_rect, true);
+ mBodyStack->updateLayout();
+
+}
+
+void LLFloaterIMSessionTab::restoreFloater()
+{
+ if(!isMessagePaneExpanded())
+ {
+ if(isMinimized())
+ {
+ setMinimized(false);
+ }
+ mContentPanel->setVisible(true);
+ mToolbarPanel->setVisible(true);
+ LLRect floater_rect = getRect();
+ floater_rect.mTop = floater_rect.mBottom + mFloaterHeight;
+ setShape(floater_rect, true);
+ mBodyStack->updateLayout();
+ mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon"));
+ setMessagePaneExpanded(true);
+ enableResizeCtrls(true, true, true);
+ }
+}
+
+/*virtual*/
+void LLFloaterIMSessionTab::onOpen(const LLSD& key)
+{
+ if (!checkIfTornOff())
+ {
+ LLFloaterIMContainer* host_floater = dynamic_cast<LLFloaterIMContainer*>(getHost());
+ // Show the messages pane when opening a floater hosted in the Conversations
+ host_floater->collapseMessagesPane(false);
+ }
+
+ mInputButtonPanel->setVisible(isTornOff());
+}
+
+
+void LLFloaterIMSessionTab::onTearOffClicked()
+{
+ restoreFloater();
+ setFollows(isTornOff()? FOLLOWS_ALL : FOLLOWS_NONE);
+ mSaveRect = isTornOff();
+ initRectControl();
+ LLFloater::onClickTearOff(this);
+ LLFloaterIMContainer* container = LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
+
+ if (isTornOff())
+ {
+ container->selectAdjacentConversation(false);
+ forceReshape();
+ }
+ //Upon re-docking the torn off floater, select the corresponding conversation line item
+ else
+ {
+ container->selectConversation(mSessionID);
+
+ }
+ mInputButtonPanel->setVisible(isTornOff());
+
+ refreshConversation();
+ updateGearBtn();
+}
+
+void LLFloaterIMSessionTab::updateGearBtn()
+{
+
+ BOOL prevVisibility = mGearBtn->getVisible();
+ mGearBtn->setVisible(checkIfTornOff() && mIsP2PChat);
+
+
+ // Move buttons if Gear button changed visibility
+ if(prevVisibility != mGearBtn->getVisible())
+ {
+ LLRect gear_btn_rect = mGearBtn->getRect();
+ LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
+ LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
+ S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
+ S32 right_shift = gear_btn_rect.getWidth() + gap_width;
+ if(mGearBtn->getVisible())
+ {
+ // Move buttons to the right to give space for Gear button
+ add_btn_rect.translate(right_shift,0);
+ call_btn_rect.translate(right_shift,0);
+ }
+ else
+ {
+ add_btn_rect.translate(-right_shift,0);
+ call_btn_rect.translate(-right_shift,0);
+ }
+ getChild<LLButton>("add_btn")->setRect(add_btn_rect);
+ getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
+ }
+}
+
+void LLFloaterIMSessionTab::initBtns()
+{
+ LLRect gear_btn_rect = mGearBtn->getRect();
+ LLRect add_btn_rect = getChild<LLButton>("add_btn")->getRect();
+ LLRect call_btn_rect = getChild<LLButton>("voice_call_btn")->getRect();
+ S32 gap_width = call_btn_rect.mLeft - add_btn_rect.mRight;
+ S32 right_shift = gear_btn_rect.getWidth() + gap_width;
+
+ add_btn_rect.translate(-right_shift,0);
+ call_btn_rect.translate(-right_shift,0);
+
+ getChild<LLButton>("add_btn")->setRect(add_btn_rect);
+ getChild<LLButton>("voice_call_btn")->setRect(call_btn_rect);
+}
+
+// static
+bool LLFloaterIMSessionTab::isChatMultiTab()
+{
+ // Restart is required in order to change chat window type.
+ return true;
+}
+
+bool LLFloaterIMSessionTab::checkIfTornOff()
+{
+ bool isTorn = !getHost();
+
+ if (isTorn != isTornOff())
+ {
+ setTornOff(isTorn);
+ refreshConversation();
+ }
+
+ return isTorn;
+}
+
+void LLFloaterIMSessionTab::doToSelected(const LLSD& userdata)
+{
+ // Get the list of selected items in the tab
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ getSelectedUUIDs(selected_uuids);
+
+ // Perform the command (IM, profile, etc...) on the list using the general conversation container method
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ // Note: By construction, those can only be participants so we can call doToParticipants() directly
+ floater_container->doToParticipants(command, selected_uuids);
+}
+
+bool LLFloaterIMSessionTab::enableContextMenuItem(const LLSD& userdata)
+{
+ // Get the list of selected items in the tab
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ getSelectedUUIDs(selected_uuids);
+
+ // Perform the item enable test on the list using the general conversation container method
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ return floater_container->enableContextMenuItem(command, selected_uuids);
+}
+
+bool LLFloaterIMSessionTab::checkContextMenuItem(const LLSD& userdata)
+{
+ // Get the list of selected items in the tab
+ std::string command = userdata.asString();
+ uuid_vec_t selected_uuids;
+ getSelectedUUIDs(selected_uuids);
+
+ // Perform the item check on the list using the general conversation container method
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ return floater_container->checkContextMenuItem(command, selected_uuids);
+}
+
+void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids)
+{
+ const std::set<LLFolderViewItem*> selected_items = mConversationsRoot->getSelectionList();
+
+ std::set<LLFolderViewItem*>::const_iterator it = selected_items.begin();
+ const std::set<LLFolderViewItem*>::const_iterator it_end = selected_items.end();
+
+ for (; it != it_end; ++it)
+ {
+ LLConversationItem* conversation_item = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+ selected_uuids.push_back(conversation_item->getUUID());
+ }
+}
+
+LLConversationItem* LLFloaterIMSessionTab::getCurSelectedViewModelItem()
+{
+ LLConversationItem *conversationItem = NULL;
+
+ if(mConversationsRoot &&
+ mConversationsRoot->getCurSelectedItem() &&
+ mConversationsRoot->getCurSelectedItem()->getViewModelItem())
+ {
+ conversationItem = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()) ;
+ }
+
+ return conversationItem;
+}
+
+BOOL LLFloaterIMSessionTab::handleKeyHere(KEY key, MASK mask )
+{
+ if(mask == MASK_ALT)
+ {
+ LLFloaterIMContainer* floater_container = LLFloaterIMContainer::getInstance();
+ if (KEY_RETURN == key && !isTornOff())
+ {
+ floater_container->expandConversation();
+ }
+ if ((KEY_UP == key) || (KEY_LEFT == key))
+ {
+ floater_container->selectNextorPreviousConversation(false);
+ }
+ if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
+ {
+ floater_container->selectNextorPreviousConversation(true);
+ }
+ }
+ return TRUE;
+}
diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h
new file mode 100644
index 0000000000..f0899a3c09
--- /dev/null
+++ b/indra/newview/llfloaterimsessiontab.h
@@ -0,0 +1,214 @@
+/**
+ * @file llfloaterimsessiontab.h
+ * @brief LLFloaterIMSessionTab class implements the common behavior of LNearbyChatBar
+ * @brief and LLFloaterIMSession 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_FLOATERIMSESSIONTAB_H
+#define LL_FLOATERIMSESSIONTAB_H
+
+#include "lllayoutstack.h"
+#include "llparticipantlist.h"
+#include "lltransientdockablefloater.h"
+#include "llviewercontrol.h"
+#include "lleventtimer.h"
+#include "llimview.h"
+#include "llconversationmodel.h"
+#include "llconversationview.h"
+#include "lltexteditor.h"
+
+class LLPanelChatControlPanel;
+class LLChatEntry;
+class LLChatHistory;
+
+class LLFloaterIMSessionTab
+ : public LLTransientDockableFloater
+{
+
+public:
+ LOG_CLASS(LLFloaterIMSessionTab);
+
+ LLFloaterIMSessionTab(const LLSD& session_id);
+ ~LLFloaterIMSessionTab();
+
+ // reload all message with new settings of visual modes
+ static void processChatHistoryStyleUpdate(bool clean_messages = false);
+ static void reloadEmptyFloaters();
+
+ /**
+ * 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 LLFloaterIMSessionTab* findConversation(const LLUUID& uuid);
+ static LLFloaterIMSessionTab* 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*/ BOOL postBuild();
+ /*virtual*/ void draw();
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void setFocus(BOOL focus);
+
+ // Handle the left hand participant list widgets
+ void addConversationViewParticipant(LLConversationItem* item);
+ void removeConversationViewParticipant(const LLUUID& participant_id);
+ void updateConversationViewParticipant(const LLUUID& participant_id);
+ void refreshConversation();
+ void buildConversationViewParticipant();
+
+ void setSortOrder(const LLConversationSort& order);
+ virtual void onTearOffClicked();
+ void updateGearBtn();
+ void initBtns();
+ virtual void updateMessages() {}
+ LLConversationItem* getCurSelectedViewModelItem();
+ void forceReshape();
+ virtual BOOL handleKeyHere( KEY key, MASK mask );
+ bool isMessagePaneExpanded(){return mMessagePaneExpanded;}
+ void setMessagePaneExpanded(bool expanded){mMessagePaneExpanded = expanded;}
+ void restoreFloater();
+
+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(LLFloaterIMSessionTab *self);
+ static void onCollapseToLine(LLFloaterIMSessionTab *self);
+ void reshapeFloater(bool collapse);
+
+ // refresh a visual state of the Call button
+ void updateCallBtnState(bool callIsActive);
+
+ 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();
+
+ // Update the input field help text and other places that need the session name
+ virtual void updateSessionName(const std::string& name);
+
+ // 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();
+ void assignResizeLimits();
+
+ S32 mFloaterExtraWidth;
+
+ bool mIsNearbyChat;
+ bool mIsP2PChat;
+
+ bool mMessagePaneExpanded;
+ bool mIsParticipantListExpanded;
+
+
+ LLIMModel::LLIMSession* mSession;
+
+ // Participants list: model and view
+ LLConversationViewParticipant* createConversationViewParticipant(LLConversationItem* item);
+
+ LLUUID mSessionID;
+ LLLayoutStack* mBodyStack;
+ LLLayoutStack* mParticipantListAndHistoryStack;
+ LLLayoutPanel* mParticipantListPanel; // add the widgets to that see mConversationsListPanel
+ LLLayoutPanel* mRightPartPanel;
+ LLLayoutPanel* mContentPanel;
+ LLLayoutPanel* mToolbarPanel;
+ LLLayoutPanel* mInputButtonPanel;
+ LLParticipantList* getParticipantList();
+ conversations_widgets_map mConversationsWidgets;
+ LLConversationViewModel mConversationViewModel;
+ LLFolderView* mConversationsRoot;
+ LLScrollContainer* mScroller;
+
+ LLOutputMonitorCtrl* mSpeakingIndicator;
+ LLChatHistory* mChatHistory;
+ LLChatEntry* mInputEditor;
+ LLLayoutPanel * mChatLayoutPanel;
+ int mInputEditorPad; // padding between input field and chat history
+
+ LLButton* mExpandCollapseLineBtn;
+ LLButton* mExpandCollapseBtn;
+ LLButton* mTearOffBtn;
+ LLButton* mCloseBtn;
+ LLButton* mGearBtn;
+
+ S32 mFloaterHeight;
+
+
+private:
+ // Handling selection and contextual menu
+ void doToSelected(const LLSD& userdata);
+ bool enableContextMenuItem(const LLSD& userdata);
+ bool checkContextMenuItem(const LLSD& userdata);
+
+ void getSelectedUUIDs(uuid_vec_t& selected_uuids);
+
+ /// 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 reshapeChatLayoutPanel();
+
+ bool checkIfTornOff();
+ bool mIsHostAttached;
+ bool mHasVisibleBeenInitialized;
+
+ LLTimer* mRefreshTimer; ///< Defines the rate at which refresh() is called.
+};
+
+
+#endif /* LL_FLOATERIMSESSIONTAB_H */
diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp
index cece8d299c..5a1dfc99ab 100644
--- a/indra/newview/llfloaterinspect.cpp
+++ b/indra/newview/llfloaterinspect.cpp
@@ -46,7 +46,9 @@
LLFloaterInspect::LLFloaterInspect(const LLSD& key)
: LLFloater(key),
- mDirty(FALSE)
+ mDirty(FALSE),
+ mOwnerNameCacheConnection(),
+ mCreatorNameCacheConnection()
{
mCommitCallbackRegistrar.add("Inspect.OwnerProfile", boost::bind(&LLFloaterInspect::onClickOwnerProfile, this));
mCommitCallbackRegistrar.add("Inspect.CreatorProfile", boost::bind(&LLFloaterInspect::onClickCreatorProfile, this));
@@ -67,6 +69,14 @@ BOOL LLFloaterInspect::postBuild()
LLFloaterInspect::~LLFloaterInspect(void)
{
+ if (mOwnerNameCacheConnection.connected())
+ {
+ mOwnerNameCacheConnection.disconnect();
+ }
+ if (mCreatorNameCacheConnection.connected())
+ {
+ mCreatorNameCacheConnection.disconnect();
+ }
if(!LLFloaterReg::instanceVisible("build"))
{
if(LLToolMgr::getInstance()->getBaseTool() == LLToolCompInspect::getInstance())
@@ -80,7 +90,6 @@ LLFloaterInspect::~LLFloaterInspect(void)
{
LLFloaterReg::showInstance("build", LLSD(), TRUE);
}
- //sInstance = NULL;
}
void LLFloaterInspect::onOpen(const LLSD& key)
@@ -167,15 +176,6 @@ LLUUID LLFloaterInspect::getSelectedUUID()
return LLUUID::null;
}
-void LLFloaterInspect::onGetAvNameCallback(const LLUUID& idCreator, const LLAvatarName& av_name, void* FloaterPtr)
-{
- if (FloaterPtr)
- {
- LLFloaterInspect* floater = (LLFloaterInspect*)FloaterPtr;
- floater->dirty();
- }
-}
-
void LLFloaterInspect::refresh()
{
LLUUID creator_id;
@@ -229,7 +229,11 @@ void LLFloaterInspect::refresh()
else
{
owner_name = LLTrans::getString("RetrievingData");
- LLAvatarNameCache::get(idOwner, boost::bind(&LLFloaterInspect::onGetAvNameCallback, _1, _2, this));
+ if (mOwnerNameCacheConnection.connected())
+ {
+ mOwnerNameCacheConnection.disconnect();
+ }
+ mOwnerNameCacheConnection = LLAvatarNameCache::get(idOwner, boost::bind(&LLFloaterInspect::onGetOwnerNameCallback, this));
}
if (LLAvatarNameCache::get(idCreator, &av_name))
@@ -239,7 +243,11 @@ void LLFloaterInspect::refresh()
else
{
creator_name = LLTrans::getString("RetrievingData");
- LLAvatarNameCache::get(idCreator, boost::bind(&LLFloaterInspect::onGetAvNameCallback, _1, _2, this));
+ if (mCreatorNameCacheConnection.connected())
+ {
+ mCreatorNameCacheConnection.disconnect();
+ }
+ mCreatorNameCacheConnection = LLAvatarNameCache::get(idCreator, boost::bind(&LLFloaterInspect::onGetCreatorNameCallback, this));
}
row["id"] = obj->getObject()->getID();
@@ -289,6 +297,18 @@ void LLFloaterInspect::dirty()
setDirty();
}
+void LLFloaterInspect::onGetOwnerNameCallback()
+{
+ mOwnerNameCacheConnection.disconnect();
+ setDirty();
+}
+
+void LLFloaterInspect::onGetCreatorNameCallback()
+{
+ mCreatorNameCacheConnection.disconnect();
+ setDirty();
+}
+
void LLFloaterInspect::draw()
{
if (mDirty)
diff --git a/indra/newview/llfloaterinspect.h b/indra/newview/llfloaterinspect.h
index 7ee83ccdb4..44381eac96 100644
--- a/indra/newview/llfloaterinspect.h
+++ b/indra/newview/llfloaterinspect.h
@@ -55,8 +55,6 @@ public:
void onClickOwnerProfile();
void onSelectObject();
- static void onGetAvNameCallback(const LLUUID& idCreator, const LLAvatarName& av_name, void* FloaterPtr);
-
LLScrollListCtrl* mObjectList;
protected:
// protected members
@@ -64,13 +62,15 @@ protected:
bool mDirty;
private:
+ void onGetOwnerNameCallback();
+ void onGetCreatorNameCallback();
LLFloaterInspect(const LLSD& key);
virtual ~LLFloaterInspect(void);
- // static data
-// static LLFloaterInspect* sInstance;
LLSafeHandle<LLObjectSelection> mObjectSelection;
+ boost::signals2::connection mOwnerNameCacheConnection;
+ boost::signals2::connection mCreatorNameCacheConnection;
};
#endif //LL_LLFLOATERINSPECT_H
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
index 5f58577a7c..a4ca014fa9 100644
--- a/indra/newview/llfloaterland.cpp
+++ b/indra/newview/llfloaterland.cpp
@@ -1046,6 +1046,8 @@ void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata)
void LLPanelLandGeneral::onClickSellLand(void* data)
{
LLViewerParcelMgr::getInstance()->startSellLand();
+ LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)data;
+ panelp->refresh();
}
// static
@@ -2734,11 +2736,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);
}
}
@@ -2783,11 +2787,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..29a3e6ac3a 100644
--- a/indra/newview/llfloateroutbox.cpp
+++ b/indra/newview/llfloateroutbox.cpp
@@ -44,14 +44,17 @@
#include "llviewernetwork.h"
#include "llwindowshade.h"
-#define USE_WINDOWSHADE_DIALOGS 0
-
///----------------------------------------------------------------------------
/// LLOutboxNotification class
///----------------------------------------------------------------------------
-bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& notify)
+LLNotificationsUI::LLOutboxNotification::LLOutboxNotification()
+ : LLSystemNotificationHandler("Outbox", "outbox")
+{
+}
+
+bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLNotificationPtr& notify)
{
LLFloaterOutbox* outbox_floater = LLFloaterReg::getTypedInstance<LLFloaterOutbox>("outbox");
@@ -60,6 +63,14 @@ bool LLNotificationsUI::LLOutboxNotification::processNotification(const LLSD& no
return false;
}
+void LLNotificationsUI::LLOutboxNotification::onDelete(LLNotificationPtr p)
+{
+ LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
+ if (notification_handler)
+ {
+ notification_handler->onDelete(p);
+ }
+}
///----------------------------------------------------------------------------
/// LLOutboxAddedObserver helper class
@@ -168,9 +179,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 +254,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 +391,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 +448,10 @@ void LLFloaterOutbox::onOutboxChanged()
{
llassert(!mOutboxId.isNull());
- if (mOutboxInventoryPanel)
- {
- mOutboxInventoryPanel->requestSort();
- }
+ //if (mOutboxInventoryPanel)
+ //{
+ // mOutboxInventoryPanel->requestSort();
+ //}
fetchOutboxContents();
@@ -516,52 +527,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);
- llassert(sys_handler);
-
- sys_handler->processNotification(notify);
+ LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast<LLNotificationsUI::LLNotificationHandler*>(LLNotifications::instance().getChannel("AlertModal").get());
+ llassert(notification_handler);
-#endif
+ notification_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 542e96cf16..a28af2101b 100755
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -51,11 +51,11 @@
#include "llfloaterabout.h"
#include "llfloaterhardwaresettings.h"
#include "llfloatersidepanelcontainer.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
#include "llkeyboard.h"
#include "llmodaldialog.h"
#include "llnavigationbar.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llnotificationtemplate.h"
@@ -306,7 +306,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
mAvatarDataInitialized(false),
mClickActionDirty(false)
{
-
+ LLConversationLog::instance().addObserver(this);
+
//Build Floater is now Called from LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterPreference>);
static bool registered_dialog = false;
@@ -329,8 +330,6 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
mCommitCallbackRegistrar.add("Pref.VoiceSetKey", boost::bind(&LLFloaterPreference::onClickSetKey, this));
mCommitCallbackRegistrar.add("Pref.VoiceSetMiddleMouse", boost::bind(&LLFloaterPreference::onClickSetMiddleMouse, this));
mCommitCallbackRegistrar.add("Pref.SetSounds", boost::bind(&LLFloaterPreference::onClickSetSounds, this));
-// mCommitCallbackRegistrar.add("Pref.ClickSkipDialogs", boost::bind(&LLFloaterPreference::onClickSkipDialogs, this));
-// mCommitCallbackRegistrar.add("Pref.ClickResetDialogs", boost::bind(&LLFloaterPreference::onClickResetDialogs, this));
mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this));
mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this));
mCommitCallbackRegistrar.add("Pref.LogPath", boost::bind(&LLFloaterPreference::onClickLogPath, this));
@@ -351,13 +350,16 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
sSkin = gSavedSettings.getString("SkinCurrent");
- mCommitCallbackRegistrar.add("Pref.ClickActionChange", boost::bind(&LLFloaterPreference::onClickActionChange, this));
+ mCommitCallbackRegistrar.add("Pref.ClickActionChange", boost::bind(&LLFloaterPreference::onClickActionChange, this));
gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2));
gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2));
gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2));
LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this );
+
+ mCommitCallbackRegistrar.add("Pref.ClearLog", boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()));
+ mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", boost::bind(&LLFloaterPreference::onDeleteTranscripts, this));
}
void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type )
@@ -425,13 +427,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(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false));
gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged));
@@ -447,28 +443,45 @@ BOOL LLFloaterPreference::postBuild()
std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "");
setCacheLocation(cache_location);
+ getChild<LLUICtrl>("log_path_string")->setEnabled(FALSE); // make it read-only but selectable
+
getChild<LLComboBox>("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this));
- // if floater is opened before login set default localized busy message
+ getChild<LLComboBox>("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions"));
+ getChild<LLComboBox>("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions"));
+ getChild<LLComboBox>("ConferenceIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ConferenceIMOptions"));
+ getChild<LLComboBox>("GroupChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"GroupChatOptions"));
+ getChild<LLComboBox>("NearbyChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NearbyChatOptions"));
+
+ // if floater is opened before login set default localized do not disturb message
if (LLStartUp::getStartupState() < STATE_STARTED)
{
- gSavedPerAccountSettings.setString("BusyModeResponse", LLTrans::getString("BusyModeResponseDefault"));
+ gSavedPerAccountSettings.setString("DoNotDisturbModeResponse", LLTrans::getString("DoNotDisturbModeResponseDefault"));
}
+ // set 'enable' property for 'Clear log...' button
+ changed();
+
+ LLLogChat::setSaveHistorySignal(boost::bind(&LLFloaterPreference::onLogChatHistorySaved, this));
+
return TRUE;
}
-void LLFloaterPreference::onBusyResponseChanged()
+void LLFloaterPreference::updateDeleteTranscriptsButton()
{
- // 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 );
- }
+ std::vector<std::string> list_of_transcriptions_file_names;
+ LLLogChat::getListOfTranscriptFiles(list_of_transcriptions_file_names);
+ getChild<LLButton>("delete_transcripts")->setEnabled(list_of_transcriptions_file_names.size() > 0);
+}
+
+void LLFloaterPreference::onDoNotDisturbResponseChanged()
+{
+ // set "DoNotDisturbResponseChanged" TRUE if user edited message differs from default, FALSE otherwise
+ bool response_changed_flag =
+ LLTrans::getString("DoNotDisturbModeResponseDefault")
+ != getChild<LLUICtrl>("do_not_disturb_response")->getValue().asString();
+
+ gSavedPerAccountSettings.setBOOL("DoNotDisturbResponseChanged", response_changed_flag );
}
LLFloaterPreference::~LLFloaterPreference()
@@ -479,6 +492,8 @@ LLFloaterPreference::~LLFloaterPreference()
{
ctrl_window_size->setCurrentByIndex(i);
}
+
+ LLConversationLog::instance().removeObserver(this);
}
void LLFloaterPreference::draw()
@@ -551,14 +566,8 @@ void LLFloaterPreference::apply()
LLViewerMedia::setProxyConfig(proxy_enable, proxy_address, proxy_port);
}
-// 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)
{
-// gSavedSettings.setString("BusyModeResponse2", std::string(wstring_to_utf8str(busy_response)));
bool new_im_via_email = getChild<LLUICtrl>("send_im_to_email")->getValue().asBoolean();
bool new_hide_online = getChild<LLUICtrl>("online_visibility")->getValue().asBoolean();
@@ -644,21 +653,21 @@ void LLFloaterPreference::cancel()
void LLFloaterPreference::onOpen(const LLSD& key)
{
- // this variable and if that follows it are used to properly handle busy mode response message
+ // this variable and if that follows it are used to properly handle do not disturb mode response message
static bool initialized = FALSE;
- // if user is logged in and we haven't initialized busy_response yet, do it
+ // if user is logged in and we haven't initialized do not disturb mode response yet, do it
if (!initialized && LLStartUp::getStartupState() == STATE_STARTED)
{
- // Special approach is used for busy response localization, because "BusyModeResponse" is
+ // Special approach is used for do not disturb response localization, because "DoNotDisturbModeResponse" is
// in non-localizable xml, and also because it may be changed by user and in this case it shouldn't be localized.
- // To keep track of whether busy response is default or changed by user additional setting BusyResponseChanged
+ // To keep track of whether do not disturb response is default or changed by user additional setting DoNotDisturbResponseChanged
// was added into per account settings.
// initialization should happen once,so setting variable to TRUE
initialized = TRUE;
- // this connection is needed to properly set "BusyResponseChanged" setting when user makes changes in
- // busy response message.
- gSavedPerAccountSettings.getControl("BusyModeResponse")->getSignal()->connect(boost::bind(&LLFloaterPreference::onBusyResponseChanged, this));
+ // this connection is needed to properly set "DoNotDisturbResponseChanged" setting when user makes changes in
+ // do not disturb response message.
+ gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->getSignal()->connect(boost::bind(&LLFloaterPreference::onDoNotDisturbResponseChanged, this));
}
gAgent.sendAgentUserInfoRequest();
@@ -705,6 +714,14 @@ void LLFloaterPreference::onOpen(const LLSD& key)
// while preferences floater was closed.
buildPopupLists();
+
+ //get the options that were checked
+ onNotificationsChange("FriendIMOptions");
+ onNotificationsChange("NonFriendIMOptions");
+ onNotificationsChange("ConferenceIMOptions");
+ onNotificationsChange("GroupChatOptions");
+ onNotificationsChange("NearbyChatOptions");
+
LLPanelLogin::setAlwaysRefresh(true);
refresh();
@@ -720,12 +737,12 @@ void LLFloaterPreference::onVertexShaderEnable()
}
//static
-void LLFloaterPreference::initBusyResponse()
+void LLFloaterPreference::initDoNotDisturbResponse()
{
- if (!gSavedPerAccountSettings.getBOOL("BusyResponseChanged"))
+ if (!gSavedPerAccountSettings.getBOOL("DoNotDisturbResponseChanged"))
{
- //LLTrans::getString("BusyModeResponseDefault") is used here for localization (EXT-5885)
- gSavedPerAccountSettings.setString("BusyModeResponse", LLTrans::getString("BusyModeResponseDefault"));
+ //LLTrans::getString("DoNotDisturbModeResponseDefault") is used here for localization (EXT-5885)
+ gSavedPerAccountSettings.setString("DoNotDisturbModeResponse", LLTrans::getString("DoNotDisturbModeResponseDefault"));
}
}
@@ -780,8 +797,31 @@ void LLFloaterPreference::onBtnOK()
apply();
closeFloater(false);
+ //Conversation transcript and log path changed so reload conversations based on new location
+ if(mPriorInstantMessageLogPath.length())
+ {
+ if(moveTranscriptsAndLog())
+ {
+ //When floaters are empty but have a chat history files, reload chat history into them
+ LLFloaterIMSessionTab::reloadEmptyFloaters();
+ }
+ //Couldn't move files so restore the old path and show a notification
+ else
+ {
+ gSavedPerAccountSettings.setString("InstantMessageLogPath", mPriorInstantMessageLogPath);
+ LLNotificationsUtil::add("PreferenceChatPathChanged");
+ }
+ mPriorInstantMessageLogPath.clear();
+ }
+
LLUIColorTable::instance().saveUserSettings();
gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE);
+
+ //Only save once logged in and loaded per account settings
+ if(mGotPersonalInfo)
+ {
+ gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE);
+ }
}
else
{
@@ -834,12 +874,12 @@ void LLFloaterPreference::onBtnCancel()
}
// static
-void LLFloaterPreference::updateUserInfo(const std::string& visibility, bool im_via_email, const std::string& email)
+void LLFloaterPreference::updateUserInfo(const std::string& visibility, bool im_via_email)
{
LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences");
if (instance)
{
- instance->setPersonalInfo(visibility, im_via_email, email);
+ instance->setPersonalInfo(visibility, im_via_email);
}
}
@@ -881,6 +921,23 @@ void LLFloaterPreference::onLanguageChange()
}
}
+void LLFloaterPreference::onNotificationsChange(const std::string& OptionName)
+{
+ mNotificationOptions[OptionName] = getChild<LLComboBox>(OptionName)->getSelectedItemLabel();
+
+ bool show_notifications_alert = true;
+ for (notifications_map::iterator it_notification = mNotificationOptions.begin(); it_notification != mNotificationOptions.end(); it_notification++)
+ {
+ if(it_notification->second != "None")
+ {
+ show_notifications_alert = false;
+ break;
+ }
+ }
+
+ getChild<LLTextBox>("notifications_alert")->setVisible(show_notifications_alert);
+}
+
void LLFloaterPreference::onNameTagOpacityChange(const LLSD& newvalue)
{
LLColorSwatchCtrl* color_swatch = findChild<LLColorSwatchCtrl>("background");
@@ -1402,17 +1459,94 @@ void LLFloaterPreference::setAllIgnored()
void LLFloaterPreference::onClickLogPath()
{
std::string proposed_name(gSavedPerAccountSettings.getString("InstantMessageLogPath"));
+ mPriorInstantMessageLogPath.clear();
LLDirPicker& picker = LLDirPicker::instance();
+ //Launches a directory picker and waits for feedback
if (!picker.getDir(&proposed_name ) )
{
return; //Canceled!
}
- gSavedPerAccountSettings.setString("InstantMessageLogPath", picker.getDirName());
+ //Gets the path from the directory picker
+ std::string dir_name = picker.getDirName();
+
+ //Path changed
+ if(proposed_name != dir_name)
+ {
+ gSavedPerAccountSettings.setString("InstantMessageLogPath", dir_name);
+ mPriorInstantMessageLogPath = proposed_name;
+
+ // enable/disable 'Delete transcripts button
+ updateDeleteTranscriptsButton();
+}
+}
+
+bool LLFloaterPreference::moveTranscriptsAndLog()
+{
+ std::string instantMessageLogPath(gSavedPerAccountSettings.getString("InstantMessageLogPath"));
+ std::string chatLogPath = gDirUtilp->add(instantMessageLogPath, gDirUtilp->getUserName());
+
+ bool madeDirectory = false;
+
+ //Does the directory really exist, if not then make it
+ if(!LLFile::isdir(chatLogPath))
+ {
+ //mkdir success is defined as zero
+ if(LLFile::mkdir(chatLogPath) != 0)
+ {
+ return false;
+ }
+ madeDirectory = true;
+ }
+
+ std::string originalConversationLogDir = LLConversationLog::instance().getFileName();
+ std::string targetConversationLogDir = gDirUtilp->add(chatLogPath, "conversation.log");
+ //Try to move the conversation log
+ if(!LLConversationLog::instance().moveLog(originalConversationLogDir, targetConversationLogDir))
+ {
+ //Couldn't move the log and created a new directory so remove the new directory
+ if(madeDirectory)
+ {
+ LLFile::rmdir(chatLogPath);
+ }
+ return false;
+ }
+
+ //Attempt to move transcripts
+ std::vector<std::string> listOfTranscripts;
+ std::vector<std::string> listOfFilesMoved;
+
+ LLLogChat::getListOfTranscriptFiles(listOfTranscripts);
+
+ if(!LLLogChat::moveTranscripts(gDirUtilp->getChatLogsDir(),
+ instantMessageLogPath,
+ listOfTranscripts,
+ listOfFilesMoved))
+ {
+ //Couldn't move all the transcripts so restore those that moved back to their old location
+ LLLogChat::moveTranscripts(instantMessageLogPath,
+ gDirUtilp->getChatLogsDir(),
+ listOfFilesMoved);
+
+ //Move the conversation log back
+ LLConversationLog::instance().moveLog(targetConversationLogDir, originalConversationLogDir);
+
+ if(madeDirectory)
+ {
+ LLFile::rmdir(chatLogPath);
+ }
+
+ return false;
+ }
+
+ gDirUtilp->setChatLogsDir(instantMessageLogPath);
+ gDirUtilp->updatePerAccountChatLogsDir();
+
+ return true;
}
-void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im_via_email, const std::string& email)
+void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im_via_email)
{
mGotPersonalInfo = true;
mOriginalIMViaEmail = im_via_email;
@@ -1434,37 +1568,14 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im
}
getChild<LLUICtrl>("online_searchresults")->setEnabled(TRUE);
-
- getChildView("include_im_in_chat_history")->setEnabled(TRUE);
- getChildView("show_timestamps_check_im")->setEnabled(TRUE);
getChildView("friends_online_notify_checkbox")->setEnabled(TRUE);
-
getChild<LLUICtrl>("online_visibility")->setValue(mOriginalHideOnlineStatus);
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);
-// getChildView("log_instant_messages_timestamp")->setEnabled(TRUE);
-// getChildView("log_chat_timestamp")->setEnabled(TRUE);
- getChildView("log_chat_IM")->setEnabled(TRUE);
- getChildView("log_date_timestamp")->setEnabled(TRUE);
-
-// getChild<LLUICtrl>("busy_response")->setValue(gSavedSettings.getString("BusyModeResponse2"));
-
getChildView("favorites_on_login_check")->setEnabled(TRUE);
- getChildView("log_nearby_chat")->setEnabled(TRUE);
- getChildView("log_instant_messages")->setEnabled(TRUE);
- getChildView("show_timestamps_check_im")->setEnabled(TRUE);
- getChildView("log_path_string")->setEnabled(FALSE);// LineEditor becomes readonly in this case.
getChildView("log_path_button")->setEnabled(TRUE);
- childEnable("logfile_name_datestamp");
- std::string display_email(email);
- getChild<LLUICtrl>("email_address")->setValue(display_email);
-
+ getChildView("chat_font_size")->setEnabled(TRUE);
}
void LLFloaterPreference::onUpdateSliderText(LLUICtrl* ctrl, const LLSD& name)
@@ -1526,7 +1637,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()
@@ -1554,6 +1666,33 @@ void LLFloaterPreference::onClickActionChange()
mClickActionDirty = true;
}
+void LLFloaterPreference::onDeleteTranscripts()
+{
+ LLSD args;
+ args["FOLDER"] = gDirUtilp->getUserName();
+
+ LLNotificationsUtil::add("PreferenceChatDeleteTranscripts", args, LLSD(), boost::bind(&LLFloaterPreference::onDeleteTranscriptsResponse, this, _1, _2));
+}
+
+void LLFloaterPreference::onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response)
+{
+ if (0 == LLNotificationsUtil::getSelectedOption(notification, response))
+ {
+ LLLogChat::deleteTranscripts();
+ updateDeleteTranscriptsButton();
+ }
+}
+
+void LLFloaterPreference::onLogChatHistorySaved()
+{
+ LLButton * delete_transcripts_buttonp = getChild<LLButton>("delete_transcripts");
+
+ if (!delete_transcripts_buttonp->getEnabled())
+ {
+ delete_transcripts_buttonp->setEnabled(true);
+ }
+}
+
void LLFloaterPreference::updateClickActionSettings()
{
const int single_clk_action = getChild<LLComboBox>("single_click_action_combo")->getValue().asInteger();
@@ -1592,6 +1731,35 @@ void LLFloaterPreference::setCacheLocation(const LLStringExplicit& location)
cache_location_editor->setToolTip(location);
}
+void LLFloaterPreference::selectPanel(const LLSD& name)
+{
+ LLTabContainer * tab_containerp = getChild<LLTabContainer>("pref core");
+ LLPanel * panel = tab_containerp->getPanelByName(name);
+ if (NULL != panel)
+ {
+ tab_containerp->selectTabPanel(panel);
+ }
+}
+
+void LLFloaterPreference::selectPrivacyPanel()
+{
+ selectPanel("im");
+}
+
+void LLFloaterPreference::selectChatPanel()
+{
+ selectPanel("chat");
+}
+
+void LLFloaterPreference::changed()
+{
+ getChild<LLButton>("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0);
+
+ // set 'enable' property for 'Delete transcripts...' button
+ updateDeleteTranscriptsButton();
+
+}
+
//------------------------------Updater---------------------------------------
static bool handleBandwidthChanged(const LLSD& newvalue)
@@ -1650,6 +1818,17 @@ LLPanelPreference::LLPanelPreference()
//virtual
BOOL LLPanelPreference::postBuild()
{
+ ////////////////////// PanelGeneral ///////////////////
+ if (hasChild("display_names_check"))
+ {
+ BOOL use_people_api = gSavedSettings.getBOOL("UsePeopleAPI");
+ LLCheckBoxCtrl* ctrl_display_name = getChild<LLCheckBoxCtrl>("display_names_check");
+ ctrl_display_name->setEnabled(use_people_api);
+ if (!use_people_api)
+ {
+ ctrl_display_name->setValue(FALSE);
+ }
+ }
////////////////////// PanelVoice ///////////////////
if (hasChild("voice_unavailable"))
@@ -1674,12 +1853,6 @@ BOOL LLPanelPreference::postBuild()
}
- if (hasChild("online_visibility") && hasChild("send_im_to_email"))
- {
- getChild<LLUICtrl>("email_address")->setValue(getString("log_in_to_change") );
-// getChild<LLUICtrl>("busy_response")->setValue(getString("log_in_to_change"));
- }
-
//////////////////////PanelPrivacy ///////////////////
if (hasChild("media_enabled"))
{
@@ -1701,7 +1874,7 @@ BOOL LLPanelPreference::postBuild()
getChild<LLCheckBoxCtrl>("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2));
}
- // Panel Advanced
+ //////////////////////PanelAdvanced ///////////////////
if (hasChild("modifier_combo"))
{
//localizing if push2talk button is set to middle mouse
@@ -1856,7 +2029,7 @@ public:
for (control_values_map_t::iterator it = mSavedValues.begin(); it != mSavedValues.end(); )
{
const std::string setting = it->first->getName();
- if (std::find(mAccountIndependentSettings.begin(),
+ if (find(mAccountIndependentSettings.begin(),
mAccountIndependentSettings.end(), setting) == mAccountIndependentSettings.end())
{
mSavedValues.erase(it++);
@@ -2145,4 +2318,4 @@ void LLFloaterPreferenceProxy::onChangeSocksSettings()
otherHttpProxy->selectFirstItem();
}
-};
+}
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index b71f7c647b..22e80a21cb 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -35,7 +35,9 @@
#include "llfloater.h"
#include "llavatarpropertiesprocessor.h"
+#include "llconversationlog.h"
+class LLConversationLogObserver;
class LLPanelPreference;
class LLPanelLCD;
class LLPanelDebug;
@@ -45,6 +47,8 @@ class LLSliderCtrl;
class LLSD;
class LLTextBox;
+typedef std::map<std::string, std::string> notifications_map;
+
typedef enum
{
GS_LOW_GRAPHICS,
@@ -56,7 +60,7 @@ typedef enum
// Floater to control preferences (display, audio, bandwidth, general.
-class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver
+class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver, public LLConversationLogObserver
{
public:
LLFloaterPreference(const LLSD& key);
@@ -68,20 +72,24 @@ public:
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
/*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ void changed();
+ /*virtual*/ void changed(const LLUUID& session_id, U32 mask) {};
// static data update, called from message handler
- static void updateUserInfo(const std::string& visibility, bool im_via_email, const std::string& email);
+ static void updateUserInfo(const std::string& visibility, bool im_via_email);
// refresh all the graphics preferences menus
static void refreshEnabledGraphics();
- // translate user's busy response message according to current locale if message is default, otherwise do nothing
- static void initBusyResponse();
+ // translate user's do not disturb response message according to current locale if message is default, otherwise do nothing
+ static void initDoNotDisturbResponse();
void processProperties( void* pData, EAvatarProcessorType type );
void processProfileProperties(const LLAvatarData* pAvatarData );
void storeAvatarProperties( const LLAvatarData* pAvatarData );
void saveAvatarProperties( void );
+ void selectPrivacyPanel();
+ void selectChatPanel();
protected:
void onBtnOK();
@@ -91,11 +99,12 @@ protected:
void onClickClearCache(); // Clear viewer texture cache, vfs, and VO cache on next startup
void onClickBrowserClearCache(); // Clear web history and caches as well as viewer caches above
void onLanguageChange();
+ void onNotificationsChange(const std::string& OptionName);
void onNameTagOpacityChange(const LLSD& newvalue);
- // set value of "BusyResponseChanged" in account settings depending on whether busy response
+ // set value of "DoNotDisturbResponseChanged" in account settings depending on whether do not disturb response
// string differs from default after user changes.
- void onBusyResponseChanged();
+ void onDoNotDisturbResponseChanged();
// if the custom settings box is clicked
void onChangeCustom();
void updateMeterText(LLUICtrl* ctrl);
@@ -129,15 +138,14 @@ public:
void setKey(KEY key);
void onClickSetMiddleMouse();
void onClickSetSounds();
-// void onClickSkipDialogs();
-// void onClickResetDialogs();
void onClickEnablePopup();
void onClickDisablePopup();
void resetAllIgnored();
void setAllIgnored();
- void onClickLogPath();
+ void onClickLogPath();
+ bool moveTranscriptsAndLog();
void enableHistory();
- void setPersonalInfo(const std::string& visibility, bool im_via_email, const std::string& email);
+ void setPersonalInfo(const std::string& visibility, bool im_via_email);
void refreshEnabledState();
void disableUnavailableSettings();
void onCommitWindowedMode();
@@ -161,16 +169,25 @@ public:
void onClickSpellChecker();
void applyUIColor(LLUICtrl* ctrl, const LLSD& param);
void getUIColor(LLUICtrl* ctrl, const LLSD& param);
-
+ void onLogChatHistorySaved();
void buildPopupLists();
static void refreshSkin(void* data);
+ void selectPanel(const LLSD& name);
+
private:
+
+ void onDeleteTranscripts();
+ void onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response);
+ void updateDeleteTranscriptsButton();
+
static std::string sSkin;
+ notifications_map mNotificationOptions;
bool mClickActionDirty; ///< Set to true when the click/double-click options get changed by user.
bool mGotPersonalInfo;
bool mOriginalIMViaEmail;
bool mLanguageChanged;
bool mAvatarDataInitialized;
+ std::string mPriorInstantMessageLogPath;
bool mOriginalHideOnlineStatus;
std::string mDirectoryVisibility;
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index fe29bb38c7..e6b76159a1 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -647,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);
@@ -924,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);
+ }
}
@@ -1470,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);
@@ -1646,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;
}
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
index 3ec1e372eb..0e4c7406c5 100644
--- a/indra/newview/llfloaterreporter.cpp
+++ b/indra/newview/llfloaterreporter.cpp
@@ -103,7 +103,8 @@ LLFloaterReporter::LLFloaterReporter(const LLSD& key)
mPicking( FALSE),
mPosition(),
mCopyrightWarningSeen( FALSE ),
- mResourceDatap(new LLResourceData())
+ mResourceDatap(new LLResourceData()),
+ mAvatarNameCacheConnection()
{
}
@@ -187,6 +188,11 @@ BOOL LLFloaterReporter::postBuild()
// virtual
LLFloaterReporter::~LLFloaterReporter()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
// child views automatically deleted
mObjectID = LLUUID::null;
@@ -285,10 +291,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);
}
}
@@ -310,11 +319,17 @@ void LLFloaterReporter::setFromAvatarID(const LLUUID& avatar_id)
std::string avatar_link = LLSLURL("agent", mObjectID, "inspect").getSLURLString();
getChild<LLUICtrl>("owner_name")->setValue(avatar_link);
- LLAvatarNameCache::get(avatar_id, boost::bind(&LLFloaterReporter::onAvatarNameCache, this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(avatar_id, boost::bind(&LLFloaterReporter::onAvatarNameCache, this, _1, _2));
}
void LLFloaterReporter::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
if (mObjectID == avatar_id)
{
mOwnerName = av_name.getCompleteName();
diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h
index 7d68431710..d54e7f6ab0 100644
--- a/indra/newview/llfloaterreporter.h
+++ b/indra/newview/llfloaterreporter.h
@@ -137,6 +137,7 @@ private:
std::list<LLMeanCollisionData*> mMCDList;
std::string mDefaultSummary;
LLResourceData* mResourceDatap;
+ boost::signals2::connection mAvatarNameCacheConnection;
};
#endif
diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp
index a50907601c..8d8bba7b17 100644
--- a/indra/newview/llfloaterscriptlimits.cpp
+++ b/indra/newview/llfloaterscriptlimits.cpp
@@ -602,15 +602,7 @@ void LLPanelScriptLimitsRegionMemory::onNameCache(
return;
}
- std::string name;
- if (LLAvatarNameCache::useDisplayNames())
- {
- name = LLCacheName::buildUsername(full_name);
- }
- else
- {
- name = full_name;
- }
+ std::string name = LLCacheName::buildUsername(full_name);
std::vector<LLSD>::iterator id_itor;
for (id_itor = mObjectListItems.begin(); id_itor != mObjectListItems.end(); ++id_itor)
@@ -713,10 +705,7 @@ void LLPanelScriptLimitsRegionMemory::setRegionDetails(LLSD content)
else
{
name_is_cached = gCacheName->getFullName(owner_id, owner_buf); // username
- if (LLAvatarNameCache::useDisplayNames())
- {
- owner_buf = LLCacheName::buildUsername(owner_buf);
- }
+ owner_buf = LLCacheName::buildUsername(owner_buf);
}
if(!name_is_cached)
{
diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp
index 64c0dfa023..0cb37dabe7 100644
--- a/indra/newview/llfloatersellland.cpp
+++ b/indra/newview/llfloatersellland.cpp
@@ -81,6 +81,7 @@ private:
LLUUID mAuthorizedBuyer;
bool mParcelSoldWithObjects;
SelectionObserver mParcelSelectionObserver;
+ boost::signals2::connection mAvatarNameCacheConnection;
void updateParcelInfo();
void refreshUI();
@@ -129,13 +130,18 @@ LLFloater* LLFloaterSellLand::buildFloater(const LLSD& key)
LLFloaterSellLandUI::LLFloaterSellLandUI(const LLSD& key)
: LLFloater(key),
mParcelSelectionObserver(this),
- mRegion(0)
+ mRegion(0),
+ mAvatarNameCacheConnection()
{
LLViewerParcelMgr::getInstance()->addObserver(&mParcelSelectionObserver);
}
LLFloaterSellLandUI::~LLFloaterSellLandUI()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
LLViewerParcelMgr::getInstance()->removeObserver(&mParcelSelectionObserver);
}
@@ -230,15 +236,20 @@ void LLFloaterSellLandUI::updateParcelInfo()
if(mSellToBuyer)
{
- LLAvatarNameCache::get(mAuthorizedBuyer,
- boost::bind(&LLFloaterSellLandUI::onBuyerNameCache, this, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAuthorizedBuyer, boost::bind(&LLFloaterSellLandUI::onBuyerNameCache, this, _2));
}
}
void LLFloaterSellLandUI::onBuyerNameCache(const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
getChild<LLUICtrl>("sell_to_agent")->setValue(av_name.getCompleteName());
- getChild<LLUICtrl>("sell_to_agent")->setToolTip(av_name.mUsername);
+ getChild<LLUICtrl>("sell_to_agent")->setToolTip(av_name.getUserName());
}
void LLFloaterSellLandUI::setBadge(const char* id, Badge badge)
@@ -392,7 +403,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 1eb7f4469a..14923eec3c 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 2d91a61b54..7530c72dd2 100644
--- a/indra/newview/llfloatertopobjects.cpp
+++ b/indra/newview/llfloatertopobjects.cpp
@@ -199,17 +199,9 @@ void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
// Owner names can have trailing spaces sent from server
LLStringUtil::trim(owner_buf);
- if (LLAvatarNameCache::useDisplayNames())
- {
- // ...convert hard-coded name from server to a username
- // *TODO: Send owner_id from server and look up display name
- owner_buf = LLCacheName::buildUsername(owner_buf);
- }
- else
- {
- // ...just strip out legacy "Resident" name
- owner_buf = LLCacheName::cleanFullName(owner_buf);
- }
+ // *TODO: Send owner_id from server and look up display name
+ owner_buf = LLCacheName::buildUsername(owner_buf);
+
columns[column_num]["column"] = "owner";
columns[column_num]["value"] = owner_buf;
columns[column_num++]["font"] = "SANSSERIF";
diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp
index 1a17183efd..33f2c35239 100644
--- a/indra/newview/llfloatertranslationsettings.cpp
+++ b/indra/newview/llfloatertranslationsettings.cpp
@@ -29,7 +29,7 @@
#include "llfloatertranslationsettings.h"
// Viewer includes
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychat.h"
#include "lltranslate.h"
#include "llviewercontrol.h" // for gSavedSettings
@@ -225,11 +225,10 @@ void LLFloaterTranslationSettings::updateControlsEnabledState()
mGoogleVerifyBtn->setEnabled(on && google_selected &&
!mGoogleKeyVerified && !getEnteredGoogleKey().empty());
- mOKBtn->setEnabled(
- !on || (
- (bing_selected && mBingKeyVerified) ||
- (google_selected && mGoogleKeyVerified)
- ));
+ bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified);
+ gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified);
+
+ mOKBtn->setEnabled(!on || service_verified);
}
void LLFloaterTranslationSettings::verifyKey(int service, const std::string& key, bool alert)
@@ -285,7 +284,16 @@ void LLFloaterTranslationSettings::onBtnGoogleVerify()
verifyKey(LLTranslate::SERVICE_GOOGLE, key);
}
}
+void LLFloaterTranslationSettings::onClose(bool app_quitting)
+{
+ std::string service = gSavedSettings.getString("TranslationService");
+ bool bing_selected = service == "bing";
+ bool google_selected = service == "google";
+
+ bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified);
+ gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified);
+}
void LLFloaterTranslationSettings::onBtnOK()
{
gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean());
@@ -293,6 +301,7 @@ void LLFloaterTranslationSettings::onBtnOK()
gSavedSettings.setString("TranslationService", getSelectedService());
gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey());
gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey());
- LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured());
+ (LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))->
+ showTranslationCheckbox(LLTranslate::isTranslationConfigured());
closeFloater(false);
}
diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h
index 9b47ad72ed..b9bfd6265a 100644
--- a/indra/newview/llfloatertranslationsettings.h
+++ b/indra/newview/llfloatertranslationsettings.h
@@ -44,6 +44,7 @@ public:
void setBingVerified(bool ok, bool alert);
void setGoogleVerified(bool ok, bool alert);
+ void onClose(bool app_quitting);
private:
std::string getSelectedService() const;
diff --git a/indra/newview/llfloatervoicevolume.cpp b/indra/newview/llfloatervoicevolume.cpp
new file mode 100644
index 0000000000..38446e46df
--- /dev/null
+++ b/indra/newview/llfloatervoicevolume.cpp
@@ -0,0 +1,220 @@
+/**
+ * @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;
+ boost::signals2::connection mAvatarNameCacheConnection;
+};
+
+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()
+, mAvatarNameCacheConnection()
+{
+ LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::GLOBAL, this);
+ LLTransientFloater::init(this);
+}
+
+LLFloaterVoiceVolume::~LLFloaterVoiceVolume()
+{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ 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();
+
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = 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.getUserName(), " 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)
+{
+ mAvatarNameCacheConnection.disconnect();
+
+ 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..586965e5a0
--- /dev/null
+++ b/indra/newview/llfolderviewmodelinventory.cpp
@@ -0,0 +1,314 @@
+/*
+ * @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"
+#include "llfavoritesbar.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)
+ {
+ 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.
+ // In that folder, landmarks should be always sorted as in the Favorites bar. See EXT-719
+ 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);
+ // If both landmarks are in the Favorites folder...
+ if (gInventory.isObjectDescendentOf(a->getUUID(), favorites_folder_id) && gInventory.isObjectDescendentOf(b->getUUID(), favorites_folder_id))
+ {
+ // Get their index in that folder
+ S32 a_sort = LLFavoritesOrderStorage::instance().getSortIndex(a->getUUID());
+ S32 b_sort = LLFavoritesOrderStorage::instance().getSortIndex(b->getUUID());
+ // Note: this test is a bit overkill: since they are both in the Favorites folder, we shouldn't get negative index values...
+ if (!((a_sort < 0) && (b_sort < 0)))
+ {
+ 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..a4dfd94496 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()
{
@@ -533,7 +533,7 @@ void LLFriendCardsManager::addFriendCardToInventory(const LLUUID& avatarID)
bool shouldBeAdded = true;
LLAvatarName av_name;
LLAvatarNameCache::get(avatarID, &av_name);
- const std::string& name = av_name.mUsername;
+ const std::string& name = av_name.getAccountName();
lldebugs << "Processing buddy name: " << name
<< ", id: " << avatarID
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
index 66ca76bfb0..f307505ff8 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 "llfloaterimnearbychat.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<LLFloaterIMNearbyChat>("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..a0f2918bd7 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 "llfloaterimcontainer.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);
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
}
make_ui_sound("UISndStartIM");
return session_id;
diff --git a/indra/newview/llgroupiconctrl.cpp b/indra/newview/llgroupiconctrl.cpp
index 2f9810775b..188c4bcf25 100644
--- a/indra/newview/llgroupiconctrl.cpp
+++ b/indra/newview/llgroupiconctrl.cpp
@@ -38,7 +38,7 @@
#include "llcachename.h"
#include "llagentdata.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
*/
static LLDefaultChildRegistry::Register<LLGroupIconCtrl> g_i("group_icon");
diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp
index 129cddda45..aba3d74d87 100644
--- a/indra/newview/llgrouplist.cpp
+++ b/indra/newview/llgrouplist.cpp
@@ -86,7 +86,7 @@ LLGroupList::LLGroupList(const Params& p)
registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2));
enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2));
- LLMenuGL* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups.xml",
+ LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_people_groups.xml",
gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
if(context_menu)
mContextMenuHandle = context_menu->getHandle();
@@ -112,7 +112,7 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask);
- LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get();
+ LLToggleableMenu* context_menu = mContextMenuHandle.get();
if (context_menu && size() > 0)
{
context_menu->buildDrawLabels();
@@ -406,7 +406,7 @@ void LLGroupListItem::setActive(bool active)
// *BUG: setName() overrides the style params.
// Active group should be bold.
- LLFontDescriptor new_desc(mGroupNameBox->getDefaultFont()->getFontDesc());
+ LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc());
// *NOTE dzaporozhan
// On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font
diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h
index 8abf14b3d0..e96a720886 100644
--- a/indra/newview/llgrouplist.h
+++ b/indra/newview/llgrouplist.h
@@ -28,10 +28,13 @@
#define LL_LLGROUPLIST_H
#include "llevent.h"
+#include "llpointer.h"
+
#include "llflatlistview.h"
#include "llpanel.h"
-#include "llpointer.h"
#include "llstyle.h"
+#include "lltoggleablemenu.h"
+
#include "llgroupmgr.h"
/**
@@ -45,6 +48,10 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener
{
LOG_CLASS(LLGroupList);
public:
+ struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
+ {
+ Params(){};
+ };
LLGroupList(const Params& p);
virtual ~LLGroupList();
@@ -57,6 +64,8 @@ public:
void toggleIcons();
bool getIconsVisible() const { return mShowIcons; }
+ LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); }
+
private:
void setDirty(bool val = true) { mDirty = val; }
void refresh();
@@ -66,7 +75,7 @@ private:
bool onContextMenuItemClick(const LLSD& userdata);
bool onContextMenuItemEnable(const LLSD& userdata);
- LLHandle<LLView> mContextMenuHandle;
+ LLHandle<LLToggleableMenu> mContextMenuHandle;
bool mShowIcons;
bool mDirty;
diff --git a/indra/newview/llhudnametag.cpp b/indra/newview/llhudnametag.cpp
index 482294c8a6..3336097955 100644
--- a/indra/newview/llhudnametag.cpp
+++ b/indra/newview/llhudnametag.cpp
@@ -166,7 +166,6 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3&
}
// scale screen size of borders down
- //RN: for now, text on hud objects is never occluded
LLVector3 x_pixel_vec;
LLVector3 y_pixel_vec;
@@ -187,45 +186,29 @@ BOOL LLHUDNameTag::lineSegmentIntersect(const LLVector3& start, const LLVector3&
+ (y_pixel_vec * screen_offset.mV[VY]);
- //if (mUseBubble)
+ LLVector3 bg_pos = render_position
+ + (F32)mOffsetY * y_pixel_vec
+ - (width_vec / 2.f)
+ - (height_vec);
+
+ LLVector3 v[] =
{
- LLVector3 bg_pos = render_position
- + (F32)mOffsetY * y_pixel_vec
- - (width_vec / 2.f)
- - (height_vec);
- //LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
+ bg_pos,
+ bg_pos + width_vec,
+ bg_pos + width_vec + height_vec,
+ bg_pos + height_vec,
+ };
- LLVector3 v[] =
- {
- bg_pos,
- bg_pos + width_vec,
- bg_pos + width_vec + height_vec,
- bg_pos + height_vec,
- };
+ LLVector3 dir = end-start;
+ F32 a, b, t;
- if (debug_render)
+ if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) ||
+ LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) )
+ {
+ if (t <= 1.f)
{
- gGL.begin(LLRender::LINE_STRIP);
- gGL.vertex3fv(v[0].mV);
- gGL.vertex3fv(v[1].mV);
- gGL.vertex3fv(v[2].mV);
- gGL.vertex3fv(v[3].mV);
- gGL.vertex3fv(v[0].mV);
- gGL.vertex3fv(v[2].mV);
- gGL.end();
- }
-
- LLVector3 dir = end-start;
- F32 a, b, t;
-
- if (LLTriangleRayIntersect(v[0], v[1], v[2], start, dir, a, b, t, FALSE) ||
- LLTriangleRayIntersect(v[2], v[3], v[0], start, dir, a, b, t, FALSE) )
- {
- if (t <= 1.f)
- {
- intersection = start + dir*t;
- return TRUE;
- }
+ intersection = start + dir*t;
+ return TRUE;
}
}
@@ -241,12 +224,6 @@ void LLHUDNameTag::render()
}
}
-void LLHUDNameTag::renderForSelect()
-{
- LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
- renderText(TRUE);
-}
-
void LLHUDNameTag::renderText(BOOL for_select)
{
if (!mVisible || mHidden)
@@ -299,24 +276,6 @@ void LLHUDNameTag::renderText(BOOL for_select)
LLColor4 bg_color = LLUIColorTable::instance().getColor("NameTagBackground");
bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
- // maybe a no-op?
- //const S32 border_height = 16;
- //const S32 border_width = 16;
- const S32 border_height = 8;
- const S32 border_width = 8;
-
- // *TODO move this into helper function
- F32 border_scale = 1.f;
-
- if (border_height * 2 > mHeight)
- {
- border_scale = (F32)mHeight / ((F32)border_height * 2.f);
- }
- if (border_width * 2 > mWidth)
- {
- border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
- }
-
// scale screen size of borders down
//RN: for now, text on hud objects is never occluded
@@ -325,152 +284,34 @@ void LLHUDNameTag::renderText(BOOL for_select)
LLViewerCamera::getInstance()->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
- LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getTextureWidth(), (F32)border_height / (F32)imagep->getTextureHeight());
LLVector3 width_vec = mWidth * x_pixel_vec;
LLVector3 height_vec = mHeight * y_pixel_vec;
- LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
- LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
mRadius = (width_vec + height_vec).magVec() * 0.5f;
LLCoordGL screen_pos;
LLViewerCamera::getInstance()->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
- LLVector2 screen_offset;
-// if (!mUseBubble)
-// {
-// screen_offset = mPositionOffset;
-// }
-// else
-// {
- screen_offset = updateScreenPos(mPositionOffset);
-// }
+ LLVector2 screen_offset = updateScreenPos(mPositionOffset);
LLVector3 render_position = mPositionAgent
+ (x_pixel_vec * screen_offset.mV[VX])
+ (y_pixel_vec * screen_offset.mV[VY]);
-// if (mUseBubble)
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ LLRect screen_rect;
+ screen_rect.setCenterAndSize(0, static_cast<S32>(lltrunc(-mHeight / 2 + mOffsetY)), static_cast<S32>(lltrunc(mWidth)), static_cast<S32>(lltrunc(mHeight)));
+ imagep->draw3D(render_position, x_pixel_vec, y_pixel_vec, screen_rect, bg_color);
+ if (mLabelSegments.size())
{
- LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
- LLUI::pushMatrix();
- {
- LLVector3 bg_pos = render_position
- + (F32)mOffsetY * y_pixel_vec
- - (width_vec / 2.f)
- - (height_vec);
- LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
-
- if (for_select)
- {
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- S32 name = mSourceObject->mGLName;
- LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name);
- gGL.color4ubv(coloru.mV);
- gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
- LLUI::popMatrix();
- return;
- }
- else
- {
- gGL.getTexUnit(0)->bind(imagep->getImage());
-
- gGL.color4fv(bg_color.mV);
- gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
-
- if ( mLabelSegments.size())
- {
- LLUI::pushMatrix();
- {
- gGL.color4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
- LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec;
- LLVector3 label_offset = height_vec - label_height;
- LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]);
- gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height);
- }
- LLUI::popMatrix();
- }
- }
-
- BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f;
- BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f);
+ LLUIImagePtr rect_top_image = LLUI::getUIImage("Rounded_Rect_Top");
+ LLRect label_top_rect = screen_rect;
+ const S32 label_height = llround((mFontp->getLineHeight() * (F32)mLabelSegments.size() + (VERTICAL_PADDING / 3.f)));
+ label_top_rect.mBottom = label_top_rect.mTop - label_height;
+ LLColor4 label_top_color = text_color;
+ label_top_color.mV[VALPHA] = gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor;
- // draw line segments pointing to parent object
- if (!mOffscreen && (outside_width || outside_height))
- {
- LLUI::pushMatrix();
- {
- gGL.color4fv(bg_color.mV);
- LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec);
- target_pos += (width_vec / 2.f);
- target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero;
- target_pos -= 3.f * x_pixel_vec;
- target_pos -= 6.f * y_pixel_vec;
- LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]);
- gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec);
- }
- LLUI::popMatrix();
-
- gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
- LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE);
-
- LLVector3 box_center_offset;
- box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f);
- LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]);
- gGL.color4fv(bg_color.mV);
- LLUI::setLineWidth(2.0);
- gGL.begin(LLRender::LINES);
- {
- if (outside_width)
- {
- LLVector3 vert;
- // draw line in x then y
- if (mPositionOffset.mV[VX] < 0.f)
- {
- // start at right edge
- vert = width_vec * 0.5f;
- gGL.vertex3fv(vert.mV);
- }
- else
- {
- // start at left edge
- vert = width_vec * -0.5f;
- gGL.vertex3fv(vert.mV);
- }
- vert = -mPositionOffset.mV[VX] * x_pixel_vec;
- gGL.vertex3fv(vert.mV);
- gGL.vertex3fv(vert.mV);
- vert -= mPositionOffset.mV[VY] * y_pixel_vec;
- vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
- gGL.vertex3fv(vert.mV);
- }
- else
- {
- LLVector3 vert;
- // draw line in y then x
- if (mPositionOffset.mV[VY] < 0.f)
- {
- // start at top edge
- vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
- gGL.vertex3fv(vert.mV);
- }
- else
- {
- // start at bottom edge
- vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
- gGL.vertex3fv(vert.mV);
- }
- vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec;
- vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
- gGL.vertex3fv(vert.mV);
- }
- }
- gGL.end();
- LLUI::setLineWidth(1.0);
-
- }
- }
- LLUI::popMatrix();
+ rect_top_image->draw3D(render_position, x_pixel_vec, y_pixel_vec, label_top_rect, label_top_color);
}
F32 y_offset = (F32)mOffsetY;
@@ -874,29 +715,26 @@ void LLHUDNameTag::updateAll()
for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it)
{
LLHUDNameTag* textp = (*r_it);
-// if (textp->mUseBubble)
-// {
- if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
- {
- textp->setLOD(3);
- }
- else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
- {
- textp->setLOD(2);
- }
- else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
- {
- textp->setLOD(1);
- }
- else
- {
- textp->setLOD(0);
- }
- textp->updateSize();
- // find on-screen position and initialize collision rectangle
- textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
- current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
-// }
+ if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
+ {
+ textp->setLOD(3);
+ }
+ else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
+ {
+ textp->setLOD(2);
+ }
+ else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
+ {
+ textp->setLOD(1);
+ }
+ else
+ {
+ textp->setLOD(0);
+ }
+ textp->updateSize();
+ // find on-screen position and initialize collision rectangle
+ textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
+ current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
}
LLStat* camera_vel_stat = LLViewerCamera::getInstance()->getVelocityStat();
@@ -914,20 +752,12 @@ void LLHUDNameTag::updateAll()
{
LLHUDNameTag* src_textp = (*src_it);
-// if (!src_textp->mUseBubble)
-// {
-// continue;
-// }
VisibleTextObjectIterator dst_it = src_it;
++dst_it;
for (; dst_it != sVisibleTextObjects.end(); ++dst_it)
{
LLHUDNameTag* dst_textp = (*dst_it);
-// if (!dst_textp->mUseBubble)
-// {
-// continue;
-// }
if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect))
{
LLRectf intersect_rect = src_textp->mSoftScreenRect;
@@ -976,10 +806,6 @@ void LLHUDNameTag::updateAll()
VisibleTextObjectIterator this_object_it;
for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it)
{
-// if (!(*this_object_it)->mUseBubble)
-// {
-// continue;
-// }
(*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
}
}
@@ -1037,10 +863,6 @@ void LLHUDNameTag::addPickable(std::set<LLViewerObject*> &pick_list)
VisibleTextObjectIterator text_it;
for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it)
{
-// if (!(*text_it)->mUseBubble)
-// {
-// continue;
-// }
pick_list.insert((*text_it)->mSourceObject);
}
}
diff --git a/indra/newview/llhudnametag.h b/indra/newview/llhudnametag.h
index 3325c22def..72647d5b26 100644
--- a/indra/newview/llhudnametag.h
+++ b/indra/newview/llhudnametag.h
@@ -118,7 +118,6 @@ public:
/*virtual*/ void markDead();
friend class LLHUDObject;
/*virtual*/ F32 getDistance() const { return mLastDistance; }
- //void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; }
S32 getLOD() { return mLOD; }
BOOL getVisible() { return mVisible; }
BOOL getHidden() const { return mHidden; }
@@ -136,7 +135,6 @@ protected:
LLHUDNameTag(const U8 type);
/*virtual*/ void render();
- /*virtual*/ void renderForSelect();
void renderText(BOOL for_select);
static void updateAll();
void setLOD(S32 lod);
diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp
deleted file mode 100644
index 63eedcdfea..0000000000
--- a/indra/newview/llimfloater.cpp
+++ /dev/null
@@ -1,1195 +0,0 @@
-/**
- * @file llimfloater.cpp
- * @brief LLIMFloater 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 "llimfloater.h"
-
-#include "llnotificationsutil.h"
-
-#include "llagent.h"
-#include "llappviewer.h"
-#include "llavatarnamecache.h"
-#include "llbutton.h"
-#include "llchannelmanager.h"
-#include "llchiclet.h"
-#include "llchicletbar.h"
-#include "llfloaterreg.h"
-#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container
-#include "llinventoryfunctions.h"
-#include "lllayoutstack.h"
-#include "lllineeditor.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 "llautoreplace.h"
-
-LLIMFloater::LLIMFloater(const LLUUID& session_id)
- : LLTransientDockableFloater(NULL, true, session_id),
- mControlPanel(NULL),
- mSessionID(session_id),
- mLastMessageIndex(-1),
- mDialog(IM_NOTHING_SPECIAL),
- mChatHistory(NULL),
- mInputEditor(NULL),
- mSavedTitle(),
- mTypingStart(),
- mShouldSendTypingState(false),
- mMeTyping(false),
- mOtherTyping(false),
- mTypingTimer(),
- mTypingTimeoutTimer(),
- mPositioned(false),
- mSessionInitialized(false)
-{
- LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID);
- if (im_session)
- {
- mSessionInitialized = im_session->mSessionInitialized;
-
- 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);
-
- setDocked(true);
-}
-
-void LLIMFloater::onFocusLost()
-{
- LLIMModel::getInstance()->resetActiveSessionID();
-
- LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false);
-}
-
-void LLIMFloater::onFocusReceived()
-{
- LLIMModel::getInstance()->setActiveSessionID(mSessionID);
-
- LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true);
-
- if (getVisible())
- {
- LLIMModel::instance().sendNoUnreadMessages(mSessionID);
- }
-}
-
-// 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);
-}
-
-/* static */
-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())
- {
- floater->updateMessages();
- }
- }
-}
-
-void LLIMFloater::onVisibilityChange(const LLSD& new_visibility)
-{
- bool visible = new_visibility.asBoolean();
-
- LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
-
- if (visible && voice_channel &&
- voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED)
- {
- LLFloaterReg::showInstance("voice_call", mSessionID);
- }
- else
- {
- LLFloaterReg::hideInstance("voice_call", mSessionID);
- }
-}
-
-void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata )
-{
- LLIMFloater* self = (LLIMFloater*) userdata;
- self->sendMsg();
- self->setTyping(false);
-}
-
-void LLIMFloater::sendMsg()
-{
- if (!gAgent.isGodlike()
- && (mDialog == IM_NOTHING_SPECIAL)
- && mOtherParticipantUUID.isNull())
- {
- llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
- return;
- }
-
- if (mInputEditor)
- {
- LLWString text = mInputEditor->getConvertedText();
- if(!text.empty())
- {
- // Truncate and convert to UTF8 for transport
- std::string utf8_text = wstring_to_utf8str(text);
- utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
-
- 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);
- }
-
- mInputEditor->setText(LLStringUtil::null);
-
- updateMessages();
- }
- }
-}
-
-
-
-LLIMFloater::~LLIMFloater()
-{
- LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
-}
-
-//virtual
-BOOL LLIMFloater::postBuild()
-{
- const LLUUID& other_party_id = LLIMModel::getInstance()->getOtherParticipantID(mSessionID);
- if (other_party_id.notNull())
- {
- mOtherParticipantUUID = other_party_id;
- }
-
- mControlPanel->setSessionId(mSessionID);
- mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel"));
-
- 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));
-
- 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));
-
- 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 );
-
- childSetCommitCallback("chat_editor", onSendMsg, this);
-
- mChatHistory = getChild<LLChatHistory>("chat_history");
-
- setDocked(true);
-
- mTypingStart = LLTrans::getString("IM_typing_start_string");
-
- // Disable input editor if session cannot accept text
- LLIMModel::LLIMSession* im_session =
- LLIMModel::instance().findIMSession(mSessionID);
- if( im_session && !im_session->mTextIMPossible )
- {
- mInputEditor->setEnabled(FALSE);
- mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label"));
- }
-
- if ( im_session && im_session->isP2PSessionType())
- {
- // look up display name for window title
- LLAvatarNameCache::get(im_session->mOtherParticipantID,
- boost::bind(&LLIMFloater::onAvatarNameCache,
- this, _1, _2));
- }
- else
- {
- std::string session_name(LLIMModel::instance().getName(mSessionID));
- updateSessionName(session_name, session_name);
- }
-
- //*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())
- {
- return LLFloater::postBuild();
- }
- else
- {
- return LLDockableFloater::postBuild();
- }
-}
-
-void LLIMFloater::updateSessionName(const std::string& ui_title,
- const std::string& ui_label)
-{
- mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + ui_label);
- setTitle(ui_title);
-}
-
-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);
-}
-
-// virtual
-void LLIMFloater::draw()
-{
- if ( mMeTyping )
- {
- // Time out if user hasn't typed for a while.
- if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS )
- {
- setTyping(false);
- }
- }
-
- LLTransientDockableFloater::draw();
-}
-
-
-// static
-void* LLIMFloater::createPanelIMControl(void* userdata)
-{
- LLIMFloater *self = (LLIMFloater*)userdata;
- self->mControlPanel = new LLPanelIMControlPanel();
- self->mControlPanel->setXMLFilename("panel_im_control_panel.xml");
- return self->mControlPanel;
-}
-
-
-// static
-void* LLIMFloater::createPanelGroupControl(void* userdata)
-{
- LLIMFloater *self = (LLIMFloater*)userdata;
- self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID);
- self->mControlPanel->setXMLFilename("panel_group_control_panel.xml");
- return self->mControlPanel;
-}
-
-// static
-void* LLIMFloater::createPanelAdHocControl(void* userdata)
-{
- LLIMFloater *self = (LLIMFloater*)userdata;
- self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID);
- self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml");
- return self->mControlPanel;
-}
-
-void LLIMFloater::onSlide()
-{
- mControlPanel->getParent()->setVisible(!mControlPanel->getParent()->getVisible());
-
- gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getParent()->getVisible());
-
- getChild<LLButton>("slide_left_btn")->setVisible(mControlPanel->getParent()->getVisible());
- getChild<LLButton>("slide_right_btn")->setVisible(!mControlPanel->getParent()->getVisible());
-}
-
-//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);
- }
- }
- }
-
- bool exist = findInstance(session_id);
-
- LLIMFloater* floater = getInstance(session_id);
- if (!floater) return NULL;
-
- if(isChatMultiTab())
- {
- LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance();
-
- // do not add existed floaters to avoid adding torn off instances
- if (!exist)
- {
- // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
- // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists
- LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END;
-
- if (floater_container)
- {
- floater_container->addFloater(floater, TRUE, i_pt);
- }
- }
-
- 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));
- }
-
- // window is positioned, now we can show it.
- }
- floater->setVisible(TRUE);
-
- return floater;
-}
-
-void LLIMFloater::setDocked(bool docked, bool pop_on_undock)
-{
- // update notification channel state
- LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
- (LLNotificationsUI::LLChannelManager::getInstance()->
- findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
-
- if(!isChatMultiTab())
- {
- LLTransientDockableFloater::setDocked(docked, pop_on_undock);
- }
-
- // update notification channel state
- if(channel)
- {
- channel->updateShowToastsState();
- channel->redrawToasts();
- }
-}
-
-void LLIMFloater::setVisible(BOOL visible)
-{
- LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
- (LLNotificationsUI::LLChannelManager::getInstance()->
- findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
- LLTransientDockableFloater::setVisible(visible);
-
- // update notification channel state
- if(channel)
- {
- channel->updateShowToastsState();
- 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);
- if(chiclet)
- {
- chiclet->setToggleState(false);
- }
- }
-}
-
-BOOL LLIMFloater::getVisible()
-{
- if(isChatMultiTab())
- {
- LLIMFloaterContainer* im_container = LLIMFloaterContainer::getInstance();
-
- // Treat inactive floater as invisible.
- bool is_active = im_container->getActiveFloater() == this;
-
- //torn off floater is always inactive
- if (!is_active && getHost() != im_container)
- {
- return LLTransientDockableFloater::getVisible();
- }
-
- // getVisible() returns TRUE when Tabbed IM window is minimized.
- return is_active && !im_container->isMinimized() && im_container->getVisible();
- }
- else
- {
- return LLTransientDockableFloater::getVisible();
- }
-}
-
-//static
-bool LLIMFloater::toggle(const LLUUID& session_id)
-{
- if(!isChatMultiTab())
- {
- 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
- // scroll/text entry state
- floater->setVisible(false);
- return false;
- }
- else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus()))
- {
- floater->setVisible(TRUE);
- floater->setFocus(TRUE);
- return true;
- }
- }
-
- // ensure the list of messages is updated when floater is made visible
- show(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;
-
- //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);
- }
-
- // 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();
- }
-
- //*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)
- {
- LLIMModel::sendMessage(iter->asString(), mSessionID,
- mOtherParticipantUUID, mDialog);
- }
-}
-
-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);
- }
-
- 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();
- for (; iter != iter_end; ++iter)
- {
- LLSD msg = *iter;
-
- std::string time = msg["time"].asString();
- LLUUID from_id = msg["from_id"].asUUID();
- std::string from = msg["from"].asString();
- std::string message = msg["message"].asString();
- bool is_history = msg["is_history"].asBoolean();
-
- LLChat chat;
- chat.mFromID = from_id;
- chat.mSessionID = mSessionID;
- chat.mFromName = from;
- chat.mTimeStr = time;
- chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle;
-
- // process offer notification
- if (msg.has("notification_id"))
- {
- chat.mNotifId = msg["notification_id"].asUUID();
- // if notification exists - embed it
- if (LLNotificationsUtil::find(chat.mNotifId) != NULL)
- {
- // remove embedded notification from channel
- LLNotificationsUI::LLScreenChannel* channel = static_cast<LLNotificationsUI::LLScreenChannel*>
- (LLNotificationsUI::LLChannelManager::getInstance()->
- findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
- if (getVisible())
- {
- // toast will be automatically closed since it is not storable toast
- channel->hideToast(chat.mNotifId);
- }
- }
- // if notification doesn't exist - try to use next message which should be log entry
- else
- {
- continue;
- }
- }
- //process text message
- else
- {
- chat.mText = message;
- }
-
- mChatHistory->appendMessage(chat, chat_args);
- mLastMessageIndex = msg["index"].asInteger();
-
- // if it is a notification - next message is a notification history log, so skip it
- if (chat.mNotifId.notNull() && LLNotificationsUtil::find(chat.mNotifId) != NULL)
- {
- if (++iter == iter_end)
- {
- break;
- }
- else
- {
- mLastMessageIndex++;
- }
- }
- }
- }
-}
-
-void LLIMFloater::reloadMessages()
-{
- mChatHistory->clear();
- mLastMessageIndex = -1;
- updateMessages();
-}
-
-// static
-void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata )
-{
- LLIMFloater* self= (LLIMFloater*) userdata;
-
- // Allow enabling the LLIMFloater input editor only if session can accept text
- LLIMModel::LLIMSession* im_session =
- LLIMModel::instance().findIMSession(self->mSessionID);
- //TODO: While disabled lllineeditor can receive focus we need to check if it is enabled (EK)
- if( im_session && im_session->mTextIMPossible && self->mInputEditor->getEnabled())
- {
- //in disconnected state IM input editor should be disabled
- self->mInputEditor->setEnabled(!gDisconnected);
- }
-}
-
-// static
-void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata)
-{
- LLIMFloater* self = (LLIMFloater*) userdata;
- self->setTyping(false);
-}
-
-// static
-void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
-{
- LLIMFloater* self = (LLIMFloater*)userdata;
- std::string text = self->mInputEditor->getText();
- if (!text.empty())
- {
- self->setTyping(true);
- }
- else
- {
- // Deleting all text counts as stopping typing.
- self->setTyping(false);
- }
-}
-
-void LLIMFloater::setTyping(bool typing)
-{
- if ( typing )
- {
- // Started or proceeded typing, reset the typing timeout timer
- mTypingTimeoutTimer.reset();
- }
-
- if ( mMeTyping != typing )
- {
- // Typing state is changed
- mMeTyping = typing;
- // So, should send current state
- mShouldSendTypingState = true;
- // In case typing is started, send state after some delay
- mTypingTimer.reset();
- }
-
- // Don't want to send typing indicators to multiple people, potentially too
- // much network traffic. Only send in person-to-person IMs.
- if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL )
- {
- if ( mMeTyping )
- {
- if ( mTypingTimer.getElapsedTimeF32() > 1.f )
- {
- // Still typing, send 'start typing' notification
- LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE);
- mShouldSendTypingState = false;
- }
- }
- else
- {
- // Send 'stop typing' notification immediately
- LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE);
- mShouldSendTypingState = 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)
-{
- if ( typing )
- {
- // other user started typing
- addTypingIndicator(im_info);
- }
- else
- {
- // other user stopped typing
- removeTypingIndicator(im_info);
- }
-}
-
-void LLIMFloater::processAgentListUpdates(const LLSD& body)
-{
- if ( !body.isMap() ) return;
-
- if ( 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 agent_info = agent_data["info"];
-
- if (agent_info.has("mutes"))
- {
- 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);
-
- if (moderator_muted_text)
- LLNotificationsUtil::add("TextChatIsMutedByModerator");
- }
- }
- }
-}
-
-void LLIMFloater::updateChatHistoryStyle()
-{
- mChatHistory->clear();
- mLastMessageIndex = -1;
- updateMessages();
-}
-
-void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue)
-{
- LLFontGL* font = LLViewerChat::getChatFont();
- LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
- for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
- iter != inst_list.end(); ++iter)
- {
- LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter);
- if (floater)
- {
- floater->updateChatHistoryStyle();
- floater->mInputEditor->setFont(font);
- }
- }
-
-}
-
-void LLIMFloater::processSessionUpdate(const LLSD& session_update)
-{
- // *TODO : verify following code when moderated mode will be implemented
- if ( false && session_update.has("moderated_mode") &&
- session_update["moderated_mode"].has("voice") )
- {
- BOOL voice_moderated = session_update["moderated_mode"]["voice"];
- const std::string session_label = LLIMModel::instance().getName(mSessionID);
-
- if (voice_moderated)
- {
- setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label"));
- }
- else
- {
- setTitle(session_label);
- }
-
- // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added
- //update the speakers dropdown too
- //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated);
- }
-}
-
-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())
- {
- *accept = ACCEPT_NO;
-
- if (cargo_type == DAD_CALLINGCARD)
- {
- if (dropCallingCard((LLInventoryItem*)cargo_data, drop))
- {
- *accept = ACCEPT_YES_MULTI;
- }
- }
- else if (cargo_type == DAD_CATEGORY)
- {
- if (dropCategory((LLInventoryCategory*)cargo_data, drop))
- {
- *accept = ACCEPT_YES_MULTI;
- }
- }
- }
- return TRUE;
-}
-
-BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, 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
- {
- // set to false if creator uuid is null.
- rv = FALSE;
- }
- return rv;
-}
-
-BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop)
-{
- BOOL rv = isInviteAllowed();
- if(rv && category)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLUniqueBuddyCollector buddies;
- gInventory.collectDescendentsIf(category->getUUID(),
- cats,
- items,
- LLInventoryModel::EXCLUDE_TRASH,
- buddies);
- S32 count = items.count();
- if(count == 0)
- {
- rv = FALSE;
- }
- else if(drop)
- {
- uuid_vec_t ids;
- ids.reserve(count);
- for(S32 i = 0; i < count; ++i)
- {
- ids.push_back(items.get(i)->getCreatorUUID());
- }
- inviteToSession(ids);
- }
- }
- return rv;
-}
-
-BOOL LLIMFloater::isInviteAllowed() const
-{
-
- return ( (IM_SESSION_CONFERENCE_START == mDialog)
- || (IM_SESSION_INVITE == mDialog) );
-}
-
-class LLSessionInviteResponder : public LLHTTPClient::Responder
-{
-public:
- LLSessionInviteResponder(const LLUUID& session_id)
- {
- mSessionID = session_id;
- }
-
- void error(U32 statusNum, const std::string& reason)
- {
- llinfos << "Error inviting all agents to session" << llendl;
- //throw something back to the viewer here?
- }
-
-private:
- LLUUID mSessionID;
-};
-
-BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids)
-{
- LLViewerRegion* region = gAgent.getRegion();
- if (!region)
- {
- return FALSE;
- }
-
- S32 count = ids.size();
-
- if( isInviteAllowed() && (count > 0) )
- {
- llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl;
-
- std::string url = region->getCapability("ChatSessionRequest");
-
- 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
- {
- llinfos << "LLIMFloater::inviteToSession -"
- << " no need to invite agents for "
- << mDialog << llendl;
- // successful add, because everyone that needed to get added
- // was added.
- }
-
- return TRUE;
-}
-
-void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info)
-{
- // We may have lost a "stop-typing" packet, don't add it twice
- if ( im_info && !mOtherTyping )
- {
- mOtherTyping = true;
-
- // Save and set new title
- mSavedTitle = getTitle();
- setTitle (mTypingStart);
-
- // Update speaker
- LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
- if ( speaker_mgr )
- {
- speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE);
- }
- }
-}
-
-void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info)
-{
- if ( mOtherTyping )
- {
- mOtherTyping = false;
-
- // Revert the title to saved one
- setTitle(mSavedTitle);
-
- if ( im_info )
- {
- // Update speaker
- LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
- if ( speaker_mgr )
- {
- speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE);
- }
- }
-
- }
-}
-
-// static
-void LLIMFloater::closeHiddenIMToasts()
-{
- class IMToastMatcher: public LLNotificationsUI::LLScreenChannel::Matcher
- {
- public:
- bool matches(const LLNotificationPtr notification) const
- {
- // "notifytoast" type of notifications is reserved for IM notifications
- return "notifytoast" == notification->getType();
- }
- };
-
- LLNotificationsUI::LLScreenChannel* channel = LLNotificationsUI::LLChannelManager::getNotificationScreenChannel();
- if (channel != NULL)
- {
- channel->closeHiddenToasts(IMToastMatcher());
- }
-}
-// static
-void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& response)
-{
- S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
- const LLSD& payload = notification["payload"];
- LLUUID session_id = payload["session_id"];
-
- LLFloater* im_floater = LLFloaterReg::findInstance("impanel", session_id);
- if (option == 0 && im_floater != NULL)
- {
- im_floater->closeFloater();
- }
-
- return;
-}
-
-// 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;
-
- LLUUID from_id = data["from_id"];
- if (gAgentID == from_id || LLUUID::null == from_id) return;
-
- LLIMFloater* floater = LLIMFloater::findInstance(session_id);
- if (!floater) return;
-
- if (IM_NOTHING_SPECIAL != floater->mDialog) return;
-
- floater->removeTypingIndicator();
-}
-
-void LLIMFloater::onIMChicletCreated( const LLUUID& session_id )
-{
-
- if (isChatMultiTab())
- {
- LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance();
- if (!im_box) return;
-
- if (LLIMFloater::findInstance(session_id)) return;
-
- LLIMFloater* new_tab = LLIMFloater::getInstance(session_id);
-
- im_box->addFloater(new_tab, FALSE, LLTabContainer::END);
- }
-
-}
-
-void LLIMFloater::onClickCloseBtn()
-{
-
- LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(
- mSessionID);
-
- if (session == NULL)
- {
- llwarns << "Empty session." << llendl;
- return;
- }
-
- bool is_call_with_chat = session->isGroupSessionType()
- || session->isAdHocSessionType() || session->isP2PSessionType();
-
- LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
-
- if (is_call_with_chat && voice_channel != NULL && voice_channel->isActive())
- {
- LLSD payload;
- payload["session_id"] = mSessionID;
- LLNotificationsUtil::add("ConfirmLeaveCall", LLSD(), payload, confirmLeaveCallCallback);
- return;
- }
-
- LLFloater::onClickCloseBtn();
-}
diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp
deleted file mode 100644
index 0f0ae896a2..0000000000
--- a/indra/newview/llimfloatercontainer.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * @file llimfloatercontainer.cpp
- * @brief Multifloater containing active IM sessions in separate tab container tabs
- *
- * $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 "llimfloatercontainer.h"
-#include "llfloaterreg.h"
-#include "llimview.h"
-#include "llavatariconctrl.h"
-#include "llgroupiconctrl.h"
-#include "llagent.h"
-#include "lltransientfloatermgr.h"
-
-//
-// LLIMFloaterContainer
-//
-LLIMFloaterContainer::LLIMFloaterContainer(const LLSD& seed)
-: LLMultiFloater(seed)
-{
- mAutoResize = FALSE;
- LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
-}
-
-LLIMFloaterContainer::~LLIMFloaterContainer()
-{
- mNewMessageConnection.disconnect();
- LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
-}
-
-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()
- 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();
- }
- }
-*/
-}
-
-void LLIMFloaterContainer::addFloater(LLFloater* floaterp,
- BOOL select_added_floater,
- LLTabContainer::eInsertionPoint insertion_point)
-{
- if(!floaterp) return;
-
- // already here
- if (floaterp->getHost() == this)
- {
- openFloater(floaterp->getKey());
- return;
- }
-
- LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
-
- LLUUID session_id = floaterp->getKey();
-
- LLIconCtrl* icon = 0;
-
- if(gAgent.isInGroup(session_id, TRUE))
- {
- LLGroupIconCtrl::Params icon_params;
- icon_params.group_id = session_id;
- icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params);
-
- mSessions[session_id] = floaterp;
- floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id));
- }
- else
- {
- LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id);
-
- LLAvatarIconCtrl::Params icon_params;
- icon_params.avatar_id = avatar_id;
- icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params);
-
- mSessions[session_id] = floaterp;
- floaterp->mCloseSignal.connect(boost::bind(&LLIMFloaterContainer::onCloseFloater, this, session_id));
- }
- mTabContainer->setTabImage(floaterp, icon);
-}
-
-void LLIMFloaterContainer::onCloseFloater(LLUUID& id)
-{
- mSessions.erase(id);
- setFocus(TRUE);
-}
-
-void LLIMFloaterContainer::onNewMessageReceived(const LLSD& data)
-{
- LLUUID session_id = data["session_id"].asUUID();
- LLFloater* floaterp = get_ptr_in_map(mSessions, session_id);
- LLFloater* current_floater = LLMultiFloater::getActiveFloater();
-
- if(floaterp && current_floater && floaterp != current_floater)
- {
- if(LLMultiFloater::isFloaterFlashing(floaterp))
- LLMultiFloater::setFloaterFlashing(floaterp, FALSE);
- LLMultiFloater::setFloaterFlashing(floaterp, TRUE);
- }
-}
-
-LLIMFloaterContainer* LLIMFloaterContainer::findInstance()
-{
- return LLFloaterReg::findTypedInstance<LLIMFloaterContainer>("im_container");
-}
-
-LLIMFloaterContainer* LLIMFloaterContainer::getInstance()
-{
- return LLFloaterReg::getTypedInstance<LLIMFloaterContainer>("im_container");
-}
-
-void LLIMFloaterContainer::setMinimized(BOOL b)
-{
- if (isMinimized() == b) return;
-
- LLMultiFloater::setMinimized(b);
- // Hide minimized floater (see EXT-5315)
- setVisible(!b);
-
- if (isMinimized()) return;
-
- if (getActiveFloater())
- {
- getActiveFloater()->setVisible(TRUE);
- }
-}
-
-// EOF
diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h
deleted file mode 100644
index 892ecef48d..0000000000
--- a/indra/newview/llimfloatercontainer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * @file llimfloatercontainer.h
- * @brief Multifloater containing active IM sessions in separate tab container tabs
- *
- * $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_LLIMFLOATERCONTAINER_H
-#define LL_LLIMFLOATERCONTAINER_H
-
-#include <map>
-#include <vector>
-
-#include "llfloater.h"
-#include "llmultifloater.h"
-#include "llavatarpropertiesprocessor.h"
-#include "llgroupmgr.h"
-
-class LLTabContainer;
-
-class LLIMFloaterContainer : public LLMultiFloater
-{
-public:
- LLIMFloaterContainer(const LLSD& seed);
- virtual ~LLIMFloaterContainer();
-
- /*virtual*/ BOOL postBuild();
- /*virtual*/ void onOpen(const LLSD& key);
- void onCloseFloater(LLUUID& id);
-
- /*virtual*/ void addFloater(LLFloater* floaterp,
- BOOL select_added_floater,
- LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END);
-
- static LLFloater* getCurrentVoiceFloater();
-
- static LLIMFloaterContainer* findInstance();
-
- static LLIMFloaterContainer* getInstance();
-
- virtual void setMinimized(BOOL b);
-
-private:
- typedef std::map<LLUUID,LLFloater*> avatarID_panel_map_t;
- avatarID_panel_map_t mSessions;
- boost::signals2::connection mNewMessageConnection;
-
- void onNewMessageReceived(const LLSD& data);
-};
-
-#endif // LL_LLIMFLOATERCONTAINER_H
diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp
index 07d73c8c66..c2b29f36e8 100644
--- a/indra/newview/llimhandler.cpp
+++ b/indra/newview/llimhandler.cpp
@@ -36,11 +36,12 @@
using namespace LLNotificationsUI;
+extern void process_dnd_im(const LLSD& notification);
+
//--------------------------------------------------------------------------
-LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id)
+LLIMHandler::LLIMHandler()
+: LLCommunicationNotificationHandler("IM Notifications", "notifytoast")
{
- mType = type;
-
// Getting a Channel for our notifications
mChannel = LLChannelManager::getInstance()->createNotificationChannel()->getHandle();
}
@@ -59,72 +60,57 @@ 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;
-}
+ if(notification->isDND())
+ {
+ LLSD data = notification->asLLSD(); //don't need this if retrieve needed data from notification getters
+ process_dnd_im(data);
+ }
+ else
+ {
+ if(mChannel.isDead())
+ {
+ return false;
+ }
+
+ // arrange a channel on a screen
+ if(!mChannel.get()->getVisible())
+ {
+ initChannel();
+ }
+
+ 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/llimpanel.cpp b/indra/newview/llimpanel.cpp
index 0250af6a0e..c64ecdc47a 100644
--- a/indra/newview/llimpanel.cpp
+++ b/indra/newview/llimpanel.cpp
@@ -171,7 +171,7 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& session_label,
// enable line history support for instant message bar
mInputEditor->setEnableLineHistory(TRUE);
- //*TODO we probably need the same "awaiting message" thing in LLIMFloater
+ //*TODO we probably need the same "awaiting message" thing in LLFloaterIMSession
LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionUUID);
if (!im_session)
{
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
index 4000570872..8c862548bb 100644
--- a/indra/newview/llimview.cpp
+++ b/indra/newview/llimview.cpp
@@ -29,6 +29,8 @@
#include "llimview.h"
#include "llavatarnamecache.h" // IDEVO
+#include "llavataractions.h"
+#include "llfloaterconversationlog.h"
#include "llfloaterreg.h"
#include "llfontgl.h"
#include "llgl.h"
@@ -41,14 +43,15 @@
#include "lltextutil.h"
#include "lltrans.h"
#include "lluictrlfactory.h"
-
+#include "llfloaterimsessiontab.h"
#include "llagent.h"
#include "llagentui.h"
#include "llappviewer.h"
#include "llavatariconctrl.h"
#include "llcallingcard.h"
#include "llchat.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
+#include "llfloaterimcontainer.h"
#include "llgroupiconctrl.h"
#include "llmd5.h"
#include "llmutelist.h"
@@ -57,12 +60,14 @@
#include "llviewerwindow.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llspeakers.h" //for LLIMSpeakerMgr
#include "lltextbox.h"
#include "lltoolbarview.h"
#include "llviewercontrol.h"
#include "llviewerparcelmgr.h"
+#include "llconversationlog.h"
+#include "message.h"
const static std::string ADHOC_NAME_SUFFIX(" Conference");
@@ -97,6 +102,47 @@ BOOL LLSessionTimeoutTimer::tick()
return TRUE;
}
+
+
+void process_dnd_im(const LLSD& notification)
+{
+ LLSD data = notification["substitutions"];
+ LLUUID sessionID = data["SESSION_ID"].asUUID();
+ LLUUID fromID = data["FROM_ID"].asUUID();
+
+ //re-create the IM session if needed
+ //(when coming out of DND mode upon app restart)
+ if(!gIMMgr->hasSession(sessionID))
+ {
+ //reconstruct session using data from the notification
+ std::string name = data["FROM"];
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(data["FROM_ID"], &av_name))
+ {
+ name = av_name.getDisplayName();
+ }
+
+
+ LLIMModel::getInstance()->newSession(sessionID,
+ name,
+ IM_NOTHING_SPECIAL,
+ fromID,
+ false,
+ false); //will need slight refactor to retrieve whether offline message or not (assume online for now)
+
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+
+ if (im_box)
+ {
+ im_box->flashConversationItemWidget(sessionID, true);
+ }
+
+ }
+}
+
+
+
+
static void on_avatar_name_cache_toast(const LLUUID& agent_id,
const LLAvatarName& av_name,
LLSD msg)
@@ -108,77 +154,183 @@ 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()));
-}
-
-void toast_callback(const LLSD& msg){
- // do not show toast in busy mode or it goes from agent
- if (gAgent.getBusy() || gAgent.getID() == msg["from_id"])
- {
- 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 for system messages
- if (msg["from_id"].asUUID() == LLUUID::null)
- {
- return;
- }
-
- // *NOTE Skip toasting if the user disable it in preferences/debug settings ~Alexandrea
- LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(
- msg["session_id"]);
- if (!gSavedSettings.getBOOL("EnableGroupChatPopups")
- && session->isGroupSessionType())
- {
- return;
- }
- if (!gSavedSettings.getBOOL("EnableIMChatPopups")
- && !session->isGroupSessionType())
+ args["SESSION_TYPE"] = msg["session_type"];
+ LLNotificationsUtil::add("IMToast", args, args, boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID()));
+}
+
+void on_new_message(const LLSD& msg)
+{
+ std::string action;
+ LLUUID participant_id = msg["from_id"].asUUID();
+ LLUUID session_id = msg["session_id"].asUUID();
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
+
+ // determine action for this session
+
+ if (session_id.isNull())
+ {
+ action = gSavedSettings.getString("NotificationNearbyChatOptions");
+ }
+ else if(session->isP2PSessionType())
+ {
+ if (LLAvatarTracker::instance().isBuddy(participant_id))
+ {
+ action = gSavedSettings.getString("NotificationFriendIMOptions");
+ }
+ else
+ {
+ action = gSavedSettings.getString("NotificationNonFriendIMOptions");
+ }
+ }
+ else if(session->isAdHocSessionType())
+ {
+ action = gSavedSettings.getString("NotificationConferenceIMOptions");
+ }
+ else if(session->isGroupSessionType())
+ {
+ action = gSavedSettings.getString("NotificationGroupChatOptions");
+ }
+
+ // do not show notification which goes from agent
+ if (gAgent.getID() == participant_id)
+ {
+ return;
+ }
+
+ // execution of the action
+
+ LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id);
+
+ if (im_box->isFrontmost() && im_box->getSelectedSession() == session_id
+ && !(session_floater->getHost() ? im_box->isMinimized() : session_floater->isMinimized()))
{
return;
}
+
+ //session floater not focused (visible or not)
+ bool session_floater_not_focused = session_floater && !session_floater->hasFocus();
+
+ //conv. floater is closed
+ bool conversation_floater_is_closed =
+ !( im_box
+ && im_box->isInVisibleChain()
+ && !im_box->isMinimized());
+
+ //conversation floater not focused (visible or not)
+ bool conversation_floater_not_focused =
+ conversation_floater_is_closed || !im_box->hasFocus();
+ // sess. floater is open
+ bool session_floater_is_open =
+ session_floater
+ && session_floater->isInVisibleChain()
+ && !session_floater->isMinimized()
+ && !(session_floater->getHost() && session_floater->getHost()->isMinimized());
+
+ bool conversation_floater_collapsed = !session_floater->isMessagePaneExpanded();
+ if (("toast" == action && !session_floater_is_open) || conversation_floater_collapsed)
+ {
+ //User is not focused on conversation containing the message
+ if(session_floater_not_focused || conversation_floater_collapsed)
+ {
+ if(!LLMuteList::getInstance()->isMuted(participant_id))
+ {
+ im_box->flashConversationItemWidget(session_id, true);
+ }
+ //The conversation floater isn't focused/open
+ if(conversation_floater_not_focused || conversation_floater_collapsed)
+ {
+ if(!LLMuteList::getInstance()->isMuted(participant_id)
+ && !gAgent.isDoNotDisturb())
+ {
+ gToolBarView->flashCommand(LLCommandId("chat"), true);
+ }
+
+ //Show IM toasts (upper right toasts)
+ // Skip toasting for system messages and for nearby chat
+ if(session_id.notNull() && participant_id.notNull())
+ {
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
+ }
+ }
+
+ else if ("flash" == action)
+ {
+ if (!gAgent.isDoNotDisturb())
+ {
+ im_box->flashConversationItemWidget(session_id, true);
+ if(conversation_floater_not_focused)
+ {
+ //User is not focused on conversation containing the message
+ gToolBarView->flashCommand(LLCommandId("chat"), true);
+ }
+ }
+ else if(session_id.notNull() && participant_id.notNull())
+ {
+ //If a DND message, allow notification to be stored so upon DND exit
+ //useMostItrusiveIMNotification will be called to notify user a message exists
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
- // 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;
- }
+ else if("openconversations" == action)
+ {
+ //User is not focused on conversation containing the message
+ if(session_floater_not_focused)
+ {
+ //Flash line item
+ im_box->flashConversationItemWidget(session_id, true);
- 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;
- }
+ if(!gAgent.isDoNotDisturb())
+ {
+ //Surface conversations floater
+ LLFloaterReg::showInstance("im_container");
+ im_box->collapseMessagesPane(false);
+ if (session_floater)
+ {
+ if (session_floater->getHost())
+ {
+ if (NULL != im_box && im_box->isMinimized())
+ {
+ LLFloater::onClickMinimize(im_box);
+ }
+ }
+ else
+ {
+ if (session_floater->isMinimized())
+ {
+ LLFloater::onClickMinimize(session_floater);
+ }
+ }
+ }
+ }
- mActiveSessionID = session_id;
+ //If in DND mode, allow notification to be stored so upon DND exit
+ //useMostItrusiveIMNotification will be called to notify user a message exists
+ if(session_id.notNull()
+ && participant_id.notNull()
+ && gAgent.isDoNotDisturb()
+ && !session_floater_is_open)
+ {
+ LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg));
+ }
+ }
+ }
}
LLIMModel::LLIMModel()
{
- addNewMsgCallback(boost::bind(&LLIMFloater::newIMCallback, _1));
- addNewMsgCallback(boost::bind(&toast_callback, _1));
+ addNewMsgCallback(boost::bind(&LLFloaterIMSession::newIMCallback, _1));
+ addNewMsgCallback(boost::bind(&on_new_message, _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),
@@ -190,7 +342,8 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&
mTextIMPossible(true),
mOtherParticipantIsAvatar(true),
mStartCallOnInitialize(false),
- mStartedAsIMCall(voice)
+ mStartedAsIMCall(voice),
+ mAvatarNameCacheConnection()
{
// set P2P type by default
mSessionType = P2P_SESSION;
@@ -256,30 +409,22 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string&
}
buildHistoryFileName();
-
- if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
- {
- std::list<LLSD> chat_history;
-
- //involves parsing of a chat history
- LLLogChat::loadAllHistory(mHistoryFileName, chat_history);
- addMessagesFromHistory(chat_history);
- }
+ loadHistory();
// Localizing name of ad-hoc session. STORM-153
// Changing name should happen here- after the history file was created, so that
// history files have consistent (English) names in different locales.
if (isAdHocSessionType() && IM_SESSION_INVITE == mType)
{
- LLAvatarNameCache::get(mOtherParticipantID,
- boost::bind(&LLIMModel::LLIMSession::onAdHocNameCache,
- this, _2));
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mOtherParticipantID,boost::bind(&LLIMModel::LLIMSession::onAdHocNameCache,this, _2));
}
}
void LLIMModel::LLIMSession::onAdHocNameCache(const LLAvatarName& av_name)
{
- if (av_name.mIsTemporaryName)
+ mAvatarNameCacheConnection.disconnect();
+
+ if (!av_name.isValidName())
{
S32 separator_index = mName.rfind(" ");
std::string name = mName.substr(0, separator_index);
@@ -375,6 +520,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES
break;
}
}
+ default:
+ break;
}
// Update speakers list when connected
if (LLVoiceChannel::STATE_CONNECTED == new_state)
@@ -385,6 +532,11 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES
LLIMModel::LLIMSession::~LLIMSession()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+
delete mSpeakers;
mSpeakers = NULL;
@@ -450,11 +602,11 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& histo
{
const LLSD& msg = *it;
- std::string from = msg[IM_FROM];
+ std::string from = msg[LL_IM_FROM];
LLUUID from_id;
- if (msg[IM_FROM_ID].isDefined())
+ if (msg[LL_IM_FROM_ID].isDefined())
{
- from_id = msg[IM_FROM_ID].asUUID();
+ from_id = msg[LL_IM_FROM_ID].asUUID();
}
else
{
@@ -463,8 +615,8 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list<LLSD>& histo
gCacheName->getUUID(legacy_name, from_id);
}
- std::string timestamp = msg[IM_TIME];
- std::string text = msg[IM_TEXT];
+ std::string timestamp = msg[LL_IM_TIME];
+ std::string text = msg[LL_IM_TEXT];
addMessage(from, from_id, text, timestamp, true);
@@ -488,10 +640,23 @@ void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const
}
}
+void LLIMModel::LLIMSession::loadHistory()
+{
+ mMsgs.clear();
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ {
+ std::list<LLSD> chat_history;
+
+ //involves parsing of a chat history
+ LLLogChat::loadChatHistory(mHistoryFileName, chat_history);
+ addMessagesFromHistory(chat_history);
+ }
+}
+
LLIMModel::LLIMSession* LLIMModel::findIMSession(const LLUUID& session_id) const
{
- return get_if_there(mId2SessionMap, session_id,
- (LLIMModel::LLIMSession*) NULL);
+ return get_if_there(mId2SessionMap, session_id, (LLIMModel::LLIMSession*) NULL);
}
//*TODO consider switching to using std::set instead of std::list for holding LLUUIDs across the whole code
@@ -533,7 +698,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 +718,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 +747,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
{
@@ -584,15 +762,7 @@ void LLIMModel::LLIMSession::buildHistoryFileName()
// so no need for a callback in LLAvatarNameCache::get()
if (LLAvatarNameCache::get(mOtherParticipantID, &av_name))
{
- if (av_name.mUsername.empty())
- {
- // Display names are off, use mDisplayName which will be the legacy name
- mHistoryFileName = LLCacheName::buildUsername(av_name.mDisplayName);
- }
- else
- {
- mHistoryFileName = av_name.mUsername;
- }
+ mHistoryFileName = LLCacheName::buildUsername(av_name.getUserName());
}
else
{
@@ -603,7 +773,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 +787,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,16 +801,19 @@ 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);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(old_session_id);
if (im_floater)
{
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 +849,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,22 +863,23 @@ 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
// same if it is an adhoc session (in this case name is localized in LLIMSession constructor).
std::string session_name = LLIMModel::getInstance()->getName(session_id);
- LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, session_name, other_participant_id);
+ LLIMMgr::getInstance()->notifyObserverSessionAdded(session_id, session_name, other_participant_id,has_offline_msg);
return true;
}
-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);
+ uuid_vec_t ids;
+ ids.push_back(other_participant_id);
+ return newSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg);
}
bool LLIMModel::clearSession(const LLUUID& session_id)
@@ -716,6 +890,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 +941,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);
@@ -781,19 +958,20 @@ bool LLIMModel::addToHistory(const LLUUID& session_id, const std::string& from,
bool LLIMModel::logToFile(const std::string& file_name, const std::string& from, const LLUUID& from_id, const std::string& utf8_text)
{
- if (gSavedPerAccountSettings.getBOOL("LogInstantMessages"))
+ if (gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 1)
{
std::string from_name = from;
LLAvatarName av_name;
if (!from_id.isNull() &&
LLAvatarNameCache::get(from_id, &av_name) &&
- !av_name.mIsDisplayNameDefault)
+ !av_name.isDisplayNameDefault())
{
from_name = av_name.getCompleteName();
}
LLLogChat::saveHistory(file_name, from_name, from_id, utf8_text);
+ LLConversationLog::instance().cache(); // update the conversation log too
return true;
}
else
@@ -831,6 +1009,7 @@ bool LLIMModel::addMessage(const LLUUID& session_id, const std::string& from, co
arg["from"] = from;
arg["from_id"] = from_id;
arg["time"] = LLLogChat::timestamp(false);
+ arg["session_type"] = session->mSessionType;
mNewMsgSignal(arg);
return true;
@@ -878,7 +1057,7 @@ const std::string LLIMModel::getName(const LLUUID& session_id) const
{
LLIMSession* session = findIMSession(session_id);
- if (!session)
+ if (!session)
{
llwarns << "session " << session_id << "does not exist " << llendl;
return LLTrans::getString("no_session_message");
@@ -904,7 +1083,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;
}
@@ -1376,7 +1555,7 @@ public:
&& LLIMModel::getInstance()->findIMSession(mSessionID))
{
// TODO remove in 2010, for voice calls we do not open an IM window
- //LLIMFloater::show(mSessionID);
+ //LLFloaterIMSession::show(mSessionID);
}
gIMMgr->clearPendingAgentListUpdates(mSessionID);
@@ -1444,6 +1623,11 @@ LLUUID LLIMMgr::computeSessionID(
session_id = other_participant_id ^ agent_id;
}
}
+
+ if (gAgent.isInGroup(session_id) && (session_id != other_participant_id))
+ {
+ llwarns << "Group session id different from group id: IM type = " << dialog << ", session id = " << session_id << ", group id = " << other_participant_id << llendl;
+ }
return session_id;
}
@@ -1520,7 +1704,7 @@ LLIMMgr::onConfirmForceCloseError(
//only 1 option really
LLUUID session_id = notification["payload"]["session_id"];
- LLFloater* floater = LLIMFloater::findInstance(session_id);
+ LLFloater* floater = LLFloaterIMSession::findInstance(session_id);
if ( floater )
{
floater->closeFloater(FALSE);
@@ -1878,7 +2062,7 @@ void LLOutgoingCallDialog::show(const LLSD& key)
LLAvatarName av_name;
if (LLAvatarNameCache::get(callee_id, &av_name))
{
- final_callee_name = av_name.mDisplayName;
+ final_callee_name = av_name.getDisplayName();
title = av_name.getCompleteName();
}
}
@@ -1980,7 +2164,8 @@ BOOL LLOutgoingCallDialog::postBuild()
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) :
-LLCallDialog(payload)
+LLCallDialog(payload),
+mAvatarNameCacheConnection()
{
}
@@ -2050,9 +2235,11 @@ BOOL LLIncomingCallDialog::postBuild()
else
{
// Get the full name information
- LLAvatarNameCache::get(caller_id,
- boost::bind(&LLIncomingCallDialog::onAvatarNameCache,
- this, _1, _2, call_type));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(caller_id, boost::bind(&LLIncomingCallDialog::onAvatarNameCache, this, _1, _2, call_type));
}
setIcon(session_id, caller_id);
@@ -2078,7 +2265,6 @@ BOOL LLIncomingCallDialog::postBuild()
getChildView("Start IM")->setVisible( is_avatar && notify_box_type != "VoiceInviteAdHoc" && notify_box_type != "VoiceInviteGroup");
setCanDrag(FALSE);
-
return TRUE;
}
@@ -2086,7 +2272,6 @@ void LLIncomingCallDialog::setCallerName(const std::string& ui_title,
const std::string& ui_label,
const std::string& call_type)
{
- setTitle(ui_title);
// call_type may be a string like " is calling."
LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name");
@@ -2097,14 +2282,15 @@ void LLIncomingCallDialog::onAvatarNameCache(const LLUUID& agent_id,
const LLAvatarName& av_name,
const std::string& call_type)
{
+ mAvatarNameCacheConnection.disconnect();
std::string title = av_name.getCompleteName();
- setCallerName(title, av_name.mDisplayName, call_type);
+ setCallerName(title, av_name.getCompleteName(), call_type);
}
void LLIncomingCallDialog::onOpen(const LLSD& key)
{
LLCallDialog::onOpen(key);
-
+ make_ui_sound("UISndStartIM");
LLStringUtil::format_map_t args;
LLGroupData data;
// if it's a group call, retrieve group name to use it in question
@@ -2112,18 +2298,6 @@ void LLIncomingCallDialog::onOpen(const LLSD& key)
{
args["[GROUP]"] = data.mName;
}
- // tell the user which voice channel they would be leaving
- LLVoiceChannel *voice = LLVoiceChannel::getCurrentVoiceChannel();
- if (voice && !voice->getSessionName().empty())
- {
- args["[CURRENT_CHAT]"] = voice->getSessionName();
- getChild<LLUICtrl>("question")->setValue(getString(key["question_type"].asString(), args));
- }
- else
- {
- args["[CURRENT_CHAT]"] = getString("localchat");
- getChild<LLUICtrl>("question")->setValue(getString(key["question_type"].asString(), args));
- }
}
//static
@@ -2184,6 +2358,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response, const LLSD &payload
{
gIMMgr->startCall(session_id, LLVoiceChannel::INCOMING_CALL);
}
+ else
+ {
+ LLAvatarActions::startIM(caller_id);
+ }
gIMMgr->clearPendingAgentListUpdates(session_id);
gIMMgr->clearPendingInvitation(session_id);
@@ -2386,7 +2564,7 @@ LLIMMgr::LLIMMgr()
mPendingInvitations = LLSD::emptyMap();
mPendingAgentListUpdates = LLSD::emptyMap();
- LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1));
+ LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLFloaterIMSession::sRemoveTypingIndicator, _1));
}
// Add a message to a session.
@@ -2395,6 +2573,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,
@@ -2403,6 +2582,7 @@ void LLIMMgr::addMessage(
bool link_name) // If this is true, then we insert the name and link it to a profile
{
LLUUID other_participant_id = target_id;
+
LLUUID new_session_id = session_id;
if (new_session_id.isNull())
{
@@ -2412,15 +2592,22 @@ void LLIMMgr::addMessage(
//*NOTE session_name is empty in case of incoming P2P sessions
std::string fixed_session_name = from;
+ bool name_is_setted = false;
if(!session_name.empty() && session_name.size()>1)
{
fixed_session_name = session_name;
+ name_is_setted = true;
}
bool new_session = !hasSession(new_session_id);
if (new_session)
{
- LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id);
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(other_participant_id, &av_name) && !name_is_setted)
+ {
+ fixed_session_name = av_name.getDisplayName();
+ }
+ 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
@@ -2455,16 +2642,35 @@ void LLIMMgr::addMessage(
return;
}
- make_ui_sound("UISndNewIncomingIMSession");
+ //Play sound for new conversations
+ if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE))
+ {
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
}
- bool skip_message = (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") &&
- LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL);
+ bool skip_message = false;
+ if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly"))
+ {
+ // Evaluate if we need to skip this message when that setting is true (default is false)
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id);
+ skip_message = (LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); // Skip non friends...
+ skip_message &= !session->isGroupSessionType(); // Do not skip group chats...
+ skip_message &= !(other_participant_id == gAgentID); // You are your best friend... Don't skip yourself
+ }
if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message)
{
LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg);
}
+
+ // Open conversation floater if offline messages are present
+ if (is_offline_msg)
+ {
+ LLFloaterReg::showInstance("im_container");
+ LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->
+ flashConversationItemWidget(new_session_id, true);
+ }
}
void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args)
@@ -2479,11 +2685,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)
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
{
nearby_chat->addMessage(chat);
}
@@ -2497,6 +2701,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 +2784,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 +2794,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,7 +2810,21 @@ LLUUID LLIMMgr::addSession(
LLUUID session_id = computeSessionID(dialog,other_participant_id);
- bool new_session = !LLIMModel::getInstance()->findIMSession(session_id);
+ if (floater_id.notNull())
+ {
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(floater_id);
+
+ if (im_floater)
+ {
+ // The IM floater should be initialized with a new session_id
+ // so that it is found by that id when creating a chiclet in LLFloaterIMSession::onIMChicletCreated,
+ // and a new floater is not created.
+ im_floater->initIMSession(session_id);
+ im_floater->reloadMessages();
+ }
+ }
+
+ bool new_session = (LLIMModel::getInstance()->findIMSession(session_id) == NULL);
//works only for outgoing ad-hoc sessions
if (new_session && IM_SESSION_CONFERENCE_START == dialog && ids.size())
@@ -2616,10 +2837,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 +2862,8 @@ LLUUID LLIMMgr::addSession(
noteMutedUsers(session_id, ids);
}
+ notifyObserverSessionVoiceOrIMStarted(session_id);
+
return session_id;
}
@@ -2739,12 +2969,17 @@ void LLIMMgr::inviteToSession(
if (voice_invite)
{
- if ( // if we are rejecting group calls
- (gSavedSettings.getBOOL("VoiceCallsRejectGroup") && notify_box_type == "VoiceInviteGroup") ||
- // or we're rejecting non-friend voice calls and this isn't a friend
- (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL))
- )
+ bool isRejectGroupCall = (gSavedSettings.getBOOL("VoiceCallsRejectGroup") && (notify_box_type == "VoiceInviteGroup"));
+ bool isRejectNonFriendCall = (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && (LLAvatarTracker::instance().getBuddyInfo(caller_id) == NULL));
+ bool isRejectDoNotDisturb = (gAgent.isDoNotDisturb() && !hasSession(session_id));
+ if (isRejectGroupCall || isRejectNonFriendCall || isRejectDoNotDisturb)
{
+ if (isRejectDoNotDisturb && !isRejectGroupCall && !isRejectNonFriendCall)
+ {
+ LLSD args;
+ addSystemMessage(session_id, "you_auto_rejected_call", args);
+ send_do_not_disturb_message(gMessageSystem, caller_id, session_id);
+ }
// silently decline the call
LLIncomingCallDialog::processCallResponse(1, payload);
return;
@@ -2806,7 +3041,7 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id)
void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body)
{
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if ( im_floater )
{
im_floater->processAgentListUpdates(body);
@@ -2912,11 +3147,27 @@ void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id)
}
}
-void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
+void LLIMMgr::notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg)
{
for (session_observers_list_t::iterator it = mSessionObservers.begin(); it != mSessionObservers.end(); it++)
{
- (*it)->sessionAdded(session_id, name, other_participant_id);
+ (*it)->sessionAdded(session_id, name, other_participant_id, has_offline_msg);
+ }
+}
+
+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);
}
}
@@ -3016,7 +3267,7 @@ void LLIMMgr::noteOfflineUsers(
{
LLUIString offline = LLTrans::getString("offline_message");
// Use display name only because this user is your friend
- offline.setArg("[NAME]", av_name.mDisplayName);
+ offline.setArg("[NAME]", av_name.getDisplayName());
im_model.proccessOnlineOfflineNotification(session_id, offline);
}
}
@@ -3064,7 +3315,7 @@ void LLIMMgr::processIMTypingStop(const LLIMInfo* im_info)
void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
{
LLUUID session_id = computeSessionID(im_info->mIMType, im_info->mFromID);
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if ( im_floater )
{
im_floater->processIMTyping(im_info, typing);
@@ -3109,7 +3360,7 @@ public:
speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id));
}
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if ( im_floater )
{
if ( body.has("session_info") )
@@ -3203,7 +3454,7 @@ public:
const LLSD& input) const
{
LLUUID session_id = input["body"]["session_id"].asUUID();
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if ( im_floater )
{
im_floater->processSessionUpdate(input["body"]["info"]);
@@ -3248,13 +3499,11 @@ public:
time_t timestamp =
(time_t) message_params["timestamp"].asInteger();
- BOOL is_busy = gAgent.getBusy();
- BOOL is_muted = LLMuteList::getInstance()->isMuted(
- from_id,
- name,
- LLMute::flagTextChat);
+ BOOL is_do_not_disturb = gAgent.isDoNotDisturb();
- if (is_busy || is_muted)
+ //don't return if user is muted b/c proper way to ignore a muted user who
+ //initiated an adhoc/group conference is to create then leave the session (see STORM-1731)
+ if (is_do_not_disturb)
{
return;
}
@@ -3279,6 +3528,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..da6039a3ae 100644
--- a/indra/newview/llimview.h
+++ b/indra/newview/llimview.h
@@ -27,7 +27,7 @@
#ifndef LL_LLIMVIEW_H
#define LL_LLIMVIEW_H
-#include "lldockablefloater.h"
+#include "../llui/lldockablefloater.h"
#include "lleventtimer.h"
#include "llinstantmessage.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,10 +95,14 @@ 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();
+ void loadHistory();
+
LLUUID mSessionID;
std::string mName;
EInstantMessage mType;
@@ -133,22 +138,18 @@ 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);
+ boost::signals2::connection mAvatarNameCacheConnection;
};
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 +182,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 +193,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 +200,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 +283,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);
@@ -297,7 +298,9 @@ class LLIMSessionObserver
{
public:
virtual ~LLIMSessionObserver() {}
- virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) = 0;
+ virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg) = 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 +327,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 +351,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.
@@ -459,7 +465,10 @@ private:
static void onInviteNameLookup(LLSD payload, const LLUUID& id, const std::string& name, bool is_group);
- void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id);
+ void notifyObserverSessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg);
+ //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);
@@ -541,7 +550,14 @@ class LLIncomingCallDialog : public LLCallDialog
{
public:
LLIncomingCallDialog(const LLSD& payload);
-
+ ~LLIncomingCallDialog()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
+
/*virtual*/ BOOL postBuild();
/*virtual*/ void onOpen(const LLSD& key);
@@ -558,6 +574,8 @@ private:
const LLAvatarName& av_name,
const std::string& call_type);
+ boost::signals2::connection mAvatarNameCacheConnection;
+
/*virtual*/ void onLifetimeExpired();
};
diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp
index 17d0b0ffbb..1e15dc832c 100644
--- a/indra/newview/llinspectavatar.cpp
+++ b/indra/newview/llinspectavatar.cpp
@@ -29,37 +29,24 @@
// 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;
@@ -80,70 +67,30 @@ public:
// Inspector will be positioned relative to current mouse position
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:
// Make network requests for all the data to display in this view.
// 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);
@@ -155,6 +102,7 @@ private:
// an in-flight request for avatar properties from LLAvatarPropertiesProcessor
// is represented by this object
LLFetchAvatarData* mPropertiesRequest;
+ boost::signals2::connection mAvatarNameCacheConnection;
};
//////////////////////////////////////////////////////////////////////////////
@@ -207,41 +155,11 @@ LLInspectAvatar::LLInspectAvatar(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(),
- mPropertiesRequest(NULL)
+ mPropertiesRequest(NULL),
+ mAvatarNameCacheConnection()
{
- 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);
@@ -249,6 +167,10 @@ LLInspectAvatar::LLInspectAvatar(const LLSD& sd)
LLInspectAvatar::~LLInspectAvatar()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
// clean up any pending requests so they don't call back into a deleted
// view
delete mPropertiesRequest;
@@ -260,12 +182,6 @@ LLInspectAvatar::~LLInspectAvatar()
/*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) );
@@ -275,7 +191,6 @@ BOOL LLInspectAvatar::postBuild(void)
return TRUE;
}
-
// Multiple calls to showInstance("inspect_avatar", foo) will provide different
// LLSD for foo, which we will catch here.
//virtual
@@ -287,11 +202,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 +214,15 @@ 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 +249,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.
@@ -374,9 +260,11 @@ void LLInspectAvatar::requestUpdate()
getChild<LLUICtrl>("avatar_icon")->setValue(LLSD(mAvatarID) );
- LLAvatarNameCache::get(mAvatarID,
- boost::bind(&LLInspectAvatar::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID,boost::bind(&LLInspectAvatar::onAvatarNameCache,this, _1, _2));
}
void LLInspectAvatar::processAvatarData(LLAvatarData* data)
@@ -405,141 +293,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);
@@ -558,12 +311,11 @@ void LLInspectAvatar::updateVolumeSlider()
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);
+ bool is_muted = LLAvatarActions::isVoiceMuted(mAvatarID);
LLUICtrl* mute_btn = getChild<LLUICtrl>("mute_btn");
- bool is_linden = LLStringUtil::endsWith(mAvatarName.getLegacyName(), " Linden");
+ bool is_linden = LLStringUtil::endsWith(mAvatarName.getDisplayName(), " Linden");
mute_btn->setEnabled( !is_linden);
mute_btn->setValue( is_muted );
@@ -594,7 +346,7 @@ void LLInspectAvatar::onClickMuteVolume()
LLMuteList* mute_list = LLMuteList::getInstance();
bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat);
- LLMute mute(mAvatarID, mAvatarName.getLegacyName(), LLMute::AGENT);
+ LLMute mute(mAvatarID, mAvatarName.getDisplayName(), LLMute::AGENT);
if (!is_muted)
{
mute_list->add(mute, LLMute::flagVoiceChat);
@@ -617,11 +369,13 @@ void LLInspectAvatar::onAvatarNameCache(
const LLUUID& agent_id,
const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
if (agent_id == mAvatarID)
{
- getChild<LLUICtrl>("user_name")->setValue(av_name.mDisplayName);
- getChild<LLUICtrl>("user_name_small")->setValue(av_name.mDisplayName);
- getChild<LLUICtrl>("user_slid")->setValue(av_name.mUsername);
+ getChild<LLUICtrl>("user_name")->setValue(av_name.getDisplayName());
+ getChild<LLUICtrl>("user_name_small")->setValue(av_name.getDisplayName());
+ getChild<LLUICtrl>("user_slid")->setValue(av_name.getUserName());
mAvatarName = av_name;
// show smaller display name if too long to display in regular size
@@ -640,215 +394,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 949de312be..0ee78d57bd 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 "llfloaterimcontainer.h"
#include "llimview.h"
#include "llclipboard.h"
#include "llinventorydefines.h"
@@ -94,21 +95,6 @@ struct LLMoveInv
using namespace LLOldEvents;
-// Helpers
-// bug in busy count inc/dec right now, logic is complex... do we really need it?
-void inc_busy_count()
-{
-// gViewerWindow->getWindow()->incBusyCount();
-// check balance of these calls if this code is changed to ever actually
-// *do* something!
-}
-void dec_busy_count()
-{
-// gViewerWindow->getWindow()->decBusyCount();
-// check balance of these calls if this code is changed to ever actually
-// *do* something!
-}
-
// Function declarations
void remove_inventory_category_from_avatar(LLInventoryCategory* category);
void remove_inventory_category_from_avatar_step2( BOOL proceed, LLUUID category_id);
@@ -118,10 +104,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
@@ -169,7 +155,6 @@ public:
{
if (clear_observer)
{
- dec_busy_count();
gInventory.removeObserver(this);
delete this;
}
@@ -192,7 +177,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();
@@ -211,7 +197,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
@@ -230,9 +220,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
{
@@ -250,6 +255,11 @@ BOOL LLInvFVBridge::isLink() const
return mIsLink;
}
+BOOL LLInvFVBridge::isLibraryItem() const
+{
+ return gInventory.isObjectDescendentOf(getUUID(),gInventory.getLibraryRootFolderID());
+}
+
/*virtual*/
/**
* @brief Adds this item into clipboard storage
@@ -286,7 +296,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;
@@ -295,11 +305,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)
@@ -312,7 +322,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)
@@ -330,7 +340,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
@@ -346,14 +356,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)
@@ -366,7 +376,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)
@@ -407,7 +417,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)
@@ -501,8 +511,10 @@ BOOL LLInvFVBridge::isClipboardPasteable() const
// Each item must be copyable to be pastable
LLItemBridge item_br(mInventoryPanel.get(), mRoot, item_id);
if (!item_br.isItemCopyable())
- return FALSE;
- }
+ {
+ return FALSE;
+ }
+ }
return TRUE;
}
@@ -879,6 +891,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();
@@ -931,7 +949,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())
{
@@ -971,7 +989,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;
}
@@ -1003,6 +1021,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)
@@ -1253,10 +1272,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();
}
}
@@ -1264,7 +1283,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)
{
@@ -1274,7 +1293,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);
}
}
}
@@ -1286,14 +1305,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
@@ -1302,6 +1337,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset
actual_asset_type,
inv_type,
inventory,
+ view_model,
root,
uuid,
flags);
@@ -1358,10 +1394,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)
@@ -1374,10 +1407,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)
@@ -1386,10 +1419,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))
@@ -1399,7 +1432,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());
}
else if ("copy_slurl" == action)
@@ -1515,6 +1548,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();
@@ -1523,26 +1565,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
{
@@ -1658,18 +1701,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())
@@ -1677,7 +1719,6 @@ BOOL LLItemBridge::removeItem()
return FALSE;
}
-
// move it to the trash
LLPreview::hide(mUUID, TRUE);
LLInventoryModel* model = getInventoryModel();
@@ -1815,6 +1856,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
@@ -1823,11 +1957,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;
};
@@ -1841,7 +1975,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;
@@ -2080,7 +2214,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();
@@ -2299,7 +2433,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);
}
}
}
@@ -2373,7 +2507,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);
}
@@ -2551,7 +2685,6 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
llwarns << "LLRightClickInventoryFetchDescendentsObserver::done with empty mCompleteFolders" << llendl;
if (clear_observer)
{
- dec_busy_count();
gInventory.removeObserver(this);
delete this;
}
@@ -2565,7 +2698,6 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
// could notify observers and throw us into an infinite loop.
if (clear_observer)
{
- dec_busy_count();
gInventory.removeObserver(this);
delete this;
}
@@ -2636,7 +2768,6 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
{
// 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(outfit);
}
*/
@@ -2655,7 +2786,6 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer)
{
// 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(categories);
}
}
@@ -2734,7 +2864,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);
@@ -2781,10 +2911,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)
@@ -2825,7 +2952,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
@@ -2921,17 +3048,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);
@@ -2995,6 +3129,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();
@@ -3012,7 +3159,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)
{
@@ -3035,7 +3182,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);
}
}
@@ -3077,7 +3224,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);
}
}
@@ -3087,6 +3235,7 @@ void LLFolderBridge::pasteFromClipboard()
llassert(viitem);
if (viitem)
{
+ //changeItemParent() implicity calls dirtyFilter
changeItemParent(model, viitem, parent_id, FALSE);
}
}
@@ -3207,7 +3356,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);
@@ -3219,33 +3368,33 @@ 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 (favorites == mUUID)
{
- mDisabledItems.push_back(std::string("New Folder"));
+ disabled_items.push_back(std::string("New Folder"));
}
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
{
@@ -3259,40 +3408,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"));
}
}
}
@@ -3321,20 +3470,43 @@ 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.
+ 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();
@@ -3361,21 +3533,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
@@ -3390,7 +3562,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)
@@ -3398,25 +3570,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"));
}
}
@@ -3425,49 +3597,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;
@@ -3557,25 +3708,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);
@@ -3641,6 +3773,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)
{
@@ -3743,9 +3893,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(
@@ -3794,7 +3945,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 &current_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false);
@@ -3911,13 +4062,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)
@@ -3929,6 +4077,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();
@@ -3946,8 +4096,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);
}
}
@@ -3979,7 +4129,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());
}
@@ -4134,13 +4284,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)
@@ -4180,10 +4327,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))
@@ -4203,13 +4350,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;
}
@@ -4218,7 +4365,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;
}
@@ -4229,15 +4376,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());
}
// +=================================================+
@@ -4339,15 +4486,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;
@@ -4565,7 +4703,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();
@@ -4581,19 +4719,16 @@ void LLCallingCardBridge::performAction(LLInventoryModel* model, std::string act
if (item && (item->getCreatorUUID() != gAgent.getID()) &&
(!item->getCreatorUUID().isNull()))
{
- std::string callingcard_name;
- gCacheName->getFullName(item->getCreatorUUID(), callingcard_name);
- // IDEVO
+ std::string callingcard_name = LLCacheName::getDefaultName();
LLAvatarName av_name;
- if (LLAvatarNameCache::useDisplayNames()
- && LLAvatarNameCache::get(item->getCreatorUUID(), &av_name))
+ if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name))
{
- callingcard_name = av_name.mDisplayName + " (" + av_name.mUsername + ")";
+ callingcard_name = av_name.getCompleteName();
}
LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID());
if (session_id != LLUUID::null)
{
- LLIMFloater::show(session_id);
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
}
}
}
@@ -5339,6 +5474,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
p.on_enable.parameter = cbparams;
LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu;
LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
+ items.push_back(p.name);
}
}
}
@@ -5360,11 +5496,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())
{
@@ -5965,16 +6100,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;
@@ -6063,14 +6188,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();
}
}
@@ -6401,9 +6527,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());
@@ -6415,42 +6540,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 118430efe1..5c6cf0f0f0 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) {}
@@ -102,9 +107,10 @@ public:
virtual BOOL isItemMovable() const;
virtual BOOL isItemInTrash() const;
virtual BOOL isLink() const;
+ virtual BOOL isLibraryItem() 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 +121,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 +129,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 +148,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 +170,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;
+ 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;
@@ -205,7 +221,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;
@@ -214,18 +229,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
@@ -233,15 +247,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();
@@ -251,7 +268,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);
@@ -259,11 +278,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,
@@ -276,20 +296,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);
@@ -309,8 +333,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);
@@ -322,10 +344,11 @@ public:
static void staticFolderOptionsMenu();
private:
- BOOL mCallingCards;
- BOOL mWearables;
- menuentry_vec_t mItems;
- menuentry_vec_t mDisabledItems;
+
+ bool mCallingCards;
+ bool mWearables;
+ bool mIsLoading;
+ LLTimer mTimeSinceRequestStart;
LLRootHandle<LLFolderBridge> mHandle;
};
@@ -355,7 +378,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*);
};
@@ -545,7 +567,6 @@ class LLMeshBridge : public LLItemBridge
public:
virtual LLUIImagePtr getIcon() const;
virtual void openItem();
- virtual void previewItem();
virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
protected:
@@ -621,7 +642,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.
@@ -630,6 +651,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..92f2d33073 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"
@@ -44,107 +44,86 @@
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 = dynamic_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 = dynamic_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 +134,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 +149,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 +165,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 +253,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con
}
}
}
-
+
return TRUE;
}
@@ -347,13 +332,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 +359,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 +380,20 @@ 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;
+ return mFilterSubString.size() ? item->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 +405,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 +423,9 @@ BOOL LLInventoryFilter::isActive() const
|| mFilterOps.mHoursAgo != 0;
}
-BOOL LLInventoryFilter::isModified() const
-{
- return mModified;
-}
-
-BOOL LLInventoryFilter::isModifiedAndClear()
+bool LLInventoryFilter::isModified() const
{
- BOOL ret = mModified;
- mModified = FALSE;
- return ret;
+ return mFilterModified != FILTER_NONE;
}
void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types)
@@ -613,9 +589,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 +611,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 +700,6 @@ void LLInventoryFilter::setShowFolderState(EFolderShow state)
}
}
-void LLInventoryFilter::setSortOrder(U32 order)
-{
- if (mOrder != order)
- {
- mOrder = order;
- setModified();
- }
-}
-
void LLInventoryFilter::markDefault()
{
mDefaultFilterOps = mFilterOps;
@@ -742,83 +711,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 +780,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 +909,6 @@ const std::string& LLInventoryFilter::getFilterText()
}
else
{
- //mFilterText += "No ";
mFilterText += LLTrans::getString("No Filters");
mFilterText += not_filtered_types;
}
@@ -985,66 +918,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 +979,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 +1020,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 +1037,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 +1053,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 +1068,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 68732024de..f1a4889f5a 100644
--- a/indra/newview/llinventoryfunctions.cpp
+++ b/indra/newview/llinventoryfunctions.cpp
@@ -45,7 +45,8 @@
// newview includes
#include "llappearancemgr.h"
#include "llappviewer.h"
-//#include "llfirstuse.h"
+#include "llclipboard.h"
+#include "lldonotdisturbnotificationstorage.h"
#include "llfloaterinventory.h"
#include "llfloatersidepanelcontainer.h"
#include "llfocusmgr.h"
@@ -74,8 +75,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"
@@ -959,7 +962,7 @@ void LLSaveFolderState::setApply(BOOL apply)
void LLSaveFolderState::doFolder(LLFolderViewFolder* folder)
{
- LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getListener();
+ LLInvFVBridge* bridge = (LLInvFVBridge*)folder->getViewModelItem();
if(!bridge) return;
if(mApply)
@@ -994,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);
}
@@ -1002,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);
}
@@ -1015,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())
@@ -1028,14 +1031,12 @@ void LLSelectFirstFilteredItem::doItem(LLFolderViewItem *item)
void LLSelectFirstFilteredItem::doFolder(LLFolderViewFolder* folder)
{
- if (folder->getFiltered() && !mItemSelected)
+ // Skip if folder or item already found, if not filtered or if no parent (root folder is not selectable)
+ if (!mFolderSelected && !mItemSelected && folder->LLFolderViewItem::passedFilter() && folder->getParentFolder())
{
folder->getRoot()->setSelection(folder, FALSE, FALSE);
- if (folder->getParentFolder())
- {
- folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
- }
- mItemSelected = TRUE;
+ folder->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP);
+ mFolderSelected = TRUE;
}
}
@@ -1055,3 +1056,113 @@ 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->getSelectedCount() > 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::removeItemFromDND(LLFolderView* root)
+{
+ if(gAgent.isDoNotDisturb())
+ {
+ //Get selected items
+ LLFolderView::selected_items_t selectedItems = root->getSelectedItems();
+ LLFolderViewModelItemInventory * viewModel = NULL;
+
+ //If user is in DND and deletes item, make sure the notification is not displayed by removing the notification
+ //from DND history and .xml file. Once this is done, upon exit of DND mode the item deleted will not show a notification.
+ for(LLFolderView::selected_items_t::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
+ {
+ viewModel = dynamic_cast<LLFolderViewModelItemInventory *>((*it)->getViewModelItem());
+
+ if(viewModel && viewModel->getUUID().notNull())
+ {
+ //Will remove the item offer notification
+ LLDoNotDisturbNotificationStorage::instance().removeNotification(LLDoNotDisturbNotificationStorage::offerName, viewModel->getUUID());
+ }
+ }
+ }
+}
+
+void LLInventoryAction::onItemsRemovalConfirmation( const LLSD& notification, const LLSD& response, LLFolderView* root )
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0)
+ {
+ //Need to remove item from DND before item is removed from root folder view
+ //because once removed from root folder view the item is no longer a selected item
+ removeItemFromDND(root);
+ root->removeSelectedItems();
+ }
+}
diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h
index 909f7fd10b..f1066a4dc9 100644
--- a/indra/newview/llinventoryfunctions.h
+++ b/indra/newview/llinventoryfunctions.h
@@ -419,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
{
@@ -443,49 +428,14 @@ 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);
+ static void removeItemFromDND(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 f7567baa2b..fabcd50c7d 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 "llfloaterimcontainer.h"
#include "llimview.h"
#include "llinventorybridge.h"
#include "llinventoryfunctions.h"
#include "llinventorymodelbackgroundfetch.h"
+#include "llpreview.h"
#include "llsidepanelinventory.h"
#include "lltrans.h"
#include "llviewerattachmenu.h"
@@ -54,8 +56,16 @@ 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;
+// statics
+bool LLInventoryPanel::sColorSetInitialized = false;
+LLUIColor LLInventoryPanel::sDefaultColor;
+LLUIColor LLInventoryPanel::sDefaultHighlightColor;
+LLUIColor LLInventoryPanel::sLibraryColor;
+LLUIColor LLInventoryPanel::sLinkColor;
+
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Class LLInventoryPanelObserver
@@ -135,76 +145,86 @@ 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
+ if (!sColorSetInitialized)
+ {
+ sDefaultColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+ sDefaultHighlightColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
+ sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE);
+ sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE);
+ sColorSetInitialized = true;
+ }
+
+ // 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());
-
- const LLFolderType::EType preferred_type = LLViewerFolderType::lookupTypeFromNewCategoryName(start_folder_name);
-
- LLUUID root_id;
-
- if ("LIBRARY" == params.start_folder())
- {
- root_id = gInventory.getLibraryRootFolderID();
- }
- else
- {
- root_id = (preferred_type != LLFolderType::FT_NONE)
- ? gInventory.findCategoryUUIDForType(preferred_type, false, false)
- : LLUUID::null;
- }
-
- if ((root_id == LLUUID::null) && !start_folder_name.empty())
- {
- llwarns << "No category found that matches start_folder: " << start_folder_name << llendl;
- root_id = LLUUID::generateNewID();
- }
-
- LLInvFVBridge* new_listener = mInvFVBridgeBuilder->createBridge(LLAssetType::AT_CATEGORY,
+ 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";
- mFolderRoot = createFolderView(new_listener, params.use_label_suffix());
+ return LLUICtrlFactory::create<LLFolderView>(p);
}
void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
+ // save off copy of params
+ mParams = params;
+ // Clear up the root view
+ // Note: This needs to be done *before* we build the new folder view
+ LLUUID root_id = getRootFolderID();
+ if (mFolderRoot)
+ {
+ removeItemID(root_id);
+ mFolderRoot->destroyView();
+ mFolderRoot = NULL;
+ }
+
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);
@@ -212,7 +232,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);
@@ -227,6 +246,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params)
{
initializeViews();
}
+
gIdleCallbacks.addFunction(onIdle, (void*)this);
if (mSortOrderSetting != INHERIT_SORT_ORDER)
@@ -239,32 +259,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);
@@ -280,82 +299,68 @@ LLInventoryPanel::~LLInventoryPanel()
void LLInventoryPanel::draw()
{
// Select the desired item (in case it wasn't loaded when the selection was requested)
- mFolderRoot->updateSelection();
-
- // Nudge the filter if the clipboard state changed
- if (mClipboardState != LLClipboard::instance().getGeneration())
- {
- mClipboardState = LLClipboard::instance().getGeneration();
- getFilter()->setModified(LLClipboard::instance().isCutMode() ? LLInventoryFilter::FILTER_MORE_RESTRICTIVE : LLInventoryFilter::FILTER_LESS_RESTRICTIVE);
- }
+ updateSelection();
LLPanel::draw();
}
-LLInventoryFilter* LLInventoryPanel::getFilter()
+const LLInventoryFilter& LLInventoryPanel::getFilter() const
{
- if (mFolderRoot)
- {
- return mFolderRoot->getFilter();
- }
- return NULL;
+ return getFolderViewModel()->getFilter();
}
-const LLInventoryFilter* LLInventoryPanel::getFilter() const
+LLInventoryFilter& LLInventoryPanel::getFilter()
{
- if (mFolderRoot)
- {
- return mFolderRoot->getFilter();
- }
- return NULL;
+ return getFolderViewModel()->getFilter();
}
void LLInventoryPanel::setFilterTypes(U64 types, LLInventoryFilter::EFilterType filter_type)
{
if (filter_type == LLInventoryFilter::FILTERTYPE_OBJECT)
- getFilter()->setFilterObjectTypes(types);
+ getFilter().setFilterObjectTypes(types);
if (filter_type == LLInventoryFilter::FILTERTYPE_CATEGORY)
- getFilter()->setFilterCategoryTypes(types);
+ getFilter().setFilterCategoryTypes(types);
}
U32 LLInventoryPanel::getFilterObjectTypes() const
{
- return mFolderRoot->getFilterObjectTypes();
+ return getFilter().getFilterObjectTypes();
}
U32 LLInventoryPanel::getFilterPermMask() const
{
- return mFolderRoot->getFilterPermissions();
+ return getFilter().getFilterPermissions();
}
void LLInventoryPanel::setFilterPermMask(PermissionMask filter_perm_mask)
{
- getFilter()->setFilterPermissions(filter_perm_mask);
+ getFilter().setFilterPermissions(filter_perm_mask);
}
void LLInventoryPanel::setFilterWearableTypes(U64 types)
{
- getFilter()->setFilterWearableTypes(types);
+ getFilter().setFilterWearableTypes(types);
}
void LLInventoryPanel::setFilterSubString(const std::string& string)
{
- getFilter()->setFilterSubString(string);
+ getFilter().setFilterSubString(string);
}
const std::string LLInventoryPanel::getFilterSubString()
{
- return mFolderRoot->getFilterSubString();
+ return getFilter().getFilterSubString();
}
void LLInventoryPanel::setSortOrder(U32 order)
{
- getFilter()->setSortOrder(order);
- if (getFilter()->isModified())
+ LLInventorySort sorter(order);
+ if (order != getFolderViewModel()->getSorter().getSortOrder())
{
- mFolderRoot->setSortOrder(order);
+ getFolderViewModel()->setSorter(sorter);
+ mFolderRoot->arrangeAll();
// try to keep selection onscreen, even if it wasn't to start with
mFolderRoot->scrollToShowSelection();
}
@@ -363,37 +368,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)
@@ -417,11 +417,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
@@ -432,7 +441,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();
@@ -448,11 +457,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);
}
@@ -474,7 +487,7 @@ void LLInventoryPanel::modelChanged(U32 mask)
{
if (view_folder)
{
- view_folder->requestSort();
+ view_folder->getViewModelItem()->requestSort();
}
}
@@ -509,20 +522,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();
@@ -534,9 +551,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();
}
}
@@ -548,6 +566,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)
@@ -567,16 +622,73 @@ 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
+ {
+ // Default case: always add "My Inventory" first, "Library" second
+ buildNewViews(gInventory.getRootFolderID()); // My Inventory
+ buildNewViews(gInventory.getLibraryRootFolderID()); // Library
+ }
+
+ gIdleCallbacks.addFunction(idle, this);
mViewsInitialized = true;
@@ -586,14 +698,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);
@@ -601,79 +713,35 @@ 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;
+ params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
+ params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
+
return LLUICtrlFactory::create<LLFolderViewFolder>(params);
}
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;
params.rect = LLRect (0, 0, 0, 0);
params.tool_tip = params.name;
+
+ params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor));
+ params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor));
return LLUICtrlFactory::create<LLFolderViewItem>(params);
}
@@ -681,20 +749,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)
@@ -712,16 +775,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
@@ -732,28 +791,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)
+ if (folder_view_item)
{
- itemp->addToFolder(parent_folder, mFolderRoot);
- }
+ 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;
@@ -770,7 +829,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();
@@ -783,7 +842,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.
@@ -794,8 +853,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;
@@ -812,7 +871,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();
}
@@ -916,7 +975,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)
@@ -929,7 +988,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)
@@ -938,7 +997,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())
@@ -958,40 +1017,34 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it
}
}
-void LLInventoryPanel::doToSelected(const LLSD& userdata)
-{
- mFolderRoot->doToSelected(&gInventory, userdata);
-}
-
void LLInventoryPanel::doCreate(const LLSD& userdata)
{
reset_inventory_filter();
- menu_create_inventory_item(mFolderRoot, LLFolderBridge::sSelf.get(), userdata);
+ menu_create_inventory_item(this, LLFolderBridge::sSelf.get(), userdata);
}
bool LLInventoryPanel::beginIMSession()
{
- std::set<LLUUID> selected_items = mFolderRoot->getSelectionList();
+ std::set<LLFolderViewItem*> selected_items = mFolderRoot->getSelectionList();
std::string name;
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;
@@ -1025,9 +1078,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)
{
@@ -1059,7 +1110,7 @@ bool LLInventoryPanel::beginIMSession()
LLUUID session_id = gIMMgr->addSession(name, type, members[0], members);
if (session_id != LLUUID::null)
{
- LLIMFloater::show(session_id);
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
}
return true;
@@ -1068,13 +1119,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.
@@ -1087,7 +1138,7 @@ bool LLInventoryPanel::attachObject(const LLSD& userdata)
BOOL LLInventoryPanel::getSinceLogoff()
{
- return getFilter()->isSinceLogoff();
+ return getFilter().isSinceLogoff();
}
// DEBUG ONLY
@@ -1213,14 +1264,150 @@ 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;
+}
+
+BOOL LLInventoryPanel::handleKeyHere( KEY key, MASK mask )
+{
+ BOOL handled = FALSE;
+ switch (key)
+ {
+ case KEY_RETURN:
+ // Open selected items if enter key hit on the inventory panel
+ if (mask == MASK_NONE)
+ {
+ LLInventoryAction::doToSelected(mInventory, mFolderRoot, "open");
+ handled = TRUE;
+ }
+ break;
+ case KEY_DELETE:
+ case KEY_BACKSPACE:
+ // Delete selected items if delete or backspace key hit on the inventory panel
+ // Note: on Mac laptop keyboards, backspace and delete are one and the same
+ if (isSelectionRemovable() && (mask == MASK_NONE))
+ {
+ LLInventoryAction::doToSelected(mInventory, mFolderRoot, "delete");
+ handled = TRUE;
+ }
+ break;
+ }
+ return handled;
+}
+
+bool LLInventoryPanel::isSelectionRemovable()
+{
+ bool can_delete = false;
+ if (mFolderRoot)
+ {
+ std::set<LLFolderViewItem*> selection_set = mFolderRoot->getSelectionList();
+ if (!selection_set.empty())
+ {
+ can_delete = true;
+ for (std::set<LLFolderViewItem*>::iterator iter = selection_set.begin();
+ iter != selection_set.end();
+ ++iter)
+ {
+ LLFolderViewItem *item = *iter;
+ const LLFolderViewModelItemInventory *listener = static_cast<const LLFolderViewModelItemInventory*>(item->getViewModelItem());
+ if (!listener)
+ {
+ can_delete = false;
+ }
+ else
+ {
+ can_delete &= listener->isItemRemovable() && !listener->isItemInTrash();
+ }
+ }
+ }
+ }
+ return can_delete;
+}
/************************************************************************/
/* Recent Inventory Panel related class */
@@ -1239,7 +1426,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:
@@ -1254,3 +1441,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..00a90325ad 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,27 +112,38 @@ 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();
+ /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask );
BOOL handleHover(S32 x, S32 y, MASK mask);
BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
EDragAndDropType cargo_type,
@@ -137,8 +162,9 @@ 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;
+ bool isSelectionRemovable();
+ 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 +182,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 +194,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 +210,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 +260,6 @@ public:
void setSortOrder(U32 order);
U32 getSortOrder() const;
- void requestSort();
private:
std::string mSortOrderSetting;
@@ -231,26 +272,27 @@ 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);
+ // Specific inventory colors
+ static bool sColorSetInitialized;
+ static LLUIColor sDefaultColor;
+ static LLUIColor sDefaultHighlightColor;
+ static LLUIColor sLibraryColor;
+ static LLUIColor sLinkColor;
+
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..2d7454b636 100644
--- a/indra/newview/lllogchat.cpp
+++ b/indra/newview/lllogchat.cpp
@@ -28,11 +28,13 @@
#include "llagent.h"
#include "llagentui.h"
+#include "llavatarnamecache.h"
#include "lllogchat.h"
#include "lltrans.h"
#include "llviewercontrol.h"
#include "lldiriterator.h"
+#include "llfloaterimsessiontab.h"
#include "llinstantmessage.h"
#include "llsingleton.h" // for LLSingleton
@@ -40,6 +42,7 @@
#include <boost/algorithm/string/replace.hpp>
#include <boost/regex.hpp>
#include <boost/regex/v4/match_results.hpp>
+#include <boost/foreach.hpp>
#if LL_MSVC
#pragma warning(push)
@@ -58,10 +61,11 @@
const S32 LOG_RECALL_SIZE = 2048;
-const std::string IM_TIME("time");
-const std::string IM_TEXT("message");
-const std::string IM_FROM("from");
-const std::string IM_FROM_ID("from_id");
+const std::string LL_IM_TIME("time");
+const std::string LL_IM_TEXT("message");
+const std::string LL_IM_FROM("from");
+const std::string LL_IM_FROM_ID("from_id");
+const std::string LL_TRANSCRIPT_FILE_EXTENSION("txt");
const static std::string IM_SEPARATOR(": ");
const static std::string NEW_LINE("\n");
@@ -84,6 +88,7 @@ const static std::string MULTI_LINE_PREFIX(" ");
* Note: "You" was used as an avatar names in viewers of previous versions
*/
const static boost::regex TIMESTAMP_AND_STUFF("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}\\]\\s+)?(.*)$");
+const static boost::regex TIMESTAMP("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}\\]|\\[\\d{1,2}:\\d{2}\\]).*");
/**
* Regular expression suitable to match names like
@@ -116,6 +121,15 @@ const static int IDX_TEXT = 3;
using namespace boost::posix_time;
using namespace boost::gregorian;
+void append_to_last_message(std::list<LLSD>& messages, const std::string& line)
+{
+ if (!messages.size()) return;
+
+ std::string im_text = messages.back()[LL_IM_TEXT].asString();
+ im_text.append(line);
+ messages.back()[LL_IM_TEXT] = im_text;
+}
+
class LLLogChatTimeScanner: public LLSingleton<LLLogChatTimeScanner>
{
public:
@@ -191,15 +205,17 @@ private:
std::stringstream mTimeStream;
};
+LLLogChat::save_history_signal_t * LLLogChat::sSaveHistorySignal = NULL;
+
//static
std::string LLLogChat::makeLogFileName(std::string filename)
{
/**
- * Testing for in bound and out bound ad-hoc file names
- * if it is then skip date stamping.
- **/
- //LL_INFOS("") << "Befor:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */
- boost::match_results<std::string::const_iterator> matches;
+ * Testing for in bound and out bound ad-hoc file names
+ * if it is then skip date stamping.
+ **/
+
+ boost::match_results<std::string::const_iterator> matches;
bool inboundConf = boost::regex_match(filename, matches, INBOUND_CONFERENCE);
bool outboundConf = boost::regex_match(filename, matches, OUTBOUND_CONFERENCE);
if (!(inboundConf || outboundConf))
@@ -220,17 +236,17 @@ std::string LLLogChat::makeLogFileName(std::string filename)
filename += dbuffer;
}
}
- //LL_INFOS("") << "After:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */
+
filename = cleanFileName(filename);
- filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename);
- filename += ".txt";
- //LL_INFOS("") << "Full:" << filename << LL_ENDL;/* uncomment if you want to verify step, delete on commit */
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS, filename);
+ filename += '.' + LL_TRANSCRIPT_FILE_EXTENSION;
+
return filename;
}
std::string LLLogChat::cleanFileName(std::string filename)
{
- std::string invalidChars = "\"\'\\/?*:.<>|[]{}~"; // Cannot match glob or illegal filename chars
+ std::string invalidChars = "\"\'\\/?*:.<>|[]{}~"; // Cannot match glob or illegal filename chars
std::string::size_type position = filename.find_first_of(invalidChars);
while (position != filename.npos)
{
@@ -242,27 +258,24 @@ std::string LLLogChat::cleanFileName(std::string filename)
std::string LLLogChat::timestamp(bool withdate)
{
- time_t utc_time;
- utc_time = time_corrected();
-
std::string timeStr;
- LLSD substitution;
- substitution["datetime"] = (S32) utc_time;
-
if (withdate)
{
- timeStr = "["+LLTrans::getString ("TimeYear")+"]/["
- +LLTrans::getString ("TimeMonth")+"]/["
- +LLTrans::getString ("TimeDay")+"] ["
- +LLTrans::getString ("TimeHour")+"]:["
- +LLTrans::getString ("TimeMin")+"]";
+ timeStr = "[" + LLTrans::getString ("TimeYear") + "]/["
+ + LLTrans::getString ("TimeMonth") + "]/["
+ + LLTrans::getString ("TimeDay") + "] ["
+ + LLTrans::getString ("TimeHour") + "]:["
+ + LLTrans::getString ("TimeMin") + "]";
}
else
{
timeStr = "[" + LLTrans::getString("TimeHour") + "]:["
- + LLTrans::getString ("TimeMin")+"]";
+ + LLTrans::getString ("TimeMin")+"]";
}
+ LLSD substitution;
+ substitution["datetime"] = (S32)time_corrected();
+
LLStringUtil::format (timeStr, substitution);
return timeStr;
}
@@ -270,9 +283,9 @@ std::string LLLogChat::timestamp(bool withdate)
//static
void LLLogChat::saveHistory(const std::string& filename,
- const std::string& from,
- const LLUUID& from_id,
- const std::string& line)
+ const std::string& from,
+ const LLUUID& from_id,
+ const std::string& line)
{
std::string tmp_filename = filename;
LLStringUtil::trim(tmp_filename);
@@ -312,108 +325,41 @@ void LLLogChat::saveHistory(const std::string& filename,
file << LLChatLogFormatter(item) << std::endl;
file.close();
-}
-void LLLogChat::loadHistory(const std::string& filename, void (*callback)(ELogLineType, const LLSD&, void*), void* userdata)
-{
- if(!filename.size())
- {
- llwarns << "Filename is Empty!" << llendl;
- return ;
- }
-
- LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/
- if (!fptr)
- {
- callback(LOG_EMPTY, LLSD(), userdata);
- return; //No previous conversation with this name.
- }
- else
+ if (NULL != sSaveHistorySignal)
{
- char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
- char *bptr;
- S32 len;
- bool firstline=TRUE;
-
- if ( fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END) )
- { //File is smaller than recall size. Get it all.
- firstline = FALSE;
- if ( fseek(fptr, 0, SEEK_SET) )
- {
- fclose(fptr);
- return;
- }
- }
-
- while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) )
- {
- len = strlen(buffer) - 1; /*Flawfinder: ignore*/
- for ( bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
-
- if (!firstline)
- {
- LLSD item;
- std::string line(buffer);
- std::istringstream iss(line);
-
- if (!LLChatLogParser::parse(line, item))
- {
- item["message"] = line;
- callback(LOG_LINE, item, userdata);
- }
- else
- {
- callback(LOG_LLSD, item, userdata);
- }
- }
- else
- {
- firstline = FALSE;
- }
- }
- callback(LOG_END, LLSD(), userdata);
-
- fclose(fptr);
+ (*sSaveHistorySignal)();
}
}
-void append_to_last_message(std::list<LLSD>& messages, const std::string& line)
-{
- if (!messages.size()) return;
-
- std::string im_text = messages.back()[IM_TEXT].asString();
- im_text.append(line);
- messages.back()[IM_TEXT] = im_text;
-}
-
// 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 ;
}
- //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 */
+
+ bool load_all_history = load_params.has("load_all_history") ? load_params["load_all_history"].asBoolean() : false;
+
LLFILE* fptr = LLFile::fopen(makeLogFileName(file_name), "r");/*Flawfinder: ignore*/
if (!fptr)
- {
+ {
fptr = LLFile::fopen(oldLogFileName(file_name), "r");/*Flawfinder: ignore*/
- if (!fptr)
- {
- if (!fptr) return; //No previous conversation with this name.
- }
+ if (!fptr)
+ {
+ return; //No previous conversation with this name.
+ }
}
- //LL_INFOS("") << "Reading:" << file_name << LL_ENDL;
char buffer[LOG_RECALL_SIZE]; /*Flawfinder: ignore*/
char *bptr;
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,9 +395,9 @@ 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;
+ item[LL_IM_TEXT] = line;
}
messages.push_back(item);
}
@@ -459,9 +405,259 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list<LLSD>& me
fclose(fptr);
}
+// static
+std::string LLLogChat::oldLogFileName(std::string filename)
+{
+ // get Users log directory
+ std::string directory = gDirUtilp->getPerAccountChatLogsDir();
+
+ // add final OS dependent delimiter
+ directory += gDirUtilp->getDirDelimiter();
+
+ // lest make sure the file name has no invalid characters before making the pattern
+ filename = cleanFileName(filename);
+
+ // create search pattern
+ std::string pattern = filename + ( filename == "chat" ? "-???\?-?\?-??.txt" : "-???\?-??.txt");
+
+ std::vector<std::string> allfiles;
+ LLDirIterator iter(directory, pattern);
+ std::string scanResult;
+
+ while (iter.next(scanResult))
+ {
+ allfiles.push_back(scanResult);
+ }
+
+ if (allfiles.size() == 0) // if no result from date search, return generic filename
+ {
+ scanResult = directory + filename + '.' + LL_TRANSCRIPT_FILE_EXTENSION;
+ }
+ else
+ {
+ sort(allfiles.begin(), allfiles.end());
+ scanResult = directory + allfiles.back();
+ // this file is now the most recent version of the file.
+ }
+
+ return scanResult;
+}
+
+// static
+void LLLogChat::findTranscriptFiles(std::string pattern, std::vector<std::string>& list_of_transcriptions)
+{
+ // get Users log directory
+ std::string dirname = gDirUtilp->getPerAccountChatLogsDir();
+
+ // add final OS dependent delimiter
+ dirname += gDirUtilp->getDirDelimiter();
+
+ LLDirIterator iter(dirname, pattern);
+ std::string filename;
+ while (iter.next(filename))
+ {
+ std::string fullname = gDirUtilp->add(dirname, filename);
+
+ LLFILE * filep = LLFile::fopen(fullname, "rb");
+ if (NULL != filep)
+ {
+ char buffer[LOG_RECALL_SIZE];
+
+ fseek(filep, 0, SEEK_END); // seek to end of file
+ S32 bytes_to_read = ftell(filep); // get current file pointer
+ fseek(filep, 0, SEEK_SET); // seek back to beginning of file
+
+ // limit the number characters to read from file
+ if (bytes_to_read >= LOG_RECALL_SIZE)
+ {
+ bytes_to_read = LOG_RECALL_SIZE - 1;
+ }
+
+ if (bytes_to_read > 0 && NULL != fgets(buffer, bytes_to_read, filep))
+ {
+ //matching a timestamp
+ boost::match_results<std::string::const_iterator> matches;
+ if (boost::regex_match(std::string(buffer), matches, TIMESTAMP))
+ {
+ list_of_transcriptions.push_back(gDirUtilp->add(dirname, filename));
+ }
+ }
+ LLFile::close(filep);
+ }
+ }
+}
+
+// static
+void LLLogChat::getListOfTranscriptFiles(std::vector<std::string>& list_of_transcriptions)
+{
+ // create search pattern
+ std::string pattern = "*." + LL_TRANSCRIPT_FILE_EXTENSION;
+ findTranscriptFiles(pattern, list_of_transcriptions);
+}
+
+// static
+void LLLogChat::getListOfTranscriptBackupFiles(std::vector<std::string>& list_of_transcriptions)
+{
+ // create search pattern
+ std::string pattern = "*." + LL_TRANSCRIPT_FILE_EXTENSION + ".backup*";
+ findTranscriptFiles(pattern, list_of_transcriptions);
+}
+
+//static
+boost::signals2::connection LLLogChat::setSaveHistorySignal(const save_history_signal_t::slot_type& cb)
+{
+ if (NULL == sSaveHistorySignal)
+ {
+ sSaveHistorySignal = new save_history_signal_t();
+ }
+
+ return sSaveHistorySignal->connect(cb);
+}
+
+//static
+bool LLLogChat::moveTranscripts(const std::string originDirectory,
+ const std::string targetDirectory,
+ std::vector<std::string>& listOfFilesToMove,
+ std::vector<std::string>& listOfFilesMoved)
+{
+ std::string newFullPath;
+ bool movedAllTranscripts = true;
+ std::string backupFileName;
+ unsigned backupFileCount;
+
+ BOOST_FOREACH(const std::string& fullpath, listOfFilesToMove)
+ {
+ backupFileCount = 0;
+ newFullPath = targetDirectory + fullpath.substr(originDirectory.length(), std::string::npos);
+
+ //The target directory contains that file already, so lets store it
+ if(LLFile::isfile(newFullPath))
+ {
+ backupFileName = newFullPath + ".backup";
+
+ //If needed store backup file as .backup1 etc.
+ while(LLFile::isfile(backupFileName))
+ {
+ ++backupFileCount;
+ backupFileName = newFullPath + ".backup" + boost::lexical_cast<std::string>(backupFileCount);
+ }
+
+ //Rename the file to its backup name so it is not overwritten
+ LLFile::rename(newFullPath, backupFileName);
+ }
+
+ S32 retry_count = 0;
+ while (retry_count < 5)
+ {
+ //success is zero
+ if (LLFile::rename(fullpath, newFullPath) != 0)
+ {
+ retry_count++;
+ S32 result = errno;
+ LL_WARNS("LLLogChat::moveTranscripts") << "Problem renaming " << fullpath << " - errorcode: "
+ << result << " attempt " << retry_count << LL_ENDL;
+
+ ms_sleep(100);
+ }
+ else
+ {
+ listOfFilesMoved.push_back(newFullPath);
+
+ if (retry_count)
+ {
+ LL_WARNS("LLLogChat::moveTranscripts") << "Successfully renamed " << fullpath << LL_ENDL;
+ }
+ break;
+ }
+ }
+ }
+
+ if(listOfFilesMoved.size() != listOfFilesToMove.size())
+ {
+ movedAllTranscripts = false;
+ }
+
+ return movedAllTranscripts;
+}
+
+//static
+bool LLLogChat::moveTranscripts(const std::string currentDirectory,
+ const std::string newDirectory,
+ std::vector<std::string>& listOfFilesToMove)
+{
+ std::vector<std::string> listOfFilesMoved;
+ return moveTranscripts(currentDirectory, newDirectory, listOfFilesToMove, listOfFilesMoved);
+}
+
+//static
+void LLLogChat::deleteTranscripts()
+{
+ std::vector<std::string> list_of_transcriptions;
+ getListOfTranscriptFiles(list_of_transcriptions);
+ getListOfTranscriptBackupFiles(list_of_transcriptions);
+
+ BOOST_FOREACH(const std::string& fullpath, list_of_transcriptions)
+ {
+ S32 retry_count = 0;
+ while (retry_count < 5)
+ {
+ if (0 != LLFile::remove(fullpath))
+ {
+ retry_count++;
+ S32 result = errno;
+ LL_WARNS("LLLogChat::deleteTranscripts") << "Problem removing " << fullpath << " - errorcode: "
+ << result << " attempt " << retry_count << LL_ENDL;
+
+ if(retry_count >= 5)
+ {
+ LL_WARNS("LLLogChat::deleteTranscripts") << "Failed to remove " << fullpath << LL_ENDL;
+ return;
+ }
+
+ ms_sleep(100);
+ }
+ else
+ {
+ if (retry_count)
+ {
+ LL_WARNS("LLLogChat::deleteTranscripts") << "Successfully removed " << fullpath << LL_ENDL;
+ }
+ break;
+ }
+ }
+ }
+
+ LLFloaterIMSessionTab::processChatHistoryStyleUpdate(true);
+}
+
+// static
+bool LLLogChat::isTranscriptExist(const LLUUID& avatar_id)
+{
+ std::vector<std::string> list_of_transcriptions;
+ LLLogChat::getListOfTranscriptFiles(list_of_transcriptions);
+
+ if (list_of_transcriptions.size() > 0)
+ {
+ LLAvatarName avatar_name;
+ LLAvatarNameCache::get(avatar_id, &avatar_name);
+ std::string avatar_user_name = avatar_name.getAccountName();
+ std::replace(avatar_user_name.begin(), avatar_user_name.end(), '.', '_');
+
+ BOOST_FOREACH(std::string& transcript_file_name, list_of_transcriptions)
+ {
+ if (std::string::npos != transcript_file_name.find(avatar_user_name))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
//which are more strict by its nature (only firstname and secondname)
-//Example, an object's name can be writen like "Object <actual_object's_name>"
+//Example, an object's name can be written like "Object <actual_object's_name>"
void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
{
if (!im.isMap())
@@ -470,19 +666,19 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
return;
}
- if (im[IM_TIME].isDefined())
+ if (im[LL_IM_TIME].isDefined())
{
- std::string timestamp = im[IM_TIME].asString();
+ std::string timestamp = im[LL_IM_TIME].asString();
boost::trim(timestamp);
ostr << '[' << timestamp << ']' << TWO_SPACES;
}
//*TODO mark object's names in a special way so that they will be distinguishable form avatar name
//which are more strict by its nature (only firstname and secondname)
- //Example, an object's name can be writen like "Object <actual_object's_name>"
- if (im[IM_FROM].isDefined())
+ //Example, an object's name can be written like "Object <actual_object's_name>"
+ if (im[LL_IM_FROM].isDefined())
{
- std::string from = im[IM_FROM].asString();
+ std::string from = im[LL_IM_FROM].asString();
boost::trim(from);
if (from.size())
{
@@ -490,9 +686,9 @@ void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const
}
}
- if (im[IM_TEXT].isDefined())
+ if (im[LL_IM_TEXT].isDefined())
{
- std::string im_text = im[IM_TEXT].asString();
+ std::string im_text = im[LL_IM_TEXT].asString();
//multilined text will be saved with prepended spaces
boost::replace_all(im_text, NEW_LINE, NEW_LINE_SPACE_PREFIX);
@@ -500,10 +696,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,13 +715,18 @@ 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);
- im[IM_TIME] = timestamp;
+
+ if (cut_off_todays_date)
+ {
+ LLLogChatTimeScanner::instance().checkAndCutOffDate(timestamp);
+ }
+
+ im[LL_IM_TIME] = timestamp;
}
else
{
//timestamp is optional
- im[IM_TIME] = "";
+ im[LL_IM_TIME] = "";
}
bool has_stuff = matches[IDX_STUFF].matched;
@@ -550,8 +752,8 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im)
if (!has_name || name == SYSTEM_FROM)
{
//name is optional too
- im[IM_FROM] = SYSTEM_FROM;
- im[IM_FROM_ID] = LLUUID::null;
+ im[LL_IM_FROM] = SYSTEM_FROM;
+ im[LL_IM_FROM_ID] = LLUUID::null;
}
//possibly a case of complex object names consisting of 3+ words
@@ -560,8 +762,8 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im)
U32 divider_pos = stuff.find(NAME_TEXT_DIVIDER);
if (divider_pos != std::string::npos && divider_pos < (stuff.length() - NAME_TEXT_DIVIDER.length()))
{
- im[IM_FROM] = stuff.substr(0, divider_pos);
- im[IM_TEXT] = stuff.substr(divider_pos + NAME_TEXT_DIVIDER.length());
+ im[LL_IM_FROM] = stuff.substr(0, divider_pos);
+ im[LL_IM_TEXT] = stuff.substr(divider_pos + NAME_TEXT_DIVIDER.length());
return true;
}
}
@@ -569,7 +771,7 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im)
if (!has_name)
{
//text is mandatory
- im[IM_TEXT] = stuff;
+ im[LL_IM_TEXT] = stuff;
return true; //parse as a message from Second Life
}
@@ -581,45 +783,15 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im)
{
std::string agent_name;
LLAgentUI::buildFullname(agent_name);
- im[IM_FROM] = agent_name;
- im[IM_FROM_ID] = gAgentID;
+ im[LL_IM_FROM] = agent_name;
+ im[LL_IM_FROM_ID] = gAgentID;
}
else
{
- im[IM_FROM] = name;
+ im[LL_IM_FROM] = name;
}
- im[IM_TEXT] = name_and_text[IDX_TEXT];
+ im[LL_IM_TEXT] = name_and_text[IDX_TEXT];
return true; //parsed name and message text, maybe have a timestamp too
}
-std::string LLLogChat::oldLogFileName(std::string filename)
-{
- std::string scanResult;
- std::string directory = gDirUtilp->getPerAccountChatLogsDir();/* get Users log directory */
- directory += gDirUtilp->getDirDelimiter();/* add final OS dependent delimiter */
- filename=cleanFileName(filename);/* lest make shure the file name has no invalad charecters befor making the pattern */
- std::string pattern = (filename+(( filename == "chat" ) ? "-???\?-?\?-??.txt" : "-???\?-??.txt"));/* create search pattern*/
- //LL_INFOS("") << "Checking:" << directory << " for " << pattern << LL_ENDL;/* uncomment if you want to verify step, delete on commit */
- std::vector<std::string> allfiles;
-
- LLDirIterator iter(directory, pattern);
- while (iter.next(scanResult))
- {
- //LL_INFOS("") << "Found :" << scanResult << LL_ENDL;
- allfiles.push_back(scanResult);
- }
-
- if (allfiles.size() == 0) // if no result from date search, return generic filename
- {
- scanResult = directory + filename + ".txt";
- }
- else
- {
- std::sort(allfiles.begin(), allfiles.end());
- scanResult = directory + allfiles.back();
- // thisfile is now the most recent version of the file.
- }
- //LL_INFOS("") << "Reading:" << scanResult << LL_ENDL;/* uncomment if you want to verify step, delete on commit */
- return scanResult;
-}
diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h
index 27752452c9..e819f00dd9 100644
--- a/indra/newview/lllogchat.h
+++ b/indra/newview/lllogchat.h
@@ -49,15 +49,29 @@ public:
const std::string& from,
const LLUUID& from_id,
const std::string& line);
+ static void findTranscriptFiles(std::string pattern, std::vector<std::string>& list_of_transcriptions);
+ static void getListOfTranscriptFiles(std::vector<std::string>& list);
+ static void getListOfTranscriptBackupFiles(std::vector<std::string>& list_of_transcriptions);
- /** @deprecated @see loadAllHistory() */
- static void loadHistory(const std::string& filename,
- void (*callback)(ELogLineType, const LLSD&, void*),
- void* userdata);
+ static void loadChatHistory(const std::string& file_name, std::list<LLSD>& messages, const LLSD& load_params = LLSD());
+
+ typedef boost::signals2::signal<void ()> save_history_signal_t;
+ static boost::signals2::connection setSaveHistorySignal(const save_history_signal_t::slot_type& cb);
+
+ static bool moveTranscripts(const std::string currentDirectory,
+ const std::string newDirectory,
+ std::vector<std::string>& listOfFilesToMove,
+ std::vector<std::string>& listOfFilesMoved);
+ static bool moveTranscripts(const std::string currentDirectory,
+ const std::string newDirectory,
+ std::vector<std::string>& listOfFilesToMove);
+
+ static void deleteTranscripts();
+ static bool isTranscriptExist(const LLUUID& avatar_id);
- static void loadAllHistory(const std::string& file_name, std::list<LLSD>& messages);
private:
static std::string cleanFileName(std::string filename);
+ static save_history_signal_t * sSaveHistorySignal;
};
/**
@@ -105,7 +119,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();
@@ -113,9 +127,10 @@ protected:
};
// LLSD map lookup constants
-extern const std::string IM_TIME; //("time");
-extern const std::string IM_TEXT; //("message");
-extern const std::string IM_FROM; //("from");
-extern const std::string IM_FROM_ID; //("from_id");
+extern const std::string LL_IM_TIME; //("time");
+extern const std::string LL_IM_TEXT; //("message");
+extern const std::string LL_IM_FROM; //("from");
+extern const std::string LL_IM_FROM_ID; //("from_id");
+extern const std::string LL_TRANSCRIPT_FILE_EXTENSION; //("txt");
#endif
diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp
index 1f1f89349a..7ddd04fed0 100644
--- a/indra/newview/llnamelistctrl.cpp
+++ b/indra/newview/llnamelistctrl.cpp
@@ -64,7 +64,8 @@ LLNameListCtrl::LLNameListCtrl(const LLNameListCtrl::Params& p)
mNameColumnIndex(p.name_column.column_index),
mNameColumn(p.name_column.column_name),
mAllowCallingCardDrop(p.allow_calling_card_drop),
- mShortNames(p.short_names)
+ mShortNames(p.short_names),
+ mAvatarNameCacheConnection()
{}
// public
@@ -320,16 +321,20 @@ LLScrollListItem* LLNameListCtrl::addNameItemRow(
else if (LLAvatarNameCache::get(id, &av_name))
{
if (mShortNames)
- fullname = av_name.mDisplayName;
+ fullname = av_name.getDisplayName();
else
fullname = av_name.getCompleteName();
}
else
{
// ...schedule a callback
- LLAvatarNameCache::get(id,
- boost::bind(&LLNameListCtrl::onAvatarNameCache,
- this, _1, _2, item->getHandle()));
+ // This is not correct and will likely lead to partially populated lists in cases where avatar names are not cached.
+ // *TODO : Change this to have 2 callbacks : one callback per list item and one for the whole list.
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(id,boost::bind(&LLNameListCtrl::onAvatarNameCache,this, _1, _2, item->getHandle()));
}
break;
}
@@ -388,9 +393,11 @@ void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id,
const LLAvatarName& av_name,
LLHandle<LLNameListItem> item)
{
+ mAvatarNameCacheConnection.disconnect();
+
std::string name;
if (mShortNames)
- name = av_name.mDisplayName;
+ name = av_name.getDisplayName();
else
name = av_name.getCompleteName();
diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h
index 09c3d49fe7..5aa1e1c458 100644
--- a/indra/newview/llnamelistctrl.h
+++ b/indra/newview/llnamelistctrl.h
@@ -111,6 +111,13 @@ public:
protected:
LLNameListCtrl(const Params&);
+ virtual ~LLNameListCtrl()
+ {
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ }
friend class LLUICtrlFactory;
public:
// Add a user to the list by name. It will be added, the name
@@ -154,6 +161,7 @@ private:
std::string mNameColumn;
BOOL mAllowCallingCardDrop;
bool mShortNames; // display name only, no SLID
+ boost::signals2::connection mAvatarNameCacheConnection;
};
diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp
deleted file mode 100644
index a7303ad035..0000000000
--- a/indra/newview/llnearbychat.cpp
+++ /dev/null
@@ -1,338 +0,0 @@
-/**
- * @file LLNearbyChat.cpp
- * @brief Nearby chat history scrolling panel implementation
- *
- * $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 "llnearbychat.h"
-#include "llviewercontrol.h"
-#include "llviewerwindow.h"
-#include "llrootview.h"
-//#include "llchatitemscontainerctrl.h"
-#include "lliconctrl.h"
-#include "llfloatersidepanelcontainer.h"
-#include "llfocusmgr.h"
-#include "lllogchat.h"
-#include "llresizebar.h"
-#include "llresizehandle.h"
-#include "llmenugl.h"
-#include "llviewermenu.h"//for gMenuHolder
-
-#include "llnearbychathandler.h"
-#include "llchannelmanager.h"
-
-#include "llagent.h" // gAgent
-#include "llchathistory.h"
-#include "llstylemap.h"
-
-#include "llavatarnamecache.h"
-
-#include "lldraghandle.h"
-
-#include "llnearbychatbar.h"
-#include "llfloaterreg.h"
-#include "lltrans.h"
-
-static const S32 RESIZE_BAR_THICKNESS = 3;
-
-
-static LLRegisterPanelClassWrapper<LLNearbyChat> t_panel_nearby_chat("panel_nearby_chat");
-
-LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p)
-: LLPanel(p),
- mChatHistory(NULL)
-{
-}
-
-BOOL LLNearbyChat::postBuild()
-{
- //menu
- LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
- LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
-
- enable_registrar.add("NearbyChat.Check", boost::bind(&LLNearbyChat::onNearbyChatCheckContextMenuItem, this, _2));
- registrar.add("NearbyChat.Action", boost::bind(&LLNearbyChat::onNearbyChatContextMenuItemClicked, this, _2));
-
-
- LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_nearby_chat.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
- if(menu)
- mPopupMenuHandle = menu->getHandle();
-
- gSavedSettings.declareS32("nearbychat_showicons_and_names",2,"NearByChat header settings",true);
-
- mChatHistory = getChild<LLChatHistory>("chat_history");
-
- if(!LLPanel::postBuild())
- return false;
-
- return true;
-}
-
-std::string 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 LLNearbyChat::addMessage(const LLChat& chat,bool archive,const LLSD &args)
-{
- LLChat& tmp_chat = const_cast<LLChat&>(chat);
-
- if(tmp_chat.mTimeStr.empty())
- tmp_chat.mTimeStr = appendTime();
-
- bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory");
-
- if (!chat.mMuted)
- {
- tmp_chat.mFromName = chat.mFromName;
- LLSD chat_args = args;
- chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history;
- mChatHistory->appendMessage(chat, chat_args);
- }
-
- if(archive)
- {
- mMessageArchive.push_back(chat);
- if(mMessageArchive.size()>200)
- mMessageArchive.erase(mMessageArchive.begin());
- }
-
- if (args["do_not_log"].asBoolean())
- {
- return;
- }
-
- 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);
-
- if (!av_name.mIsDisplayNameDefault)
- {
- from_name = av_name.getCompleteName();
- }
- }
-
- LLLogChat::saveHistory("chat", from_name, chat.mFromID, chat.mText);
- }
-}
-
-void LLNearbyChat::onNearbySpeakers()
-{
- LLSD param;
- param["people_panel_tab_name"] = "nearby_panel";
- 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();
- 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)
-{
- if(visible)
- {
- removeScreenChat();
- }
-
- LLPanel::setVisible(visible);
-}
-
-
-void LLNearbyChat::getAllowedRect(LLRect& rect)
-{
- rect = gViewerWindow->getWorldViewRectScaled();
-}
-
-void LLNearbyChat::updateChatHistoryStyle()
-{
- mChatHistory->clear();
-
- LLSD do_not_log;
- do_not_log["do_not_log"] = true;
- for(std::vector<LLChat>::iterator it = mMessageArchive.begin();it!=mMessageArchive.end();++it)
- {
- // Update the messages without re-writing them to a log file.
- addMessage(*it,false, do_not_log);
- }
-}
-
-//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);
-
- std::list<LLSD>::const_iterator it = history.begin();
- while (it != history.end())
- {
- const LLSD& msg = *it;
-
- std::string from = msg[IM_FROM];
- LLUUID from_id;
- if (msg[IM_FROM_ID].isDefined())
- {
- from_id = msg[IM_FROM_ID].asUUID();
- }
- else
- {
- std::string legacy_name = gCacheName->buildLegacyName(from);
- gCacheName->getUUID(legacy_name, from_id);
- }
-
- LLChat chat;
- chat.mFromName = from;
- chat.mFromID = from_id;
- chat.mText = msg[IM_TEXT].asString();
- chat.mTimeStr = msg[IM_TIME].asString();
- chat.mChatStyle = CHAT_STYLE_HISTORY;
-
- chat.mSourceType = CHAT_SOURCE_AGENT;
- if (from_id.isNull() && SYSTEM_FROM == from)
- {
- chat.mSourceType = CHAT_SOURCE_SYSTEM;
-
- }
- else if (from_id.isNull())
- {
- chat.mSourceType = isWordsName(from) ? CHAT_SOURCE_UNKNOWN : CHAT_SOURCE_OBJECT;
- }
-
- addMessage(chat, true, do_not_log);
-
- it++;
- }
-}
-
-//static
-LLNearbyChat* LLNearbyChat::getInstance()
-{
- LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar");
- return chat_bar->findChild<LLNearbyChat>("nearby_chat");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-void LLNearbyChat::onFocusReceived()
-{
- setBackgroundOpaque(true);
- LLPanel::onFocusReceived();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-void LLNearbyChat::onFocusLost()
-{
- setBackgroundOpaque(false);
- LLPanel::onFocusLost();
-}
-
-BOOL LLNearbyChat::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- //fix for EXT-6625
- //highlight NearbyChat history whenever mouseclick happen in NearbyChat
- //setting focus to eidtor will force onFocusLost() call that in its turn will change
- //background opaque. This all happenn since NearByChat is "chrome" and didn't process focus change.
-
- if(mChatHistory)
- mChatHistory->setFocus(TRUE);
- return LLPanel::handleMouseDown(x, y, mask);
-}
-
-void LLNearbyChat::draw()
-{
- // *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);
- }
-
- LLPanel::draw();
-}
diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h
deleted file mode 100644
index 7c5975cbc5..0000000000
--- a/indra/newview/llnearbychat.h
+++ /dev/null
@@ -1,83 +0,0 @@
- /**
- * @file llnearbychat.h
- * @brief nearby chat history scrolling panel implementation
- *
- * $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$
- */
-
-#ifndef LL_LLNEARBYCHAT_H_
-#define LL_LLNEARBYCHAT_H_
-
-#include "llscrollbar.h"
-#include "llviewerchat.h"
-#include "llfloater.h"
-
-class LLResizeBar;
-class LLChatHistory;
-
-class LLNearbyChat: public LLPanel
-{
-public:
- LLNearbyChat(const Params& p = LLPanel::getDefaultParams());
-
- BOOL postBuild ();
-
- /** @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 onNearbyChatContextMenuItemClicked(const LLSD& userdata);
- bool onNearbyChatCheckContextMenuItem(const LLSD& userdata);
-
- 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 processChatHistoryStyleUpdate(const LLSD& newvalue);
-
- void loadHistory();
-
- static LLNearbyChat* getInstance();
- void removeScreenChat();
-
-private:
-
- void getAllowedRect (LLRect& rect);
-
- void onNearbySpeakers ();
-
-
-private:
- LLHandle<LLView> mPopupMenuHandle;
- LLChatHistory* mChatHistory;
-
- std::vector<LLChat> mMessageArchive;
-};
-
-#endif
-
-
diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp
deleted file mode 100644
index c00dc4bc89..0000000000
--- a/indra/newview/llnearbychatbar.cpp
+++ /dev/null
@@ -1,684 +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;
- }
- else if (KEY_RETURN == key && mask == MASK_SHIFT)
- {
- // whisper
- sendChat(CHAT_TYPE_WHISPER);
- 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();
-
-}
-
-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);
- }
- // If the user wants to stop chatting on hitting return, lose focus
- // and go out of chat mode.
- if (gSavedSettings.getBOOL("CloseChatOnReturn"))
- {
- stopChat();
- }
- 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/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp
index 89fe7bb3c2..58a9b01a45 100644
--- a/indra/newview/llnotificationalerthandler.cpp
+++ b/indra/newview/llnotificationalerthandler.cpp
@@ -29,6 +29,7 @@
#include "llnotificationhandler.h"
+#include "llagentcamera.h"
#include "llnotifications.h"
#include "llprogressview.h"
#include "lltoastnotifypanel.h"
@@ -40,10 +41,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)
+: LLSystemNotificationHandler(name, notification_type),
+ mIsModal(is_modal)
{
- mType = type;
-
LLScreenChannelBase::Params p;
p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID"));
p.display_toasts_always = true;
@@ -68,79 +69,83 @@ 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::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);
+}
+
//--------------------------------------------------------------------------
+LLViewerAlertHandler::LLViewerAlertHandler(const std::string& name, const std::string& notification_type)
+ : LLSystemNotificationHandler(name, notification_type)
+{
+}
-void LLAlertHandler::onDeleteToast(LLToast* toast)
+bool LLViewerAlertHandler::processNotification(const LLNotificationPtr& p)
{
+ if (gHeadlessClient)
+ {
+ LL_INFOS("LLViewerAlertHandler") << "Alert: " << p->getName() << LL_ENDL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgentCamera.cameraMouselook() )
+ {
+ gAgentCamera.changeCameraToDefault();
+ }
+
+ return false;
}
-//--------------------------------------------------------------------------
diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp
index ad51389241..8fef102cf8 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()
+: LLCommunicationNotificationHandler("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..bff4efa9ea 100644
--- a/indra/newview/llnotificationhandler.h
+++ b/indra/newview/llnotificationhandler.h
@@ -27,33 +27,20 @@
#ifndef LL_LLNOTIFICATIONHANDLER_H
#define LL_LLNOTIFICATIONHANDLER_H
+#include <boost/intrusive_ptr.hpp>
#include "llwindow.h"
-//#include "llnotificationsutil.h"
+#include "llnotifications.h"
#include "llchannelmanager.h"
#include "llchat.h"
#include "llinstantmessage.h"
#include "llnotificationptr.h"
-class LLIMFloater;
+class LLFloaterIMSession;
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 +68,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 +78,6 @@ protected:
virtual void initChannel()=0;
LLHandle<LLScreenChannelBase> mChannel;
- e_notification_type mType;
-
};
// LLSysHandler and LLChatHandler are more specific base classes
@@ -115,24 +87,37 @@ protected:
/**
* Handler for system notifications.
*/
-class LLSysHandler : public LLEventHandler
+class LLNotificationHandler : public LLEventHandler, public LLNotificationChannel
{
public:
- LLSysHandler();
- virtual ~LLSysHandler() {};
+ LLNotificationHandler(const std::string& name, const std::string& notification_type, const std::string& parentName);
+ virtual ~LLNotificationHandler() {};
- virtual bool processNotification(const LLSD& notify)=0;
+ // base interface functions
+ virtual void onAdd(LLNotificationPtr p) { processNotification(p); }
+ virtual void onChange(LLNotificationPtr p) { processNotification(p); }
+ virtual void onLoad(LLNotificationPtr p) { processNotification(p); }
+ virtual void onDelete(LLNotificationPtr p) { if (mChannel.get()) mChannel.get()->removeToastByNotificationID(p->getID());}
-protected :
- static void init();
- void removeExclusiveNotifications(const LLNotificationPtr& notif);
+ virtual bool processNotification(const LLNotificationPtr& notify) = 0;
+};
- typedef std::list< std::set<std::string> > exclusive_notif_sets;
- static exclusive_notif_sets sExclusiveNotificationGroups;
+class LLSystemNotificationHandler : public LLNotificationHandler
+{
+public:
+ LLSystemNotificationHandler(const std::string& name, const std::string& notification_type);
+ virtual ~LLSystemNotificationHandler() {};
+};
+
+class LLCommunicationNotificationHandler : public LLNotificationHandler
+{
+public:
+ LLCommunicationNotificationHandler(const std::string& name, const std::string& notification_type);
+ virtual ~LLCommunicationNotificationHandler() {};
};
/**
- * Handler for chat message notifications.
+ * Handler for chat message notifications.
*/
class LLChatHandler : public LLEventHandler
{
@@ -146,17 +131,14 @@ public:
* Handler for IM notifications.
* It manages life time of IMs, group messages.
*/
-class LLIMHandler : public LLSysHandler
+class LLIMHandler : public LLCommunicationNotificationHandler
{
public:
- LLIMHandler(e_notification_type type, const LLSD& id);
+ LLIMHandler();
virtual ~LLIMHandler();
-
- // base interface functions
- virtual bool processNotification(const LLSD& notify);
+ bool processNotification(const LLNotificationPtr& p);
protected:
- virtual void onDeleteToast(LLToast* toast);
virtual void initChannel();
};
@@ -164,18 +146,15 @@ protected:
* Handler for system informational notices.
* It manages life time of tip notices.
*/
-class LLTipHandler : public LLSysHandler
+class LLTipHandler : public LLSystemNotificationHandler
{
public:
- LLTipHandler(e_notification_type type, const LLSD& id);
+ LLTipHandler();
virtual ~LLTipHandler();
- // base interface functions
- virtual bool processNotification(const LLSD& notify);
+ virtual bool processNotification(const LLNotificationPtr& p);
protected:
- virtual void onDeleteToast(LLToast* toast);
- virtual void onRejectToast(const LLUUID& id);
virtual void initChannel();
};
@@ -183,168 +162,144 @@ protected:
* Handler for system informational notices.
* It manages life time of script notices.
*/
-class LLScriptHandler : public LLSysHandler
+class LLScriptHandler : public LLSystemNotificationHandler
{
public:
- LLScriptHandler(e_notification_type type, const LLSD& id);
+ LLScriptHandler();
virtual ~LLScriptHandler();
- // base interface functions
- virtual bool processNotification(const LLSD& notify);
+ virtual void onDelete(LLNotificationPtr p);
+ virtual bool processNotification(const LLNotificationPtr& p);
protected:
virtual void onDeleteToast(LLToast* toast);
virtual void initChannel();
-
- // own handlers
- void onRejectToast(LLUUID& id);
};
/**
* Handler for group system notices.
*/
-class LLGroupHandler : public LLSysHandler
+class LLGroupHandler : public LLCommunicationNotificationHandler
{
public:
- LLGroupHandler(e_notification_type type, const LLSD& id);
+ LLGroupHandler();
virtual ~LLGroupHandler();
- // 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);
};
/**
* Handler for alert system notices.
*/
-class LLAlertHandler : public LLSysHandler
+class LLAlertHandler : public LLSystemNotificationHandler
{
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 bool processNotification(const LLNotificationPtr& p);
protected:
- virtual void onDeleteToast(LLToast* toast);
virtual void initChannel();
bool mIsModal;
};
+class LLViewerAlertHandler : public LLSystemNotificationHandler
+{
+ LOG_CLASS(LLViewerAlertHandler);
+public:
+ LLViewerAlertHandler(const std::string& name, const std::string& notification_type);
+ virtual ~LLViewerAlertHandler() {};
+
+ virtual void onDelete(LLNotificationPtr p) {};
+ virtual bool processNotification(const LLNotificationPtr& p);
+
+protected:
+ virtual void initChannel() {};
+};
+
/**
* Handler for offers notices.
* It manages life time of offer notices.
*/
-class LLOfferHandler : public LLSysHandler
+class LLOfferHandler : public LLCommunicationNotificationHandler
{
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);
};
/**
* Handler for UI hints.
*/
-class LLHintHandler : public LLSingleton<LLHintHandler>
+class LLHintHandler : public LLSystemNotificationHandler
{
public:
LLHintHandler();
- virtual ~LLHintHandler();
+ 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);
+ virtual bool processNotification(const LLNotificationPtr& p);
+
+protected:
+ virtual void initChannel() {};
};
/**
* Handler for browser notifications
*/
-class LLBrowserNotification : public LLSingleton<LLBrowserNotification>
+class LLBrowserNotification : public LLSystemNotificationHandler
{
public:
- virtual bool processNotification(const LLSD& notify);
+ LLBrowserNotification();
+ virtual ~LLBrowserNotification() {}
+
+ virtual bool processNotification(const LLNotificationPtr& p);
+
+protected:
+ virtual void initChannel() {};
};
/**
* Handler for outbox notifications
*/
-class LLOutboxNotification : public LLSingleton<LLOutboxNotification>
+class LLOutboxNotification : public LLSystemNotificationHandler
{
public:
- virtual bool processNotification(const LLSD& notify);
+ LLOutboxNotification();
+ virtual ~LLOutboxNotification() {};
+ virtual void onChange(LLNotificationPtr p) { }
+ virtual void onDelete(LLNotificationPtr p);
+ virtual bool processNotification(const LLNotificationPtr& p);
+
+protected:
+ virtual void initChannel() {};
};
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 +310,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 +356,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 34cb27d5ce..eb4601a469 100644
--- a/indra/newview/llnotificationhandlerutil.cpp
+++ b/indra/newview/llnotificationhandlerutil.cpp
@@ -34,219 +34,34 @@
#include "llurlaction.h"
#include "llagent.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
#include "llimview.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llnotificationhandler.h"
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");
+LLNotificationHandler::LLNotificationHandler(const std::string& name, const std::string& notification_type, const std::string& parentName)
+: LLNotificationChannel(name, parentName, LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, notification_type))
+{}
- sExclusiveNotificationGroups.push_back(online_offline_group);
-}
-
-LLSysHandler::LLSysHandler()
-{
- if(sExclusiveNotificationGroups.empty())
- {
- init();
- }
-}
+LLSystemNotificationHandler::LLSystemNotificationHandler(const std::string& name, const std::string& notification_type)
+ : LLNotificationHandler(name, notification_type, "System")
+{}
-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();
-}
-
-// 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);
-}
+LLCommunicationNotificationHandler::LLCommunicationNotificationHandler(const std::string& name, const std::string& notification_type)
+ : LLNotificationHandler(name, notification_type, "Communication")
+{}
// 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;
+ LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance<LLFloaterIMSession>("impanel", session_id);
- LLIMFloater* im_floater = findIMFloater(notification);
if (im_floater != NULL)
{
res = im_floater->getVisible() == TRUE;
@@ -255,19 +70,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 +101,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 +111,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 +125,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())
@@ -367,7 +143,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(
@@ -398,8 +173,8 @@ void LLHandlerUtil::logGroupNoticeToIMGroup(
// static
void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type)
{
- LLNearbyChat* nearby_chat = LLNearbyChat::getInstance();
- if(nearby_chat)
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
{
LLChat chat_msg(notification->getMessage());
chat_msg.mSourceType = type;
@@ -478,7 +253,7 @@ void LLHandlerUtil::addNotifPanelToIM(const LLNotificationPtr& notification)
// static
void LLHandlerUtil::updateIMFLoaterMesages(const LLUUID& session_id)
{
- LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id);
if (im_floater != NULL && im_floater->getVisible())
{
im_floater->updateMessages();
@@ -502,14 +277,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--;
@@ -518,3 +289,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..f40369a2e0 100644
--- a/indra/newview/llnotificationhinthandler.cpp
+++ b/indra/newview/llnotificationhinthandler.cpp
@@ -34,25 +34,26 @@
using namespace LLNotificationsUI;
LLHintHandler::LLHintHandler()
+ : LLSystemNotificationHandler("Hints", "hint")
{
}
-LLHintHandler::~LLHintHandler()
+void LLHintHandler::onAdd(LLNotificationPtr p)
{
+ LLHints::show(p);
}
-bool LLHintHandler::processNotification(const LLSD& notify)
+void LLHintHandler::onLoad(LLNotificationPtr p)
+{
+ LLHints::show(p);
+}
+
+void LLHintHandler::onDelete(LLNotificationPtr p)
+{
+ LLHints::hide(p);
+}
+
+bool LLHintHandler::processNotification(const LLNotificationPtr& p)
{
- 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;
}
diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp
index 3d8150eed3..152581c5a0 100644
--- a/indra/newview/llnotificationmanager.cpp
+++ b/indra/newview/llnotificationmanager.cpp
@@ -31,7 +31,7 @@
#include "llnotificationmanager.h"
-#include "llnearbychathandler.h"
+#include "llfloaterimnearbychathandler.h"
#include "llnotifications.h"
#include <boost/bind.hpp>
@@ -42,114 +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;
-
- // Don't bother if we're going down.
- // Otherwise we might crash when trying to use handlers that are already dead.
- if( LLApp::isExiting() )
- {
- return false;
- }
-
- 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<LLFloaterIMNearbyChatHandler>(new LLFloaterIMNearbyChatHandler());
}
//--------------------------------------------------------------------------
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);
+ if(mChatHandler)
+ mChatHandler->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;
-}
-
-//--------------------------------------------------------------------------
diff --git a/indra/newview/llnotificationmanager.h b/indra/newview/llnotificationmanager.h
index 27b6ba1c71..f37c6b833c 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 LLFloaterIMNearbyChatHandler> mChatHandler;
+ std::vector<LLNotificationChannelPtr> mChannels;
};
}
diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp
index 1552ed3346..2657b84ef3 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()
+: LLCommunicationNotificationHandler("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,124 @@ 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"];
+
+ //Will not play a notification sound for inventory and teleport offer based upon chat preference
+ bool playSound = (!notification->isDND()
+ && ((notification->getName() == "UserGiveItem"
+ && gSavedSettings.getBOOL("PlaySoundInventoryOffer"))
+ || (notification->getName() == "TeleportOffered"
+ && gSavedSettings.getBOOL("PlaySoundTeleportOffer"))));
+
+ if(playSound)
+ {
+ notification->playSound();
+ }
+
+ 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;
+ p.force_show = notification->getOfferFromAgent();
+
+ 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"] )
{
- LLNotifications::instance().cancel(notification);
+ // Remove original inventory offer script floater
+ LLScriptFloaterManager::instance().onRemoveNotification(notification->getID());
+ }
+ else
+ {
+ 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..08c98e4f28 100644
--- a/indra/newview/llnotificationscripthandler.cpp
+++ b/indra/newview/llnotificationscripthandler.cpp
@@ -27,6 +27,7 @@
#include "llviewerprecompiledheaders.h" // must be first include
+#include "llagent.h"
#include "llnotificationhandler.h"
#include "lltoastnotifypanel.h"
#include "llviewercontrol.h"
@@ -37,21 +38,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()
+: LLSystemNotificationHandler("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 +65,83 @@ 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());
+ 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);
+ if(gAgent.isDoNotDisturb())
+ {
+ p.force_show = notification->getName() == "SystemMessage"
+ || notification->getName() == "GodMessage"
+ || notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH;
}
- else
+
+ 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 4cad96fdc7..b6184f09bf 100644
--- a/indra/newview/llnotificationstorage.cpp
+++ b/indra/newview/llnotificationstorage.cpp
@@ -25,225 +25,111 @@
*/
#include "llviewerprecompiledheaders.h" // must be first include
+
#include "llnotificationstorage.h"
-#include "llxmlnode.h" // for linux compilers
+#include <string>
+#include <map>
-#include "llchannelmanager.h"
-#include "llscreenchannel.h"
-#include "llscriptfloater.h"
+#include "llerror.h"
+#include "llfile.h"
+#include "llnotifications.h"
+#include "llpointer.h"
+#include "llsd.h"
#include "llsdserialize.h"
-#include "llviewermessage.h"
+#include "llsingleton.h"
+#include "llregistry.h"
+#include "llviewermessage.h"
-//////////////////////////////////////////////////////////////////////////
+typedef boost::function<LLNotificationResponderInterface * (const LLSD& pParams)> responder_constructor_t;
-class LLResponderRegistry
+class LLResponderRegistry : public LLRegistrySingleton<std::string, responder_constructor_t, LLResponderRegistry>
{
-public:
-
- static void registerResponders();
-
- static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params);
-
-private:
-
- template<typename RESPONDER_TYPE>
- static LLNotificationResponderInterface* create(const LLSD& params)
- {
- RESPONDER_TYPE* responder = new RESPONDER_TYPE();
- responder->fromLLSD(params);
- return responder;
- }
-
- typedef boost::function<LLNotificationResponderInterface* (const LLSD& params)> responder_constructor_t;
+ public:
+ template<typename RESPONDER_TYPE> static LLNotificationResponderInterface * create(const LLSD& pParams);
+ LLNotificationResponderInterface * createResponder(const std::string& pNotificationName, const LLSD& pParams);
+};
- static void add(const std::string& notification_name, const responder_constructor_t& ctr);
+template<typename RESPONDER_TYPE> LLNotificationResponderInterface * LLResponderRegistry::create(const LLSD& pParams)
+{
+ RESPONDER_TYPE* responder = new RESPONDER_TYPE();
+ responder->fromLLSD(pParams);
+ return responder;
+}
-private:
- typedef std::map<std::string, responder_constructor_t> build_map_t;
+LLNotificationResponderInterface * LLResponderRegistry::createResponder(const std::string& pNotificationName, const LLSD& pParams)
+{
+ responder_constructor_t * factoryFunc = (LLResponderRegistry::getValue(pNotificationName));
+
+ if(factoryFunc)
+ {
+ return (*factoryFunc)(pParams);
+ }
+
+ return NULL;
+}
- static build_map_t sBuildMap;
-};
+LLResponderRegistry::StaticRegistrar sRegisterObjectGiveItem("ObjectGiveItem", &LLResponderRegistry::create<LLOfferInfo>);
+LLResponderRegistry::StaticRegistrar sRegisterUserGiveItem("UserGiveItem", &LLResponderRegistry::create<LLOfferInfo>);
+LLResponderRegistry::StaticRegistrar sRegisterOfferInfo("offer_info", &LLResponderRegistry::create<LLOfferInfo>);
-//////////////////////////////////////////////////////////////////////////
-LLPersistentNotificationStorage::LLPersistentNotificationStorage()
+LLNotificationStorage::LLNotificationStorage(std::string pFileName)
+ : mFileName(pFileName)
{
- mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" );
}
-bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
+LLNotificationStorage::~LLNotificationStorage()
{
- // we ignore "load" messages, but rewrite the persistence file on any other
- const std::string sigtype = payload["sigtype"].asString();
- if ("load" != sigtype)
- {
- saveNotifications();
- }
- return false;
}
-// Storing or loading too many persistent notifications will severely hurt
-// viewer load times, possibly to the point of failing to log in. Example case
-// from MAINT-994 is 821 notifications.
-static const S32 MAX_PERSISTENT_NOTIFICATIONS = 250;
-
-void LLPersistentNotificationStorage::saveNotifications()
+bool LLNotificationStorage::writeNotifications(const LLSD& pNotificationData) const
{
- // TODO - think about save optimization.
- llofstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
+ llofstream notifyFile(mFileName.c_str());
+ bool didFileOpen = notifyFile.is_open();
+
+ if (!didFileOpen)
{
- llwarns << "Failed to open " << mFileName << llendl;
- return;
+ LL_WARNS("LLNotificationStorage") << "Failed to open file '" << mFileName << "'" << LL_ENDL;
}
-
- LLSD output;
- LLSD& data = output["data"];
-
- LLNotificationChannelPtr history_channel = LLNotifications::instance().getChannel("Persistent");
- LLNotificationSet::iterator it = history_channel->begin();
-
- for ( ; history_channel->end() != it; ++it)
+ else
{
- LLNotificationPtr notification = *it;
-
- // After a notification was placed in Persist channel, it can become
- // responded, expired or canceled - in this case we are should not save it
- if(notification->isRespondedTo() || notification->isCancelled()
- || notification->isExpired())
- {
- continue;
- }
-
- data.append(notification->asLLSD());
-
- if (data.size() >= MAX_PERSISTENT_NOTIFICATIONS)
- {
- llwarns << "Too many persistent notifications."
- << " Saved " << MAX_PERSISTENT_NOTIFICATIONS << " of " << history_channel->size() << " persistent notifications." << llendl;
- break;
- }
+ LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
+ formatter->format(pNotificationData, notifyFile, LLSDFormatter::OPTIONS_PRETTY);
}
- LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter();
- formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY);
+ return didFileOpen;
}
-void LLPersistentNotificationStorage::loadNotifications()
+bool LLNotificationStorage::readNotifications(LLSD& pNotificationData) const
{
- LLResponderRegistry::registerResponders();
-
- llifstream notify_file(mFileName.c_str());
- if (!notify_file.is_open())
- {
- llwarns << "Failed to open " << mFileName << llendl;
- return;
- }
+ bool didFileRead;
- LLSD input;
- LLPointer<LLSDParser> parser = new LLSDXMLParser();
- if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0)
- {
- llwarns << "Failed to parse open notifications" << llendl;
- return;
- }
-
- if (input.isUndefined())
- {
- return;
- }
+ pNotificationData.clear();
- LLSD& data = input["data"];
- if (data.isUndefined())
+ llifstream notifyFile(mFileName.c_str());
+ didFileRead = notifyFile.is_open();
+ if (!didFileRead)
{
- return;
+ LL_WARNS("LLNotificationStorage") << "Failed to open file '" << mFileName << "'" << LL_ENDL;
}
-
- using namespace LLNotificationsUI;
- LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
- findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
-
- LLNotifications& instance = LLNotifications::instance();
- S32 processed_notifications = 0;
- for (LLSD::array_const_iterator notification_it = data.beginArray();
- notification_it != data.endArray();
- ++notification_it)
+ else
{
- LLSD notification_params = *notification_it;
-
- if (instance.templateExists(notification_params["name"].asString()))
+ LLPointer<LLSDParser> parser = new LLSDXMLParser();
+ didFileRead = (parser->parse(notifyFile, pNotificationData, LLSDSerialize::SIZE_UNLIMITED) >= 0);
+ if (!didFileRead)
{
- LLNotificationPtr notification(new LLNotification(notification_params));
-
- LLNotificationResponderPtr responder(LLResponderRegistry::
- createResponder(notification_params["name"], notification_params["responder"]));
- notification->setResponseFunctor(responder);
-
- instance.add(notification);
-
- // hide script floaters so they don't confuse the user and don't overlap startup toast
- LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
-
- if(notification_channel)
- {
- // hide saved toasts so they don't confuse the user
- notification_channel->hideToast(notification->getID());
- }
- }
- else
- {
- llwarns << "Failed to find template for persistent notification " << notification_params["name"].asString() << llendl;
- }
-
- ++processed_notifications;
- if (processed_notifications >= MAX_PERSISTENT_NOTIFICATIONS)
- {
- llwarns << "Too many persistent notifications."
- << " Processed " << MAX_PERSISTENT_NOTIFICATIONS << " of " << data.size() << " persistent notifications." << llendl;
- break;
+ LL_WARNS("LLNotificationStorage") << "Failed to parse open notifications from file '" << mFileName
+ << "'" << LL_ENDL;
}
}
- LLNotifications::instance().getChannel("Persistent")->
- connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
+ return didFileRead;
}
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap;
-
-void LLResponderRegistry::registerResponders()
+LLNotificationResponderInterface * LLNotificationStorage::createResponder(const std::string& pNotificationName, const LLSD& pParams) const
{
- sBuildMap.clear();
-
- add("ObjectGiveItem", &create<LLOfferInfo>);
- add("UserGiveItem", &create<LLOfferInfo>);
+ return LLResponderRegistry::getInstance()->createResponder(pNotificationName, pParams);
}
-
-LLNotificationResponderInterface* LLResponderRegistry::createResponder(const std::string& notification_name, const LLSD& params)
-{
- build_map_t::const_iterator it = sBuildMap.find(notification_name);
- if(sBuildMap.end() == it)
- {
- return NULL;
- }
- responder_constructor_t ctr = it->second;
- return ctr(params);
-}
-
-void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr)
-{
- if(sBuildMap.find(notification_name) != sBuildMap.end())
- {
- llwarns << "Responder is already registered : " << notification_name << llendl;
- llassert(!"Responder already registered");
- }
- sBuildMap[notification_name] = ctr;
-}
-
-// EOF
diff --git a/indra/newview/llnotificationstorage.h b/indra/newview/llnotificationstorage.h
index 8635c797c0..7aabf7d09e 100644
--- a/indra/newview/llnotificationstorage.h
+++ b/indra/newview/llnotificationstorage.h
@@ -27,32 +27,27 @@
#ifndef LL_NOTIFICATIONSTORAGE_H
#define LL_NOTIFICATIONSTORAGE_H
-#include "llnotifications.h"
-
-// Class that saves not responded(unread) notifications.
-// Unread notifications are saved in open_notifications.xml in SL account folder
-//
-// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
-// Notifications using functor responders are saved automatically (see llviewermessage.cpp
-// lure_callback_reg for example).
-// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
-// be a) serializable(implement LLNotificationResponderInterface),
-// b) registered with LLResponderRegistry (found in llnotificationstorage.cpp).
-class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>
-{
- LOG_CLASS(LLPersistentNotificationStorage);
-public:
+#include <string>
- LLPersistentNotificationStorage();
+#include "llerror.h"
- void saveNotifications();
+class LLNotificationResponderInterface;
+class LLSD;
- void loadNotifications();
+class LLNotificationStorage
+{
+ LOG_CLASS(LLNotificationStorage);
+public:
+ LLNotificationStorage(std::string pFileName);
+ ~LLNotificationStorage();
-private:
+protected:
+ bool writeNotifications(const LLSD& pNotificationData) const;
+ bool readNotifications(LLSD& pNotificationData) const;
- bool onPersistentChannelChanged(const LLSD& payload);
+ LLNotificationResponderInterface* createResponder(const std::string& pNotificationName, const LLSD& pParams) const;
+private:
std::string mFileName;
};
diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp
index e397cfa046..a85335f1ba 100644
--- a/indra/newview/llnotificationtiphandler.cpp
+++ b/indra/newview/llnotificationtiphandler.cpp
@@ -28,8 +28,8 @@
#include "llviewerprecompiledheaders.h" // must be first include
#include "llfloaterreg.h"
-#include "llnearbychat.h"
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychat.h"
+#include "llfloaterimnearbychat.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()
+: LLSystemNotificationHandler("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
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("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..6c26073d5b 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
@@ -72,8 +73,8 @@ LLOutputMonitorCtrl::LLOutputMonitorCtrl(const LLOutputMonitorCtrl::Params& p)
mAutoUpdate(p.auto_update),
mSpeakerId(p.speaker_id),
mIsAgentControl(false),
- mIsSwitchDirty(false),
- mShouldSwitchOn(false)
+ mIndicatorToggled(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);
@@ -114,26 +115,6 @@ void LLOutputMonitorCtrl::setPower(F32 val)
void LLOutputMonitorCtrl::draw()
{
- // see also switchIndicator()
- if (mIsSwitchDirty)
- {
- mIsSwitchDirty = false;
- if (mShouldSwitchOn)
- {
- // just notify parent visibility may have changed
- notifyParentVisibilityChanged();
- }
- else
- {
- // make itself invisible and notify parent about this
- setVisible(FALSE);
- notifyParentVisibilityChanged();
-
- // no needs to render for invisible element
- return;
- }
- }
-
// Copied from llmediaremotectrl.cpp
// *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then
// call directly into LLVoiceClient::getInstance() to ask if that agent-id is muted, is
@@ -156,6 +137,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,14 +240,34 @@ 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())
{
LLSpeakingIndicatorManager::unregisterSpeakingIndicator(mSpeakerId, this);
+ switchIndicator(false);
+ mSpeakerId = speaker_id;
}
- if (speaker_id.isNull() || speaker_id == mSpeakerId) return;
+ if (speaker_id.isNull() || (speaker_id == mSpeakerId))
+ {
+ return;
+ }
if (mSpeakerId.notNull())
{
@@ -256,6 +275,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);
@@ -264,12 +284,12 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& s
{
if (speaker_id == gAgentID)
{
- setIsMuted(false);
+ mIsMuted = false;
}
else
{
// check only blocking on voice. EXT-3542
- setIsMuted(LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat));
+ mIsMuted = LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat);
LLMuteList::getInstance()->addObserver(this);
}
}
@@ -278,32 +298,34 @@ void LLOutputMonitorCtrl::setSpeakerId(const LLUUID& speaker_id, const LLUUID& s
void LLOutputMonitorCtrl::onChange()
{
// check only blocking on voice. EXT-3542
- setIsMuted(LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat));
+ mIsMuted = LLMuteList::getInstance()->isMuted(mSpeakerId, LLMute::flagVoiceChat);
}
// virtual
void LLOutputMonitorCtrl::switchIndicator(bool switch_on)
{
- // ensure indicator is visible in case it is not in visible chain
- // to be called when parent became visible next time to notify parent that visibility is changed.
- setVisible(TRUE);
-
- // if parent is in visible chain apply switch_on state and notify it immediately
- if (getParent() && getParent()->isInVisibleChain())
- {
- LL_DEBUGS("SpeakingIndicator") << "Indicator is in visible chain, notifying parent: " << mSpeakerId << LL_ENDL;
- setVisible((BOOL)switch_on);
- notifyParentVisibilityChanged();
- }
- // otherwise remember necessary state and mark itself as dirty.
- // State will be applied in next draw when parents chain becomes visible.
- else
- {
- LL_DEBUGS("SpeakingIndicator") << "Indicator is not in visible chain, parent won't be notified: " << mSpeakerId << LL_ENDL;
- mIsSwitchDirty = true;
- mShouldSwitchOn = switch_on;
- }
+ if(getVisible() != (BOOL)switch_on)
+ {
+ setVisible(switch_on);
+
+ //Let parent adjust positioning of icons adjacent to speaker indicator
+ //(when speaker indicator hidden, adjacent icons move to right and when speaker
+ //indicator visible, adjacent icons move to the left)
+ if (getParent() && getParent()->isInVisibleChain())
+ {
+ notifyParentVisibilityChanged();
+ //Ignore toggled state in case it was set when parent visibility was hidden
+ mIndicatorToggled = false;
+ }
+ else
+ {
+ //Makes sure to only adjust adjacent icons when parent becomes visible
+ //(!mIndicatorToggled ensures that changes of TFT and FTF are discarded, real state changes are TF or FT)
+ mIndicatorToggled = !mIndicatorToggled;
+ }
+
+ }
}
//////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h
index 2d23753d46..a346909027 100644
--- a/indra/newview/lloutputmonitorctrl.h
+++ b/indra/newview/lloutputmonitorctrl.h
@@ -28,10 +28,10 @@
#define LL_LLOUTPUTMONITORCTRL_H
#include "v4color.h"
-#include "llview.h"
+#include "../llui/llview.h"
#include "llmutelist.h"
#include "llspeakingindicatormanager.h"
-#include "lluiimage.h"
+#include "../llui/lluiimage.h"
class LLTextBox;
class LLUICtrlFactory;
@@ -68,19 +68,19 @@ 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; }
- bool getIsMuted() const { return mIsMuted; }
- void setIsMuted(bool val) { mIsMuted = val; }
-
// For the current user, need to know the PTT state to show
// correct button image.
void setIsAgentControl(bool val) { mIsAgentControl = val; }
void setIsTalking(bool val) { mIsTalking = val; }
+ void setShowParticipantsSpeaking(bool show) { mShowParticipantsSpeaking = show; }
+
/**
* Sets avatar UUID to interact with voice channel.
*
@@ -89,7 +89,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();
@@ -105,6 +105,8 @@ public:
* It will be applied in next draw and parent will be notified.
*/
virtual void switchIndicator(bool switch_on);
+ bool getIndicatorToggled() { return mIndicatorToggled;}
+ void setIndicatorToggled(bool value) { mIndicatorToggled = value;}
private:
@@ -131,6 +133,7 @@ private:
bool mIsAgentControl;
bool mIsMuted;
bool mIsTalking;
+ bool mShowParticipantsSpeaking;
LLPointer<LLUIImage> mImageMute;
LLPointer<LLUIImage> mImageOff;
LLPointer<LLUIImage> mImageOn;
@@ -144,9 +147,7 @@ private:
/** uuid of a speaker being monitored */
LLUUID mSpeakerId;
- /** indicates if the instance is dirty and should notify parent */
- bool mIsSwitchDirty;
- bool mShouldSwitchOn;
+ bool mIndicatorToggled;
};
#endif
diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp
index 5c85ec438c..115114bb53 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,47 @@ 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;
+ default:
+ llwarns << "Unrecognized sort order for blocked list" << llendl;
+ 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,97 +123,112 @@ 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);
+ getChildView("blocked_gear_btn")->setEnabled(hasSelected);
+}
- std::vector<LLMute> mutes = LLMuteList::getInstance()->getMutes();
- std::vector<LLMute>::iterator it;
- for (it = mutes.begin(); it != mutes.end(); ++it)
+void LLPanelBlockedList::unblockItem()
+{
+ LLBlockedListItem* item = mBlockedList->getBlockedItem();
+ if (item)
{
- LLScrollListItem::Params item_p;
- item_p.enabled(TRUE);
- item_p.value(it->mID); // link UUID of blocked item with ScrollListItem
- item_p.columns.add().column("item_name").value(it->mName);//.type("text");
- item_p.columns.add().column("item_type").value(it->getDisplayType());//.type("text").width(111);
-
- mBlockedList->addRow(item_p, ADD_BOTTOM);
+ LLMute mute(item->getUUID(), item->getName());
+ LLMuteList::instance().remove(mute);
}
}
-void LLPanelBlockedList::updateButtons()
+void LLPanelBlockedList::onCustomAction(const LLSD& userdata)
{
- bool hasSelected = NULL != mBlockedList->getFirstSelected();
- getChildView("Unblock")->setEnabled(hasSelected);
-}
-
+ const std::string command_name = userdata.asString();
-
-void LLPanelBlockedList::onBackBtnClick()
-{
- LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
- if(parent)
+ if ("block_obj_by_name" == command_name)
+ {
+ blockObjectByName();
+ }
+ else if ("block_res_by_name" == command_name)
+ {
+ blockResidentByName();
+ }
+ else if ("sort_by_name" == command_name)
+ {
+ mBlockedList->sortByName();
+ gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_NAME);
+ }
+ else if ("sort_by_type" == command_name)
{
- parent->openPreviousPanel();
+ mBlockedList->sortByType();
+ gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_TYPE);
}
}
-void LLPanelBlockedList::onRemoveBtnClick()
+BOOL LLPanelBlockedList::isActionChecked(const LLSD& userdata)
{
- std::string name = mBlockedList->getSelectedItemLabel();
- LLUUID id = mBlockedList->getStringUUIDSelectedItem();
- LLMute mute(id, name);
-
- S32 last_selected = mBlockedList->getFirstSelectedIndex();
- if (LLMuteList::getInstance()->remove(mute))
+ std::string item = userdata.asString();
+ U32 sort_order = gSavedSettings.getU32("BlockPeopleSortOrder");
+
+ if ("sort_by_name" == item)
+ {
+ return E_SORT_BY_NAME == sort_order;
+ }
+ else if ("sort_by_type" == item)
{
- // Above removals may rebuild this dialog.
-
- if (last_selected == mBlockedList->getItemCount())
- {
- // we were on the last item, so select the last item again
- mBlockedList->selectNthItem(last_selected - 1);
- }
- else
- {
- // else select the item after the last item previously selected
- mBlockedList->selectNthItem(last_selected);
- }
+ return E_SORT_BY_TYPE == sort_order;
}
+
+ return false;
}
-void LLPanelBlockedList::onPickBtnClick()
+void LLPanelBlockedList::blockResidentByName()
{
const BOOL allow_multiple = FALSE;
const BOOL close_on_select = TRUE;
- /*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;
- LLMute mute(ids[0], names[0].getLegacyName(), LLMute::AGENT);
+ LLMute mute(ids[0], names[0].getAccountName(), LLMute::AGENT);
LLMuteList::getInstance()->add(mute);
showPanelAndSelect(mute.mID);
}
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/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp
index 993ffb7825..0cd93b330a 100644
--- a/indra/newview/llpanelgroupgeneral.cpp
+++ b/indra/newview/llpanelgroupgeneral.cpp
@@ -79,13 +79,18 @@ LLPanelGroupGeneral::LLPanelGroupGeneral()
mCtrlReceiveNotices(NULL),
mCtrlListGroup(NULL),
mActiveTitleLabel(NULL),
- mComboActiveTitle(NULL)
+ mComboActiveTitle(NULL),
+ mAvatarNameCacheConnection()
{
}
LLPanelGroupGeneral::~LLPanelGroupGeneral()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
}
BOOL LLPanelGroupGeneral::postBuild()
@@ -727,9 +732,12 @@ void LLPanelGroupGeneral::updateMembers()
else
{
// If name is not cached, onNameCache() should be called when it is cached and add this member to list.
- LLAvatarNameCache::get(mMemberProgress->first,
- boost::bind(&LLPanelGroupGeneral::onNameCache,
- this, gdatap->getMemberVersion(), member, _2));
+ // *TODO : Use a callback per member, not for the panel group.
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupGeneral::onNameCache, this, gdatap->getMemberVersion(), member, _2));
}
}
@@ -769,6 +777,8 @@ void LLPanelGroupGeneral::addMember(LLGroupMemberData* member)
void LLPanelGroupGeneral::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
if (!gdatap
diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h
index 1b4e8e2645..b7f4a01139 100644
--- a/indra/newview/llpanelgroupgeneral.h
+++ b/indra/newview/llpanelgroupgeneral.h
@@ -111,6 +111,7 @@ private:
LLComboBox *mComboMature;
LLGroupMgrGroupData::member_list_t::iterator mMemberProgress;
+ boost::signals2::connection mAvatarNameCacheConnection;
};
#endif
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
index b9b347d4be..133b269c11 100644
--- a/indra/newview/llpanelgroupinvite.cpp
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -89,6 +89,8 @@ public:
void (*mCloseCallback)(void* data);
void* mCloseCallbackUserData;
+
+ boost::signals2::connection mAvatarNameCacheConnection;
};
@@ -102,12 +104,17 @@ LLPanelGroupInvite::impl::impl(const LLUUID& group_id):
mGroupName( NULL ),
mConfirmedOwnerInvite( false ),
mCloseCallback( NULL ),
- mCloseCallbackUserData( NULL )
+ mCloseCallbackUserData( NULL ),
+ mAvatarNameCacheConnection()
{
}
LLPanelGroupInvite::impl::~impl()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
}
void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names,
@@ -301,11 +308,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);
}
}
}
@@ -378,8 +387,24 @@ void LLPanelGroupInvite::impl::callbackAddUsers(const uuid_vec_t& agent_ids, voi
std::vector<std::string> names;
for (S32 i = 0; i < (S32)agent_ids.size(); i++)
{
- LLAvatarNameCache::get(agent_ids[i],
- boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data));
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(agent_ids[i], &av_name))
+ {
+ LLPanelGroupInvite::impl::onAvatarNameCache(agent_ids[i], av_name, user_data);
+ }
+ else
+ {
+ impl* selfp = (impl*) user_data;
+ if (selfp)
+ {
+ if (selfp->mAvatarNameCacheConnection.connected())
+ {
+ selfp->mAvatarNameCacheConnection.disconnect();
+ }
+ // *TODO : Add a callback per avatar name being fetched.
+ selfp->mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_ids[i],boost::bind(&LLPanelGroupInvite::impl::onAvatarNameCache, _1, _2, user_data));
+ }
+ }
}
}
@@ -392,6 +417,10 @@ void LLPanelGroupInvite::impl::onAvatarNameCache(const LLUUID& agent_id,
if (selfp)
{
+ if (selfp->mAvatarNameCacheConnection.connected())
+ {
+ selfp->mAvatarNameCacheConnection.disconnect();
+ }
std::vector<std::string> names;
uuid_vec_t agent_ids;
agent_ids.push_back(agent_id);
@@ -471,8 +500,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
if (!LLAvatarNameCache::get(agent_id, &av_name))
{
// actually it should happen, just in case
- LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(
- &LLPanelGroupInvite::addUserCallback, this, _1, _2));
+ //LLAvatarNameCache::get(LLUUID(agent_id), boost::bind(&LLPanelGroupInvite::addUserCallback, this, _1, _2));
// for this special case!
//when there is no cached name we should remove resident from agent_ids list to avoid breaking of sequence
// removed id will be added in callback
@@ -480,7 +508,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids)
}
else
{
- names.push_back(av_name.getLegacyName());
+ names.push_back(av_name.getAccountName());
}
}
}
@@ -493,7 +521,7 @@ void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& a
std::vector<std::string> names;
uuid_vec_t agent_ids;
agent_ids.push_back(id);
- names.push_back(av_name.getLegacyName());
+ names.push_back(av_name.getAccountName());
mImplementation->addUsers(names, agent_ids);
}
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
index 31c0e3d01a..93b108efcc 100644
--- a/indra/newview/llpanelgroupnotices.cpp
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -543,10 +543,7 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg)
msg->getU32("Data","Timestamp",timestamp,i);
// we only have the legacy name here, convert it to a username
- if (LLAvatarNameCache::useDisplayNames())
- {
- name = LLCacheName::buildUsername(name);
- }
+ name = LLCacheName::buildUsername(name);
LLSD row;
row["id"] = id;
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
index ff106882f4..cfdac11d26 100644
--- a/indra/newview/llpanelgrouproles.cpp
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -743,12 +743,17 @@ LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab()
mChanged(FALSE),
mPendingMemberUpdate(FALSE),
mHasMatch(FALSE),
- mNumOwnerAdditions(0)
+ mNumOwnerAdditions(0),
+ mAvatarNameCacheConnection()
{
}
LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
if (mMembersList)
{
gSavedSettings.setString("GroupMembersSortOrder", mMembersList->getSortColumnName());
@@ -1604,6 +1609,8 @@ void LLPanelGroupMembersSubTab::addMemberToList(LLGroupMemberData* data)
void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemberData* member, const LLAvatarName& av_name)
{
+ mAvatarNameCacheConnection.disconnect();
+
LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mGroupID);
if (!gdatap
|| gdatap->getMemberVersion() != update_id
@@ -1613,7 +1620,7 @@ void LLPanelGroupMembersSubTab::onNameCache(const LLUUID& update_id, LLGroupMemb
}
// trying to avoid unnecessary hash lookups
- if (matchesSearchFilter(av_name.getLegacyName()))
+ if (matchesSearchFilter(av_name.getAccountName()))
{
addMemberToList(member);
if(!mMembersList->getEnabled())
@@ -1667,7 +1674,7 @@ void LLPanelGroupMembersSubTab::updateMembers()
LLAvatarName av_name;
if (LLAvatarNameCache::get(mMemberProgress->first, &av_name))
{
- if (matchesSearchFilter(av_name.getLegacyName()))
+ if (matchesSearchFilter(av_name.getAccountName()))
{
addMemberToList(mMemberProgress->second);
}
@@ -1675,8 +1682,12 @@ void LLPanelGroupMembersSubTab::updateMembers()
else
{
// If name is not cached, onNameCache() should be called when it is cached and add this member to list.
- LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupMembersSubTab::onNameCache,
- this, gdatap->getMemberVersion(), mMemberProgress->second, _2));
+ // *TODO : Add one callback per fetched avatar name
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(mMemberProgress->first, boost::bind(&LLPanelGroupMembersSubTab::onNameCache, this, gdatap->getMemberVersion(), mMemberProgress->second, _2));
}
}
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
index bead8bd85b..78bb3c57a1 100644
--- a/indra/newview/llpanelgrouproles.h
+++ b/indra/newview/llpanelgrouproles.h
@@ -214,6 +214,7 @@ protected:
U32 mNumOwnerAdditions;
LLGroupMgrGroupData::member_list_t::iterator mMemberProgress;
+ boost::signals2::connection mAvatarNameCacheConnection;
};
class LLPanelGroupRolesSubTab : public LLPanelGroupSubTab
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 d6fccb9705..88400e4ef2 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,14 +404,14 @@ void LLLandmarksPanel::setItemSelected(const LLUUID& obj_id, BOOL take_keyboard_
bool LLLandmarksPanel::isLandmarkSelected() const
{
- LLFolderViewItem* current_item = getCurSelectedItem();
- return current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_LANDMARK;
+ LLFolderViewModelItemInventory* current_item = getCurSelectedViewModelItem();
+ return current_item && (current_item->getInventoryType() == LLInventoryType::IT_LANDMARK);
}
bool LLLandmarksPanel::isFolderSelected() const
{
- LLFolderViewItem* current_item = getCurSelectedItem();
- return current_item && current_item->getListener()->getInventoryType() == LLInventoryType::IT_CATEGORY;
+ LLFolderViewModelItemInventory* current_item = getCurSelectedViewModelItem();
+ return current_item && (current_item->getInventoryType() == LLInventoryType::IT_CATEGORY);
}
bool LLLandmarksPanel::isReceivedFolderSelected() const
@@ -441,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);
@@ -457,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,
@@ -467,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;
@@ -509,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));
}
}
@@ -544,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);
}
@@ -555,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));
@@ -584,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);
@@ -596,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));
@@ -666,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();
}
}
@@ -732,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;
@@ -777,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)
{
@@ -792,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));
@@ -817,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)
@@ -835,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)
{
@@ -849,7 +835,7 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const
}
else
{
- mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name);
+ mCurrentSelectedList->doToSelected(command_name);
}
}
@@ -894,7 +880,7 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata)
{
if(mCurrentSelectedList)
{
- mCurrentSelectedList->getRootFolder()->doToSelected(&gInventory, userdata);
+ mCurrentSelectedList->doToSelected(userdata);
}
}
}
@@ -916,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)
{
@@ -978,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;
@@ -1013,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();
@@ -1050,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() );
@@ -1106,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.
@@ -1165,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;
@@ -1203,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)
{
@@ -1263,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)
{
@@ -1392,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
@@ -1406,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 4e787317ba..8fae0f0b67 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
{
@@ -88,6 +89,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 fabb8daa6e..d6535c88e9 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");
@@ -115,7 +117,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);
@@ -128,7 +130,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");
@@ -136,7 +138,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;
@@ -147,7 +149,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));
}
@@ -166,11 +168,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);
}
}
@@ -207,24 +212,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;
}
}
@@ -284,7 +293,7 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask)
void LLPanelMainInventory::doToSelected(const LLSD& userdata)
{
- getPanel()->getRootFolder()->doToSelected(&gInventory, userdata);
+ getPanel()->doToSelected(userdata);
}
void LLPanelMainInventory::closeAllFolders()
@@ -306,13 +315,13 @@ void LLPanelMainInventory::newWindow()
void LLPanelMainInventory::doCreate(const LLSD& userdata)
{
reset_inventory_filter();
- menu_create_inventory_item(getPanel()->getRootFolder(), NULL, userdata);
+ menu_create_inventory_item(getPanel(), NULL, userdata);
}
void LLPanelMainInventory::resetFilters()
{
LLFloaterInventoryFinder *finder = getFinder();
- getActivePanel()->getFilter()->resetDefault();
+ getActivePanel()->getFilter().resetDefault();
if (finder)
{
finder->updateElementsFromFilter();
@@ -417,7 +426,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string )
}
// save current folder open state if no filter currently applied
- if (!mActivePanel->getRootFolder()->isFilterModified())
+ if (!mActivePanel->getFilter().isNotDefault())
{
mSavedFolderState->setApply(FALSE);
mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState);
@@ -479,13 +488,13 @@ void LLPanelMainInventory::onFilterSelected()
}
setFilterSubString(mFilterSubString);
- LLInventoryFilter* filter = mActivePanel->getFilter();
+ LLInventoryFilter& filter = mActivePanel->getFilter();
LLFloaterInventoryFinder *finder = getFinder();
if (finder)
{
- finder->changeFilter(filter);
+ finder->changeFilter(&filter);
}
- if (filter->isActive())
+ if (filter.isActive())
{
// If our filter is active we may be the first thing requiring a fetch so we better start it here.
LLInventoryModelBackgroundFetch::instance().start();
@@ -598,7 +607,7 @@ void LLPanelMainInventory::onFocusReceived()
void LLPanelMainInventory::setFilterTextFromFilter()
{
- mFilterText = mActivePanel->getFilter()->getFilterText();
+ mFilterText = mActivePanel->getFilter().getFilterText();
}
void LLPanelMainInventory::toggleFindOptions()
@@ -647,7 +656,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();
@@ -959,7 +968,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)
@@ -970,7 +979,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)
{
@@ -1043,7 +1052,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)
{
@@ -1058,7 +1067,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")
@@ -1068,17 +1077,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);
}
}
@@ -1087,11 +1096,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);
}
}
@@ -1103,28 +1112,7 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata)
const std::string command_name = userdata.asString();
if (command_name == "delete")
{
- BOOL can_delete = FALSE;
- LLFolderView* root = getActivePanel()->getRootFolder();
- if (root)
- {
- can_delete = TRUE;
- std::set<LLUUID> selection_set = root->getSelectionList();
- if (selection_set.empty()) return FALSE;
- for (std::set<LLUUID>::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();
- llassert(listener);
- if (!listener) return FALSE;
- can_delete &= listener->isItemRemovable();
- can_delete &= !listener->isItemInTrash();
- }
- return can_delete;
- }
- return FALSE;
+ return getActivePanel()->isSelectionRemovable();
}
if (command_name == "save_texture")
{
@@ -1134,7 +1122,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())
{
@@ -1146,11 +1134,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()))
{
@@ -1163,7 +1151,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..adfb2dee86 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"
@@ -39,6 +40,8 @@
#define DEBUGGING_FRESHNESS 0
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
+
//
// statics
//
@@ -53,107 +56,42 @@ 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)
{
+ LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+
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;
+ params.font_color = item_color;
+ params.font_highlight_color = item_color;
return LLUICtrlFactory::create<LLInboxFolderViewFolder>(params);
}
LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge)
{
+ LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+
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;
params.rect = LLRect (0, 0, 0, 0);
params.tool_tip = params.name;
+ params.font_color = item_color;
+ params.font_highlight_color = item_color;
return LLUICtrlFactory::create<LLInboxFolderViewItem>(params);
}
@@ -163,26 +101,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 +159,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 +181,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 +190,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 +214,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 +237,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..7555ac7b2c 100644
--- a/indra/newview/llpanelobjectinventory.cpp
+++ b/indra/newview/llpanelobjectinventory.cpp
@@ -66,17 +66,19 @@
#include "llviewerobjectlist.h"
#include "llviewermessage.h"
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
///----------------------------------------------------------------------------
/// 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 +104,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 +136,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 +156,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 +340,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 +374,6 @@ void LLTaskInvFVBridge::openItem()
lldebugs << "LLTaskInvFVBridge::openItem()" << llendl;
}
-void LLTaskInvFVBridge::previewItem()
-{
- openItem();
-}
-
BOOL LLTaskInvFVBridge::isItemRenameable() const
{
if(gAgent.isGodlike()) return TRUE;
@@ -467,7 +484,7 @@ BOOL LLTaskInvFVBridge::removeItem()
return FALSE;
}
-void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
+void LLTaskInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
{
if (!mPanel)
{
@@ -507,7 +524,7 @@ void LLTaskInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>&
}
}
-void LLTaskInvFVBridge::move(LLFolderViewEventListener* parent_listener)
+void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener)
{
}
@@ -709,7 +726,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 +734,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 +757,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 +785,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 +1499,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 +1524,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 +1536,8 @@ void LLPanelObjectInventory::clearContents()
LLToolDragAndDrop::getInstance()->endDrag();
}
+ clearItemIDs();
+
if( mScroller )
{
// removes mFolders
@@ -1541,21 +1553,26 @@ void LLPanelObjectInventory::reset()
{
clearContents();
- //setBorderVisible(FALSE);
-
mCommitCallbackRegistrar.pushScope(); // push local callbacks
+ // Reset the inventory model to show all folders by default
+ mInventoryViewModel.getFilter().setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);
+
+ // Create a new folder view root
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;
+ p.options_menu = "menu_inventory.xml";
+
mFolders = LLUICtrlFactory::create<LLFolderView>(p);
- // this ensures that we never say "searching..." or "no items found"
- mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS);
+
mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar);
if (hasFocus())
@@ -1600,7 +1617,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 +1632,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 +1653,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 +1677,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 +1698,10 @@ void LLPanelObjectInventory::updateInventory()
}
}
- mFolders->requestArrange();
+ if (mFolders)
+ {
+ mFolders->requestArrange();
+ }
mInventoryNeedsUpdate = FALSE;
// Edit menu handler is set in onFocusReceived
}
@@ -1694,19 +1722,24 @@ void LLPanelObjectInventory::createFolderViews(LLInventoryObject* inventory_root
bridge = LLTaskInvFVBridge::createObjectBridge(this, inventory_root);
if(bridge)
{
- LLFolderViewFolder* new_folder = NULL;
+ LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+
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);
+ p.font_color = item_color;
+ p.font_highlight_color = item_color;
+
+ 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);
+ }
}
}
@@ -1716,6 +1749,8 @@ void LLPanelObjectInventory::createViewsForCategory(LLInventoryObject::object_li
LLInventoryObject* parent,
LLFolderViewFolder* folder)
{
+ LLUIColor item_color = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+
// Find all in the first pass
LLDynamicArray<obj_folder_pair*> child_categories;
LLTaskInvFVBridge* bridge;
@@ -1738,11 +1773,11 @@ 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;
+ p.font_color = item_color;
+ p.font_highlight_color = item_color;
view = LLUICtrlFactory::create<LLFolderViewFolder>(p);
child_categories.put(new obj_folder_pair(obj,
(LLFolderViewFolder*)view));
@@ -1751,15 +1786,17 @@ 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);
params.rect(LLRect());
params.tool_tip = params.name;
+ params.font_color = item_color;
+ params.font_highlight_color = item_color;
view = LLUICtrlFactory::create<LLFolderViewItem> (params);
}
- view->addToFolder(folder, mFolders);
+ view->addToFolder(folder);
+ addItemID(obj->getUUID(), view);
}
}
@@ -1827,6 +1864,7 @@ void LLPanelObjectInventory::refresh()
removeVOInventoryListener();
clearContents();
}
+ mInventoryViewModel.setTaskID(mTaskUUID);
//llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl;
}
@@ -1914,7 +1952,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 +1980,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 36234b9536..c09d4393c8 100644
--- a/indra/newview/llpaneloutfitedit.cpp
+++ b/indra/newview/llpaneloutfitedit.cpp
@@ -169,7 +169,7 @@ public:
return menu;
}
-
+
private:
static void onCreate(const LLSD& param)
{
@@ -186,11 +186,8 @@ private:
// Populate the menu with items like "New Skin", "New Pants", etc.
static void populateCreateWearableSubmenus(LLMenuGL* menu)
{
- // MAINT-2276...these menus are created as dummies because they are not available
- // when this function is called. This prevents their parent from popping up later.
- //
- //LLView* menu_clothes = gMenuHolder->getChildView("COF.Gear.New_Clothes", FALSE);
- //LLView* menu_bp = gMenuHolder->getChildView("COF.Geear.New_Body_Parts", FALSE);
+ LLView* menu_clothes = gMenuHolder->getChildView("COF.Gear.New_Clothes", FALSE);
+ LLView* menu_bp = gMenuHolder->getChildView("COF.Gear.New_Body_Parts", FALSE);
for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i)
{
@@ -203,11 +200,7 @@ private:
p.on_click.function_name = "Wearable.Create";
p.on_click.parameter = LLSD(type_name);
- //LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ? menu_clothes : menu_bp;
- // This is a work-around for MAINT-2276 wherein the parent toggleable menu does not appear
- // It puts everything under one menu, but that menu appears, which is better than not.
- //
- LLView* parent = menu;
+ LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ? menu_clothes : menu_bp;
LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
}
}
@@ -276,7 +269,7 @@ private:
if (inventory_panel->getVisible())
{
- inventory_panel->setSortOrder(sort_order);
+ inventory_panel->getFolderViewModel()->setSorter(sort_order);
}
else
{
@@ -744,7 +737,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);
@@ -891,13 +884,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())
@@ -1316,7 +1309,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();
@@ -1333,9 +1326,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())
{
@@ -1380,13 +1377,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();
}
@@ -1404,7 +1401,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..4138558bad 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"));
@@ -592,23 +611,15 @@ BOOL LLPanelPeople::postBuild()
mGroupList->setNoItemsMsg(getString("no_groups_msg"));
mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg"));
- mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
- mRecentList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
- mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
- mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
+ mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyPeopleContextMenu);
+ mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false);
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,63 +799,45 @@ 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
{
bool is_friend = true;
-
+ bool is_self = false;
// Check whether selected avatar is our friend.
if (item_selected)
{
selected_id = selected_uuids.front();
is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL;
+ is_self = gAgent.getID() == selected_id;
}
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 && !is_self);
+
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 +868,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 +891,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 +961,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 +1022,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)
@@ -1097,6 +1033,10 @@ void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl)
}
LLUUID clicked_id = item->getAvatarId();
+ if(gAgent.getID() == clicked_id)
+ {
+ return;
+ }
#if 0 // SJB: Useful for testing, but not currently functional or to spec
LLAvatarActions::showProfile(clicked_id);
@@ -1127,12 +1067,6 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list)
updateButtons();
}
-void LLPanelPeople::onViewProfileButtonClicked()
-{
- LLUUID id = getCurrentItemID();
- LLAvatarActions::showProfile(id);
-}
-
void LLPanelPeople::onAddFriendButtonClicked()
{
LLUUID id = getCurrentItemID();
@@ -1160,8 +1094,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 +1107,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 +1131,6 @@ void LLPanelPeople::onDeleteFriendButtonClicked()
}
}
-void LLPanelPeople::onGroupInfoButtonClicked()
-{
- LLGroupActions::show(getCurrentItemID());
-}
-
void LLPanelPeople::onChatButtonClicked()
{
LLUUID group_id = getCurrentItemID();
@@ -1203,6 +1138,17 @@ 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.
+ if (getActiveTabName() == NEARBY_TAB_NAME)
+ LLPanelPeopleMenus::gNearbyPeopleContextMenu.show(btn, selected_uuids, 0, 0);
+ else
+ LLPanelPeopleMenus::gPeopleContextMenu.show(btn, selected_uuids, 0, 0);
+}
+
void LLPanelPeople::onImButtonClicked()
{
uuid_vec_t selected_uuids;
@@ -1219,11 +1165,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 +1172,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 +1225,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 +1257,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 +1290,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 +1318,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..49f7361c4a 100644
--- a/indra/newview/llpanelpeoplemenus.cpp
+++ b/indra/newview/llpanelpeoplemenus.cpp
@@ -37,20 +37,25 @@
#include "llagentdata.h" // for gAgentID
#include "llavataractions.h"
#include "llcallingcard.h" // for LLAvatarTracker
+#include "lllogchat.h"
#include "llviewermenu.h" // for gMenuHolder
+#include "llconversationmodel.h"
+#include "llviewerobjectlist.h"
namespace LLPanelPeopleMenus
{
-NearbyMenu gNearbyMenu;
+PeopleContextMenu gPeopleContextMenu;
+NearbyPeopleContextMenu gNearbyPeopleContextMenu;
-//== NearbyMenu ===============================================================
+//== PeopleContextMenu ===============================================================
-LLContextMenu* NearbyMenu::createMenu()
+LLContextMenu* PeopleContextMenu::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 )
{
@@ -62,38 +67,86 @@ LLContextMenu* NearbyMenu::createMenu()
registrar.add("Avatar.RemoveFriend", boost::bind(&LLAvatarActions::removeFriendDialog, id));
registrar.add("Avatar.IM", boost::bind(&LLAvatarActions::startIM, id));
registrar.add("Avatar.Call", boost::bind(&LLAvatarActions::startCall, id));
- registrar.add("Avatar.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this));
+ registrar.add("Avatar.OfferTeleport", boost::bind(&PeopleContextMenu::offerTeleport, this));
+ registrar.add("Avatar.ZoomIn", boost::bind(&handle_zoom_to_object, id));
registrar.add("Avatar.ShowOnMap", boost::bind(&LLAvatarActions::showOnMap, id));
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));
+ enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2));
+ enable_registrar.add("Avatar.CheckItem", boost::bind(&PeopleContextMenu::checkContextMenuItem, this, _2));
// create the context menu from the XUI
- return createFromFile("menu_people_nearby.xml");
+ menu = createFromFile("menu_people_nearby.xml");
+ buildContextMenu(*menu, 0x0);
}
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.OfferTeleport", boost::bind(&NearbyMenu::offerTeleport, this));
- registrar.add("Avatar.RemoveFriend",boost::bind(&LLAvatarActions::removeFriendsDialog, 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(&PeopleContextMenu::offerTeleport, this));
+ registrar.add("Avatar.RemoveFriend", boost::bind(&LLAvatarActions::removeFriendsDialog, mUUIDs));
// registrar.add("Avatar.Share", boost::bind(&LLAvatarActions::startIM, mUUIDs)); // *TODO: unimplemented
- // registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mUUIDs)); // *TODO: unimplemented
- enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2));
+ // registrar.add("Avatar.Pay", boost::bind(&LLAvatarActions::pay, mUUIDs)); // *TODO: unimplemented
+
+ enable_registrar.add("Avatar.EnableItem", boost::bind(&PeopleContextMenu::enableContextMenuItem, this, _2));
// create the context menu from the XUI
- return createFromFile("menu_people_nearby_multiselect.xml");
+ menu = createFromFile("menu_people_nearby_multiselect.xml");
+ buildContextMenu(*menu, ITEM_IN_MULTI_SELECTION);
}
+
+ return menu;
+}
+
+void PeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ if (flags & ITEM_IN_MULTI_SELECTION)
+ {
+ items.push_back(std::string("add_friends"));
+ items.push_back(std::string("remove_friends"));
+ items.push_back(std::string("im"));
+ items.push_back(std::string("call"));
+ items.push_back(std::string("share"));
+ items.push_back(std::string("pay"));
+ items.push_back(std::string("offer_teleport"));
+ }
+ else
+ {
+ 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"));
+ }
+
+ hide_context_entries(menu, items, disabled_items);
}
-bool NearbyMenu::enableContextMenuItem(const LLSD& userdata)
+bool PeopleContextMenu::enableContextMenuItem(const LLSD& userdata)
{
+ if(gAgent.getID() == mUUIDs.front())
+ {
+ return false;
+ }
std::string item = userdata.asString();
// Note: can_block and can_delete is used only for one person selected menu
@@ -160,6 +213,12 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata)
{
return LLAvatarActions::canCall();
}
+ else if (item == std::string("can_zoom_in"))
+ {
+ const LLUUID& id = mUUIDs.front();
+
+ return gObjectList.findObject(id);
+ }
else if (item == std::string("can_show_on_map"))
{
const LLUUID& id = mUUIDs.front();
@@ -171,10 +230,19 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata)
{
return LLAvatarActions::canOfferTeleport(mUUIDs);
}
+ else if (item == std::string("can_callog"))
+ {
+ return LLLogChat::isTranscriptExist(mUUIDs.front());
+ }
+ else if (item == std::string("can_im") || item == std::string("can_invite") ||
+ item == std::string("can_share") || item == std::string("can_pay"))
+ {
+ return true;
+ }
return false;
}
-bool NearbyMenu::checkContextMenuItem(const LLSD& userdata)
+bool PeopleContextMenu::checkContextMenuItem(const LLSD& userdata)
{
std::string item = userdata.asString();
const LLUUID& id = mUUIDs.front();
@@ -187,11 +255,50 @@ bool NearbyMenu::checkContextMenuItem(const LLSD& userdata)
return false;
}
-void NearbyMenu::offerTeleport()
+void PeopleContextMenu::offerTeleport()
{
// boost::bind cannot recognize overloaded method LLAvatarActions::offerTeleport(),
// so we have to use a wrapper.
LLAvatarActions::offerTeleport(mUUIDs);
}
+//== NearbyPeopleContextMenu ===============================================================
+
+void NearbyPeopleContextMenu::buildContextMenu(class LLMenuGL& menu, U32 flags)
+{
+ menuentry_vec_t items;
+ menuentry_vec_t disabled_items;
+
+ if (flags & ITEM_IN_MULTI_SELECTION)
+ {
+ items.push_back(std::string("add_friends"));
+ items.push_back(std::string("remove_friends"));
+ items.push_back(std::string("im"));
+ items.push_back(std::string("call"));
+ items.push_back(std::string("share"));
+ items.push_back(std::string("pay"));
+ items.push_back(std::string("offer_teleport"));
+ }
+ else
+ {
+ 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("zoom_in"));
+ 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"));
+ }
+
+ hide_context_entries(menu, items, disabled_items);
+}
+
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelpeoplemenus.h b/indra/newview/llpanelpeoplemenus.h
index d51eaec716..0a1dcef303 100644
--- a/indra/newview/llpanelpeoplemenus.h
+++ b/indra/newview/llpanelpeoplemenus.h
@@ -33,19 +33,33 @@ namespace LLPanelPeopleMenus
{
/**
- * Menu used in the nearby people list.
+ * Menu used in the people lists.
*/
-class NearbyMenu : public LLListContextMenu
+class PeopleContextMenu : public LLListContextMenu
{
public:
/*virtual*/ LLContextMenu* createMenu();
+
+protected:
+ virtual void buildContextMenu(class LLMenuGL& menu, U32 flags);
+
private:
bool enableContextMenuItem(const LLSD& userdata);
bool checkContextMenuItem(const LLSD& userdata);
void offerTeleport();
};
-extern NearbyMenu gNearbyMenu;
+/**
+ * Menu used in the nearby people list.
+ */
+class NearbyPeopleContextMenu : public PeopleContextMenu
+{
+protected:
+ /*virtual*/ void buildContextMenu(class LLMenuGL& menu, U32 flags);
+};
+
+extern PeopleContextMenu gPeopleContextMenu;
+extern NearbyPeopleContextMenu gNearbyPeopleContextMenu;
} // namespace LLPanelPeopleMenus
diff --git a/indra/newview/llpanelplaceprofile.cpp b/indra/newview/llpanelplaceprofile.cpp
index ce8057eead..83b70d9f29 100644
--- a/indra/newview/llpanelplaceprofile.cpp
+++ b/indra/newview/llpanelplaceprofile.cpp
@@ -499,9 +499,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel,
std::string parcel_owner =
LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString();
mParcelOwner->setText(parcel_owner);
- LLAvatarNameCache::get(region->getOwner(),
- boost::bind(&LLPanelPlaceInfo::onAvatarNameCache,
- _1, _2, mRegionOwnerText));
+ LLAvatarNameCache::get(region->getOwner(), boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mRegionOwnerText));
}
if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus())
@@ -523,9 +521,7 @@ void LLPanelPlaceProfile::displaySelectedParcelInfo(LLParcel* parcel,
const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
if(auth_buyer_id.notNull())
{
- LLAvatarNameCache::get(auth_buyer_id,
- boost::bind(&LLPanelPlaceInfo::onAvatarNameCache,
- _1, _2, mSaleToText));
+ LLAvatarNameCache::get(auth_buyer_id, boost::bind(&LLPanelPlaceInfo::onAvatarNameCache, _1, _2, mSaleToText));
// Show sales info to a specific person or a group he belongs to.
if (auth_buyer_id != gAgent.getID() && !gAgent.isInGroup(auth_buyer_id))
diff --git a/indra/newview/llpanelplaceprofile.h b/indra/newview/llpanelplaceprofile.h
index a33fc12ce4..f4c6145881 100644
--- a/indra/newview/llpanelplaceprofile.h
+++ b/indra/newview/llpanelplaceprofile.h
@@ -38,7 +38,7 @@ class LLPanelPlaceProfile : public LLPanelPlaceInfo
public:
LLPanelPlaceProfile();
/*virtual*/ ~LLPanelPlaceProfile();
-
+
/*virtual*/ BOOL postBuild();
/*virtual*/ void resetLocation();
diff --git a/indra/newview/llpaneltopinfobar.cpp b/indra/newview/llpaneltopinfobar.cpp
index 1830086da2..343c140bbb 100644
--- a/indra/newview/llpaneltopinfobar.cpp
+++ b/indra/newview/llpaneltopinfobar.cpp
@@ -232,7 +232,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..c53760bca1 100644
--- a/indra/newview/llparticipantlist.cpp
+++ b/indra/newview/llparticipantlist.cpp
@@ -1,6 +1,6 @@
/**
* @file llparticipantlist.cpp
- * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages
+ * @brief LLParticipantList : model of a conversation session with added speaker events handling
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -26,38 +26,17 @@
#include "llviewerprecompiledheaders.h"
-// common includes
-#include "lltrans.h"
-#include "llavataractions.h"
-#include "llagent.h"
-
+#include "llavatarnamecache.h"
#include "llimview.h"
-#include "llnotificationsutil.h"
+#include "llfloaterimcontainer.h"
#include "llparticipantlist.h"
#include "llspeakers.h"
-#include "llviewercontrol.h"
-#include "llviewermenu.h"
-#include "llvoiceclient.h"
//LLParticipantList retrieves add, clear and remove events and updates view accordingly
#if LL_MSVC
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
#endif
-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)
- {
- LLOutputMonitorCtrl* indicator = item->getChild<LLOutputMonitorCtrl>("speaking_indicator");
- indicator->setIsMuted(is_muted);
- }
-}
-
-
// See EXT-4301.
/**
* class LLAvalineUpdater - observe the list of voice participants in session and check
@@ -197,15 +176,9 @@ private:
uuid_set_t mAvalineCallers;
};
-LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source,
- LLAvatarList* avatar_list,
- bool use_context_menu/* = true*/,
- bool exclude_agent /*= true*/,
- bool can_toggle_icons /*= true*/) :
+LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLFolderViewModelInterface& root_view_model) :
+ LLConversationItemSession(data_source->getSessionID(), root_view_model),
mSpeakerMgr(data_source),
- mAvatarList(avatar_list),
- mParticipantListMenu(NULL),
- mExcludeAgent(exclude_agent),
mValidateSpeakerCallback(NULL)
{
@@ -216,36 +189,16 @@ 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
- {
- mAvatarList->setContextMenu(NULL);
- }
-
- if (use_context_menu && can_toggle_icons)
- {
- mAvatarList->setShowIcons("ParticipantListShowIcons");
- mAvatarListToggleIconsConnection = gSavedSettings.getControl("ParticipantListShowIcons")->getSignal()->connect(boost::bind(&LLAvatarList::toggleIcons, mAvatarList));
- }
+ setSessionID(mSpeakerMgr->getSessionID());
//Lets fill avatarList with existing speakers
LLSpeakerMgr::speaker_list_t speaker_list;
@@ -264,138 +217,32 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source,
mModeratorToRemoveList.insert(speakerp->mID);
}
}
- // we need to exclude agent id for non group chat
- sort();
-}
-
-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())
- {
- mParticipantListMenu->hide();
- }
-
- if (mParticipantListMenu)
- {
- delete mParticipantListMenu;
- mParticipantListMenu = NULL;
- }
-
- mAvatarList->setContextMenu(NULL);
- mAvatarList->setComparator(NULL);
-
- delete mAvalineUpdater;
-}
-
-void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible)
-{
- mAvatarList->setSpeakingIndicatorsVisible(visible);
-};
-
-void LLParticipantList::onAvatarListDoubleClicked(LLUICtrl* ctrl)
-{
- LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl);
- if(!item)
- {
- return;
- }
-
- LLUUID clicked_id = item->getAvatarId();
-
- if (clicked_id.isNull() || clicked_id == gAgent.getID())
- return;
- LLAvatarActions::startIM(clicked_id);
-}
-
-void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param)
-{
- LLAvatarList* list = dynamic_cast<LLAvatarList*>(ctrl);
- if (list)
+ // Identify and store what kind of session we are
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(data_source->getSessionID());
+ if (im_session)
{
- 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)
+ // 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())
{
- 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)
- {
- 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);
- }
- }
- }
-
- 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)
- {
- 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)
- {
- name += " ";
- name += moderator_indicator;
- item->setAvatarName(name);
- }
- found = tooltip.find(moderator_indicator);
- if (found == std::string::npos)
- {
- tooltip += " ";
- tooltip += moderator_indicator;
- item->setAvatarToolTip(tooltip);
- }
- }
+ mConvType = CONV_SESSION_AD_HOC;
}
-
- // 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++)
+ else if (im_session->isGroupSessionType())
{
- const LLPointer<LLSpeaker>& speakerp = *it;
-
- if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
- {
- update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice);
- }
+ 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()
+{
+ delete mAvalineUpdater;
}
/*
@@ -411,31 +258,11 @@ void LLParticipantList::onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param)
*/
void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id)
{
- LLPanel* item = mAvatarList->getItemByValue(participant_id);
-
- if (NULL == item)
+ LLConversationItemParticipant* participant = findParticipant(participant_id);
+ if (participant)
{
- LL_WARNS("Avaline") << "Something wrong. Unable to find item for: " << participant_id << LL_ENDL;
- return;
+ removeParticipant(participant);
}
-
- 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;
-
- // 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);
-
// re-add avaline caller with a correct class instance.
addAvatarIDExceptAgent(participant_id);
}
@@ -447,23 +274,6 @@ void LLParticipantList::onAvalineCallerRemoved(const LLUUID& participant_id)
mSpeakerMgr->removeAvalineSpeaker(participant_id);
}
-void LLParticipantList::setSortOrder(EParticipantSortOrder order)
-{
- const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
-
- if ( speaker_sort_order != order )
- {
- gSavedSettings.setU32("SpeakerParticipantDefaultOrder", (U32)order);
- sort();
- }
-}
-
-const LLParticipantList::EParticipantSortOrder LLParticipantList::getSortOrder() const
-{
- const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
- return EParticipantSortOrder(speaker_sort_order);
-}
-
void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb)
{
mValidateSpeakerCallback = cb;
@@ -472,19 +282,6 @@ void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t c
void LLParticipantList::update()
{
mSpeakerMgr->update(true);
-
- if (E_SORT_BY_RECENT_SPEAKERS == getSortOrder() && !isHovered())
- {
- // Resort avatar list
- sort();
- }
-}
-
-bool LLParticipantList::isHovered()
-{
- S32 x, y;
- LLUI::getMousePositionScreen(&x, &y);
- return mAvatarList->calcScreenRect().pointInRect(x, y);
}
bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
@@ -497,27 +294,34 @@ bool LLParticipantList::onAddItemEvent(LLPointer<LLOldEvents::LLEvent> event, co
}
addAvatarIDExceptAgent(uu_id);
- sort();
return true;
}
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())
- {
- group_members.erase(pos);
- mAvatarList->setDirty();
- }
+ LLUUID avatar_id = event->getValue().asUUID();
+ 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();
+ 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"];
+ LLFloaterIMContainer* im_box = LLFloaterIMContainer::findInstance();
+ if (im_box)
+ {
+ im_box->setTimeNow(mUUID,participant_id);
+ }
+ }
return true;
}
@@ -541,9 +345,7 @@ bool LLParticipantList::onModeratorUpdateEvent(LLPointer<LLOldEvents::LLEvent> e
mModeratorList.erase(id);
}
}
-
- // apply changes immediately
- onAvatarListRefreshed(mAvatarList, LLSD());
+ // *TODO : do we have to fire an event so that LLFloaterIMSessionTab::refreshConversation() gets called
}
}
return true;
@@ -557,60 +359,45 @@ bool LLParticipantList::onSpeakerMuteEvent(LLPointer<LLOldEvents::LLEvent> event
// update UI on confirmation of moderator mutes
if (event->getValue().asString() == "voice")
{
- update_speaker_indicator(mAvatarList, speakerp->mID, speakerp->mModeratorMutedVoice);
+ setParticipantIsMuted(speakerp->mID, speakerp->mModeratorMutedVoice);
}
return true;
}
-void LLParticipantList::sort()
+void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
{
- if ( !mAvatarList )
- return;
-
- switch ( getSortOrder() )
+ // Do not add if already in there, is the session id (hence not an avatar) or excluded for some reason
+ if (findParticipant(avatar_id) || (avatar_id == mUUID))
{
- case E_SORT_BY_NAME :
- // if mExcludeAgent == true , then no need to keep agent on top of the list
- if(mExcludeAgent)
- {
- mAvatarList->sortByName();
- }
- else
- {
- mAvatarList->setComparator(&AGENT_ON_TOP_NAME_COMPARATOR);
- mAvatarList->sort();
- }
- break;
- case E_SORT_BY_RECENT_SPEAKERS:
- if (mSortByRecentSpeakers.isNull())
- mSortByRecentSpeakers = new LLAvatarItemRecentSpeakerComparator(*this);
- mAvatarList->setComparator(mSortByRecentSpeakers.get());
- mAvatarList->sort();
- break;
- default :
- llwarns << "Unrecognized sort order for " << mAvatarList->getName() << llendl;
- return;
+ return;
}
-}
-
-void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)
-{
- if (mExcludeAgent && gAgent.getID() == avatar_id) return;
- if (mAvatarList->contains(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.getDisplayName() , avatar_id, mRootViewModel);
+ participant->fetchAvatarName();
}
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);
mAvalineUpdater->watchAvalineCaller(avatar_id);
}
+
+ // *TODO : 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);
}
@@ -629,12 +416,12 @@ void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)
bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata)
{
/**
- * We need to filter speaking objects. These objects shouldn't appear in the list
+ * We need to filter speaking objects. These objects shouldn't appear in the list.
* @see LLFloaterChat::addChat() in llviewermessage.cpp to get detailed call hierarchy
*/
const LLUUID& speaker_id = event->getValue().asUUID();
LLPointer<LLSpeaker> speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id);
- if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT)
+ if (speaker.isNull() || (speaker->mType == LLSpeaker::SPEAKER_OBJECT))
{
return false;
}
@@ -658,6 +445,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,377 +465,4 @@ 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)
- {
- LLPointer<LLSpeaker> lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId());
- LLPointer<LLSpeaker> rhs = mParent.mSpeakerMgr->findSpeaker(avatar_item2->getAvatarId());
- if ( lhs.notNull() && rhs.notNull() )
- {
- // Compare by last speaking time
- if( lhs->mLastSpokeTime != rhs->mLastSpokeTime )
- return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime );
- else if ( lhs->mSortIndex != rhs->mSortIndex )
- return ( lhs->mSortIndex < rhs->mSortIndex );
- }
- else if ( lhs.notNull() )
- {
- // True if only avatar_item1 speaker info available
- return true;
- }
- else if ( rhs.notNull() )
- {
- // False if only avatar_item2 speaker info available
- return false;
- }
- }
- // By default compare by name.
- return LLAvatarItemNameComparator::doCompare(avatar_item1, avatar_item2);
-}
-
//EOF
diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h
index 53966c15fe..3a3ae76604 100644
--- a/indra/newview/llparticipantlist.h
+++ b/indra/newview/llparticipantlist.h
@@ -1,6 +1,6 @@
/**
* @file llparticipantlist.h
- * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages
+ * @brief LLParticipantList : model of a conversation session with added speaker events handling
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
@@ -28,35 +28,21 @@
#define LL_PARTICIPANTLIST_H
#include "llviewerprecompiledheaders.h"
-#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:
typedef boost::function<bool (const LLUUID& speaker_id)> validate_speaker_callback_t;
- LLParticipantList(LLSpeakerMgr* data_source,
- LLAvatarList* avatar_list,
- bool use_context_menu = true,
- bool exclude_agent = true,
- bool can_toggle_icons = true);
+ LLParticipantList(LLSpeakerMgr* data_source, LLFolderViewModelInterface& root_view_model);
~LLParticipantList();
- void setSpeakingIndicatorsVisible(BOOL visible);
-
- enum EParticipantSortOrder
- {
- E_SORT_BY_NAME = 0,
- E_SORT_BY_RECENT_SPEAKERS = 1,
- };
/**
* Adds specified avatar ID to the existing list if it is not Agent's ID
@@ -66,12 +52,6 @@ public:
void addAvatarIDExceptAgent(const LLUUID& avatar_id);
/**
- * Set and sort Avatarlist by given order
- */
- void setSortOrder(EParticipantSortOrder order = E_SORT_BY_NAME);
- const EParticipantSortOrder getSortOrder() const;
-
- /**
* Refreshes the participant list.
*/
void update();
@@ -93,14 +73,10 @@ 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);
/**
- * Sorts the Avatarlist by stored order
- */
- void sort();
-
- /**
* List of listeners implementing LLOldEvents::LLSimpleListener.
* There is no way to handle all the events in one listener as LLSpeakerMgr registers
* listeners in such a way that one listener can handle only one type of event
@@ -134,6 +110,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:
@@ -149,98 +132,7 @@ protected:
/*virtual*/ bool handleEvent(LLPointer<LLOldEvents::LLEvent> event, const LLSD& userdata);
};
- /**
- * 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
- {
- LOG_CLASS(LLAvatarItemRecentSpeakerComparator);
- public:
- LLAvatarItemRecentSpeakerComparator(LLParticipantList& parent):mParent(parent){};
- virtual ~LLAvatarItemRecentSpeakerComparator() {};
- protected:
- virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const;
- private:
- LLParticipantList& mParent;
- };
-
private:
- void onAvatarListDoubleClicked(LLUICtrl* ctrl);
- void onAvatarListRefreshed(LLUICtrl* ctrl, const LLSD& param);
-
void onAvalineCallerFound(const LLUUID& participant_id);
void onAvalineCallerRemoved(const LLUUID& participant_id);
@@ -251,10 +143,7 @@ private:
*/
void adjustParticipant(const LLUUID& speaker_id);
- bool isHovered();
-
LLSpeakerMgr* mSpeakerMgr;
- LLAvatarList* mAvatarList;
std::set<LLUUID> mModeratorList;
std::set<LLUUID> mModeratorToRemoveList;
@@ -262,25 +151,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
- * Also by default this field is controlling a sort procedure, @c sort()
- */
- bool mExcludeAgent;
-
- // boost::connections
- boost::signals2::connection mAvatarListDoubleClickConnection;
- boost::signals2::connection mAvatarListRefreshConnection;
- boost::signals2::connection mAvatarListReturnConnection;
- boost::signals2::connection mAvatarListToggleIconsConnection;
-
- LLPointer<LLAvatarItemRecentSpeakerComparator> mSortByRecentSpeakers;
validate_speaker_callback_t mValidateSpeakerCallback;
LLAvalineUpdater* mAvalineUpdater;
};
diff --git a/indra/newview/llpathfindingobject.cpp b/indra/newview/llpathfindingobject.cpp
index 858d3203c0..900763eae4 100644
--- a/indra/newview/llpathfindingobject.cpp
+++ b/indra/newview/llpathfindingobject.cpp
@@ -173,6 +173,7 @@ void LLPathfindingObject::fetchOwnerName()
mHasOwnerName = LLAvatarNameCache::get(mOwnerUUID, &mOwnerName);
if (!mHasOwnerName)
{
+ disconnectAvatarNameCacheConnection();
mAvatarNameCacheConnection = LLAvatarNameCache::get(mOwnerUUID, boost::bind(&LLPathfindingObject::handleAvatarNameFetch, this, _1, _2));
}
}
diff --git a/indra/newview/llpersistentnotificationstorage.cpp b/indra/newview/llpersistentnotificationstorage.cpp
new file mode 100644
index 0000000000..11c12e6c10
--- /dev/null
+++ b/indra/newview/llpersistentnotificationstorage.cpp
@@ -0,0 +1,145 @@
+/**
+* @file llpersistentnotificationstorage.cpp
+* @brief Implementation of llpersistentnotificationstorage
+* @author Stinson@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 "llpersistentnotificationstorage.h"
+
+#include "llchannelmanager.h"
+#include "llnotificationstorage.h"
+#include "llscreenchannel.h"
+#include "llscriptfloater.h"
+#include "llviewermessage.h"
+
+LLPersistentNotificationStorage::LLPersistentNotificationStorage()
+ : LLSingleton<LLPersistentNotificationStorage>()
+ , LLNotificationStorage(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml"))
+{
+}
+
+LLPersistentNotificationStorage::~LLPersistentNotificationStorage()
+{
+}
+
+static LLFastTimer::DeclareTimer FTM_SAVE_NOTIFICATIONS("Save Notifications");
+
+void LLPersistentNotificationStorage::saveNotifications()
+{
+ LLFastTimer _(FTM_SAVE_NOTIFICATIONS);
+
+ boost::intrusive_ptr<LLPersistentNotificationChannel> history_channel = boost::dynamic_pointer_cast<LLPersistentNotificationChannel>(LLNotifications::instance().getChannel("Persistent"));
+ if (!history_channel)
+ {
+ return;
+ }
+
+ LLSD output = LLSD::emptyMap();
+ LLSD& data = output["data"];
+
+ for ( std::vector<LLNotificationPtr>::iterator it = history_channel->beginHistory(), end_it = history_channel->endHistory();
+ it != end_it;
+ ++it)
+ {
+ LLNotificationPtr notification = *it;
+
+ // After a notification was placed in Persist channel, it can become
+ // responded, expired or canceled - in this case we are should not save it
+ if(notification->isRespondedTo() || notification->isCancelled()
+ || notification->isExpired())
+ {
+ continue;
+ }
+
+ data.append(notification->asLLSD(true));
+ }
+
+ writeNotifications(output);
+}
+
+static LLFastTimer::DeclareTimer FTM_LOAD_NOTIFICATIONS("Load Notifications");
+
+void LLPersistentNotificationStorage::loadNotifications()
+{
+ LLFastTimer _(FTM_LOAD_NOTIFICATIONS);
+
+ LLNotifications::instance().getChannel("Persistent")->
+ connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1));
+
+ LLSD input;
+ if (!readNotifications(input) ||input.isUndefined())
+ {
+ return;
+ }
+
+ LLSD& data = input["data"];
+ if (data.isUndefined())
+ {
+ return;
+ }
+
+ using namespace LLNotificationsUI;
+ LLScreenChannel* notification_channel = dynamic_cast<LLScreenChannel*>(LLChannelManager::getInstance()->
+ findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
+
+ LLNotifications& instance = LLNotifications::instance();
+
+ for (LLSD::array_const_iterator notification_it = data.beginArray();
+ notification_it != data.endArray();
+ ++notification_it)
+ {
+ LLSD notification_params = *notification_it;
+ LLNotificationPtr notification(new LLNotification(notification_params));
+
+ LLNotificationResponderPtr responder(createResponder(notification_params["name"], notification_params["responder"]));
+ notification->setResponseFunctor(responder);
+
+ instance.add(notification);
+
+ // hide script floaters so they don't confuse the user and don't overlap startup toast
+ LLScriptFloaterManager::getInstance()->setFloaterVisible(notification->getID(), false);
+
+ if(notification_channel)
+ {
+ // hide saved toasts so they don't confuse the user
+ notification_channel->hideToast(notification->getID());
+ }
+ }
+}
+
+bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload)
+{
+ // we ignore "load" messages, but rewrite the persistence file on any other
+ const std::string sigtype = payload["sigtype"].asString();
+ if ("load" != sigtype)
+ {
+ saveNotifications();
+ }
+ return false;
+}
+
+// EOF
diff --git a/indra/newview/llpersistentnotificationstorage.h b/indra/newview/llpersistentnotificationstorage.h
new file mode 100644
index 0000000000..98a825d2c1
--- /dev/null
+++ b/indra/newview/llpersistentnotificationstorage.h
@@ -0,0 +1,63 @@
+/**
+* @file llpersistentnotificationstorage.h
+* @brief Header file for llpersistentnotificationstorage
+* @author Stinson@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_LLPERSISTENTNOTIFICATIONSTORAGE_H
+#define LL_LLPERSISTENTNOTIFICATIONSTORAGE_H
+
+#include "llerror.h"
+#include "llnotificationstorage.h"
+#include "llsingleton.h"
+
+class LLSD;
+
+// Class that saves not responded(unread) notifications.
+// Unread notifications are saved in open_notifications.xml in SL account folder
+//
+// Notifications that should be saved(if unread) are marked with persist="true" in notifications.xml
+// Notifications using functor responders are saved automatically (see llviewermessage.cpp
+// lure_callback_reg for example).
+// Notifications using object responders(LLOfferInfo) need additional tuning. Responder object should
+// be a) serializable(implement LLNotificationResponderInterface),
+// b) registered with LLResponderRegistry (found in llpersistentnotificationstorage.cpp).
+
+class LLPersistentNotificationStorage : public LLSingleton<LLPersistentNotificationStorage>, public LLNotificationStorage
+{
+ LOG_CLASS(LLPersistentNotificationStorage);
+public:
+ LLPersistentNotificationStorage();
+ ~LLPersistentNotificationStorage();
+
+ void saveNotifications();
+ void loadNotifications();
+
+protected:
+
+private:
+ bool onPersistentChannelChanged(const LLSD& payload);
+};
+
+#endif // LL_LLPERSISTENTNOTIFICATIONSTORAGE_H
+
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..4c2213c198 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,35 @@ 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;
+ p.options_menu = "menu_inventory.xml";
+
+ return LLUICtrlFactory::create<LLPlacesFolderView>(p);
}
-
// save current folder open state
void LLPlacesInventoryPanel::saveFolderState()
{
@@ -128,59 +120,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 9c25e69db0..968a912ea2 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/llprogressview.cpp b/indra/newview/llprogressview.cpp
index f86e583b9e..989f0b0e60 100644
--- a/indra/newview/llprogressview.cpp
+++ b/indra/newview/llprogressview.cpp
@@ -96,7 +96,7 @@ BOOL LLProgressView::postBuild()
getChild<LLTextBox>("message_text")->setClickedCallback(onClickMessage, this);
// hidden initially, until we need it
- LLPanel::setVisible(FALSE);
+ setVisible(FALSE);
LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLProgressView::onAlertModal, this, _1));
@@ -265,7 +265,7 @@ void LLProgressView::draw()
gFocusMgr.releaseFocusIfNeeded( this );
// turn off panel that hosts intro so we see the world
- LLPanel::setVisible(FALSE);
+ setVisible(FALSE);
// stop observing events since we no longer care
mMediaCtrl->remObserver( this );
diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp
index d2280ea089..154555b261 100644
--- a/indra/newview/llscreenchannel.cpp
+++ b/indra/newview/llscreenchannel.cpp
@@ -39,7 +39,7 @@
#include "lldockablefloater.h"
#include "llsyswellwindow.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
#include "llscriptfloater.h"
#include "llrootview.h"
@@ -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();
}
@@ -685,7 +702,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/llscriptfloater.cpp b/indra/newview/llscriptfloater.cpp
index 6f98be1cb8..0e0da6bdc7 100644
--- a/indra/newview/llscriptfloater.cpp
+++ b/indra/newview/llscriptfloater.cpp
@@ -41,7 +41,7 @@
#include "lltoastscripttextbox.h"
#include "lltrans.h"
#include "llviewerwindow.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -95,7 +95,12 @@ bool LLScriptFloater::toggle(const LLUUID& notification_id)
show(notification_id);
}
- LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(notification_id, true);
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
+ {
+ chiclet_panelp->setChicletToggleState(notification_id, true);
+ }
+
return true;
}
@@ -206,10 +211,14 @@ void LLScriptFloater::setVisible(BOOL visible)
if(!visible)
{
- LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(getNotificationId());
- if(chiclet)
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
{
- chiclet->setToggleState(false);
+ LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(getNotificationId());
+ if(NULL != chicletp)
+ {
+ chicletp->setToggleState(false);
+ }
}
}
}
@@ -218,15 +227,19 @@ void LLScriptFloater::onMouseDown()
{
if(getNotificationId().notNull())
{
- // Remove new message icon
- LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(getNotificationId());
- if (chiclet == NULL)
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
{
- llerror("Dock chiclet for LLScriptFloater doesn't exist", 0);
- }
- else
- {
- chiclet->setShowNewMessagesIcon(false);
+ LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(getNotificationId());
+ // Remove new message icon
+ if (NULL == chicletp)
+ {
+ llerror("Dock chiclet for LLScriptFloater doesn't exist", 0);
+ }
+ else
+ {
+ chicletp->setShowNewMessagesIcon(false);
+ }
}
}
}
@@ -262,7 +275,11 @@ void LLScriptFloater::onFocusLost()
{
if(getNotificationId().notNull())
{
- LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(getNotificationId(), false);
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
+ {
+ chiclet_panelp->setChicletToggleState(getNotificationId(), false);
+ }
}
}
@@ -271,7 +288,11 @@ void LLScriptFloater::onFocusReceived()
// first focus will be received before setObjectId() call - don't toggle chiclet
if(getNotificationId().notNull())
{
- LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(getNotificationId(), true);
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
+ {
+ chiclet_panelp->setChicletToggleState(getNotificationId(), true);
+ }
}
}
@@ -279,28 +300,30 @@ void LLScriptFloater::dockToChiclet(bool dock)
{
if (getDockControl() == NULL)
{
- LLChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLChiclet>(getNotificationId());
- if (chiclet == NULL)
- {
- llwarns << "Dock chiclet for LLScriptFloater doesn't exist" << llendl;
- return;
- }
- else
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
{
- LLChicletBar::getInstance()->getChicletPanel()->scrollToChiclet(chiclet);
- }
+ LLChiclet * chicletp = chiclet_panelp->findChiclet<LLChiclet>(getNotificationId());
+ if (NULL == chicletp)
+ {
+ llwarns << "Dock chiclet for LLScriptFloater doesn't exist" << llendl;
+ return;
+ }
- // Stop saving position while we dock floater
- bool save = getSavePosition();
- setSavePosition(false);
+ chiclet_panelp->scrollToChiclet(chicletp);
- setDockControl(new LLDockControl(chiclet, this, getDockTongue(),
- LLDockControl::BOTTOM));
+ // Stop saving position while we dock floater
+ bool save = getSavePosition();
+ setSavePosition(false);
- setDocked(dock);
+ setDockControl(new LLDockControl(chicletp, this, getDockTongue(),
+ LLDockControl::BOTTOM));
- // Restore saving
- setSavePosition(save);
+ setDocked(dock);
+
+ // Restore saving
+ setSavePosition(save);
+ }
}
}
@@ -347,11 +370,15 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
script_notification_map_t::const_iterator it = findUsingObjectId(object_id);
if(it != mNotifications.end())
{
- LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(it->first);
- if(chiclet)
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
{
- // Pass the new_message icon state further.
- set_new_message = chiclet->getShowNewMessagesIcon();
+ LLIMChiclet * chicletp = chiclet_panelp->findChiclet<LLIMChiclet>(it->first);
+ if(NULL != chicletp)
+ {
+ // Pass the new_message icon state further.
+ set_new_message = chicletp->getShowNewMessagesIcon();
+ }
}
LLScriptFloater* floater = LLFloaterReg::findTypedInstance<LLScriptFloater>("script_floater", it->first);
@@ -367,14 +394,18 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id)
mNotifications.insert(std::make_pair(notification_id, object_id));
- // Create inventory offer chiclet for offer type notifications
- if( OBJ_GIVE_INVENTORY == obj_type )
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
{
- LLChicletBar::instance().getChicletPanel()->createChiclet<LLInvOfferChiclet>(notification_id);
- }
- else
- {
- LLChicletBar::getInstance()->getChicletPanel()->createChiclet<LLScriptChiclet>(notification_id);
+ // Create inventory offer chiclet for offer type notifications
+ if( OBJ_GIVE_INVENTORY == obj_type )
+ {
+ chiclet_panelp->createChiclet<LLInvOfferChiclet>(notification_id);
+ }
+ else
+ {
+ chiclet_panelp->createChiclet<LLScriptChiclet>(notification_id);
+ }
}
LLIMWellWindow::getInstance()->addObjectRow(notification_id, set_new_message);
@@ -410,7 +441,11 @@ void LLScriptFloaterManager::onRemoveNotification(const LLUUID& notification_id)
// remove related chiclet
if (LLChicletBar::instanceExists())
{
- LLChicletBar::getInstance()->getChicletPanel()->removeChiclet(notification_id);
+ LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel();
+ if (NULL != chiclet_panelp)
+ {
+ chiclet_panelp->removeChiclet(notification_id);
+ }
}
LLIMWellWindow* im_well_window = LLIMWellWindow::findInstance();
diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp
index d909a218e3..adb97ac800 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 af740fe73d..3ed6e24498 100644
--- a/indra/newview/llspatialpartition.cpp
+++ b/indra/newview/llspatialpartition.cpp
@@ -2746,7 +2746,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);
@@ -3997,7 +3997,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..8783d99b11 100644
--- a/indra/newview/llspeakers.cpp
+++ b/indra/newview/llspeakers.cpp
@@ -31,12 +31,15 @@
#include "llagent.h"
#include "llappviewer.h"
#include "llimview.h"
+#include "llgroupmgr.h"
#include "llsdutil.h"
#include "lluicolortable.h"
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
#include "llworld.h"
+extern LLControlGroup gSavedSettings;
+
const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
@@ -84,6 +87,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),
@@ -241,16 +257,63 @@ bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_
return true;
}
+bool LLSpeakersDelayActionsStorage::isTimerStarted(const LLUUID& speaker_id)
+{
+ return (mActionTimersMap.size() > 0) && (mActionTimersMap.find(speaker_id) != mActionTimersMap.end());
+}
+
+//
+// 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
//
LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :
- mVoiceChannel(channelp)
-, mVoiceModerated(false)
-, mModerateModeHandledFirstTime(false)
+ mVoiceChannel(channelp),
+ mVoiceModerated(false),
+ mModerateModeHandledFirstTime(false),
+ mSpeakerListUpdated(false)
{
+ mGetListTime.reset();
static LLUICachedControl<F32> remove_delay ("SpeakerParticipantRemoveDelay", 10.0);
mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay);
@@ -263,7 +326,11 @@ LLSpeakerMgr::~LLSpeakerMgr()
LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
{
- if (id.isNull()) return NULL;
+ LLUUID session_id = getSessionID();
+ if (id.isNull() || (id == session_id))
+ {
+ return NULL;
+ }
LLPointer<LLSpeaker> speakerp;
if (mSpeakers.find(id) == mSpeakers.end())
@@ -345,12 +412,10 @@ void LLSpeakerMgr::update(BOOL resort_ok)
// update status of all current speakers
BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
- for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)
+ for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); speaker_it++)
{
LLUUID speaker_id = speaker_it->first;
LLSpeaker* speakerp = speaker_it->second;
-
- speaker_map_t::iterator cur_speaker_it = speaker_it++;
if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))
{
@@ -374,6 +439,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
@@ -448,24 +514,89 @@ void LLSpeakerMgr::update(BOOL resort_ok)
void LLSpeakerMgr::updateSpeakerList()
{
- // are we bound to the currently active voice channel?
+ // Are we bound to the currently active voice channel?
if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
{
- std::set<LLUUID> participants;
- LLVoiceClient::getInstance()->getParticipantList(participants);
- // add new participants to our list of known speakers
- for (std::set<LLUUID>::iterator participant_it = participants.begin();
- participant_it != participants.end();
- ++participant_it)
+ std::set<LLUUID> participants;
+ LLVoiceClient::getInstance()->getParticipantList(participants);
+ // If we are, add all voice client participants to our list of known speakers
+ for (std::set<LLUUID>::iterator participant_it = participants.begin(); participant_it != participants.end(); ++participant_it)
{
setSpeaker(*participant_it,
LLVoiceClient::getInstance()->getDisplayName(*participant_it),
LLSpeaker::STATUS_VOICE_ACTIVE,
(LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL));
-
-
}
}
+ else
+ {
+ // If not, check if the list is empty, except if it's Nearby Chat (session_id NULL).
+ LLUUID session_id = getSessionID();
+ if (!session_id.isNull() && !mSpeakerListUpdated)
+ {
+ // If the list is empty, we update it with whatever we have locally so that it doesn't stay empty too long.
+ // *TODO: Fix the server side code that sometimes forgets to send back the list of participants after a chat started.
+ // (IOW, fix why we get no ChatterBoxSessionAgentListUpdates message after the initial ChatterBoxSessionStartReply)
+ LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (session->isGroupSessionType() && (mSpeakers.size() <= 1))
+ {
+ const F32 load_group_timeout = gSavedSettings.getF32("ChatLoadGroupTimeout");
+ // For groups, we need to hit the group manager.
+ // Note: The session uuid and the group uuid are actually one and the same. If that was to change, this will fail.
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(session_id);
+ if (!gdatap && (mGetListTime.getElapsedTimeF32() >= load_group_timeout))
+ {
+ // Request the data the first time around
+ LLGroupMgr::getInstance()->sendCapGroupMembersRequest(session_id);
+ }
+ else if (gdatap && gdatap->isMemberDataComplete() && !gdatap->mMembers.empty())
+ {
+ // Add group members when we get the complete list (note: can take a while before we get that list)
+ LLGroupMgrGroupData::member_list_t::iterator member_it = gdatap->mMembers.begin();
+ const S32 load_group_max_members = gSavedSettings.getS32("ChatLoadGroupMaxMembers");
+ S32 updated = 0;
+ while (member_it != gdatap->mMembers.end())
+ {
+ LLGroupMemberData* member = member_it->second;
+ LLUUID id = member_it->first;
+ // Add only members who are online and not already in the list
+ if ((member->getOnlineStatus() == "Online") && (mSpeakers.find(id) == mSpeakers.end()))
+ {
+ LLPointer<LLSpeaker> speakerp = setSpeaker(id, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
+ speakerp->mIsModerator = ((member->getAgentPowers() & GP_SESSION_MODERATOR) == GP_SESSION_MODERATOR);
+ updated++;
+ }
+ ++member_it;
+ // Limit the number of "manually updated" participants to a reasonable number to avoid severe fps drop
+ // *TODO : solve the perf issue of having several hundreds of widgets in the conversation list
+ if (updated >= load_group_max_members)
+ break;
+ }
+ mSpeakerListUpdated = true;
+ }
+ }
+ else if (mSpeakers.size() == 0)
+ {
+ // For all other session type (ad-hoc, P2P, avaline), we use the initial participants targets list
+ for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it)
+ {
+ // Add buddies if they are on line, add any other avatar.
+ if (!LLAvatarTracker::instance().isBuddy(*it) || LLAvatarTracker::instance().isBuddyOnline(*it))
+ {
+ setSpeaker(*it, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
+ }
+ }
+ mSpeakerListUpdated = true;
+ }
+ else
+ {
+ // The list has been updated the normal way (i.e. by a ChatterBoxSessionAgentListUpdates received from the server)
+ mSpeakerListUpdated = true;
+ }
+ }
+ }
+ // Always add the current agent (it has to be there...). Will do nothing if already there.
+ setSpeaker(gAgentID, "", LLSpeaker::STATUS_VOICE_ACTIVE, LLSpeaker::SPEAKER_AGENT);
}
void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp)
@@ -530,6 +661,10 @@ const LLUUID LLSpeakerMgr::getSessionID()
return mVoiceChannel->getSessionID();
}
+bool LLSpeakerMgr::isSpeakerToBeRemoved(const LLUUID& speaker_id)
+{
+ return mSpeakerDelayRemover && mSpeakerDelayRemover->isTimerStarted(speaker_id);
+}
void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing)
{
@@ -548,6 +683,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
{
speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
speakerp->mHasSpoken = TRUE;
+ fireEvent(new LLSpeakerUpdateSpeakerEvent(speakerp), "update_speaker");
}
}
@@ -718,43 +854,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..e953dd0e1a 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:
@@ -185,6 +193,8 @@ public:
void unsetActionTimer(const LLUUID& speaker_id);
void removeAllTimers();
+
+ bool isTimerStarted(const LLUUID& speaker_id);
private:
/**
* Callback of the each instance of LLSpeakerActionTimer.
@@ -229,6 +239,7 @@ public:
void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text);
LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; }
const LLUUID getSessionID();
+ bool isSpeakerToBeRemoved(const LLUUID& speaker_id);
/**
* Removes avaline speaker.
@@ -252,6 +263,8 @@ protected:
typedef std::map<LLUUID, LLPointer<LLSpeaker> > speaker_map_t;
speaker_map_t mSpeakers;
+ bool mSpeakerListUpdated;
+ LLTimer mGetListTime;
speaker_list_t mSpeakersSorted;
LLFrameTimer mSpeechTimer;
diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp
index 9b38bf22ff..07e9371124 100644
--- a/indra/newview/llspeakingindicatormanager.cpp
+++ b/indra/newview/llspeakingindicatormanager.cpp
@@ -74,6 +74,16 @@ public:
*/
void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+ /**
+ * Callback of changing voice participant list (from LLVoiceClientParticipantObserver).
+ *
+ * Switches off indicators had been switched on and switches on indicators of current participants list.
+ * There is only a few indicators in lists should be switched off/on.
+ * So, method does not calculate difference between these list it only switches off already
+ * switched on indicators and switches on indicators of voice channel participants
+ */
+ void onParticipantsChanged();
+
private:
typedef std::set<LLUUID> speaker_ids_t;
typedef std::multimap<LLUUID, LLSpeakingIndicator*> speaking_indicators_mmap_t;
@@ -94,16 +104,6 @@ private:
void sOnCurrentChannelChanged(const LLUUID& session_id);
/**
- * Callback of changing voice participant list (from LLVoiceClientParticipantObserver).
- *
- * Switches off indicators had been switched on and switches on indicators of current participants list.
- * There is only a few indicators in lists should be switched off/on.
- * So, method does not calculate difference between these list it only switches off already
- * switched on indicators and switches on indicators of voice channel participants
- */
- void onParticipantsChanged();
-
- /**
* Changes state of indicators specified by LLUUIDs
*
* @param speakers_uuids - avatars' LLUUIDs whose speaking indicators should be switched
@@ -237,28 +237,18 @@ void SpeakingIndicatorManager::switchSpeakerIndicators(const speaker_ids_t& spea
{
was_found = true;
LLSpeakingIndicator* indicator = (*it_indicator).second;
+ was_switched_on = was_switched_on || switch_on;
- 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())
- {
- switch_current_on = indicator->getTargetSessionID() == session_id;
- LL_DEBUGS("SpeakingIndicator") << "Session: " << session_id << ", target: " << indicator->getTargetSessionID() << ", the same? = " << switch_current_on << LL_ENDL;
- }
- was_switched_on = was_switched_on || switch_current_on;
-
- indicator->switchIndicator(switch_current_on);
-
+ indicator->switchIndicator(switch_on);
}
if (was_found)
{
- LL_DEBUGS("SpeakingIndicator") << mSpeakingIndicators.count(*it_uuid) << " indicators where found" << LL_ENDL;
+ LL_DEBUGS("SpeakingIndicator") << mSpeakingIndicators.count(*it_uuid) << " indicators were found" << LL_ENDL;
if (switch_on && !was_switched_on)
{
- LL_DEBUGS("SpeakingIndicator") << "but non of them where switched on" << LL_ENDL;
+ LL_DEBUGS("SpeakingIndicator") << "but none of them were switched on" << LL_ENDL;
}
if (was_switched_on)
@@ -314,5 +304,13 @@ void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speak
}
}
+void LLSpeakingIndicatorManager::updateSpeakingIndicators()
+{
+ if(SpeakingIndicatorManager::instanceExists())
+ {
+ SpeakingIndicatorManager::instance().onParticipantsChanged();
+ }
+}
+
// EOF
diff --git a/indra/newview/llspeakingindicatormanager.h b/indra/newview/llspeakingindicatormanager.h
index b0a147865b..e5afcd1cb7 100644
--- a/indra/newview/llspeakingindicatormanager.h
+++ b/indra/newview/llspeakingindicatormanager.h
@@ -78,6 +78,11 @@ namespace LLSpeakingIndicatorManager
* @param speaking_indicator instance of the speaker indicator to be unregistered.
*/
void unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator);
+
+ /**
+ * Switch on/off registered speaking indicator according to the most current voice client status
+ */
+ void updateSpeakingIndicators();
}
#endif // LL_LLSPEAKINGINDICATORMANAGER_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 0e3007724b..37e6ded986 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -54,7 +54,7 @@
#include "llfloaterreg.h"
#include "llfocusmgr.h"
#include "llhttpsender.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
#include "lllocationhistory.h"
#include "llimageworker.h"
@@ -63,7 +63,8 @@
#include "llmemorystream.h"
#include "llmessageconfig.h"
#include "llmoveview.h"
-#include "llnearbychat.h"
+#include "llfloaterimcontainer.h"
+#include "llfloaterimnearbychat.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llteleporthistory.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"
@@ -915,6 +917,13 @@ bool idle_startup()
// Overwrite default user settings with user settings
LLAppViewer::instance()->loadSettingsFromDirectory("Account");
+ // Convert 'LogInstantMessages' into 'KeepConversationLogTranscripts' for backward compatibility (CHUI-743).
+ LLControlVariablePtr logInstantMessagesControl = gSavedPerAccountSettings.getControl("LogInstantMessages");
+ if (logInstantMessagesControl.notNull())
+ {
+ gSavedPerAccountSettings.setS32("KeepConversationLogTranscripts", logInstantMessagesControl->getValue() ? 2 : 1);
+ }
+
// Need to set the LastLogoff time here if we don't have one. LastLogoff is used for "Recent Items" calculation
// and startup time is close enough if we don't have a real value.
if (gSavedPerAccountSettings.getU32("LastLogoff") == 0)
@@ -1291,6 +1300,8 @@ bool idle_startup()
display_startup();
LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );
+ LLConversationLog::getInstance();
+
return FALSE;
}
@@ -1401,14 +1412,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
+ LLFloaterIMContainer::getInstance();
// *Note: this is where gWorldMap used to be initialized.
@@ -1519,7 +1525,7 @@ bool idle_startup()
}
//---------------------------------------------------------------------
- // Agent Send
+ // World Wait
//---------------------------------------------------------------------
if(STATE_WORLD_WAIT == LLStartUp::getStartupState())
{
@@ -1845,6 +1851,10 @@ bool idle_startup()
// Set the show start location to true, now that the user has logged
// on with this install.
gSavedSettings.setBOOL("ShowStartLocation", TRUE);
+
+ // Open Conversation floater on first login.
+ LLFloaterReg::toggleInstanceOrBringToFront("im_container");
+
}
display_startup();
@@ -2167,7 +2177,6 @@ bool idle_startup()
display_startup();
// Unmute audio if desired and setup volumes.
- // Unmute audio if desired and setup volumes.
// This is a not-uncommon crash site, so surround it with
// llinfos output to aid diagnosis.
LL_INFOS("AppInit") << "Doing first audio_update_volume..." << LL_ENDL;
@@ -2188,7 +2197,6 @@ bool idle_startup()
LLAgentPicksInfo::getInstance()->requestNumberOfPicks();
- LLIMFloater::initIMFloater();
display_startup();
llassert(LLPathfindingManager::getInstance() != NULL);
@@ -2803,7 +2811,7 @@ void LLStartUp::initNameCache()
// Start cache in not-running state until we figure out if we have
// capabilities for display name lookup
- LLAvatarNameCache::initClass(false);
+ LLAvatarNameCache::initClass(false,gSavedSettings.getBOOL("UsePeopleAPI"));
LLAvatarNameCache::setUseDisplayNames(gSavedSettings.getBOOL("UseDisplayNames"));
}
diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp
index 2002647fef..e92bd766ca 100644
--- a/indra/newview/llsyswellwindow.cpp
+++ b/indra/newview/llsyswellwindow.cpp
@@ -23,29 +23,18 @@
* $/LicenseInfo$
*/
-
#include "llviewerprecompiledheaders.h" // must be first include
-
#include "llsyswellwindow.h"
-#include "llagent.h"
-#include "llavatarnamecache.h"
-
-#include "llflatlistview.h"
-#include "llfloaterreg.h"
-#include "llnotifications.h"
-
-#include "llscriptfloater.h"
-#include "llviewercontrol.h"
-#include "llviewerwindow.h"
-
#include "llchiclet.h"
#include "llchicletbar.h"
-#include "lltoastpanel.h"
+#include "llflatlistview.h"
+#include "llfloaterreg.h"
#include "llnotificationmanager.h"
#include "llnotificationsutil.h"
+#include "llscriptfloater.h"
#include "llspeakers.h"
-#include "lltoolbarview.h"
+#include "lltoastpanel.h"
//---------------------------------------------------------------------------------
LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLTransientDockableFloater(NULL, true, key),
@@ -68,10 +57,6 @@ BOOL LLSysWellWindow::postBuild()
// get a corresponding channel
initChannel();
- // click on SysWell Window should clear "new message" state (and 'Lit' status). EXT-3147.
- // mouse up callback is not called in this case.
- setMouseDownCallback(boost::bind(&LLSysWellWindow::releaseNewMessagesState, this));
-
return LLTransientDockableFloater::postBuild();
}
@@ -98,9 +83,12 @@ void LLSysWellWindow::onStartUpToastClick(S32 x, S32 y, MASK mask)
void LLSysWellWindow::setSysWellChiclet(LLSysWellChiclet* chiclet)
{
mSysWellChiclet = chiclet;
- if(mSysWellChiclet)
- mSysWellChiclet->updateWidget(isWindowEmpty());
+ if(NULL != mSysWellChiclet)
+ {
+ mSysWellChiclet->updateWidget(isWindowEmpty());
+ }
}
+
//---------------------------------------------------------------------------------
LLSysWellWindow::~LLSysWellWindow()
{
@@ -111,7 +99,10 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id)
{
if(mMessageList->removeItemByValue(id))
{
- mSysWellChiclet->updateWidget(isWindowEmpty());
+ if (NULL != mSysWellChiclet)
+ {
+ mSysWellChiclet->updateWidget(isWindowEmpty());
+ }
reshapeWindow();
}
else
@@ -165,11 +156,6 @@ void LLSysWellWindow::setVisible(BOOL visible)
mChannel->updateShowToastsState();
mChannel->redrawToasts();
}
-
- if (visible)
- {
- releaseNewMessagesState();
- }
}
//---------------------------------------------------------------------------------
@@ -219,14 +205,6 @@ void LLSysWellWindow::reshapeWindow()
}
}
-void LLSysWellWindow::releaseNewMessagesState()
-{
- if (NULL != mSysWellChiclet)
- {
- mSysWellChiclet->setNewMessagesState(false);
- }
-}
-
//---------------------------------------------------------------------------------
bool LLSysWellWindow::isWindowEmpty()
{
@@ -234,121 +212,6 @@ bool LLSysWellWindow::isWindowEmpty()
}
/************************************************************************/
-/* RowPanel implementation */
-/************************************************************************/
-
-//---------------------------------------------------------------------------------
-LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId,
- S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) :
- LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent)
-{
- buildFromFile( "panel_activeim_row.xml");
-
- // Choose which of the pre-created chiclets (IM/group) to use.
- // The other one gets hidden.
-
- LLIMChiclet::EType im_chiclet_type = LLIMChiclet::getIMSessionType(sessionId);
- switch (im_chiclet_type)
- {
- case LLIMChiclet::TYPE_GROUP:
- mChiclet = getChild<LLIMGroupChiclet>("group_chiclet");
- break;
- case LLIMChiclet::TYPE_AD_HOC:
- mChiclet = getChild<LLAdHocChiclet>("adhoc_chiclet");
- break;
- case LLIMChiclet::TYPE_UNKNOWN: // assign mChiclet a non-null value anyway
- case LLIMChiclet::TYPE_IM:
- mChiclet = getChild<LLIMP2PChiclet>("p2p_chiclet");
- break;
- }
-
- // Initialize chiclet.
- mChiclet->setChicletSizeChangedCallback(boost::bind(&LLIMWellWindow::RowPanel::onChicletSizeChanged, this, mChiclet, _2));
- mChiclet->enableCounterControl(true);
- mChiclet->setCounter(chicletCounter);
- mChiclet->setSessionId(sessionId);
- mChiclet->setIMSessionName(name);
- mChiclet->setOtherParticipantId(otherParticipantId);
- mChiclet->setVisible(true);
-
- if (im_chiclet_type == LLIMChiclet::TYPE_IM)
- {
- LLAvatarNameCache::get(otherParticipantId,
- boost::bind(&LLIMWellWindow::RowPanel::onAvatarNameCache,
- this, _1, _2));
- }
- else
- {
- LLTextBox* contactName = getChild<LLTextBox>("contact_name");
- contactName->setValue(name);
- }
-
- mCloseBtn = getChild<LLButton>("hide_btn");
- mCloseBtn->setCommitCallback(boost::bind(&LLIMWellWindow::RowPanel::onClosePanel, this));
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::RowPanel::onAvatarNameCache(const LLUUID& agent_id,
- const LLAvatarName& av_name)
-{
- LLTextBox* contactName = getChild<LLTextBox>("contact_name");
- contactName->setValue( av_name.getCompleteName() );
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::RowPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param)
-{
- LLTextBox* text = getChild<LLTextBox>("contact_name");
- S32 new_text_left = mChiclet->getRect().mRight + CHICLET_HPAD;
- LLRect text_rect = text->getRect();
- text_rect.mLeft = new_text_left;
- text->setShape(text_rect);
-}
-
-//---------------------------------------------------------------------------------
-LLIMWellWindow::RowPanel::~RowPanel()
-{
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::RowPanel::onClosePanel()
-{
- gIMMgr->leaveSession(mChiclet->getSessionId());
- // This row panel will be removed from the list in LLSysWellWindow::sessionRemoved().
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::RowPanel::onMouseEnter(S32 x, S32 y, MASK mask)
-{
- setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemSelected"));
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::RowPanel::onMouseLeave(S32 x, S32 y, MASK mask)
-{
- setTransparentColor(LLUIColorTable::instance().getColor("SysWellItemUnselected"));
-}
-
-//---------------------------------------------------------------------------------
-// virtual
-BOOL LLIMWellWindow::RowPanel::handleMouseDown(S32 x, S32 y, MASK mask)
-{
- // Pass the mouse down event to the chiclet (EXT-596).
- if (!mChiclet->pointInView(x, y) && !mCloseBtn->getRect().pointInRect(x, y)) // prevent double call of LLIMChiclet::onMouseDown()
- {
- mChiclet->onMouseDown();
- return TRUE;
- }
-
- return LLPanel::handleMouseDown(x, y, mask);
-}
-
-// virtual
-BOOL LLIMWellWindow::RowPanel::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- return mChiclet->handleRightMouseDown(x, y, mask);
-}
-/************************************************************************/
/* ObjectRowPanel implementation */
/************************************************************************/
@@ -433,13 +296,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
@@ -481,7 +350,6 @@ void LLNotificationWellWindow::addItem(LLSysWellItem::Params p)
{
mSysWellChiclet->updateWidget(isWindowEmpty());
reshapeWindow();
-
new_item->setOnItemCloseCallback(boost::bind(&LLNotificationWellWindow::onItemClose, this, _1));
new_item->setOnItemClickCallback(boost::bind(&LLNotificationWellWindow::onItemClick, this, _1));
}
@@ -519,7 +387,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 +414,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,7 +428,10 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item)
mChannel->killToastByNotificationID(id);
}
-
+void LLNotificationWellWindow::onAdd( LLNotificationPtr notify )
+{
+ removeItemByID(notify->getID());
+}
/************************************************************************/
/* LLIMWellWindow implementation */
@@ -585,12 +442,10 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item)
LLIMWellWindow::LLIMWellWindow(const LLSD& key)
: LLSysWellWindow(key)
{
- LLIMMgr::getInstance()->addSessionObserver(this);
}
LLIMWellWindow::~LLIMWellWindow()
{
- LLIMMgr::getInstance()->removeSessionObserver(this);
}
// static
@@ -611,47 +466,11 @@ BOOL LLIMWellWindow::postBuild()
BOOL rv = LLSysWellWindow::postBuild();
setTitle(getString("title_im_well_window"));
- LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLIMWellWindow::findIMChiclet, this, _1));
LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLIMWellWindow::findObjectChiclet, this, _1));
return rv;
}
-//virtual
-void LLIMWellWindow::sessionAdded(const LLUUID& session_id,
- const std::string& name, const LLUUID& other_participant_id)
-{
- LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
- if (!session) return;
-
- // no need to spawn chiclets for participants in P2P calls called through Avaline
- if (session->isP2P() && session->isOtherParticipantAvaline()) return;
-
- if (mMessageList->getItemByValue(session_id)) return;
-
- addIMRow(session_id, 0, name, other_participant_id);
- reshapeWindow();
-}
-
-//virtual
-void LLIMWellWindow::sessionRemoved(const LLUUID& sessionId)
-{
- delIMRow(sessionId);
- reshapeWindow();
-}
-
-//virtual
-void LLIMWellWindow::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id)
-{
- //for outgoing ad-hoc and group im sessions only
- LLChiclet* chiclet = findIMChiclet(old_session_id);
- if (chiclet)
- {
- chiclet->setSessionId(new_session_id);
- mMessageList->updateValue(old_session_id, new_session_id);
- }
-}
-
LLChiclet* LLIMWellWindow::findObjectChiclet(const LLUUID& notification_id)
{
if (!mMessageList) return NULL;
@@ -668,85 +487,13 @@ LLChiclet* LLIMWellWindow::findObjectChiclet(const LLUUID& notification_id)
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
-LLChiclet* LLIMWellWindow::findIMChiclet(const LLUUID& sessionId)
-{
- if (!mMessageList) return NULL;
-
- LLChiclet* res = NULL;
- RowPanel* panel = mMessageList->getTypedItemByValue<RowPanel>(sessionId);
- if (panel != NULL)
- {
- res = panel->mChiclet;
- }
-
- return res;
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::addIMRow(const LLUUID& sessionId, S32 chicletCounter,
- const std::string& name, const LLUUID& otherParticipantId)
-{
- RowPanel* item = new RowPanel(this, sessionId, chicletCounter, name, otherParticipantId);
- if (mMessageList->addItem(item, sessionId))
- {
- mSysWellChiclet->updateWidget(isWindowEmpty());
- }
- else
- {
- llwarns << "Unable to add IM Row into the list, sessionID: " << sessionId
- << ", name: " << name
- << ", other participant ID: " << otherParticipantId
- << llendl;
-
- item->die();
- }
-}
-
-//---------------------------------------------------------------------------------
-void LLIMWellWindow::delIMRow(const LLUUID& sessionId)
-{
- //fix for EXT-3252
- //without this line LLIMWellWindow receive onFocusLost
- //and hide itself. It was becaue somehow LLIMChicklet was in focus group for
- //LLIMWellWindow...
- //But I didn't find why this happen..
- gFocusMgr.clearLastFocusForGroup(this);
-
- if (mMessageList->removeItemByValue(sessionId))
- {
- mSysWellChiclet->updateWidget(isWindowEmpty());
- }
- else
- {
- llwarns << "Unable to remove IM Row from the list, sessionID: " << sessionId
- << llendl;
- }
-
- // remove all toasts that belong to this session from a screen
- if(mChannel)
- mChannel->removeToastsBySessionID(sessionId);
-
- // hide chiclet window if there are no items left
- if(isWindowEmpty())
- {
- setVisible(FALSE);
- }
- else
- {
- setFocus(true);
- }
-}
void LLIMWellWindow::addObjectRow(const LLUUID& notification_id, bool new_message/* = false*/)
{
if (mMessageList->getItemByValue(notification_id) == NULL)
{
ObjectRowPanel* item = new ObjectRowPanel(notification_id, new_message);
- if (mMessageList->addItem(item, notification_id))
- {
- mSysWellChiclet->updateWidget(isWindowEmpty());
- }
- else
+ if (!mMessageList->addItem(item, notification_id))
{
llwarns << "Unable to add Object Row into the list, notificationID: " << notification_id << llendl;
item->die();
@@ -757,14 +504,7 @@ void LLIMWellWindow::addObjectRow(const LLUUID& notification_id, bool new_messag
void LLIMWellWindow::removeObjectRow(const LLUUID& notification_id)
{
- if (mMessageList->removeItemByValue(notification_id))
- {
- if (mSysWellChiclet)
- {
- mSysWellChiclet->updateWidget(isWindowEmpty());
- }
- }
- else
+ if (!mMessageList->removeItemByValue(notification_id))
{
llwarns << "Unable to remove Object Row from the list, notificationID: " << notification_id << llendl;
}
@@ -777,21 +517,6 @@ void LLIMWellWindow::removeObjectRow(const LLUUID& notification_id)
}
}
-
-void LLIMWellWindow::addIMRow(const LLUUID& session_id)
-{
- if (hasIMRow(session_id)) return;
-
- LLIMModel* im_model = LLIMModel::getInstance();
- addIMRow(session_id, 0, im_model->getName(session_id), im_model->getOtherParticipantID(session_id));
- reshapeWindow();
-}
-
-bool LLIMWellWindow::hasIMRow(const LLUUID& session_id)
-{
- return mMessageList->getItemByValue(session_id);
-}
-
void LLIMWellWindow::closeAll()
{
// Generate an ignorable alert dialog if there is an active voice IM sesion
@@ -836,13 +561,6 @@ void LLIMWellWindow::closeAllImpl()
{
LLPanel* panel = mMessageList->getItemByValue(*iter);
- RowPanel* im_panel = dynamic_cast <RowPanel*> (panel);
- if (im_panel)
- {
- gIMMgr->leaveSession(*iter);
- continue;
- }
-
ObjectRowPanel* obj_panel = dynamic_cast <ObjectRowPanel*> (panel);
if (obj_panel)
{
@@ -867,4 +585,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..cc5c057d8b 100644
--- a/indra/newview/llsyswellwindow.h
+++ b/indra/newview/llsyswellwindow.h
@@ -27,29 +27,26 @@
#ifndef LL_LLSYSWELLWINDOW_H
#define LL_LLSYSWELLWINDOW_H
+#include "llimview.h"
+#include "llnotifications.h"
+#include "llscreenchannel.h"
#include "llsyswellitem.h"
-
#include "lltransientdockablefloater.h"
-#include "llbutton.h"
-#include "llscreenchannel.h"
-#include "llscrollcontainer.h"
-#include "llimview.h"
-
-#include "boost/shared_ptr.hpp"
class LLAvatarName;
-class LLFlatListView;
class LLChiclet;
+class LLFlatListView;
class LLIMChiclet;
class LLScriptChiclet;
class LLSysWellChiclet;
-
class LLSysWellWindow : public LLTransientDockableFloater
{
public:
+ LOG_CLASS(LLSysWellWindow);
+
LLSysWellWindow(const LLSD& key);
- ~LLSysWellWindow();
+ virtual ~LLSysWellWindow();
BOOL postBuild();
// other interface functions
@@ -84,7 +81,6 @@ protected:
virtual const std::string& getAnchorViewName() = 0;
void reshapeWindow();
- void releaseNewMessagesState();
// pointer to a corresponding channel's instance
LLNotificationsUI::LLScreenChannel* mChannel;
@@ -111,7 +107,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 +115,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 +134,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);
@@ -146,7 +150,7 @@ private:
*
* It contains a list list of all active IM sessions.
*/
-class LLIMWellWindow : public LLSysWellWindow, LLIMSessionObserver, LLInitClass<LLIMWellWindow>
+class LLIMWellWindow : public LLSysWellWindow, LLInitClass<LLIMWellWindow>
{
public:
LLIMWellWindow(const LLSD& key);
@@ -158,57 +162,19 @@ public:
/*virtual*/ BOOL postBuild();
- // 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);
- /*virtual*/ void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id);
-
void addObjectRow(const LLUUID& notification_id, bool new_message = false);
void removeObjectRow(const LLUUID& notification_id);
-
- void addIMRow(const LLUUID& session_id);
- bool hasIMRow(const LLUUID& session_id);
-
void closeAll();
protected:
/*virtual*/ const std::string& getAnchorViewName() { return IM_WELL_ANCHOR_NAME; }
private:
- LLChiclet * findIMChiclet(const LLUUID& sessionId);
LLChiclet* findObjectChiclet(const LLUUID& notification_id);
- void addIMRow(const LLUUID& sessionId, S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId);
- void delIMRow(const LLUUID& sessionId);
bool confirmCloseAll(const LLSD& notification, const LLSD& response);
void closeAllImpl();
- /**
- * Scrolling row panel.
- */
- class RowPanel: public LLPanel
- {
- public:
- RowPanel(const LLSysWellWindow* parent, const LLUUID& sessionId, S32 chicletCounter,
- const std::string& name, const LLUUID& otherParticipantId);
- virtual ~RowPanel();
- void onMouseEnter(S32 x, S32 y, MASK mask);
- void onMouseLeave(S32 x, S32 y, MASK mask);
- BOOL handleMouseDown(S32 x, S32 y, MASK mask);
- BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
-
- private:
- static const S32 CHICLET_HPAD = 10;
- void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name);
- void onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param);
- void onClosePanel();
- public:
- LLIMChiclet* mChiclet;
- private:
- LLButton* mCloseBtn;
- const LLSysWellWindow* mParent;
- };
-
class ObjectRowPanel: public LLPanel
{
public:
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
index ec36cf48c2..007eb8e33f 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,26 +651,12 @@ void LLFloaterTexturePicker::draw()
if (!is_filter_active && !mSelectedItemPinned)
{
folder_view->setPinningSelectedItem(mSelectedItemPinned);
- folder_view->dirtyFilter();
- folder_view->arrangeFromRoot();
-
+ folder_view->getViewModelItem()->dirtyFilter();
mSelectedItemPinned = TRUE;
}
}
}
-// static
-/*
-void LLFloaterTexturePicker::onSaveAnotherCopyDialog( S32 option, void* userdata )
-{
- LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
- if( 0 == option )
- {
- self->copyToInventoryFinal();
- }
-}
-*/
-
const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL copyable_only)
{
LLViewerInventoryCategory::cat_array_t cats;
@@ -815,7 +801,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 +997,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 +1311,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 +1323,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id)
}
else
{
- mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
- lldebugs << "mImageItemID: " << mImageItemID << llendl;
- mImageAssetID = floaterp->getAssetID();
- lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
+ mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
+ lldebugs << "mImageItemID: " << mImageItemID << llendl;
+ mImageAssetID = floaterp->getAssetID();
+ lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
}
if (op == TEXTURE_SELECT && mOnSelectCallback)
diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h
index e1d99b1bcb..ea62f758f8 100644
--- a/indra/newview/lltoast.h
+++ b/indra/newview/lltoast.h
@@ -169,6 +169,7 @@ public:
// get/set Toast's flags or states
// get information whether the notification corresponding to the toast is valid or not
bool isNotificationValid();
+
// get toast's Notification ID
const LLUUID getNotificationID() const { return mNotificationID;}
// get toast's Session ID
@@ -212,7 +213,7 @@ private:
//LLRootHandle<LLToast> mHandle;
- LLPanel* mWrapperPanel;
+ LLPanel* mWrapperPanel;
// timer counts a lifetime of a toast
std::auto_ptr<LLToastLifeTimer> mTimer;
@@ -220,8 +221,8 @@ private:
F32 mToastLifetime; // in seconds
F32 mToastFadingTime; // in seconds
- LLPanel* mPanel;
- LLButton* mHideBtn;
+ LLPanel* mPanel;
+ LLButton* mHideBtn;
LLColor4 mBgColor;
bool mCanFade;
diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp
index 75178a6ef8..4dc0d424ac 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)
{
@@ -69,10 +69,8 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification
//header title
std::string from_name = payload["sender_name"].asString();
- if (LLAvatarNameCache::useDisplayNames())
- {
- from_name = LLCacheName::buildUsername(from_name);
- }
+ from_name = LLCacheName::buildUsername(from_name);
+
std::stringstream from;
from << from_name << "/" << groupData.mName;
LLTextBox* pTitleText = getChild<LLTextBox>("title");
@@ -112,7 +110,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 602b924398..4ef5ad845c 100644
--- a/indra/newview/lltoastnotifypanel.cpp
+++ b/indra/newview/lltoastnotifypanel.cpp
@@ -40,11 +40,14 @@
#include "lltrans.h"
#include "llnotificationsutil.h"
#include "llviewermessage.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
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();
- }
- 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());
- }
+ init(rect, show_images);
}
- // 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)
{
@@ -385,210 +223,278 @@ 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;
-
-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;
-
- buttons.insert("Teleport");
- buttons.insert("Cancel");
-
- disable_map.insert(std::make_pair("Teleport", buttons));
- disable_map.insert(std::make_pair("Cancel", buttons));
+ deleteAllChildren();
+
+ mTextBox = NULL;
+ mInfoPanel = NULL;
+ mControlPanel = NULL;
+ mNumOptions = 0;
+ mNumButtons = 0;
+ mAddedDefaultBtn = false;
+
+ LLRect current_rect = getRect();
+
+ setXMLFilename("");
+ 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 = 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;
+
+ // setup parameters
+ // get a notification message
+ mMessage = mNotification->getMessage();
+ // init font variables
+ if (!sFont)
+ {
+ 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();
+
+ // 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");
+ }
+
+ mTextBox->setMaxTextLength(MAX_LENGTH);
+ mTextBox->setVisible(TRUE);
+ mTextBox->setPlainText(!show_images);
+ mTextBox->setValue(mNotification->getMessage());
+
+ // add buttons for a script notification
+ if (mIsTip)
+ {
+ adjustPanelForTipNotice();
+ }
+ 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());
+ }
+ }
+
+ //.xml file intially makes info panel only follow left/right/top. This is so that when control buttons are added the info panel
+ //can shift upward making room for the buttons inside mControlPanel. After the buttons are added, the info panel can then be set to follow 'all'.
+ mInfoPanel->setFollowsAll();
+ snapToMessageHeight(mTextBox, MAX_LENGTH);
- return disable_map;
+ // reshape the panel to its previous size
+ if (current_rect.notEmpty())
+ {
+ reshape(current_rect.getWidth(), current_rect.getHeight());
+ }
}
-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;
+LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */,
+ bool show_images /* = true */, LLTextBase* parent_text)
+: mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images),
+ mParentText(parent_text)
+{
+ compactButtons();
}
-button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name)
+LLIMToastNotifyPanel::~LLIMToastNotifyPanel()
{
- 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) || ("TeleportOffered_MaturityExceeded" == 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)
+void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
{
- 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);
- }
- }
+ LLToastPanel::reshape(width, height, called_from_parent);
+ snapToMessageHeight();
}
-// static
-void LLToastNotifyPanel::onClickButton(void* data)
+void LLIMToastNotifyPanel::snapToMessageHeight()
{
- 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;
- }
-
- 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)
+ if(!mTextBox)
{
- new_info = new LLOfferInfo(*old_info);
- self->mNotification->setResponder(new_info);
+ return;
}
- self->mNotification->respond(response);
-
- if(is_reusable)
- {
- sButtonClickSignal(self->mNotification->getID(), button_name);
- }
- else
+ //Add message height if it is visible
+ if (mTextBox->getVisible())
{
- // disable all buttons
- self->mControlPanel->setEnabled(FALSE);
- }
-}
+ S32 new_panel_height = computeSnappedToMessageHeight(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);
+ //reshape the panel with new height
+ if (new_panel_height != getRect().getHeight())
+ {
+ LLToastNotifyPanel::reshape( getRect().getWidth(), new_panel_height);
+ }
}
}
-void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification)
+void LLIMToastNotifyPanel::compactButtons()
{
- LLSD response = notification->getResponse();
- for (LLSD::map_const_iterator response_it = response.beginMap();
- response_it != response.endMap(); ++response_it)
+ //we can't set follows in xml since it broke toasts behavior
+ setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP);
+
+ 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++)
{
- if (response_it->second.isBoolean() && response_it->second.asBoolean())
+ LLButton * button = dynamic_cast<LLButton*> (*it);
+ if (button != NULL)
{
- // that after multiple responses there can be many pressed buttons
- // need to process them all
- disableButtons(notification->getName(), response_it->first);
+ 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();
}
}
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-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)
-{
- mTextBox->setFollowsAll();
+ if (mParentText)
+ {
+ mParentText->needsReflow();
+ }
}
-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())
+void LLIMToastNotifyPanel::updateNotification()
{
- mCloseNotificationOnDestroy = false;
+ init(LLRect(), true);
}
-}
-void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */)
+void LLIMToastNotifyPanel::init( LLRect rect, bool show_images )
{
- S32 text_height = mTextBox->getTextBoundingRect().getHeight();
- S32 widget_height = mTextBox->getRect().getHeight();
- S32 delta = text_height - widget_height;
- LLRect rc = getRect();
+ LLToastNotifyPanel::init(LLRect(), show_images);
- rc.setLeftTopAndSize(rc.mLeft, rc.mTop, width, height + delta);
- height = rc.getHeight();
- width = rc.getWidth();
-
- bool is_width_changed = width != getRect().getWidth();
-
- LLToastPanel::reshape(width, height, called_from_parent);
-
- // 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)
- {
- reshape(width, height, called_from_parent);
- }
+ compactButtons();
}
// EOF
+
diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h
index db517ec858..d02171b512 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,14 +139,27 @@ 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;
+
+private:
+ void snapToMessageHeight();
};
#endif /* LLTOASTNOTIFYPANEL_H_ */
diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp
index c33fde99c5..a30f841980 100644
--- a/indra/newview/lltoastpanel.cpp
+++ b/indra/newview/lltoastpanel.cpp
@@ -58,6 +58,25 @@ const LLUUID& LLToastPanel::getID()
return mNotification->id();
}
+S32 LLToastPanel::computeSnappedToMessageHeight(LLTextBase* message, S32 maxLineCount)
+{
+ S32 heightDelta = 0;
+ S32 maxTextHeight = message->getFont()->getLineHeight() * maxLineCount;
+
+ LLRect messageRect = message->getRect();
+ S32 oldTextHeight = messageRect.getHeight();
+
+ //Knowing the height is set to max allowed, getTextPixelHeight returns needed text height
+ //Perhaps we need to pass maxLineCount as parameter to getTextPixelHeight to avoid previous reshape.
+ S32 requiredTextHeight = message->getTextBoundingRect().getHeight();
+ S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight);
+
+ heightDelta = newTextHeight - oldTextHeight;
+ S32 new_panel_height = llmax(getRect().getHeight() + heightDelta, MIN_PANEL_HEIGHT);
+
+ return new_panel_height;
+}
+
//snap to the message height if it is visible
void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount)
{
@@ -69,22 +88,13 @@ void LLToastPanel::snapToMessageHeight(LLTextBase* message, S32 maxLineCount)
//Add message height if it is visible
if (message->getVisible())
{
- S32 heightDelta = 0;
- S32 maxTextHeight = message->getDefaultFont()->getLineHeight() * maxLineCount;
-
- LLRect messageRect = message->getRect();
- S32 oldTextHeight = messageRect.getHeight();
-
- //Knowing the height is set to max allowed, getTextPixelHeight returns needed text height
- //Perhaps we need to pass maxLineCount as parameter to getTextPixelHeight to avoid previous reshape.
- S32 requiredTextHeight = message->getTextBoundingRect().getHeight();
- S32 newTextHeight = llmin(requiredTextHeight, maxTextHeight);
-
- //Calculate last delta height deducting previous heightDelta
- heightDelta = newTextHeight - oldTextHeight - heightDelta;
+ S32 new_panel_height = computeSnappedToMessageHeight(message, maxLineCount);
//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 +108,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..e4ab95007e 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;
@@ -65,6 +59,7 @@ public:
protected:
LLNotificationPtr mNotification;
void snapToMessageHeight(LLTextBase* message, S32 maxLineCount);
+ S32 computeSnappedToMessageHeight(LLTextBase* message, S32 maxLineCount);
};
#endif /* LL_TOASTPANEL_H */
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 a29f58b319..b2318f9158 100644
--- a/indra/newview/lltoolbarview.cpp
+++ b/indra/newview/lltoolbarview.cpp
@@ -241,8 +241,9 @@ bool LLToolBarView::loadToolbars(bool force_default)
LLXUIParser parser;
if (!err)
{
- parser.readXUI(root, toolbar_set, toolbar_file);
+ parser.readXUI(root, toolbar_set, toolbar_file);
}
+
if (!err && !toolbar_set.validateBlock())
{
llwarns << "Unable to validate toolbars from file: " << toolbar_file << llendl;
@@ -254,8 +255,9 @@ bool LLToolBarView::loadToolbars(bool force_default)
if (force_default)
{
llerrs << "Unable to load toolbars from default file : " << toolbar_file << llendl;
- return false;
- }
+ return false;
+ }
+
// Try to load the default toolbars
return loadToolbars(true);
}
@@ -605,7 +607,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/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
index a0c12df834..f24891baf6 100644
--- a/indra/newview/lltoolpie.cpp
+++ b/indra/newview/lltoolpie.cpp
@@ -973,33 +973,16 @@ BOOL LLToolPie::handleTooltipObject( LLViewerObject* hover_object, std::string l
|| !existing_inspector->getVisible()
|| existing_inspector->getKey()["avatar_id"].asUUID() != hover_object->getID())
{
- // IDEVO: try to get display name + username
+ // Try to get display name + username
std::string final_name;
- std::string full_name;
- if (!gCacheName->getFullName(hover_object->getID(), full_name))
- {
- LLNameValue* firstname = hover_object->getNVPair("FirstName");
- LLNameValue* lastname = hover_object->getNVPair("LastName");
- if (firstname && lastname)
- {
- full_name = LLCacheName::buildFullName(
- firstname->getString(), lastname->getString());
- }
- else
- {
- full_name = LLTrans::getString("TooltipPerson");
- }
- }
-
LLAvatarName av_name;
- if (LLAvatarNameCache::useDisplayNames() &&
- LLAvatarNameCache::get(hover_object->getID(), &av_name))
+ if (LLAvatarNameCache::get(hover_object->getID(), &av_name))
{
final_name = av_name.getCompleteName();
}
else
{
- final_name = full_name;
+ final_name = LLTrans::getString("TooltipPerson");;
}
// *HACK: We may select this object, so pretend it was clicked
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/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp
index 8d8c401dac..564bf7997a 100644
--- a/indra/newview/llvieweraudio.cpp
+++ b/indra/newview/llvieweraudio.cpp
@@ -30,6 +30,7 @@
#include "llagent.h"
#include "llagentcamera.h"
#include "llappviewer.h"
+#include "lldeferredsounds.h"
#include "llvieweraudio.h"
#include "llviewercamera.h"
#include "llviewercontrol.h"
@@ -388,6 +389,12 @@ void audio_update_volume(bool force_update)
gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff"));
gAudiop->setMuted(mute_audio || progress_view_visible);
+ //Play any deferred sounds when unmuted
+ if(!gAudiop->getMuted())
+ {
+ LLDeferredSounds::instance().playdeferredSounds();
+ }
+
if (force_update)
{
audio_update_wind(true);
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
index ffeea2f4df..40577118ba 100644
--- a/indra/newview/llviewerdisplay.cpp
+++ b/indra/newview/llviewerdisplay.cpp
@@ -1038,7 +1038,6 @@ void render_hud_attachments()
if (LLPipeline::sShowHUDAttachments && !gDisconnected && setup_hud_matrices())
{
LLCamera hud_cam = *LLViewerCamera::getInstance();
- LLVector3 origin = hud_cam.getOrigin();
hud_cam.setOrigin(-1.f,0,0);
hud_cam.setAxes(LLVector3(1,0,0), LLVector3(0,1,0), LLVector3(0,0,1));
LLViewerCamera::updateFrustumPlanes(hud_cam, TRUE);
diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp
index 5741fab29a..6bd5631df6 100644
--- a/indra/newview/llviewerdisplayname.cpp
+++ b/indra/newview/llviewerdisplayname.cpp
@@ -53,6 +53,7 @@ namespace LLViewerDisplayName
sNameChangedSignal.connect(cb);
}
+ void doNothing() { }
}
class LLSetDisplayNameResponder : public LLHTTPClient::Responder
@@ -97,7 +98,7 @@ void LLViewerDisplayName::set(const std::string& display_name, const set_name_sl
// People API expects array of [ "old value", "new value" ]
LLSD change_array = LLSD::emptyArray();
- change_array.append(av_name.mDisplayName);
+ change_array.append(av_name.getDisplayName());
change_array.append(display_name);
llinfos << "Set name POST to " << cap_url << llendl;
@@ -139,9 +140,9 @@ public:
LLUUID agent_id = gAgent.getID();
// Flush stale data
LLAvatarNameCache::erase( agent_id );
- // Queue request for new data
- LLAvatarName ignored;
- LLAvatarNameCache::get( agent_id, &ignored );
+ // Queue request for new data: nothing to do on callback though...
+ // Note: no need to disconnect the callback as it never gets out of scope
+ LLAvatarNameCache::get(agent_id, boost::bind(&LLViewerDisplayName::doNothing));
// Kill name tag, as it is wrong
LLVOAvatar::invalidateNameTag( agent_id );
}
@@ -189,8 +190,8 @@ class LLDisplayNameUpdate : public LLHTTPNode
LLSD args;
args["OLD_NAME"] = old_display_name;
- args["SLID"] = av_name.mUsername;
- args["NEW_NAME"] = av_name.mDisplayName;
+ args["SLID"] = av_name.getUserName();
+ args["NEW_NAME"] = av_name.getDisplayName();
LLNotificationsUtil::add("DisplayNameUpdate", args);
if (agent_id == gAgent.getID())
{
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 1f7cf0cdd4..c6b28b9e5e 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -32,7 +32,6 @@
#include "llviewerfloaterreg.h"
#include "llfloaterautoreplacesettings.h"
#include "llcompilequeue.h"
-#include "llcallfloater.h"
#include "llfasttimerview.h"
#include "llfloaterabout.h"
#include "llfloaterauction.h"
@@ -50,6 +49,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"
@@ -69,7 +71,7 @@
#include "llfloatermediasettings.h"
#include "llfloaterhud.h"
#include "llfloaterimagepreview.h"
-#include "llimfloater.h"
+#include "llfloaterimsession.h"
#include "llfloaterinspect.h"
#include "llfloaterinventory.h"
#include "llfloaterjoystick.h"
@@ -114,17 +116,18 @@
#include "llfloatertranslationsettings.h"
#include "llfloateruipreview.h"
#include "llfloatervoiceeffect.h"
+#include "llfloatervoicevolume.h"
#include "llfloaterwhitelistentry.h"
#include "llfloaterwindowsize.h"
#include "llfloaterworldmap.h"
-#include "llimfloatercontainer.h"
+#include "llfloaterimcontainer.h"
#include "llinspectavatar.h"
#include "llinspectgroup.h"
#include "llinspectobject.h"
#include "llinspectremoteobject.h"
#include "llinspecttoast.h"
#include "llmoveview.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llpanelblockedlist.h"
#include "llpanelclassified.h"
#include "llpreviewanim.h"
@@ -137,7 +140,6 @@
#include "llscriptfloater.h"
#include "llfloatermodelpreview.h"
#include "llcommandhandler.h"
-#include "llnearbychatbar.h"
// *NOTE: Please add files in alphabetical order to keep merges easy.
@@ -190,9 +192,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)&LLFloaterIMNearbyChat::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>);
@@ -214,8 +217,8 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHelpBrowser>);
LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterHUD>);
- LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIMFloater>);
- LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIMFloaterContainer>);
+ LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterIMSession>);
+ LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterIMContainer>);
LLFloaterReg::add("im_well_window", "floater_sys_well.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIMWellWindow>);
LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLIncomingCallDialog>);
LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
@@ -224,6 +227,7 @@ void LLViewerFloaterReg::registerFloaters()
LLInspectGroupUtil::registerFloater();
LLInspectObjectUtil::registerFloater();
LLInspectRemoteObjectUtil::registerFloater();
+ LLFloaterVoiceVolumeUtil::registerFloater();
LLNotificationsUI::registerFloater();
LLFloaterDisplayNameUtil::registerFloater();
@@ -268,6 +272,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");
@@ -316,7 +321,6 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("upload_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterScriptPreview>, "upload");
LLFloaterReg::add("upload_sound", "floater_sound_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSoundPreview>, "upload");
- LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLCallFloater>);
LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterVoiceEffect>);
LLFloaterReg::add("web_content", "floater_web_content.xml", (LLFloaterBuildFunc)&LLFloaterWebContent::create);
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..3f35a5001d 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 "llfloaterimnearbychat.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<LLFloaterIMNearbyChat>("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..4ecdc31e21 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 "llfloaterimnearbychat.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);
+ LLFloaterIMNearbyChat::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<LLFloaterIMNearbyChat>("nearby_chat"))->getCurrentChat().empty())
{
// No existing chat in chat editor, insert '/'
- LLNearbyChatBar::startChat("/");
+ LLFloaterIMNearbyChat::startChat("/");
}
else
{
// Don't overwrite existing text in chat editor
- LLNearbyChatBar::startChat(NULL);
+ LLFloaterIMNearbyChat::startChat(NULL);
}
}
}
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
index 93e4f4428a..66db0ac8f3 100644
--- a/indra/newview/llviewermenu.cpp
+++ b/indra/newview/llviewermenu.cpp
@@ -59,6 +59,7 @@
#include "llbuycurrencyhtml.h"
#include "llfloatergodtools.h"
#include "llfloaterinventory.h"
+#include "llfloaterimcontainer.h"
#include "llfloaterland.h"
#include "llfloaterpathfindingcharacters.h"
#include "llfloaterpathfindinglinksets.h"
@@ -107,6 +108,7 @@
#include "llviewerparcelmgr.h"
#include "llviewerstats.h"
#include "llvoavatarself.h"
+#include "llvoicevivox.h"
#include "llworldmap.h"
#include "pipeline.h"
#include "llviewerjoystick.h"
@@ -178,9 +180,6 @@ LLContextMenu* gDetachPieMenu = NULL;
LLContextMenu* gDetachScreenPieMenu = NULL;
LLContextMenu* gDetachBodyPartPieMenus[8];
-LLMenuItemCallGL* gAFKMenu = NULL;
-LLMenuItemCallGL* gBusyMenu = NULL;
-
//
// Local prototypes
@@ -470,8 +469,6 @@ void init_menus()
gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", upload_cost);
gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", upload_cost);
- gAFKMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Away", TRUE);
- gBusyMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Busy", TRUE);
gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE);
gDetachSubMenu = gMenuBarView->findChildMenuByName("Detach Object", TRUE);
@@ -3295,15 +3292,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);
@@ -3521,7 +3509,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);
}
@@ -5568,16 +5557,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)
@@ -5651,18 +5630,18 @@ class LLWorldSetAway : public view_listener_t
}
};
-class LLWorldSetBusy : public view_listener_t
+class LLWorldSetDoNotDisturb : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
{
- if (gAgent.getBusy())
+ if (gAgent.isDoNotDisturb())
{
- gAgent.clearBusy();
+ gAgent.setDoNotDisturb(false);
}
else
{
- gAgent.setBusy();
- LLNotificationsUtil::add("BusyModeSet");
+ gAgent.setDoNotDisturb(true);
+ LLNotificationsUtil::add("DoNotDisturbModeSet");
}
return true;
}
@@ -5824,7 +5803,7 @@ bool complete_give_money(const LLSD& notification, const LLSD& response, LLObjec
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
if (option == 0)
{
- gAgent.clearBusy();
+ gAgent.setDoNotDisturb(false);
}
LLViewerObject* objectp = selection->getPrimaryObject();
@@ -5857,12 +5836,12 @@ bool complete_give_money(const LLSD& notification, const LLSD& response, LLObjec
void handle_give_money_dialog()
{
- LLNotification::Params params("BusyModePay");
+ LLNotification::Params params("DoNotDisturbModePay");
params.functor.function(boost::bind(complete_give_money, _1, _2, LLSelectMgr::getInstance()->getSelection()));
- if (gAgent.getBusy())
+ if (gAgent.isDoNotDisturb())
{
- // warn users of being in busy mode during a transaction
+ // warn users of being in do not disturb mode during a transaction
LLNotifications::instance().add(params);
}
else
@@ -7634,6 +7613,20 @@ void handle_web_content_test(const LLSD& param)
LLWeb::loadURLInternal(url);
}
+void handle_show_url(const LLSD& param)
+{
+ std::string url = param.asString();
+ if(gSavedSettings.getBOOL("UseExternalBrowser"))
+ {
+ LLWeb::loadURLExternal(url);
+ }
+ else
+ {
+ LLWeb::loadURLInternal(url);
+ }
+
+}
+
void handle_buy_currency_test(void*)
{
std::string url =
@@ -7887,6 +7880,22 @@ class LLViewCheckRenderType : public view_listener_t
}
};
+class LLViewStatusAway : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ return (gAgent.isInitialized() && gAgent.getAFK());
+ }
+};
+
+class LLViewStatusDoNotDisturb : public view_listener_t
+{
+ bool handleEvent(const LLSD& userdata)
+ {
+ return (gAgent.isInitialized() && gAgent.isDoNotDisturb());
+ }
+};
+
class LLViewShowHUDAttachments : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8113,11 +8122,7 @@ class LLWorldPostProcess : public view_listener_t
void handle_flush_name_caches()
{
- // Toggle display names on and off to flush
- bool use_display_names = LLAvatarNameCache::useDisplayNames();
- LLAvatarNameCache::setUseDisplayNames(!use_display_names);
- LLAvatarNameCache::setUseDisplayNames(use_display_names);
-
+ LLAvatarNameCache::cleanupClass();
if (gCacheName) gCacheName->clear();
}
@@ -8142,6 +8147,11 @@ public:
}
};
+void handle_voice_morphing_subscribe()
+{
+ LLWeb::loadURLExternal(LLTrans::getString("voice_morphing_url"));
+}
+
class LLToggleUIHints : public view_listener_t
{
bool handleEvent(const LLSD& userdata)
@@ -8275,6 +8285,7 @@ void initialize_menus()
commit.add("Inventory.NewWindow", boost::bind(&LLFloaterInventory::showAgentInventory));
+ enable.add("Conversation.IsConversationLoggingAllowed", boost::bind(&LLFloaterIMContainer::isConversationLoggingAllowed));
// Agent
commit.add("Agent.toggleFlying", boost::bind(&LLAgent::toggleFlying));
@@ -8320,14 +8331,21 @@ void initialize_menus()
view_listener_t::addMenu(new LLViewCheckShowHoverTips(), "View.CheckShowHoverTips");
view_listener_t::addMenu(new LLViewCheckHighlightTransparent(), "View.CheckHighlightTransparent");
view_listener_t::addMenu(new LLViewCheckRenderType(), "View.CheckRenderType");
+ view_listener_t::addMenu(new LLViewStatusAway(), "View.Status.CheckAway");
+ view_listener_t::addMenu(new LLViewStatusDoNotDisturb(), "View.Status.CheckDoNotDisturb");
view_listener_t::addMenu(new LLViewCheckHUDAttachments(), "View.CheckHUDAttachments");
-
+
// Me > Movement
view_listener_t::addMenu(new LLAdvancedAgentFlyingInfo(), "Agent.getFlying");
-
- // Communicate
- view_listener_t::addMenu(new LLCommunicateBlockList(), "Communicate.BlockList");
-
+
+ // Communicate > Voice morphing > Subscribe...
+ commit.add("Communicate.VoiceMorphing.Subscribe", boost::bind(&handle_voice_morphing_subscribe));
+ LLVivoxVoiceClient * voice_clientp = LLVivoxVoiceClient::getInstance();
+ enable.add("Communicate.VoiceMorphing.NoVoiceMorphing.Check"
+ , boost::bind(&LLVivoxVoiceClient::onCheckVoiceEffect, voice_clientp, "NoVoiceMorphing"));
+ commit.add("Communicate.VoiceMorphing.NoVoiceMorphing.Click"
+ , boost::bind(&LLVivoxVoiceClient::onClickVoiceEffect, voice_clientp, "NoVoiceMorphing"));
+
// World menu
view_listener_t::addMenu(new LLWorldAlwaysRun(), "World.AlwaysRun");
view_listener_t::addMenu(new LLWorldCreateLandmark(), "World.CreateLandmark");
@@ -8335,7 +8353,7 @@ void initialize_menus()
view_listener_t::addMenu(new LLWorldSetHomeLocation(), "World.SetHomeLocation");
view_listener_t::addMenu(new LLWorldTeleportHome(), "World.TeleportHome");
view_listener_t::addMenu(new LLWorldSetAway(), "World.SetAway");
- view_listener_t::addMenu(new LLWorldSetBusy(), "World.SetBusy");
+ view_listener_t::addMenu(new LLWorldSetDoNotDisturb(), "World.SetDoNotDisturb");
view_listener_t::addMenu(new LLWorldEnableCreateLandmark(), "World.EnableCreateLandmark");
view_listener_t::addMenu(new LLWorldEnableSetHomeLocation(), "World.EnableSetHomeLocation");
@@ -8451,6 +8469,7 @@ void initialize_menus()
// Advanced > UI
commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser
commit.add("Advanced.WebContentTest", boost::bind(&handle_web_content_test, _2)); // this one opens the Web Content floater
+ commit.add("Advanced.ShowURL", boost::bind(&handle_show_url, _2));
view_listener_t::addMenu(new LLAdvancedBuyCurrencyTest(), "Advanced.BuyCurrencyTest");
view_listener_t::addMenu(new LLAdvancedDumpSelectMgr(), "Advanced.DumpSelectMgr");
view_listener_t::addMenu(new LLAdvancedDumpInventory(), "Advanced.DumpInventory");
diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h
index 2eb458fa02..01534503f3 100644
--- a/indra/newview/llviewermenu.h
+++ b/indra/newview/llviewermenu.h
@@ -27,7 +27,7 @@
#ifndef LL_LLVIEWERMENU_H
#define LL_LLVIEWERMENU_H
-#include "llmenugl.h"
+#include "../llui/llmenugl.h"
#include "llsafehandle.h"
class LLMessageSystem;
@@ -184,8 +184,6 @@ extern LLContextMenu* gDetachPieMenu;
extern LLContextMenu* gAttachBodyPartPieMenus[8];
extern LLContextMenu* gDetachBodyPartPieMenus[8];
-extern LLMenuItemCallGL* gAFKMenu;
-extern LLMenuItemCallGL* gBusyMenu;
extern LLMenuItemCallGL* gMutePieMenu;
extern LLMenuItemCallGL* gMuteObjectPieMenu;
extern LLMenuItemCallGL* gBuyPassPieMenu;
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
index 6e02fafd01..b915cfe324 100755
--- a/indra/newview/llviewermessage.cpp
+++ b/indra/newview/llviewermessage.cpp
@@ -65,10 +65,11 @@
#include "llfloatersnapshot.h"
#include "llhudeffecttrail.h"
#include "llhudmanager.h"
+#include "llimview.h"
#include "llinventoryfunctions.h"
#include "llinventoryobserver.h"
#include "llinventorypanel.h"
-#include "llnearbychat.h"
+#include "llfloaterimnearbychat.h"
#include "llnotifications.h"
#include "llnotificationsutil.h"
#include "llpanelgrouplandmoney.h"
@@ -120,6 +121,8 @@
#pragma warning (disable:4702)
#endif
+extern void on_new_message(const LLSD& msg);
+
//
// Constants
//
@@ -184,78 +187,82 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response)
S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
LLMessageSystem* msg = gMessageSystem;
const LLSD& payload = notification["payload"];
-
- // add friend to recent people list
- LLRecentPeople::instance().add(payload["from_id"]);
-
- switch(option)
- {
- case 0:
- {
- // accept
- LLAvatarTracker::formFriendship(payload["from_id"]);
-
- const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD);
-
- // This will also trigger an onlinenotification if the user is online
- msg->newMessageFast(_PREHASH_AcceptFriendship);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_TransactionBlock);
- msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
- msg->nextBlockFast(_PREHASH_FolderData);
- msg->addUUIDFast(_PREHASH_FolderID, fid);
- msg->sendReliable(LLHost(payload["sender"].asString()));
-
- LLSD payload = notification["payload"];
- payload["SUPPRESS_TOAST"] = true;
- LLNotificationsUtil::add("FriendshipAcceptedByMe",
- notification["substitutions"], payload);
- break;
- }
- case 1: // Decline
- {
- LLSD payload = notification["payload"];
- payload["SUPPRESS_TOAST"] = true;
- LLNotificationsUtil::add("FriendshipDeclinedByMe",
- notification["substitutions"], payload);
- }
- // fall-through
- case 2: // Send IM - decline and start IM session
- {
- // decline
- // We no longer notify other viewers, but we DO still send
- // the rejection to the simulator to delete the pending userop.
- msg->newMessageFast(_PREHASH_DeclineFriendship);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_TransactionBlock);
- msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
- msg->sendReliable(LLHost(payload["sender"].asString()));
-
- // start IM session
- if(2 == option)
- {
- LLAvatarActions::startIM(payload["from_id"].asUUID());
- }
- }
- default:
- // close button probably, possibly timed out
- break;
- }
+ LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID());
+
+ // this will be skipped if the user offering friendship is blocked
+ if (notification_ptr)
+ {
+ // add friend to recent people list
+ LLRecentPeople::instance().add(payload["from_id"]);
+
+ switch(option)
+ {
+ case 0:
+ {
+ // accept
+ LLAvatarTracker::formFriendship(payload["from_id"]);
+
+ const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD);
+
+ // This will also trigger an onlinenotification if the user is online
+ msg->newMessageFast(_PREHASH_AcceptFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, fid);
+ msg->sendReliable(LLHost(payload["sender"].asString()));
+
+ LLSD payload = notification["payload"];
+ LLNotificationsUtil::add("FriendshipAcceptedByMe",
+ notification["substitutions"], payload);
+ break;
+ }
+ case 1: // Decline
+ {
+ LLSD payload = notification["payload"];
+ LLNotificationsUtil::add("FriendshipDeclinedByMe",
+ notification["substitutions"], payload);
+ }
+ // fall-through
+ case 2: // Send IM - decline and start IM session
+ {
+ // decline
+ // We no longer notify other viewers, but we DO still send
+ // the rejection to the simulator to delete the pending userop.
+ msg->newMessageFast(_PREHASH_DeclineFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, payload["session_id"]);
+ msg->sendReliable(LLHost(payload["sender"].asString()));
+
+ // start IM session
+ if(2 == option)
+ {
+ LLAvatarActions::startIM(payload["from_id"].asUUID());
+ }
+ }
+ default:
+ // close button probably, possibly timed out
+ 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);
static LLNotificationFunctorRegistration friendship_offer_callback_reg_nm("OfferFriendshipNoMessage", friendship_offer_callback);
-//const char BUSY_AUTO_RESPONSE[] = "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.";
-
-//
// Functions
//
@@ -726,7 +733,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 +821,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 +862,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 +1176,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)
{
@@ -1344,6 +1365,8 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id,
gSavedSettings.getString("NotificationChannelUUID")), OfferMatcher(blocked_id));
}
+std::string LLOfferInfo::mResponderType = "offer_info";
+
LLOfferInfo::LLOfferInfo()
: LLNotificationResponderInterface()
, mFromGroup(FALSE)
@@ -1389,6 +1412,7 @@ LLOfferInfo::LLOfferInfo(const LLOfferInfo& info)
LLSD LLOfferInfo::asLLSD()
{
LLSD sd;
+ sd["responder_type"] = mResponderType;
sd["im_type"] = mIM;
sd["from_id"] = mFromID;
sd["from_group"] = mFromGroup;
@@ -1478,16 +1502,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));
@@ -1500,8 +1523,8 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
// TODO: when task inventory offers can also be handled the new way, migrate the code that sets these strings here:
from_string = chatHistory_string = mFromName;
- bool busy = gAgent.getBusy();
-
+ LLNotificationFormPtr modified_form(notification_ptr ? new LLNotificationForm(*notification_ptr->getForm()) : new LLNotificationForm());
+
switch(button)
{
case IOR_SHOW:
@@ -1545,6 +1568,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 +1585,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:
{
@@ -1589,12 +1622,13 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
{
opener = discard_agent_offer;
}
-
-
- if (busy && (!mFromGroup && !mFromObject))
+
+ if (modified_form != NULL)
{
- busy_message(gMessageSystem, mFromID);
+ modified_form->setElementEnabled("Show", false);
+ modified_form->setElementEnabled("Discard", false);
}
+
break;
}
default:
@@ -1614,6 +1648,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD&
{
delete this;
}
+
return false;
}
@@ -1704,7 +1739,7 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const
from_string = chatHistory_string = mFromName;
}
- bool busy = gAgent.getBusy();
+ bool is_do_not_disturb = gAgent.isDoNotDisturb();
switch(button)
{
@@ -1777,9 +1812,9 @@ bool LLOfferInfo::inventory_task_offer_callback(const LLSD& notification, const
LLNotificationsUtil::add("SystemMessageTip", args);
}
- if (busy && (!mFromGroup && !mFromObject))
+ if (is_do_not_disturb && (!mFromGroup && !mFromObject))
{
- busy_message(msg,mFromID);
+ send_do_not_disturb_message(msg,mFromID);
}
break;
}
@@ -1931,6 +1966,7 @@ void inventory_offer_handler(LLOfferInfo* info)
p.substitutions(args).payload(payload).functor.responder(LLNotificationResponderPtr(info));
info->mPersist = true;
p.name = "UserGiveItem";
+ p.offer_from_agent = true;
// Prefetch the item into your local inventory.
LLInventoryFetchItemsObserver* fetch_item = new LLInventoryFetchItemsObserver(info->mObjectID);
@@ -1947,6 +1983,11 @@ void inventory_offer_handler(LLOfferInfo* info)
// In viewer 2 we're now auto receiving inventory offers and messaging as such (not sending reject messages).
info->send_auto_receive_response();
+ if (gAgent.isDoNotDisturb())
+ {
+ send_do_not_disturb_message(gMessageSystem, info->mFromID);
+ }
+
// Inform user that there is a script floater via toast system
{
payload["give_inventory_notification"] = TRUE;
@@ -1991,6 +2032,18 @@ bool lure_callback(const LLSD& notification, const LLSD& response)
lure_id);
break;
}
+
+ LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID());
+
+ if (notification_ptr)
+ {
+ 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);
@@ -2152,7 +2205,7 @@ static std::string clean_name_from_im(const std::string& name, EInstantMessage t
case IM_SESSION_SEND:
case IM_SESSION_LEAVE:
//IM_FROM_TASK
- case IM_BUSY_AUTO_RESPONSE:
+ case IM_DO_NOT_DISTURB_AUTO_RESPONSE:
case IM_CONSOLE_AND_CHAT_HISTORY:
case IM_LURE_USER:
case IM_LURE_ACCEPTED:
@@ -2193,16 +2246,7 @@ static std::string clean_name_from_task_im(const std::string& msg,
// Don't try to clean up group names
if (!from_group)
{
- if (LLAvatarNameCache::useDisplayNames())
- {
- // ...just convert to username
- final += LLCacheName::buildUsername(name);
- }
- else
- {
- // ...strip out legacy "Resident" name
- final += LLCacheName::cleanFullName(name);
- }
+ final += LLCacheName::buildUsername(name);
}
final += match[3].str();
return final;
@@ -2210,13 +2254,13 @@ static std::string clean_name_from_task_im(const std::string& msg,
return msg;
}
-void notification_display_name_callback(const LLUUID& id,
+static void notification_display_name_callback(const LLUUID& id,
const LLAvatarName& av_name,
const std::string& name,
LLSD& substitutions,
const LLSD& payload)
{
- substitutions["NAME"] = av_name.mDisplayName;
+ substitutions["NAME"] = av_name.getDisplayName();
LLNotificationsUtil::add(name, substitutions, payload);
}
@@ -2234,7 +2278,7 @@ protected:
};
// Callback for name resolution of a god/estate message
-void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string message)
+static void god_message_name_cb(const LLAvatarName& av_name, LLChat chat, std::string message)
{
LLSD args;
args["NAME"] = av_name.getCompleteName();
@@ -2244,12 +2288,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)
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
{
nearby_chat->addMessage(chat);
}
-
}
void process_improved_im(LLMessageSystem *msg, void **user_data)
@@ -2302,16 +2345,15 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
// IDEVO convert new-style "Resident" names for display
name = clean_name_from_im(name, dialog);
- BOOL is_busy = gAgent.getBusy();
+ BOOL is_do_not_disturb = gAgent.isDoNotDisturb();
BOOL is_muted = LLMuteList::getInstance()->isMuted(from_id, name, LLMute::flagTextChat)
// object IMs contain sender object id in session_id (STORM-1209)
|| dialog == IM_FROM_TASK && LLMuteList::getInstance()->isMuted(session_id);
- BOOL is_linden = LLMuteList::getInstance()->isLinden(name);
BOOL is_owned_by_me = FALSE;
BOOL is_friend = (LLAvatarTracker::instance().getBuddyInfo(from_id) == NULL) ? false : true;
BOOL accept_im_from_only_friend = gSavedSettings.getBOOL("VoiceCallsFriendsOnly");
- chat.mMuted = is_muted && !is_linden;
+ chat.mMuted = is_muted;
chat.mFromID = from_id;
chat.mFromName = name;
chat.mSourceType = (from_id.isNull() || (name == std::string(SYSTEM_FROM))) ? CHAT_SOURCE_SYSTEM : CHAT_SOURCE_AGENT;
@@ -2329,7 +2371,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
LLNotification::Params params;
switch(dialog)
- {
+ {
case IM_CONSOLE_AND_CHAT_HISTORY:
args["MESSAGE"] = message;
payload["from_id"] = from_id;
@@ -2340,7 +2382,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
LLPostponedNotification::add<LLPostponedIMSystemTipNotification>(params, from_id, false);
break;
- case IM_NOTHING_SPECIAL:
+ case IM_NOTHING_SPECIAL: // p2p IM
// Don't show dialog, just do IM
if (!gAgent.isGodlike()
&& gAgent.getRegion()->isPrelude()
@@ -2349,29 +2391,18 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
// do nothing -- don't distract newbies in
// Prelude with global IMs
}
- else if (offline == IM_ONLINE && !is_linden && is_busy && name != SYSTEM_FROM)
+ else if (offline == IM_ONLINE
+ && is_do_not_disturb
+ && from_id.notNull() //not a system message
+ && to_id.notNull()) //not global message
{
- // return a standard "busy" message, but only do it to online IM
+ // return a standard "do not disturb" message, but only do it to online IM
// (i.e. not other auto responses and not store-and-forward IM)
if (!gIMMgr->hasSession(session_id))
{
// if there is not a panel for this conversation (i.e. it is a new IM conversation
// initiated by the other party) then...
- std::string my_name;
- LLAgentUI::buildFullname(my_name);
- std::string response = gSavedPerAccountSettings.getString("BusyModeResponse");
- pack_instant_message(
- gMessageSystem,
- gAgent.getID(),
- FALSE,
- gAgent.getSessionID(),
- from_id,
- my_name,
- response,
- IM_ONLINE,
- IM_BUSY_AUTO_RESPONSE,
- session_id);
- gAgent.sendReliableMessage();
+ send_do_not_disturb_message(msg, from_id, session_id);
}
// now store incoming IM in chat history
@@ -2386,6 +2417,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
from_id,
name,
buffer,
+ IM_OFFLINE == offline,
LLStringUtil::null,
dialog,
parent_estate_id,
@@ -2420,24 +2452,25 @@ 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);
}
mute_im = true;
}
- if (!mute_im || is_linden)
+ if (!mute_im)
{
gIMMgr->addMessage(
session_id,
from_id,
name,
buffer,
+ IM_OFFLINE == offline,
LLStringUtil::null,
dialog,
parent_estate_id,
@@ -2582,7 +2615,10 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
payload["sender_name"] = name;
payload["group_id"] = group_id;
payload["inventory_name"] = item_name;
- payload["inventory_offer"] = info ? info->asLLSD() : LLSD();
+ if(info && info->asLLSD())
+ {
+ payload["inventory_offer"] = info->asLLSD();
+ }
LLSD args;
args["SUBJECT"] = subj;
@@ -2604,11 +2640,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
break;
case IM_GROUP_INVITATION:
{
- //if (!is_linden && (is_busy || is_muted))
- if ((is_busy || is_muted))
+ if (is_do_not_disturb || is_muted)
{
- LLMessageSystem *msg = gMessageSystem;
- busy_message(msg,from_id);
+ send_do_not_disturb_message(msg, from_id);
}
else
{
@@ -2691,7 +2725,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
info->mFromName = name;
info->mDesc = message;
info->mHost = msg->getSender();
- //if (((is_busy && !is_owned_by_me) || is_muted))
+ //if (((is_do_not_disturb && !is_owned_by_me) || is_muted))
if (is_muted)
{
// Prefetch the offered item so that it can be discarded by the appropriate observer. (EXT-4331)
@@ -2702,9 +2736,11 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
// Same as closing window
info->forceResponse(IOR_DECLINE);
}
- else if (is_busy && dialog != IM_TASK_INVENTORY_OFFERED) // busy mode must not affect interaction with objects (STORM-565)
+ // old logic: busy mode must not affect interaction with objects (STORM-565)
+ // new logic: inventory offers from in-world objects should be auto-declined (CHUI-519)
+ else if (is_do_not_disturb && dialog == IM_TASK_INVENTORY_OFFERED)
{
- // Until throttling is implemented, busy mode should reject inventory instead of silently
+ // Until throttling is implemented, do not disturb mode should reject inventory instead of silently
// accepting it. SEE SL-39554
info->forceResponse(IOR_DECLINE);
}
@@ -2747,49 +2783,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
break;
- case IM_SESSION_SEND:
- {
- if (is_busy)
- {
- return;
- }
-
- // Only show messages if we have a session open (which
- // should happen after you get an "invitation"
- if ( !gIMMgr->hasSession(session_id) )
- {
- return;
- }
-
- // standard message, not from system
- std::string saved;
- if(offline == IM_OFFLINE)
- {
- saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str());
- }
- buffer = saved + message;
- BOOL is_this_agent = FALSE;
- if(from_id == gAgentID)
- {
- is_this_agent = TRUE;
- }
- gIMMgr->addMessage(
- session_id,
- from_id,
- name,
- buffer,
- ll_safe_string((char*)binary_bucket),
- IM_SESSION_INVITE,
- parent_estate_id,
- region_id,
- position,
- true);
- }
- break;
-
case IM_FROM_TASK:
{
- if (is_busy && !is_owned_by_me)
+ if (is_do_not_disturb && !is_owned_by_me)
{
return;
}
@@ -2841,13 +2837,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();
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLFloaterIMNearbyChat>("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);
@@ -2886,8 +2881,78 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
LLPostponedNotification::add<LLPostponedServerObjectNotification>(params, from_id, from_group);
}
break;
+
+ case IM_SESSION_SEND: // ad-hoc or group IMs
+
+ // Only show messages if we have a session open (which
+ // should happen after you get an "invitation"
+ if ( !gIMMgr->hasSession(session_id) )
+ {
+ return;
+ }
+
+ else if (offline == IM_ONLINE && is_do_not_disturb)
+ {
+
+ // return a standard "do not disturb" message, but only do it to online IM
+ // (i.e. not other auto responses and not store-and-forward IM)
+ if (!gIMMgr->hasSession(session_id))
+ {
+ // if there is not a panel for this conversation (i.e. it is a new IM conversation
+ // initiated by the other party) then...
+ send_do_not_disturb_message(msg, from_id, session_id);
+ }
+
+ // now store incoming IM in chat history
+
+ buffer = message;
+
+ LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL;
+
+ // add to IM panel, but do not bother the user
+ gIMMgr->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ IM_OFFLINE == offline,
+ ll_safe_string((char*)binary_bucket),
+ IM_SESSION_INVITE,
+ parent_estate_id,
+ region_id,
+ position,
+ true);
+ }
+ else
+ {
+ // standard message, not from system
+ std::string saved;
+ if(offline == IM_OFFLINE)
+ {
+ saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str());
+ }
+
+ buffer = saved + message;
+
+ LL_INFOS("Messaging") << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << LL_ENDL;
+
+ gIMMgr->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ IM_OFFLINE == offline,
+ ll_safe_string((char*)binary_bucket),
+ IM_SESSION_INVITE,
+ parent_estate_id,
+ region_id,
+ position,
+ true);
+ }
+ break;
+
case IM_FROM_TASK_AS_ALERT:
- if (is_busy && !is_owned_by_me)
+ if (is_do_not_disturb && !is_owned_by_me)
{
return;
}
@@ -2898,17 +2963,15 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
LLNotificationsUtil::add("ObjectMessage", args);
}
break;
- case IM_BUSY_AUTO_RESPONSE:
+ case IM_DO_NOT_DISTURB_AUTO_RESPONSE:
if (is_muted)
{
- LL_DEBUGS("Messaging") << "Ignoring busy response from " << from_id << LL_ENDL;
+ LL_DEBUGS("Messaging") << "Ignoring do-not-disturb response from " << from_id << LL_ENDL;
return;
}
else
{
- // TODO: after LLTrans hits release, get "busy response" into translatable file
- buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.c_str());
- gIMMgr->addMessage(session_id, from_id, name, buffer);
+ gIMMgr->addMessage(session_id, from_id, name, message);
}
break;
@@ -2918,9 +2981,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
{
return;
}
- else if (is_busy)
+ else if (is_do_not_disturb)
{
- busy_message(msg,from_id);
+ send_do_not_disturb_message(msg, from_id);
}
else
{
@@ -3141,17 +3204,16 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
payload["online"] = (offline == IM_ONLINE);
payload["sender"] = msg->getSender().getIPandPort();
- if (is_busy)
- {
- busy_message(msg, from_id);
- LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1);
- }
- else if (is_muted)
+ if (is_muted)
{
LLNotifications::instance().forceResponse(LLNotification::Params("OfferFriendship").payload(payload), 1);
}
else
{
+ if (is_do_not_disturb)
+ {
+ send_do_not_disturb_message(msg, from_id);
+ }
args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString();
if(message.empty())
{
@@ -3184,12 +3246,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
args["NAME"] = name;
LLSD payload;
payload["from_id"] = from_id;
- LLAvatarNameCache::get(from_id, boost::bind(&notification_display_name_callback,
- _1,
- _2,
- "FriendshipAccepted",
- args,
- payload));
+ LLAvatarNameCache::get(from_id, boost::bind(&notification_display_name_callback,_1,_2,"FriendshipAccepted",args,payload));
}
break;
@@ -3207,15 +3264,15 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
}
}
-void busy_message (LLMessageSystem* msg, LLUUID from_id)
+void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id)
{
- if (gAgent.getBusy())
+ if (gAgent.isDoNotDisturb())
{
std::string my_name;
LLAgentUI::buildFullname(my_name);
- std::string response = gSavedPerAccountSettings.getString("BusyModeResponse");
+ std::string response = gSavedPerAccountSettings.getString("DoNotDisturbModeResponse");
pack_instant_message(
- gMessageSystem,
+ msg,
gAgent.getID(),
FALSE,
gAgent.getSessionID(),
@@ -3223,7 +3280,8 @@ void busy_message (LLMessageSystem* msg, LLUUID from_id)
my_name,
response,
IM_ONLINE,
- IM_BUSY_AUTO_RESPONSE);
+ IM_DO_NOT_DISTURB_AUTO_RESPONSE,
+ session_id);
gAgent.sendReliableMessage();
}
}
@@ -3258,7 +3316,7 @@ bool callingcard_offer_callback(const LLSD& notification, const LLSD& response)
msg->nextBlockFast(_PREHASH_TransactionBlock);
msg->addUUIDFast(_PREHASH_TransactionID, notification["payload"]["transaction_id"].asUUID());
msg->sendReliable(LLHost(notification["payload"]["sender"].asString()));
- busy_message(msg, notification["payload"]["source_id"].asUUID());
+ send_do_not_disturb_message(msg, notification["payload"]["source_id"].asUUID());
break;
default:
// close button probably, possibly timed out
@@ -3300,7 +3358,7 @@ void process_offer_callingcard(LLMessageSystem* msg, void**)
if(!source_name.empty())
{
- if (gAgent.getBusy()
+ if (gAgent.isDoNotDisturb()
|| LLMuteList::getInstance()->isMuted(source_id, source_name, LLMute::flagTextChat))
{
// automatically decline offer
@@ -3417,7 +3475,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
LLAvatarName av_name;
if (LLAvatarNameCache::get(from_id, &av_name))
{
- chat.mFromName = av_name.mDisplayName;
+ chat.mFromName = av_name.getDisplayName();
}
else
{
@@ -3429,7 +3487,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
chat.mFromName = from_name;
}
- BOOL is_busy = gAgent.getBusy();
+ BOOL is_do_not_disturb = gAgent.isDoNotDisturb();
BOOL is_muted = FALSE;
BOOL is_linden = FALSE;
@@ -3463,7 +3521,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
// record last audible utterance
if (is_audible
- && (is_linden || (!is_muted && !is_busy)))
+ && (is_linden || (!is_muted && !is_do_not_disturb)))
{
if (chat.mChatType != CHAT_TYPE_START
&& chat.mChatType != CHAT_TYPE_STOP)
@@ -3558,7 +3616,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
LLLocalSpeakerMgr::getInstance()->setSpeakerTyping(from_id, FALSE);
((LLVOAvatar*)chatter)->stopTyping();
- if (!is_muted && !is_busy)
+ if (!is_muted && !is_do_not_disturb)
{
visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles");
std::string formated_msg = "";
@@ -3591,7 +3649,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)
@@ -3610,6 +3667,11 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
{
LLNotificationsUI::LLNotificationManager::instance().onChat(chat, args);
}
+
+ LLSD msg_notify = LLSD(LLSD::emptyMap());
+ msg_notify["session_id"] = LLUUID();
+ msg_notify["from_id"] = chat.mFromID;
+ on_new_message(msg_notify);
}
}
@@ -4097,14 +4159,14 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)
gAgent.setFlying(gAgent.canFly());
}
- // force simulator to recognize busy state
- if (gAgent.getBusy())
+ // force simulator to recognize do not disturb state
+ if (gAgent.isDoNotDisturb())
{
- gAgent.setBusy();
+ gAgent.setDoNotDisturb(true);
}
else
{
- gAgent.clearBusy();
+ gAgent.setDoNotDisturb(false);
}
if (isAgentAvatarValid())
@@ -5607,11 +5669,9 @@ static void process_money_balance_reply_extended(LLMessageSystem* msg)
_1, _2, _3,
notification, final_args, payload));
}
- else {
- LLAvatarNameCache::get(name_id,
- boost::bind(&money_balance_avatar_notify,
- _1, _2,
- notification, final_args, payload));
+ else
+ {
+ LLAvatarNameCache::get(name_id, boost::bind(&money_balance_avatar_notify, _1, _2, notification, final_args, payload));
}
}
@@ -6787,7 +6847,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.
@@ -6908,7 +6967,7 @@ void process_user_info_reply(LLMessageSystem* msg, void**)
std::string dir_visibility;
msg->getString( "UserData", "DirectoryVisibility", dir_visibility);
- LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email);
+ LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email);
LLFloaterSnapshot::setAgentEmail(email);
}
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
index 594c22ed9c..3237f3fbdd 100644
--- a/indra/newview/llviewermessage.h
+++ b/indra/newview/llviewermessage.h
@@ -67,7 +67,6 @@ enum InventoryOfferResponse
BOOL can_afford_transaction(S32 cost);
void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group = FALSE,
S32 trx_type = TRANS_GIFT, const std::string& desc = LLStringUtil::null);
-void busy_message (LLMessageSystem* msg, LLUUID from_id);
void process_logout_reply(LLMessageSystem* msg, void**);
void process_layer_data(LLMessageSystem *mesgsys, void **user_data);
@@ -153,6 +152,8 @@ void send_group_notice(const LLUUID& group_id,
const std::string& message,
const LLInventoryItem* item);
+void send_do_not_disturb_message (LLMessageSystem* msg, const LLUUID& from_id, const LLUUID& session_id = LLUUID::null);
+
void handle_lure(const LLUUID& invitee);
void handle_lure(const uuid_vec_t& ids);
@@ -228,6 +229,7 @@ public:
void forceResponse(InventoryOfferResponse response);
+ static std::string mResponderType;
EInstantMessage mIM;
LLUUID mFromID;
BOOL mFromGroup;
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
index 54f9360b60..6d4de05ffe 100644
--- a/indra/newview/llviewerobjectlist.cpp
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -952,14 +952,14 @@ void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
llassert(objectp->isActive());
objectp->idleUpdate(agent, world, frame_time);
- }
+ }
//update flexible objects
LLVolumeImplFlexible::updateClass();
//update animated textures
LLViewerTextureAnim::updateClass();
- }
+ }
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index b9243f3fdb..6dcdd006d7 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -37,8 +37,10 @@
#include "llagent.h"
#include "llagentcamera.h"
+#include "llcommunicationchannel.h"
#include "llfloaterreg.h"
#include "llmeshrepository.h"
+#include "llnotificationhandler.h"
#include "llpanellogin.h"
#include "llviewerkeyboard.h"
#include "llviewermenu.h"
@@ -56,6 +58,7 @@
// linden library includes
#include "llaudioengine.h" // mute on minimize
+#include "llchatentry.h"
#include "indra_constants.h"
#include "llassetstorage.h"
#include "llerrorcontrol.h"
@@ -128,6 +131,7 @@
#include "llmorphview.h"
#include "llmoveview.h"
#include "llnavigationbar.h"
+#include "llnotificationhandler.h"
#include "llpaneltopinfobar.h"
#include "llpopupview.h"
#include "llpreviewtexture.h"
@@ -188,7 +192,7 @@
#include "llviewerjoystick.h"
#include "llviewernetwork.h"
#include "llpostprocess.h"
-#include "llnearbychatbar.h"
+#include "llfloaterimnearbychat.h"
#include "llagentui.h"
#include "llwearablelist.h"
@@ -198,7 +202,6 @@
#include "llfloaternotificationsconsole.h"
-#include "llnearbychat.h"
#include "llwindowlistener.h"
#include "llviewerwindowlistener.h"
#include "llpaneltopinfobar.h"
@@ -1552,16 +1555,17 @@ 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);
+ mSystemChannel.reset(new LLNotificationChannel("System", "Visible", LLNotificationFilters::includeEverything));
+ mCommunicationChannel.reset(new LLCommunicationChannel("Communication", "Visible"));
+ mAlertsChannel.reset(new LLNotificationsUI::LLViewerAlertHandler("VW_alerts", "alert"));
+ mModalAlertsChannel.reset(new LLNotificationsUI::LLViewerAlertHandler("VW_alertmodal", "alertmodal"));
+
bool ignore = gSavedSettings.getBOOL("IgnoreAllNotifications");
LLNotifications::instance().setIgnoreAllNotifications(ignore);
if (ignore)
{
- llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl;
+ llinfos << "NOTE: ALL NOTIFICATIONS THAT OCCUR WILL GET ADDED TO IGNORE LIST FOR LATER RUNS." << llendl;
}
// Default to application directory.
@@ -1825,8 +1829,8 @@ void LLViewerWindow::initBase()
gDebugView->init();
gToolTipView = getRootView()->getChild<LLToolTipView>("tooltip view");
- // Initialize busy response message when logged in
- LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLFloaterPreference::initBusyResponse));
+ // Initialize do not disturb response message when logged in
+ LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLFloaterPreference::initDoNotDisturbResponse));
// Add the progress bar view (startup view), which overrides everything
mProgressView = getRootView()->findChild<LLProgressView>("progress_view");
@@ -2500,26 +2504,20 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
return TRUE;
}
- // Traverses up the hierarchy
+ LLFloater* focused_floaterp = gFloaterView->getFocusedFloater();
+ std::string focusedFloaterName = (focused_floaterp ? focused_floaterp->getInstanceName() : "");
+
if( keyboard_focus )
{
- LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance<LLNearbyChatBar>("chat_bar");
-
- if (nearby_chat)
+ if ((focusedFloaterName == "nearby_chat") || (focusedFloaterName == "im_container") || (focusedFloaterName == "impanel"))
{
- 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"))
+ if (gSavedSettings.getBOOL("ArrowKeysAlwaysMove"))
{
// let Control-Up and Control-Down through for chat line history,
if (!(key == KEY_UP && mask == MASK_CONTROL)
- && !(key == KEY_DOWN && mask == MASK_CONTROL))
+ && !(key == KEY_DOWN && mask == MASK_CONTROL)
+ && !(key == KEY_UP && mask == MASK_ALT)
+ && !(key == KEY_DOWN && mask == MASK_ALT))
{
switch(key)
{
@@ -2537,9 +2535,9 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
break;
}
}
- }
}
}
+
if (keyboard_focus->handleKey(key, mask, FALSE))
{
return TRUE;
@@ -2570,11 +2568,19 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
if ( gSavedSettings.getS32("LetterKeysFocusChatBar") && !gAgentCamera.cameraMouselook() &&
!keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) )
{
- LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar")->getChatBox();
+ // Initialize nearby chat if it's missing
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (!nearby_chat)
+ {
+ LLSD name("im_container");
+ LLFloaterReg::toggleInstanceOrBringToFront(name);
+ }
+
+ LLChatEntry* chat_editor = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("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;
}
}
@@ -2603,7 +2609,10 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
if ((uni_char == 13 && mask != MASK_CONTROL)
|| (uni_char == 3 && mask == MASK_NONE))
{
- return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
+ if (mask != MASK_ALT)
+ {
+ return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
+ }
}
// let menus handle navigation (jump) keys
@@ -2818,7 +2827,6 @@ void LLViewerWindow::updateUI()
BOOL handled = FALSE;
- BOOL handled_by_top_ctrl = FALSE;
LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl();
LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
LLView* captor_view = dynamic_cast<LLView*>(mouse_captor);
@@ -3003,7 +3011,6 @@ void LLViewerWindow::updateUI()
S32 local_x, local_y;
top_ctrl->screenPointToLocal( x, y, &local_x, &local_y );
handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask);
- handled_by_top_ctrl = TRUE;
}
if ( !handled )
@@ -5042,25 +5049,6 @@ LLRect LLViewerWindow::getChatConsoleRect()
//----------------------------------------------------------------------------
-//static
-bool LLViewerWindow::onAlert(const LLSD& notify)
-{
- LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
-
- if (gHeadlessClient)
- {
- llinfos << "Alert: " << notification->getName() << llendl;
- }
-
- // If we're in mouselook, the mouse is hidden and so the user can't click
- // the dialog buttons. In that case, change to First Person instead.
- if( gAgentCamera.cameraMouselook() )
- {
- gAgentCamera.changeCameraToDefault();
- }
- return false;
-}
-
void LLViewerWindow::setUIVisibility(bool visible)
{
mUIVisible = visible;
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index 5f475fe145..b33488fd78 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -42,6 +42,7 @@
#include "llwindowcallbacks.h"
#include "lltimer.h"
#include "llmousehandler.h"
+#include "llnotifications.h"
#include "llhandle.h"
#include "llinitparam.h"
@@ -400,7 +401,6 @@ public:
private:
bool shouldShowToolTipFor(LLMouseHandler *mh);
- static bool onAlert(const LLSD& notify);
void switchToolByMask(MASK mask);
void destroyWindow();
@@ -417,6 +417,11 @@ private:
bool mActive;
bool mUIVisible;
+ LLNotificationChannelPtr mSystemChannel;
+ LLNotificationChannelPtr mCommunicationChannel;
+ LLNotificationChannelPtr mAlertsChannel;
+ LLNotificationChannelPtr 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 62e93b7a53..d295fc60cd 100644
--- a/indra/newview/llvoavatar.cpp
+++ b/indra/newview/llvoavatar.cpp
@@ -62,6 +62,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 +192,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 +226,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 +606,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;
@@ -667,7 +678,7 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id,
mNameString(),
mTitle(),
mNameAway(false),
- mNameBusy(false),
+ mNameDoNotDisturb(false),
mNameMute(false),
mNameAppearance(false),
mNameFriend(false),
@@ -804,14 +815,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;
@@ -1213,18 +1224,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
@@ -1273,7 +1272,7 @@ void LLVOAvatar::initClass()
void LLVOAvatar::cleanupClass()
{
deleteAndClear(sAvatarXmlInfo);
- sSkeletonXMLTree.cleanup();
+ sSkeletonXMLTree = NULL;
sXMLTree.cleanup();
}
@@ -1356,7 +1355,7 @@ void LLVOAvatar::initInstance(void)
if (LLCharacter::sInstances.size() == 1)
{
LLKeyframeMotion::setVFS(gStaticVFS);
- registerMotion( ANIM_AGENT_BUSY, LLNullMotion::create );
+ registerMotion( ANIM_AGENT_DO_NOT_DISTURB, LLNullMotion::create );
registerMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create );
registerMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create );
registerMotion( ANIM_AGENT_EXPRESS_AFRAID, LLEmote::create );
@@ -1741,33 +1740,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;
}
@@ -1776,12 +1781,11 @@ 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)
{
LLViewerJoint* joint = NULL;
-
- if (info->mIsJoint)
+ if (info.bone.isChosen())
{
joint = (LLViewerJoint*)getCharacterJoint(joint_num);
if (!joint)
@@ -1789,7 +1793,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
{
@@ -1799,7 +1819,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
@@ -1808,34 +1832,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;
}
@@ -1847,36 +1845,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;
}
@@ -2085,15 +2079,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);
+ }
+ }
}
}
@@ -2339,11 +2333,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)
{
@@ -2851,8 +2845,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;
@@ -3006,7 +3000,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
mVisibleChat = visible_chat;
new_name = TRUE;
}
-
+
if (sRenderGroupTitles != mRenderGroupTitles)
{
mRenderGroupTitles = sRenderGroupTitles;
@@ -3049,7 +3043,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
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->setVertAlignment(LLHUDNameTag::ALIGN_VERT_TOP);
@@ -3058,10 +3052,9 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)
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);
}
@@ -3076,7 +3069,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
if (!firstname || !lastname) return;
bool is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end();
- bool is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end();
+ bool is_do_not_disturb = mSignaledAnimations.find(ANIM_AGENT_DO_NOT_DISTURB) != mSignaledAnimations.end();
bool is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end();
bool is_muted;
if (isSelf())
@@ -3104,11 +3097,12 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
// Rebuild name tag if state change detected
if (mNameString.empty()
+ || (mNameString.size() == 2 && mNameString[0] == 10 && mNameString[1] == 10) // *TODO : find out why mNameString is sometimes ""
|| new_name
|| (!title && !mTitle.empty())
|| (title && mTitle != title->getString())
|| is_away != mNameAway
- || is_busy != mNameBusy
+ || is_do_not_disturb != mNameDoNotDisturb
|| is_muted != mNameMute
|| is_appearance != mNameAppearance
|| is_friend != mNameFriend
@@ -3118,7 +3112,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
clearNameTag();
- if (is_away || is_muted || is_busy || is_appearance)
+ if (is_away || is_muted || is_do_not_disturb || is_appearance)
{
std::string line;
if (is_away)
@@ -3126,9 +3120,9 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name)
line += LLTrans::getString("AvatarAway");
line += ", ";
}
- if (is_busy)
+ if (is_do_not_disturb)
{
- line += LLTrans::getString("AvatarBusy");
+ line += LLTrans::getString("AvatarDoNotDisturb");
line += ", ";
}
if (is_muted)
@@ -3149,7 +3143,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
@@ -3158,48 +3152,46 @@ 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");
static LLUICachedControl<bool> show_usernames("NameTagShowUsernames");
- if (LLAvatarNameCache::useDisplayNames())
+ if (LLAvatarName::useDisplayNames())
{
LLAvatarName av_name;
if (!LLAvatarNameCache::get(getID(), &av_name))
{
- // ...call this function back when the name arrives
- // and force a rebuild
- LLAvatarNameCache::get(getID(),
- boost::bind(&LLVOAvatar::clearNameTag, this));
+ // Force a rebuild at next idle
+ // Note: do not connect a callback on idle().
+ clearNameTag();
}
// 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());
+ addNameTagLine(av_name.getDisplayName(), name_tag_color, LLFontGL::NORMAL,
+ LLFontGL::getFontSansSerif());
}
// Suppress SLID display if display name matches exactly (ugh)
- if (show_usernames && !av_name.mIsDisplayNameDefault)
+ if (show_usernames && !av_name.isDisplayNameDefault())
{
// *HACK: Desaturate the color
LLColor4 username_color = name_tag_color * 0.83f;
- addNameTagLine(av_name.mUsername, username_color, LLFontGL::NORMAL,
- LLFontGL::getFontSansSerifSmall());
+ addNameTagLine(av_name.getUserName(), username_color, LLFontGL::NORMAL,
+ 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);
}
mNameAway = is_away;
- mNameBusy = is_busy;
+ mNameDoNotDisturb = is_do_not_disturb;
mNameMute = is_muted;
mNameAppearance = is_appearance;
mNameFriend = is_friend;
@@ -3214,7 +3206,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();
@@ -3234,13 +3226,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;
}
@@ -3267,13 +3259,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;
}
@@ -3312,6 +3304,7 @@ void LLVOAvatar::clearNameTag()
mNameText->setLabel("");
mNameText->setString( "" );
}
+ mTimeVisible.reset();
}
//static
@@ -3337,34 +3330,45 @@ void LLVOAvatar::invalidateNameTags()
if (avatar->isDead()) continue;
avatar->clearNameTag();
-
}
}
// Compute name tag position during idle update
-LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last)
+void LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last)
{
LLQuaternion root_rot = mRoot.getWorldRotation();
+ LLQuaternion inv_root_rot = ~root_rot;
LLVector3 pixel_right_vec;
LLVector3 pixel_up_vec;
LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec);
LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin();
camera_to_av.normalize();
- LLVector3 local_camera_at = camera_to_av * ~root_rot;
+ LLVector3 local_camera_at = camera_to_av * inv_root_rot;
LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis();
local_camera_up.normalize();
- local_camera_up = local_camera_up * ~root_rot;
+ local_camera_up = local_camera_up * inv_root_rot;
+
+ LLVector3 avatar_ellipsoid(mBodySize.mV[VX] * 0.4f,
+ mBodySize.mV[VY] * 0.4f,
+ mBodySize.mV[VZ] * NAMETAG_VERT_OFFSET_WEIGHT);
- local_camera_up.scaleVec(mBodySize * 0.5f);
- local_camera_at.scaleVec(mBodySize * 0.5f);
+ local_camera_up.scaleVec(avatar_ellipsoid);
+ local_camera_at.scaleVec(avatar_ellipsoid);
- LLVector3 name_position = mRoot.getWorldPosition();
- name_position[VZ] -= mPelvisToFoot;
- name_position[VZ] += (mBodySize[VZ]* 0.55f);
+ LLVector3 head_offset = (mHeadp->getLastWorldPosition() - mRoot.getLastWorldPosition()) * inv_root_rot;
+
+ if (dist_vec(head_offset, mTargetRootToHeadOffset) > NAMETAG_UPDATE_THRESHOLD)
+ {
+ mTargetRootToHeadOffset = head_offset;
+ }
+
+ mCurRootToHeadOffset = lerp(mCurRootToHeadOffset, mTargetRootToHeadOffset, LLCriticalDamp::getInterpolant(0.2f));
+
+ LLVector3 name_position = mRoot.getLastWorldPosition() + (mCurRootToHeadOffset * root_rot);
name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av));
- name_position += pixel_up_vec * 15.f;
+ name_position += pixel_up_vec * NAMETAG_VERTICAL_SCREEN_OFFSET;
- return name_position;
+ mNameText->setPositionAgent(name_position);
}
void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha)
@@ -3387,20 +3391,18 @@ LLColor4 LLVOAvatar::getNameTagColor(bool is_friend)
{
color_name = "NameTagFriend";
}
- else if (LLAvatarNameCache::useDisplayNames())
+ else if (LLAvatarName::useDisplayNames())
{
- // ...color based on whether username "matches" a computed display
- // name
+ // ...color based on whether username "matches" a computed display name
LLAvatarName av_name;
- if (LLAvatarNameCache::get(getID(), &av_name)
- && av_name.mIsDisplayNameDefault)
+ if (LLAvatarNameCache::get(getID(), &av_name) && av_name.isDisplayNameDefault())
{
color_name = "NameTagMatch";
}
else
{
color_name = "NameTagMismatch";
- }
+ }
}
else
{
@@ -3437,9 +3439,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);
}
//------------------------------------------------------------------------
@@ -3478,8 +3480,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
}
}
- LLVector3d root_pos_global;
-
if (!mIsBuilt)
{
return FALSE;
@@ -3494,7 +3494,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent)
mTimeVisible.reset();
}
-
//--------------------------------------------------------------------
// the rest should only be done occasionally for far away avatars
//--------------------------------------------------------------------
@@ -3897,10 +3896,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();
@@ -4117,13 +4112,6 @@ void LLVOAvatar::updateVisibility()
{
releaseMeshData();
}
- // this breaks off-screen chat bubbles
- //if (mNameText)
- //{
- // mNameText->markDead();
- // mNameText = NULL;
- // sNumVisibleChatBubbles--;
- //}
}
mVisible = visible;
@@ -4139,46 +4127,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()
//-----------------------------------------------------------------------------
@@ -4199,11 +4147,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)
@@ -4232,13 +4180,13 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
if (face)
{
LLVertexBuffer* vb = face->getVertexBuffer();
- if (vb)
- {
- vb->flush();
- }
+ if (vb)
+ {
+ vb->flush();
}
}
}
+ }
else
{
mNeedsSkin = FALSE;
@@ -5888,7 +5836,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())
@@ -5918,7 +5865,6 @@ BOOL LLVOAvatar::updateJointLODs()
dirtyMesh(2);
return TRUE;
}
- }
return FALSE;
}
@@ -6207,14 +6153,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
@@ -6229,8 +6170,6 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO )
}
}
}
- }
-}
//-----------------------------------------------------------------------------
// detachObject()
//-----------------------------------------------------------------------------
@@ -6379,11 +6318,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);
}
}
@@ -6433,7 +6368,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
}
}
@@ -6598,8 +6532,8 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading)
mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE);
- if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
- {
+ if (!mPreviousFullyLoaded && !loading && mFullyLoaded)
+ {
debugAvatarRezTime("AvatarRezNotification","fully loaded");
}
@@ -7192,10 +7126,6 @@ LLBBox LLVOAvatar::getHUDBBox() const
return bbox;
}
-void LLVOAvatar::rebuildHUD()
-{
-}
-
//-----------------------------------------------------------------------------
// onFirstTEMessageReceived()
//-----------------------------------------------------------------------------
@@ -7319,7 +7249,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));
}
}
@@ -7577,7 +7510,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;
@@ -7628,13 +7561,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 );
@@ -7878,111 +7804,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 1adb680962..c59a3a150c 100644
--- a/indra/newview/llvoavatar.h
+++ b/indra/newview/llvoavatar.h
@@ -67,8 +67,9 @@ class LLVoiceVisualizer;
class LLHUDNameTag;
class LLHUDEffectSpiral;
class LLTexGlobalColor;
-class LLVOAvatarBoneInfo;
-class LLVOAvatarSkeletonInfo;
+struct LLVOAvatarBoneInfo;
+struct LLVOAvatarChildJoint;
+struct LLVOAvatarSkeletonInfo;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// LLVOAvatar
@@ -243,7 +244,7 @@ public:
void idleUpdateWindEffect();
void idleUpdateNameTag(const LLVector3& root_pos_last);
void idleUpdateNameTagText(BOOL new_name);
- LLVector3 idleUpdateNameTagPosition(const LLVector3& root_pos_last);
+ void idleUpdateNameTagPosition(const LLVector3& root_pos_last);
void idleUpdateNameTagAlpha(BOOL new_name, F32 alpha);
LLColor4 getNameTagColor(bool is_friend);
void clearNameTag();
@@ -363,6 +364,8 @@ public:
F32 mLastPelvisToFoot;
F32 mPelvisFixup;
F32 mLastPelvisFixup;
+ LLVector3 mCurRootToHeadOffset;
+ LLVector3 mTargetRootToHeadOffset;
LLVector3 mHeadOffset; // current head position
LLViewerJoint mRoot;
@@ -375,7 +378,7 @@ protected:
void buildCharacter();
virtual BOOL loadAvatar();
- BOOL setupBone(const LLVOAvatarBoneInfo* info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_joint_num);
+ BOOL setupBone(const LLVOAvatarChildJoint& info, LLViewerJoint* parent, S32 &current_volume_num, S32 &current_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;
@@ -941,7 +942,7 @@ private:
std::string mNameString; // UTF-8 title + name + status
std::string mTitle;
bool mNameAway;
- bool mNameBusy;
+ bool mNameDoNotDisturb;
bool mNameMute;
bool mNameAppearance;
bool mNameFriend;
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/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 730f022c50..b46c55321c 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -541,6 +541,7 @@ void LLVoiceClient::setMuteMic(bool muted)
{
mMuteMic = muted;
updateMicMuteLogic();
+ mMicroChangedSignal();
}
@@ -551,6 +552,7 @@ void LLVoiceClient::setUserPTTState(bool ptt)
{
mUserPTTState = ptt;
updateMicMuteLogic();
+ mMicroChangedSignal();
}
bool LLVoiceClient::getUserPTTState()
diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h
index c9aeea35a9..714dd6a9f2 100644
--- a/indra/newview/llvoiceclient.h
+++ b/indra/newview/llvoiceclient.h
@@ -303,6 +303,9 @@ public:
LLVoiceClient();
~LLVoiceClient();
+ typedef boost::signals2::signal<void(void)> micro_changed_signal_t;
+ micro_changed_signal_t mMicroChangedSignal;
+
void init(LLPumpIO *pump); // Call this once at application startup (creates connector)
void terminate(); // Call this to clean up during shutdown
@@ -401,6 +404,8 @@ public:
void keyUp(KEY key, MASK mask);
void middleMouseState(bool down);
+ boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); }
+
/////////////////////////////
// Accessors for data related to nearby speakers
@@ -456,6 +461,7 @@ protected:
LLVoiceModuleInterface* mVoiceModule;
LLPumpIO *m_servicePump;
+
LLCachedControl<bool> mVoiceEffectEnabled;
LLCachedControl<std::string> mVoiceEffectDefault;
diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp
index 47060720e7..b497f80560 100644
--- a/indra/newview/llvoicevisualizer.cpp
+++ b/indra/newview/llvoicevisualizer.cpp
@@ -73,17 +73,6 @@ const F32 DEFAULT_MAXIMUM_GESTICULATION_AMPLITUDE = 1.0f;
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;
-}
-
//------------------------------------------------------------------
// Initialize the statics
//------------------------------------------------------------------
@@ -106,7 +95,7 @@ F32 LLVoiceVisualizer::sAahPowerTransfersf = 0.0f;
// constructor
//-----------------------------------------------
LLVoiceVisualizer::LLVoiceVisualizer( const U8 type )
-:LLHUDEffect( type )
+ : LLHUDEffect(type)
{
mCurrentTime = mTimer.getTotalSeconds();
mPreviousTime = mCurrentTime;
@@ -150,12 +139,12 @@ 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;
}
@@ -217,6 +206,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( )
@@ -526,10 +524,6 @@ void LLVoiceVisualizer::render()
}//---------------------------------------------------
-
-
-
-
//---------------------------------------------------
void LLVoiceVisualizer::setVoiceSourceWorldPosition( const LLVector3 &p )
{
@@ -615,11 +609,3 @@ void LLVoiceVisualizer::markDead()
LLHUDEffect::markDead();
}//------------------------------------------------------------------
-
-
-
-
-
-
-
-
diff --git a/indra/newview/llvoicevisualizer.h b/indra/newview/llvoicevisualizer.h
index e434c7f3f1..36c78252d1 100644
--- a/indra/newview/llvoicevisualizer.h
+++ b/indra/newview/llvoicevisualizer.h
@@ -71,10 +71,8 @@ class LLVoiceVisualizer : public LLHUDEffect
// public methods
//---------------------------------------------------
public:
- LLVoiceVisualizer ( const U8 type ); //constructor
+ LLVoiceVisualizer( const U8 type ); //constructor
~LLVoiceVisualizer(); //destructor
-
- friend class LLHUDObject;
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
@@ -85,8 +83,6 @@ class LLVoiceVisualizer : public LLHUDEffect
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 );
void render(); // inherited from HUD Effect
void packData(LLMessageSystem *mesgsys); // inherited from HUD Effect
@@ -108,7 +104,10 @@ class LLVoiceVisualizer : public LLHUDEffect
// 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
+
struct SoundSymbol
{
F32 mWaveExpansion [ NUM_VOICE_SYMBOL_WAVES ];
diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp
index 820d1d73e1..f3342b7ff1 100644
--- a/indra/newview/llvoicevivox.cpp
+++ b/indra/newview/llvoicevivox.cpp
@@ -34,6 +34,7 @@
#include "llvoavatarself.h"
#include "llbufferstream.h"
#include "llfile.h"
+#include "llmenugl.h"
#ifdef LL_STANDALONE
# include "expat.h"
#else
@@ -70,6 +71,9 @@
#define USE_SESSION_GROUPS 0
+extern LLMenuBarGL* gMenuBarView;
+extern void handle_voice_morphing_subscribe();
+
const F32 VOLUME_SCALE_VIVOX = 0.01f;
const F32 SPEAKING_TIMEOUT = 1.f;
@@ -291,6 +295,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mCaptureDeviceDirty(false),
mRenderDeviceDirty(false),
mSpatialCoordsDirty(false),
+ mIsInitialized(false),
mMuteMic(false),
mMuteMicDirty(false),
@@ -315,7 +320,9 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
mCaptureBufferRecording(false),
mCaptureBufferRecorded(false),
mCaptureBufferPlaying(false),
- mPlayRequestCount(0)
+ mPlayRequestCount(0),
+
+ mAvatarNameCacheConnection()
{
mSpeakerVolume = scale_speaker_volume(0);
@@ -348,6 +355,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() :
LLVivoxVoiceClient::~LLVivoxVoiceClient()
{
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
}
//---------------------------------------------------
@@ -520,7 +531,7 @@ void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries)
{
LLViewerRegion *region = gAgent.getRegion();
- if ( region && mVoiceEnabled )
+ if ( region && (mVoiceEnabled || !mIsInitialized))
{
std::string url =
region->getCapability("ProvisionVoiceAccountRequest");
@@ -691,7 +702,7 @@ void LLVivoxVoiceClient::stateMachine()
setVoiceEnabled(false);
}
- if(mVoiceEnabled)
+ if(mVoiceEnabled || !mIsInitialized)
{
updatePosition();
}
@@ -736,7 +747,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateDisabled
case stateDisabled:
- if(mTuningMode || (mVoiceEnabled && !mAccountName.empty()))
+ if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty()))
{
setState(stateStart);
}
@@ -891,7 +902,7 @@ void LLVivoxVoiceClient::stateMachine()
mTuningExitState = stateIdle;
setState(stateMicTuningStart);
}
- else if(!mVoiceEnabled)
+ else if(!mVoiceEnabled && mIsInitialized)
{
// We never started up the connector. This will shut down the daemon.
setState(stateConnectorStopped);
@@ -1085,7 +1096,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateConnectorStart
case stateConnectorStart:
- if(!mVoiceEnabled)
+ if(!mVoiceEnabled && mIsInitialized)
{
// We were never logged in. This will shut down the connector.
setState(stateLoggedOut);
@@ -1103,7 +1114,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateConnectorStarted
case stateConnectorStarted: // connector handle received
- if(!mVoiceEnabled)
+ if(!mVoiceEnabled && mIsInitialized)
{
// We were never logged in. This will shut down the connector.
setState(stateLoggedOut);
@@ -1247,7 +1258,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateCreatingSessionGroup
case stateCreatingSessionGroup:
- if(mSessionTerminateRequested || !mVoiceEnabled)
+ if(mSessionTerminateRequested || !mVoiceEnabled && mIsInitialized)
{
// *TODO: Question: is this the right way out of this state
setState(stateSessionTerminated);
@@ -1263,7 +1274,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateRetrievingParcelVoiceInfo
case stateRetrievingParcelVoiceInfo:
// wait until parcel voice info is received.
- if(mSessionTerminateRequested || !mVoiceEnabled)
+ if(mSessionTerminateRequested || !mVoiceEnabled && mIsInitialized)
{
// if a terminate request has been received,
// bail and go to the stateSessionTerminated
@@ -1283,7 +1294,7 @@ void LLVivoxVoiceClient::stateMachine()
// Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync.
sendFriendsListUpdates();
- if(mSessionTerminateRequested || !mVoiceEnabled)
+ if(mSessionTerminateRequested || !mVoiceEnabled && mIsInitialized)
{
// TODO: Question: Is this the right way out of this state?
setState(stateSessionTerminated);
@@ -1364,7 +1375,7 @@ void LLVivoxVoiceClient::stateMachine()
}
// joinedAudioSession() will transition from here to stateSessionJoined.
- if(!mVoiceEnabled)
+ if(!mVoiceEnabled && mIsInitialized)
{
// User bailed out during connect -- jump straight to teardown.
setState(stateSessionTerminated);
@@ -1411,7 +1422,7 @@ void LLVivoxVoiceClient::stateMachine()
notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
}
- else if(!mVoiceEnabled)
+ else if(!mVoiceEnabled && mIsInitialized)
{
// User bailed out during connect -- jump straight to teardown.
setState(stateSessionTerminated);
@@ -1431,7 +1442,7 @@ void LLVivoxVoiceClient::stateMachine()
//MARK: stateRunning
case stateRunning: // steady state
// Disabling voice or disconnect requested.
- if(!mVoiceEnabled || mSessionTerminateRequested)
+ if(!mVoiceEnabled && mIsInitialized || mSessionTerminateRequested)
{
leaveAudioSession();
}
@@ -1478,6 +1489,8 @@ void LLVivoxVoiceClient::stateMachine()
mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
sendPositionalUpdate();
}
+
+ mIsInitialized = true;
}
break;
@@ -1511,7 +1524,7 @@ void LLVivoxVoiceClient::stateMachine()
// Always reset the terminate request flag when we get here.
mSessionTerminateRequested = false;
- if(mVoiceEnabled && !mRelogRequested)
+ if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested)
{
// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
setState(stateNoChannel);
@@ -1539,7 +1552,7 @@ void LLVivoxVoiceClient::stateMachine()
mAccountHandle.clear();
cleanUp();
- if(mVoiceEnabled && !mRelogRequested)
+ if((mVoiceEnabled || !mIsInitialized) && !mRelogRequested)
{
// User was logged out, but wants to be logged in. Send a new login request.
setState(stateNeedsLogin);
@@ -2665,7 +2678,7 @@ void LLVivoxVoiceClient::checkFriend(const LLUUID& id)
// *NOTE: For now, we feed legacy names to Vivox because I don't know
// if their service can support a mix of new and old clients with
// different sorts of names.
- std::string name = av_name.getLegacyName();
+ std::string name = av_name.getAccountName();
const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id);
bool canSeeMeOnline = false;
@@ -3712,8 +3725,7 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager
and event is not fired.
- So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it
- in LLCallFloater::draw()
+ So, we have to call LLSpeakerMgr::update() here.
*/
LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel();
@@ -3939,7 +3951,7 @@ void LLVivoxVoiceClient::messageEvent(
sessionState *session = findSession(sessionHandle);
if(session)
{
- bool is_busy = gAgent.getBusy();
+ bool is_do_not_disturb = gAgent.isDoNotDisturb();
bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat);
bool is_linden = LLMuteList::getInstance()->isLinden(session->mName);
bool quiet_chat = false;
@@ -3953,10 +3965,10 @@ void LLVivoxVoiceClient::messageEvent(
chat.mFromName = session->mName;
chat.mSourceType = CHAT_SOURCE_AGENT;
- if(is_busy && !is_linden)
+ if(is_do_not_disturb && !is_linden)
{
quiet_chat = true;
- // TODO: Question: Return busy mode response here? Or maybe when session is started instead?
+ // TODO: Question: Return do not disturb mode response here? Or maybe when session is started instead?
}
LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL;
@@ -3964,6 +3976,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
@@ -6189,18 +6202,19 @@ void LLVivoxVoiceClient::notifyFriendObservers()
void LLVivoxVoiceClient::lookupName(const LLUUID &id)
{
- LLAvatarNameCache::get(id,
- boost::bind(&LLVivoxVoiceClient::onAvatarNameCache,
- this, _1, _2));
+ if (mAvatarNameCacheConnection.connected())
+ {
+ mAvatarNameCacheConnection.disconnect();
+ }
+ mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLVivoxVoiceClient::onAvatarNameCache, this, _1, _2));
}
void LLVivoxVoiceClient::onAvatarNameCache(const LLUUID& agent_id,
const LLAvatarName& av_name)
{
- // For Vivox, we use the legacy name because I'm uncertain whether or
- // not their service can tolerate switching to Username or Display Name
- std::string legacy_name = av_name.getLegacyName();
- avatarNameResolved(agent_id, legacy_name);
+ mAvatarNameCacheConnection.disconnect();
+ std::string display_name = av_name.getDisplayName();
+ avatarNameResolved(agent_id, display_name);
}
void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name)
@@ -6729,10 +6743,106 @@ void LLVivoxVoiceClient::removeObserver(LLVoiceEffectObserver* observer)
mVoiceFontObservers.erase(observer);
}
+// method checks the item in VoiceMorphing menu for appropriate current voice font
+bool LLVivoxVoiceClient::onCheckVoiceEffect(const std::string& voice_effect_name)
+{
+ LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface();
+ if (NULL != effect_interfacep)
+ {
+ const LLUUID& currect_voice_effect_id = effect_interfacep->getVoiceEffect();
+
+ if (currect_voice_effect_id.isNull())
+ {
+ if (voice_effect_name == "NoVoiceMorphing")
+ {
+ return true;
+ }
+ }
+ else
+ {
+ const LLSD& voice_effect_props = effect_interfacep->getVoiceEffectProperties(currect_voice_effect_id);
+ if (voice_effect_props["name"].asString() == voice_effect_name)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// method changes voice font for selected VoiceMorphing menu item
+void LLVivoxVoiceClient::onClickVoiceEffect(const std::string& voice_effect_name)
+{
+ LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface();
+ if (NULL != effect_interfacep)
+ {
+ if (voice_effect_name == "NoVoiceMorphing")
+ {
+ effect_interfacep->setVoiceEffect(LLUUID());
+ return;
+ }
+ const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList();
+ if (!effect_list.empty())
+ {
+ for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it)
+ {
+ if (voice_effect_name == it->first)
+ {
+ effect_interfacep->setVoiceEffect(it->second);
+ return;
+ }
+ }
+ }
+ }
+}
+
+// it updates VoiceMorphing menu items in accordance with purchased properties
+void LLVivoxVoiceClient::updateVoiceMorphingMenu()
+{
+ if (mVoiceFontListDirty)
+ {
+ LLVoiceEffectInterface * effect_interfacep = LLVoiceClient::instance().getVoiceEffectInterface();
+ if (effect_interfacep)
+ {
+ const voice_effect_list_t& effect_list = effect_interfacep->getVoiceEffectList();
+ if (!effect_list.empty())
+ {
+ LLMenuGL * voice_morphing_menup = gMenuBarView->findChildMenuByName("VoiceMorphing", TRUE);
+
+ if (NULL != voice_morphing_menup)
+ {
+ S32 items = voice_morphing_menup->getItemCount();
+ if (items > 0)
+ {
+ voice_morphing_menup->erase(1, items - 3, false);
+
+ S32 pos = 1;
+ for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it)
+ {
+ LLMenuItemCheckGL::Params p;
+ p.name = it->first;
+ p.label = it->first;
+ p.on_check.function(boost::bind(&LLVivoxVoiceClient::onCheckVoiceEffect, this, it->first));
+ p.on_click.function(boost::bind(&LLVivoxVoiceClient::onClickVoiceEffect, this, it->first));
+ LLMenuItemCheckGL * voice_effect_itemp = LLUICtrlFactory::create<LLMenuItemCheckGL>(p);
+ voice_morphing_menup->insert(pos++, voice_effect_itemp, false);
+ }
+
+ voice_morphing_menup->needsArrange();
+ }
+ }
+ }
+ }
+ }
+}
+
void LLVivoxVoiceClient::notifyVoiceFontObservers()
{
LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL;
+ updateVoiceMorphingMenu();
+
for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin();
it != mVoiceFontObservers.end();
)
diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h
index 1142a1a49c..574027de42 100644
--- a/indra/newview/llvoicevivox.h
+++ b/indra/newview/llvoicevivox.h
@@ -246,6 +246,8 @@ public:
//@}
+ bool onCheckVoiceEffect(const std::string& voice_effect_name);
+ void onClickVoiceEffect(const std::string& voice_effect_name);
protected:
//////////////////////
@@ -641,6 +643,7 @@ protected:
void lookupName(const LLUUID &id);
void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name);
void avatarNameResolved(const LLUUID &id, const std::string &name);
+ boost::signals2::connection mAvatarNameCacheConnection;
/////////////////////////////
// Voice fonts
@@ -741,6 +744,8 @@ private:
std::string mRenderDevice;
bool mCaptureDeviceDirty;
bool mRenderDeviceDirty;
+
+ bool mIsInitialized;
bool checkParcelChanged(bool update = false);
@@ -851,6 +856,7 @@ private:
void accountGetTemplateFontsSendMessage();
void sessionSetVoiceFontSendMessage(sessionState *session);
+ void updateVoiceMorphingMenu();
void notifyVoiceFontObservers();
typedef enum e_voice_font_type
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
index fa34a6f1f5..0b34bbb90f 100644
--- a/indra/newview/llvopartgroup.cpp
+++ b/indra/newview/llvopartgroup.cpp
@@ -152,8 +152,8 @@ bool ll_is_part_idx_allocated(S32 idx, S32* start, S32* end)
void LLVOPartGroup::freeVBSlot(S32 idx)
{
llassert(idx < LL_MAX_PARTICLE_COUNT && idx >= 0);
- llassert(sVBSlotCursor > sVBSlotFree);
- llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT));
+ //llassert(sVBSlotCursor > sVBSlotFree);
+ //llassert(ll_is_part_idx_allocated(idx, sVBSlotCursor, sVBSlotFree+LL_MAX_PARTICLE_COUNT));
if (sVBSlotCursor > sVBSlotFree)
{
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
index 09d17b3701..793becf0c8 100644
--- a/indra/newview/llworld.cpp
+++ b/indra/newview/llworld.cpp
@@ -1192,7 +1192,7 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector<LLVector3d>* positi
{
LLVOAvatar* pVOAvatar = (LLVOAvatar*) *iter;
- if (!pVOAvatar->isDead() && !pVOAvatar->isSelf() && !pVOAvatar->mIsDummy)
+ if (!pVOAvatar->isDead() && !pVOAvatar->mIsDummy)
{
LLVector3d pos_global = pVOAvatar->getPositionGlobal();
LLUUID uuid = pVOAvatar->getID();
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
index ccc513b80d..11b2770ec0 100644
--- a/indra/newview/llworldmapview.cpp
+++ b/indra/newview/llworldmapview.cpp
@@ -965,8 +965,6 @@ void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4&
S32 text_x = x;
S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight());
- BOOL is_in_window = true;
-
if( x < 0
|| y < 0
|| x >= getRect().getWidth()
@@ -979,7 +977,6 @@ void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4&
text_x = sTrackingArrowX;
text_y = sTrackingArrowY;
}
- is_in_window = false;
}
else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION &&
LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING)
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
index 75f5e16452..88bf3062c0 100644
--- a/indra/newview/pipeline.cpp
+++ b/indra/newview/pipeline.cpp
@@ -5293,11 +5293,6 @@ void LLPipeline::rebuildPools()
}
max_count--;
}
-
- if (isAgentAvatarValid())
- {
- gAgentAvatarp->rebuildHUD();
- }
}
void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp )
@@ -5782,7 +5777,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)
@@ -6813,7 +6808,7 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable)
}
void LLPipeline::resetVertexBuffers()
-{
+{
mResetVertexBuffers = true;
}
diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml
index 9bf2922033..0de217fc0d 100644
--- a/indra/newview/skins/default/colors.xml
+++ b/indra/newview/skins/default/colors.xml
@@ -1,103 +1,106 @@
<?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="BeaconColor"
+ value="0.749 0.298 0 1" />
+ <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 +118,530 @@
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="MenuItemFlashBgColor"
+ reference="BeaconColor" />
+ <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 +663,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/bottomtray/Unread_IM.png b/indra/newview/skins/default/textures/bottomtray/Unread_IM.png
deleted file mode 100644
index 5c0c85b864..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/Unread_IM.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl1_Dark.png b/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl1_Dark.png
deleted file mode 100644
index 857fa1e047..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl1_Dark.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl2_Dark.png b/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl2_Dark.png
deleted file mode 100644
index 453bb53673..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl2_Dark.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl3_Dark.png b/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl3_Dark.png
deleted file mode 100644
index 135a66ca0d..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Lvl3_Dark.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Off_Dark.png b/indra/newview/skins/default/textures/bottomtray/VoicePTT_Off_Dark.png
deleted file mode 100644
index a63aec5e6d..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/VoicePTT_Off_Dark.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/bottomtray/VoicePTT_On_Dark.png b/indra/newview/skins/default/textures/bottomtray/VoicePTT_On_Dark.png
deleted file mode 100644
index 1719eb3e84..0000000000
--- a/indra/newview/skins/default/textures/bottomtray/VoicePTT_On_Dark.png
+++ /dev/null
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_log_inbox.png b/indra/newview/skins/default/textures/icons/Conv_log_inbox.png
new file mode 100644
index 0000000000..bb6ca28147
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_log_inbox.png
Binary files differ
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
new file mode 100755
index 0000000000..0631f16f3b
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_add_person.png
Binary files differ
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
new file mode 100755
index 0000000000..578482f5ed
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_ne.png
Binary files differ
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
new file mode 100755
index 0000000000..7676131790
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_arrow_sw.png
Binary files differ
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
new file mode 100755
index 0000000000..2880eb766a
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_call_log.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png
new file mode 100755
index 0000000000..d009c8f446
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_close.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png
new file mode 100755
index 0000000000..8d82960e28
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_collapse.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png
new file mode 100755
index 0000000000..f718d3fc60
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_expand.png
Binary files differ
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
new file mode 100755
index 0000000000..315e2c581a
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_hang_up.png
Binary files differ
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
new file mode 100755
index 0000000000..732ab02a20
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_open_call.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png
new file mode 100755
index 0000000000..25a32cb2ba
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_plus.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png
new file mode 100755
index 0000000000..08debeb91f
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/Conv_toolbar_sort.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/collapse_to_one_line.png b/indra/newview/skins/default/textures/icons/collapse_to_one_line.png
new file mode 100644
index 0000000000..d57144a645
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/collapse_to_one_line.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/expand_one_liner.png b/indra/newview/skins/default/textures/icons/expand_one_liner.png
new file mode 100644
index 0000000000..58b7d90131
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/expand_one_liner.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/icons/nearby_chat_icon.png b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png
new file mode 100644
index 0000000000..5ac4258b9d
--- /dev/null
+++ b/indra/newview/skins/default/textures/icons/nearby_chat_icon.png
Binary files differ
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index 06f8f8c670..93c9cb02cb 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -162,7 +162,24 @@ 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_collapse_to_one_line" file_name="icons/collapse_to_one_line.png" preload="false" />
+ <texture name="Conv_expand_one_line" file_name="icons/expand_one_liner.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 +365,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" />
@@ -625,7 +644,6 @@ with the same filename but different name
<texture name="TrashItem_Press" file_name="icons/TrashItem_Press.png" preload="false" />
<texture name="Unread_Chiclet" file_name="bottomtray/Unread_Chiclet.png" preload="false" />
- <texture name="Unread_IM" file_name="bottomtray/Unread_IM.png" preload="false" />
<texture name="UpArrow_Off" file_name="icons/UpArrow_Off.png" preload="false" />
@@ -638,12 +656,6 @@ with the same filename but different name
<texture name="VoicePTT_Off" file_name="bottomtray/VoicePTT_Off.png" preload="false" />
<texture name="VoicePTT_On" file_name="bottomtray/VoicePTT_On.png" preload="false" />
- <texture name="VoicePTT_Lvl1_Dark" file_name="bottomtray/VoicePTT_Lvl1_Dark.png" preload="false" />
- <texture name="VoicePTT_Lvl2_Dark" file_name="bottomtray/VoicePTT_Lvl2_Dark.png" preload="false" />
- <texture name="VoicePTT_Lvl3_Dark" file_name="bottomtray/VoicePTT_Lvl3_Dark.png" preload="false" />
- <texture name="VoicePTT_Off_Dark" file_name="bottomtray/VoicePTT_Off_Dark.png" preload="false" />
- <texture name="VoicePTT_On_Dark" file_name="bottomtray/VoicePTT_On_Dark.png" preload="false" />
-
<texture name="Wearables_Divider" file_name="windows/Wearables_Divider.png" preload="false" />
<texture name="Web_Profile_Off" file_name="icons/Web_Profile_Off.png" preload="false" />
diff --git a/indra/newview/skins/default/xui/da/menu_im_well_button.xml b/indra/newview/skins/default/xui/da/menu_im_well_button.xml
deleted file mode 100644
index 4889230919..0000000000
--- a/indra/newview/skins/default/xui/da/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Luk alle" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/de/menu_im_well_button.xml
deleted file mode 100644
index f464b71f4a..0000000000
--- a/indra/newview/skins/default/xui/de/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Alle schließen" name="Close All"/>
-</context_menu>
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_camera.xml b/indra/newview/skins/default/xui/en/floater_camera.xml
index 22bc488a92..521389d7b3 100644
--- a/indra/newview/skins/default/xui/en/floater_camera.xml
+++ b/indra/newview/skins/default/xui/en/floater_camera.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
positioning="specified"
- left="458"
- bottom="-80"
+ right="-460"
+ bottom="-50"
follows="left|bottom"
legacy_header_height="18"
can_minimize="true"
diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml
deleted file mode 100644
index 405557242f..0000000000
--- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater
- positioning="specified"
- left="10"
- bottom="-10"
- height="60"
- layout="topleft"
- legacy_header_height="25"
- single_instance="true"
- title="NEARBY CHAT"
- save_rect="true"
- save_visibility="true"
- can_close="true"
- can_minimize="true"
- help_topic="chat_bar"
- min_height="60"
- min_width="150"
- can_resize="true"
- default_tab_group="1"
- name="chat_bar"
- width="300">
- <panel
- top="20"
- class="panel_nearby_chat"
- follow="all"
- width="300"
- height="0"
- visible="false"
- filename="panel_nearby_chat.xml"
- name="nearby_chat" />
- <panel width="300"
- height="31"
- left="0"
- name="bottom_panel"
- bottom="-1"
- follows="left|right|bottom"
- tab_group="1">
- <line_editor
- border_style="line"
- border_thickness="1"
- follows="left|right"
- height="23"
- label="Click here to chat."
- layout="topleft"
- left_delta="7"
- left="0"
- max_length_bytes="1023"
- name="chat_box"
- spellcheck="true"
- text_pad_left="5"
- text_pad_right="25"
- tool_tip="Press Enter to say, Ctrl+Enter to shout"
- top="2"
- width="255" />
- <output_monitor
- auto_update="true"
- follows="right"
- draw_border="false"
- height="16"
- layout="topleft"
- left_pad="-24"
- mouse_opaque="true"
- name="chat_zone_indicator"
- top="6"
- visible="true"
- width="20" />
- <button
- follows="right"
- is_toggle="true"
- width="20"
- top="2"
- layout="topleft"
- left_pad="12"
- image_disabled="ComboButton_UpOff"
- image_unselected="ComboButton_UpOff"
- image_selected="ComboButton_On"
- image_pressed="ComboButton_UpSelected"
- image_pressed_selected="ComboButton_Selected"
- height="23"
- chrome="true"
- name="show_nearby_chat"
- tool_tip="Shows/hides nearby chat log">
- </button>
- </panel>
-</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_conversation_log.xml b/indra/newview/skins/default/xui/en/floater_conversation_log.xml
new file mode 100644
index 0000000000..19a4cbc119
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_conversation_log.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<floater
+ can_resize="true"
+ positioning="cascading"
+ help_topic="conversation_log"
+ height="200"
+ min_height="100"
+ min_width="230"
+ layout="topleft"
+ name="floater_conversation_log"
+ save_rect="true"
+ single_instance="true"
+ reuse_instance="true"
+ title="CONVERSATION LOG"
+ width="300">
+ <panel
+ follows="left|top|right"
+ height="32"
+ 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="204" />
+ <menu_button
+ follows="top|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="8"
+ 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="top|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="conversations_gear_btn"
+ tool_tip="Actions on selected person or group"
+ top="3"
+ width="31" />
+ </panel>
+ <panel
+ bottom="-1"
+ follows="all"
+ left="0"
+ name="log_panel"
+ right="-1"
+ top="32">
+ <conversation_log_list
+ allow_select="true"
+ bottom="-8"
+ opaque="true"
+ follows="all"
+ left="8"
+ keep_selection_visible_on_reshape="true"
+ item_pad="2"
+ multi_select="false"
+ name="conversation_log_list"
+ right="-8"
+ top="0" />
+ </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..764b9d8385
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_conversation_preview.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ legacy_header_height="18"
+ can_resize="true"
+ default_tab_group="1"
+ help_topic="conversation_preview"
+ 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_destinations.xml b/indra/newview/skins/default/xui/en/floater_destinations.xml
index 39aa8e07bb..94ebaa9cb2 100644
--- a/indra/newview/skins/default/xui/en/floater_destinations.xml
+++ b/indra/newview/skins/default/xui/en/floater_destinations.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
- positioning="cascading"
+ positioning="cascading"
ignore_ui_scale="false"
legacy_header_height="225"
can_minimize="true"
@@ -17,11 +17,11 @@
save_rect="true"
save_visibility="true"
title="DESTINATIONS"
- width="840">
+ width="550">
<web_browser
top="25"
height="200"
- width="840"
+ width="550"
follows="all"
name="destination_guide_contents"
trusted_content="true"/>
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..65f623a47e 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,180 @@
<?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="210"
layout="topleft"
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"
+ bottom="-50"
+ right="-5"
+ width="450"
+ min_width="38">
+ <string
+ name="collapse_icon"
+ value="Conv_toolbar_collapse"/>
+ <string
+ name="expand_icon"
+ value="Conv_toolbar_expand"/>
+ <layout_stack
+ animate="true"
+ bottom="-1"
+ follows="all"
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"
- 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" />
+ left="0"
+ name="conversations_stack"
+ orientation="horizontal"
+ right="-1"
+ top="0">
+ <layout_panel
+ auto_resize="false"
+ user_resize="true"
+ name="conversations_layout_panel"
+ min_dim="38"
+ expanded_min_dim="156">
+ <layout_stack
+ animate="false"
+ follows="left|top|right"
+ height="35"
+ layout="topleft"
+ left="0"
+ name="conversations_pane_buttons_stack"
+ orientation="horizontal"
+ right="-1"
+ top="0">
+ <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="5"
+ 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="2"
+ name="add_btn"
+ tool_tip="Start a new conversation"
+ width="31"/>
+ <button
+ follows="top|left"
+ height="25"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Command_Speak_Icon"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ top="5"
+ left_pad="2"
+ name="speak_btn"
+ tool_tip="Speak with people using your microphone"
+ 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="1"
+ name="expand_collapse_btn"
+ tool_tip="Collapse/Expand this list"
+ width="31" />
+ </layout_panel>
+ </layout_stack>
+ <panel
+ bottom="-1"
+ follows="all"
+ layout="topleft"
+ name="conversations_list_panel"
+ opaque="true"
+ top="35"
+ left="5"
+ right="-1"/>
+ </layout_panel>
+ <layout_panel
+ auto_resize="true"
+ user_resize="true"
+ name="messages_layout_panel"
+ expanded_min_dim="222">
+ <panel_container
+ bottom="-1"
+ follows="all"
+ layout="topleft"
+ left="0"
+ name="im_box_tab_container"
+ right="-1"
+ top="0">
+ <panel
+ bottom="-1"
+ follows="all"
+ layout="topleft"
+ name="stub_panel"
+ opaque="true"
+ top_pad="0"
+ left="0"
+ right="-1">
+ <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"
+ right="-10"
+ name="stub_collapse_btn"
+ tool_tip="Collapse this pane"
+ width="31" />
+ <text
+ type="string"
+ clip_partial="false"
+ follows="left|top|right"
+ layout="topleft"
+ left="15"
+ right="-15"
+ name="stub_textbox"
+ top="25"
+ height="40"
+ valign="center"
+ parse_urls="true"
+ wrap="true">
+ This conversation is in a separate window. [secondlife:/// Bring it back.]
+ </text>
+ </panel>
+ </panel_container>
+ </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..d8b085063f 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,393 @@
can_dock="false"
can_minimize="true"
can_close="true"
+ save_rect="true"
visible="false"
width="394"
can_resize="true"
- min_width="250"
- min_height="190">
+ can_tear_off="false"
+ 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="collapseline_icon"
+ value="Conv_collapse_to_one_line"/>
+ <floater.string
+ name="expandline_icon"
+ value="Conv_expand_one_line"/>
+ <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"/>
+ <floater.string
+ name="start_call_button_tooltip"
+ value="Open voice connection"/>
+ <floater.string
+ name="end_call_button_tooltip"
+ value="Close voice connection"/>
+ <floater.string
+ name="expcol_button_not_tearoff_tooltip"
+ value="Collapse this pane"/>
+ <floater.string
+ name="expcol_button_tearoff_and_expanded_tooltip"
+ value="Collapse participant list"/>
+ <floater.string
+ name="expcol_button_tearoff_and_collapsed_tooltip"
+ value="Expand participant list"/>
+ <view
+ follows="all"
+ layout="topleft"
+ name="contents_view"
+ top="0"
+ left="0"
+ height="355"
+ width="394">
+ <layout_stack
+ animate="false"
+ default_tab_group="2"
+ follows="all"
+ height="355"
+ width="394"
+ layout="topleft"
+ orientation="vertical"
+ name="main_stack"
+ tab_group="1"
+ top="0"
+ left="0">
+
+ <layout_panel
+ follows="left|top|right"
+ layout="topleft"
+ name="toolbar_panel"
+ top="0"
+ left="0"
+ height="35"
+ min_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" />
+ <menu_button
+ menu_filename="menu_im_conversation.xml"
+ follows="top|left"
+ height="25"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="OptionsMenu_Off"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ top="5"
+ left_pad="2"
+ name="gear_btn"
+ visible="false"
+ tool_tip="Actions on selected person"
+ 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="2"
+ 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="2"
+ name="voice_call_btn"
+ tool_tip="Open voice connection"
+ width="31"/>
+ <output_monitor
+ auto_update="true"
+ follows="top|left"
+ draw_border="false"
+ height="16"
+ layout="topleft"
+ top="10"
+ left_pad="10"
+ mouse_opaque="true"
+ name="speaking_indicator"
+ visible="false"
+ width="20" />
+ <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="292"
+ 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="2"
+ 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="2"
+ name="tear_off_btn"
+ width="31" />
+ </layout_panel>
+ <layout_panel
+ name="body_panel"
+ follows="all"
+ width="394"
+ height="235"
+ user_resize="false"
+ auto_resize="true">
<layout_stack
animate="true"
default_tab_group="2"
follows="all"
- height="320"
+ height="275"
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"
- min_width="115"
+ name="speakers_list_panel"
+ follows="all"
+ expanded_min_dim="115"
+ min_dim="0"
width="150"
- height="320"
+ height="275"
+ user_resize="true"
auto_resize="false">
- <panel
- name="panel_im_control_panel"
- layout="topleft"
- height="320"
- width="150"
- follows="all"/>
</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="275"
+ width="244"
+ layout="topleft"
+ user_resize="true"
+ auto_resize="true"
+ visible="true"
+ name="right_part_holder"
+ min_width="221">
+ <panel
+ name="trnsAndChat_panel"
+ follows="all"
+ layout="topleft"
+ visible="true"
+ height="240"
+ width="244">
+ <layout_stack
+ animate="true"
+ default_tab_group="2"
+ follows="all"
+ height="240"
+ 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" />
- <chat_history
- font="SansSerifSmall"
- follows="left|right|top|bottom"
- height="150"
- name="chat_history"
- parse_highlights="true"
- parse_urls="true"
- left="1"
- width="238">
- </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>
+ left="0">
+ <layout_panel
+ auto_resize="false"
+ user_resize="false"
+ height="26"
+ layout="topleft"
+ left_delta="0"
+ name="translate_chat_checkbox_lp"
+ top_delta="0"
+ visible="true"
+ width="210">
+ <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
+ width="210"
+ layout="topleft"
+ follows="all"
+ left_delta="0"
+ top_delta="0"
+ bottom="0"
+ visible="true"
+ user_resize="false"
+ auto_resize="true"
+ name="chat_holder">
+ <chat_history
+ font="SansSerifSmall"
+ follows="all"
+ visible="true"
+ name="chat_history"
+ parse_highlights="true"
+ parse_urls="true"
+ layout="topleft"
+ right="-5"
+ left="5"
+ top="0"
+ bottom="1">
+ </chat_history>
+ </layout_panel>
+ </layout_stack>
+ </panel>
</layout_panel>
</layout_stack>
+ </layout_panel>
+ <layout_panel
+ height="35"
+ layout="topleft"
+ follows="left|right|bottom"
+ left_delta="0"
+ right="0"
+ top_delta="0"
+ bottom="0"
+ visible="true"
+ user_resize="false"
+ auto_resize="false"
+ name="chat_layout_panel">
+ <layout_stack
+ animate="true"
+ default_tab_group="2"
+ follows="all"
+ height="35"
+ right="0"
+ layout="topleft"
+ orientation="horizontal"
+ name="input_panels"
+ top_pad="0"
+ left="0">
+ <layout_panel
+ height="35"
+ layout="topleft"
+ follows="left|right|bottom"
+ left_delta="0"
+ top_delta="0"
+ bottom="0"
+ visible="true"
+ user_resize="false"
+ auto_resize="true"
+ name="input_editor_layout_panel">
+ <chat_editor
+ expand_lines_count="5"
+ follows="left|right|bottom"
+ font="SansSerifSmall"
+ visible="true"
+ height="20"
+ is_expandable="true"
+ label="To"
+ text_tentative_color="TextFgTentativeColor"
+ layout="topleft"
+ name="chat_editor"
+ max_length="1023"
+ spellcheck="true"
+ tab_group="3"
+ width="160"
+ top="6"
+ left="5"
+ right="-5"
+ wrap="true">
+ </chat_editor>
+ </layout_panel>
+ <layout_panel
+ height="35"
+ layout="topleft"
+ follows="left|right|bottom"
+ left_delta="0"
+ top_delta="0"
+ bottom="0"
+ width="35"
+ visible="true"
+ user_resize="false"
+ auto_resize="false"
+ name="input_button_layout_panel">
+ <button
+ follows="left|right|bottom"
+ height="25"
+ image_hover_unselected="Toolbar_Middle_Over"
+ image_overlay="Conv_expand_one_line"
+ image_selected="Toolbar_Middle_Selected"
+ image_unselected="Toolbar_Middle_Off"
+ layout="topleft"
+ name="minz_btn"
+ tool_tip="Shows/hides message panel"
+ width="28"/>
+ </layout_panel>
+ </layout_stack>
+ </layout_panel>
+ </layout_stack>
+ </view>
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml
index 81194f61cf..a7864381a9 100644
--- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml
+++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml
@@ -8,8 +8,8 @@
layout="topleft"
name="incoming call"
help_topic="incoming_call"
- title="Incoming call"
- width="410">
+ sound_flags="0"
+ width="550">
<floater.string
name="lifetime">
5
@@ -24,7 +24,7 @@
</floater.string>
<floater.string
name="VoiceInviteP2P">
- is calling.
+ is calling you.
</floater.string>
<floater.string
name="VoiceInviteAdHoc">
@@ -49,14 +49,14 @@
image_name="icon_avatar_online.tga"
layout="topleft"
left_delta="19"
- top="35"
+ top="20"
width="36" />
<group_icon
enabled="false"
follows="left|top"
height="36"
layout="topleft"
- top="35"
+ top="20"
width="36" />
<text
clip_partial="true"
@@ -67,43 +67,43 @@
name="caller name"
top="20"
use_ellipses="true"
- width="315"
+ width="475"
word_wrap="true" />
- <text
- clip_partial="true"
- font="SansSerif"
- height="30"
- layout="topleft"
- left="77"
- name="question"
- top_pad="5"
- use_ellipses="true"
- width="315"
- word_wrap="true">
- Do you want to leave [CURRENT_CHAT] and join this voice chat?
- </text>
- <button
+ <button
height="24"
- label="Accept"
- label_selected="Accept"
+ label="Answer"
+ label_selected="Answer"
layout="topleft"
left="70"
name="Accept"
- top="92"
- width="100" />
+ top_pad="5"
+ width="120" />
<button
height="24"
- label="Reject"
- label_selected="Reject"
+ label="Ignore"
+ label_selected="Ignore"
layout="topleft"
name="Reject"
left_pad="10"
- width="100" />
+ width="120" />
<button
height="24"
- label="Start IM"
+ label="Open IM instead"
layout="topleft"
name="Start IM"
left_pad="10"
- width="100" />
+ width="120" />
+ <text
+ clip_partial="true"
+ font="SansSerif"
+ height="30"
+ layout="topleft"
+ left="77"
+ name="question"
+ top_pad="5"
+ use_ellipses="true"
+ width="475"
+ word_wrap="true">
+ If you answer, you will be disconnected from your current voice conversation.
+ </text>
</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_moveview.xml b/indra/newview/skins/default/xui/en/floater_moveview.xml
index 4e7ee7913f..5e84283ab0 100644
--- a/indra/newview/skins/default/xui/en/floater_moveview.xml
+++ b/indra/newview/skins/default/xui/en/floater_moveview.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
positioning="specified"
- left="320"
- bottom="-80"
+ right="-693"
+ bottom="-50"
legacy_header_height="18"
can_dock="false"
can_minimize="true"
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..701233ba4a 100644
--- a/indra/newview/skins/default/xui/en/floater_people.xml
+++ b/indra/newview/skins/default/xui/en/floater_people.xml
@@ -6,21 +6,21 @@
can_resize="true"
height="570"
help_topic="sidebar_people"
- min_height="440"
- min_width="333"
+ min_height="220"
+ min_width="260"
layout="topleft"
name="floater_people"
save_rect="true"
single_instance="true"
reuse_instance="true"
title="PEOPLE"
- width="333">
+ width="370">
<panel_container
default_panel_name="panel_people"
follows="all"
height="570"
name="main_panel"
- width="333">
+ width="370">
<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 &amp; Objects"
- font="SansSerifBold"/>
</panel_container>
</floater>
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_controls.xml b/indra/newview/skins/default/xui/en/floater_voice_controls.xml
deleted file mode 100644
index dce2720cf8..0000000000
--- a/indra/newview/skins/default/xui/en/floater_voice_controls.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<floater
- positioning="cascading"
- can_resize="true"
- can_minimize="true"
- can_close="true"
- chrome="true"
- height="205"
- layout="topleft"
- min_height="124"
- min_width="190"
- name="floater_voice_controls"
- help_topic="floater_voice_controls"
- title="VOICE CONTROLS"
- save_dock_state="true"
- save_visibility="true"
- save_rect="true"
- single_instance="true"
- width="282">
- <string
- name="title_nearby">
- VOICE SETTINGS
- </string>
- <string
- name="title_group">
- GROUP CALL WITH [GROUP]
- </string>
- <string
- name="title_adhoc">
- CONFERENCE CALL
- </string>
- <string
- name="title_peer_2_peer">
- CALL WITH [NAME]
- </string>
- <string
- name="no_one_near">
- No one near has voice enabled
- </string>
- <layout_stack
- clip="false"
- follows="all"
- height="189"
- layout="topleft"
- left="10"
- mouse_opaque="false"
- name="my_call_stack"
- orientation="vertical"
- width="263">
- <layout_panel
- follows="top|left|right"
- auto_resize="false"
- layout="topleft"
- min_height="20"
- height="20"
- name="my_panel">
- <avatar_icon
- enabled="false"
- follows="left|top"
- height="18"
- default_icon_name="Generic_Person"
- layout="topleft"
- left="5"
- name="user_icon"
- top="0"
- width="18" />
- <text
- follows="top|left|right"
- font="SansSerifSmallBold"
- height="16"
- layout="topleft"
- left_pad="10"
- name="user_text"
- text_color="White"
- top="4"
- use_ellipses="true"
- value="My Avatar:"
- width="210" />
- <output_monitor
- auto_update="true"
- draw_border="false"
- follows="top|right"
- height="16"
- layout="topleft"
- right="-3"
- name="speaking_indicator"
- left_pad="5"
- visible="true"
- width="20" />
- </layout_panel>
- <layout_panel name="leave_call_panel" height="26" min_height="26" auto_resize="false">
- <layout_stack
- clip="true"
- follows="left|top|right"
- height="26"
- layout="topleft"
- mouse_opaque="false"
- name="voice_effect_and_leave_call_stack"
- orientation="horizontal"
- width="262">
- <layout_panel
- height="26"
- width="200">
- <panel
- class="panel_voice_effect"
- name="panel_voice_effect"
- visiblity_control="VoiceMorphingEnabled"
- filename="panel_voice_effect.xml" />
- </layout_panel>
- <layout_panel
- auto_resize="false"
- follows="top|right"
- height="23"
- visible="true"
- layout="topleft"
- name="leave_call_btn_panel"
- width="100">
- <button
- follows="right|top"
- height="23"
- label="Leave Call"
- name="leave_call_btn"
- width="100" />
- </layout_panel>
- </layout_stack>
- </layout_panel>
- <layout_panel
- follows="all"
- layout="topleft"
- left="2"
- top_pad="0"
- height="132"
- name="callers_panel"
- auto_resize="true"
- width="280">
- <avatar_list
- follows="all"
- height="132"
- ignore_online_status="true"
- layout="topleft"
- multi_select="true"
- name="speakers_list"
- width="280" />
- <panel
- filename="panel_avatar_list_item.xml"
- follows="left|right|top"
- height="24"
- layout="topleft"
- left="0"
- name="non_avatar_caller"
- top="10"
- width="276" />
- </layout_panel>
- </layout_stack>
-</floater>
diff --git a/indra/newview/skins/default/xui/en/floater_voice_effect.xml b/indra/newview/skins/default/xui/en/floater_voice_effect.xml
index 35cb2670d0..146c3d7e30 100644
--- a/indra/newview/skins/default/xui/en/floater_voice_effect.xml
+++ b/indra/newview/skins/default/xui/en/floater_voice_effect.xml
@@ -5,12 +5,13 @@
height="500"
name="voice_effects"
help_topic="voice_effects"
- title="VOICE MORPHING"
+ title="VOICE MORPHING PREVIEW"
background_visible="true"
label="Places"
layout="topleft"
min_height="360"
min_width="200"
+ save_rect="true"
width="300">
<string name="no_voice_effect">
(No Voice Morph)
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..ef4f19cd4c 100644
--- a/indra/newview/skins/default/xui/en/inspect_avatar.xml
+++ b/indra/newview/skins/default/xui/en/inspect_avatar.xml
@@ -2,14 +2,14 @@
<!--
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="164"
+ height="160"
layout="topleft"
name="inspect_avatar"
single_instance="true"
@@ -98,13 +98,13 @@
follows="top|left"
height="23"
increment="0.01"
- left="1"
+ left="10"
max_val="0.95"
min_val="0.05"
name="volume_slider"
show_text="false"
tool_tip="Voice volume"
- top_pad="0"
+ top_pad="5"
value="0.5"
width="200" />
<button
@@ -116,10 +116,21 @@
image_selected="AudioMute_Off"
image_unselected="Audio_Off"
is_toggle="true"
- left_pad="0"
+ left_pad="5"
top_delta="4"
name="mute_btn"
width="16" />
+ <text
+ follows="top|left"
+ height="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 +141,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_cof_gear.xml b/indra/newview/skins/default/xui/en/menu_cof_gear.xml
index a6e9a40e31..45cf780557 100644
--- a/indra/newview/skins/default/xui/en/menu_cof_gear.xml
+++ b/indra/newview/skins/default/xui/en/menu_cof_gear.xml
@@ -9,5 +9,5 @@
<menu
label="New Body Parts"
layout="topleft"
- name="COF.Geear.New_Body_Parts" />
+ name="COF.Gear.New_Body_Parts" />
</toggleable_menu>
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..5a13ef0a59
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_conversation.xml
@@ -0,0 +1,203 @@
+<?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"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_view_profile"/>
+ </menu_item_call>
+ <menu_item_call
+ label="IM"
+ layout="topleft"
+ name="im">
+ <on_click function="Avatar.DoToSelected" parameter="im"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_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"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_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="Remove friends"
+ layout="topleft"
+ name="remove_friends">
+ <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" />
+ <on_enable function="Avatar.EnableItem" parameter="can_invite" />
+ </menu_item_call>
+ <menu_item_separator layout="topleft" name="separator_invite_to_group"/>
+ <menu_item_call
+ label="Zoom In"
+ layout="topleft"
+ name="zoom_in">
+ <on_click function="Avatar.DoToSelected" parameter="zoom_in" />
+ <on_enable function="Avatar.EnableItem" parameter="can_zoom_in" />
+ </menu_item_call>
+ <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" />
+ <on_enable function="Avatar.EnableItem" parameter="can_share" />
+ </menu_item_call>
+ <menu_item_call
+ label="Pay"
+ layout="topleft"
+ name="pay">
+ <on_click function="Avatar.DoToSelected" parameter="pay" />
+ <on_enable function="Avatar.EnableItem" parameter="can_pay" />
+ </menu_item_call>
+ <menu_item_check
+ label="Block Voice"
+ 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_check
+ label="Block Text"
+ layout="topleft"
+ name="MuteText">
+ <on_click function="Avatar.DoToSelected" parameter="mute_unmute" />
+ <on_check function="Avatar.CheckItem" parameter="is_muted" />
+ <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"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_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"/>
+ <on_enable function="Avatar.EnableItem" parameter="can_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" />
+ <on_visible function="Avatar.VisibleItem" parameter="show_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" />
+ <on_visible function="Avatar.VisibleItem" parameter="show_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_conversation.xml b/indra/newview/skins/default/xui/en/menu_im_conversation.xml
new file mode 100644
index 0000000000..43287c6ec3
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_im_conversation.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ layout="topleft"
+ name="Conversation Gear Menu">
+ <menu_item_call
+ label="View Profile"
+ layout="topleft"
+ name="View Profile">
+ <on_click function="Avatar.GearDoToSelected" parameter="view_profile" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_view_profile" />
+ </menu_item_call>
+ <menu_item_call
+ label="Add Friend"
+ layout="topleft"
+ name="Add Friend">
+ <on_click function="Avatar.GearDoToSelected" parameter="add_friend" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_add" />
+ </menu_item_call>
+ <menu_item_call
+ label="Remove friend"
+ layout="topleft"
+ name="remove_friend">
+ <on_click function="Avatar.GearDoToSelected" parameter="remove_friend" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_delete" />
+ </menu_item_call>
+ <menu_item_call
+ label="Offer teleport"
+ layout="topleft"
+ name="offer_teleport">
+ <on_click function="Avatar.GearDoToSelected" parameter="offer_teleport"/>
+ <on_enable function="Avatar.EnableGearItem" parameter="can_offer_teleport"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Invite to group..."
+ layout="topleft"
+ name="invite_to_group">
+ <on_click function="Avatar.GearDoToSelected" parameter="invite_to_group" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_invite" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"
+ name="View Icons Separator" />
+ <menu_item_call
+ label="Chat history..."
+ layout="topleft"
+ name="chat_history">
+ <on_click function="Avatar.GearDoToSelected" parameter="chat_history"/>
+ <on_enable function="Avatar.EnableGearItem" parameter="can_chat_history"/>
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"/>
+ <menu_item_call
+ label="Zoom In"
+ layout="topleft"
+ name="zoom_in">
+ <on_click function="Avatar.DoToSelected" parameter="zoom_in" />
+ <on_enable function="Avatar.EnableItem" parameter="can_zoom_in" />
+ </menu_item_call>
+ <menu_item_call
+ label="Map"
+ layout="topleft"
+ name="map">
+ <on_click function="Avatar.GearDoToSelected" parameter="map" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_show_on_map" />
+ </menu_item_call>
+ <menu_item_call
+ label="Share"
+ layout="topleft"
+ name="Share">
+ <on_click function="Avatar.GearDoToSelected" parameter="share" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_share" />
+ </menu_item_call>
+ <menu_item_call
+ label="Pay"
+ layout="topleft"
+ name="Pay">
+ <on_click function="Avatar.GearDoToSelected" parameter="pay" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_pay" />
+ </menu_item_call>
+ <menu_item_separator
+ layout="topleft"/>
+ <menu_item_check
+ label="Block Voice"
+ layout="topleft"
+ name="Block/Unblock">
+ <on_check function="Avatar.CheckGearItem" parameter="is_blocked" />
+ <on_click function="Avatar.GearDoToSelected" parameter="block_unblock" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_block" />
+ </menu_item_check>
+ <menu_item_check
+ label="Block Text"
+ layout="topleft"
+ name="MuteText">
+ <on_check function="Avatar.CheckGearItem" parameter="is_muted" />
+ <on_click function="Avatar.GearDoToSelected" parameter="mute_unmute" />
+ <on_enable function="Avatar.EnableGearItem" parameter="can_block" />
+ </menu_item_check>
+ <menu_item_separator
+ layout="topleft"/>
+</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..b0adca0e0e
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_im_session_showmodes.xml
@@ -0,0 +1,49 @@
+<?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_im_well_button.xml b/indra/newview/skins/default/xui/en/menu_im_well_button.xml
deleted file mode 100644
index f8dfba91ff..0000000000
--- a/indra/newview/skins/default/xui/en/menu_im_well_button.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<context_menu
- layout="topleft"
- name="IM Well Button Context Menu">
- <menu_item_call
- label="Close All"
- layout="topleft"
- name="Close All">
- <menu_item_call.on_click
- function="IMWellChicletMenu.Action"
- parameter="close all" />
- <menu_item_call.on_enable
- function="IMWellChicletMenu.EnableItem"
- parameter="can close all" />
- </menu_item_call>
-</context_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 &gt;">
- <context_menu
- label="Clothes"
- layout="topleft"
- name="Clothes &gt;">
- <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_object_icon.xml b/indra/newview/skins/default/xui/en/menu_object_icon.xml
index 0c8a2af002..2d4f1792c2 100644
--- a/indra/newview/skins/default/xui/en/menu_object_icon.xml
+++ b/indra/newview/skins/default/xui/en/menu_object_icon.xml
@@ -24,4 +24,22 @@
function="ObjectIcon.Action"
parameter="block" />
</menu_item_call>
+ <menu_item_separator
+ layout="topleft" />
+ <menu_item_call
+ label="Show on Map"
+ layout="topleft"
+ name="show_on_map">
+ <menu_item_call.on_click
+ function="ObjectIcon.Action"
+ parameter="map" />
+ </menu_item_call>
+ <menu_item_call
+ label="Teleport to Object Location"
+ layout="topleft"
+ name="teleport_to_object">
+ <menu_item_call.on_click
+ function="ObjectIcon.Action"
+ parameter="teleport" />
+ </menu_item_call>
</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..7ea87ee05c
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_participant_view.xml
@@ -0,0 +1,112 @@
+<?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_call
+ label="Privacy preferences..."
+ name="privacy_preferences">
+ <on_click
+ function="IMFloaterContainer.Action"
+ parameter="privacy_preferences" />
+ </menu_item_call>
+ <menu_item_check
+ label="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.on_enable
+ function="Avatar.EnableItem"
+ parameter="conversation_log" />
+ </menu_item_check>
+ <menu_item_separator layout="topleft" />
+ <menu_item_check name="Translate_chat" label="Translate Nearby chat">
+ <menu_item_check.on_click
+ function="IMFloaterContainer.Action"
+ parameter="Translating.Toggle" />
+ <menu_item_check.on_check
+ function="IMFloaterContainer.Check"
+ parameter="Translating.On" />
+ <menu_item_check.on_enable
+ function="IMFloaterContainer.Check"
+ parameter="Translating.Enabled" />
+ </menu_item_check>
+ <menu_item_check name="Translation_settings" label="Translation settings...">
+ <menu_item_check.on_check
+ function="Floater.Visible"
+ parameter="prefs_translation" />
+ <menu_item_check.on_click
+ function="Floater.Toggle"
+ parameter="prefs_translation" />
+ </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 &amp; 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..3abb5f7bc8 100644
--- a/indra/newview/skins/default/xui/en/menu_people_nearby.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby.xml
@@ -1,28 +1,69 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<context_menu
layout="topleft"
- name="Avatar Context Menu">
+ name="Nearby People Context Menu">
<menu_item_call
label="View Profile"
layout="topleft"
- name="View Profile">
+ name="view_profile">
<menu_item_call.on_click
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.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_im"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Offer Teleport"
+ name="offer_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="voice_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.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_callog"/>
+ </menu_item_call>
+ <menu_item_separator name="separator_chat_history"/>
+ <menu_item_call
label="Add Friend"
layout="topleft"
- name="Add Friend">
+ 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>
<menu_item_call
label="Remove Friend"
layout="topleft"
- name="Remove Friend">
+ name="remove_friend">
<menu_item_call.on_click
function="Avatar.RemoveFriend" />
<menu_item_call.on_enable
@@ -30,26 +71,30 @@
parameter="can_delete" />
</menu_item_call>
<menu_item_call
- label="IM"
+ label="Invite to group..."
layout="topleft"
- name="IM">
+ name="invite_to_group">
<menu_item_call.on_click
- function="Avatar.IM" />
+ function="Avatar.InviteToGroup" />
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_invite"/>
</menu_item_call>
+ <menu_item_separator name="separator_invite_to_group"/>
<menu_item_call
- label="Call"
+ label="Zoom In"
layout="topleft"
- name="Call">
- <menu_item_call.on_click
- function="Avatar.Call" />
- <menu_item_call.on_enable
- function="Avatar.EnableItem"
- parameter="can_call" />
+ name="zoom_in">
+ <menu_item_call.on_click
+ function="Avatar.ZoomIn" />
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_zoom_in"/>
</menu_item_call>
<menu_item_call
label="Map"
layout="topleft"
- name="Map">
+ name="map">
<menu_item_call.on_click
function="Avatar.ShowOnMap" />
<menu_item_call.on_enable
@@ -59,21 +104,27 @@
<menu_item_call
label="Share"
layout="topleft"
- name="Share">
+ name="share">
<menu_item_call.on_click
function="Avatar.Share" />
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_share"/>
</menu_item_call>
<menu_item_call
label="Pay"
layout="topleft"
- name="Pay">
+ name="pay">
<menu_item_call.on_click
function="Avatar.Pay" />
+ <menu_item_call.on_enable
+ function="Avatar.EnableItem"
+ parameter="can_pay"/>
</menu_item_call>
<menu_item_check
label="Block/Unblock"
layout="topleft"
- name="Block/Unblock">
+ name="block_unblock">
<menu_item_check.on_click
function="Avatar.BlockUnblock" />
<menu_item_check.on_check
@@ -83,13 +134,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_multiselect.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
index 5d58a9d289..5f973088fd 100644
--- a/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
+++ b/indra/newview/skins/default/xui/en/menu_people_nearby_multiselect.xml
@@ -6,7 +6,7 @@
enabled="false"
label="Add Friends"
layout="topleft"
- name="Add Friends">
+ name="add_friends">
<on_click
function="Avatar.AddFriends" />
<on_enable
@@ -16,7 +16,7 @@
<menu_item_call
label="Remove Friends"
layout="topleft"
- name="Remove Friend">
+ name="remove_friends">
<menu_item_call.on_click
function="Avatar.RemoveFriend" />
<menu_item_call.on_enable
@@ -26,7 +26,7 @@
<menu_item_call
label="IM"
layout="topleft"
- name="IM">
+ name="im">
<on_click
function="Avatar.IM" />
</menu_item_call>
@@ -34,7 +34,7 @@
enabled="false"
label="Call"
layout="topleft"
- name="Call">
+ name="call">
<on_click
function="Avatar.Call" />
<on_enable
@@ -45,7 +45,7 @@
enabled="false"
label="Share"
layout="topleft"
- name="Share">
+ name="share">
<on_click
function="Avatar.Share" />
</menu_item_call>
@@ -53,13 +53,13 @@
enabled="false"
label="Pay"
layout="topleft"
- name="Pay">
+ name="pay">
<on_click
function="Avatar.Pay" />
</menu_item_call>
<menu_item_call
label="Offer Teleport"
- name="teleport">
+ name="offer_teleport">
<menu_item_call.on_click
function="Avatar.OfferTeleport"/>
<menu_item_call.on_enable
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 &amp; 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 &amp; 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_url_agent.xml b/indra/newview/skins/default/xui/en/menu_url_agent.xml
index 73f0fa7979..7cd56f257a 100644
--- a/indra/newview/skins/default/xui/en/menu_url_agent.xml
+++ b/indra/newview/skins/default/xui/en/menu_url_agent.xml
@@ -1,13 +1,27 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<context_menu
layout="topleft"
- name="Url Popup">
+ name="Url Popup">
<menu_item_call
- label="Show Resident Profile"
+ label="View Profile"
layout="topleft"
name="show_agent">
<menu_item_call.on_click
- function="Url.ShowProfile" />
+ function="Url.ShowProfile" />
+ </menu_item_call>
+ <menu_item_call
+ label="Send IM..."
+ layout="topleft"
+ name="send_im">
+ <menu_item_call.on_click
+ function="Url.SendIM" />
+ </menu_item_call>
+ <menu_item_call
+ label="Add Friend..."
+ layout="topleft"
+ name="add_friend">
+ <menu_item_call.on_click
+ function="Url.AddFriend" />
</menu_item_call>
<menu_item_separator
layout="topleft" />
diff --git a/indra/newview/skins/default/xui/en/menu_url_objectim.xml b/indra/newview/skins/default/xui/en/menu_url_objectim.xml
index 35c2269b0d..87ab58e622 100644
--- a/indra/newview/skins/default/xui/en/menu_url_objectim.xml
+++ b/indra/newview/skins/default/xui/en/menu_url_objectim.xml
@@ -3,7 +3,7 @@
layout="topleft"
name="Url Popup">
<menu_item_call
- label="Show Object Information"
+ label="Object Profile..."
layout="topleft"
name="show_object">
<menu_item_call.on_click
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index caa36e7302..544f06ac0c 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -130,19 +130,22 @@
label="Status"
name="Status"
tear_off="true">
- <menu_item_call
- label="Away"
- name="Set Away">
- <menu_item_call.on_click
+ <menu_item_check
+ label="Away">
+ <menu_item_check.on_check
+ function="View.Status.CheckAway" />
+ <menu_item_check.on_click
function="World.SetAway" />
- </menu_item_call>
- <menu_item_call
- label="Busy"
- name="Set Busy">
- <menu_item_call.on_click
- function="World.SetBusy"/>
- </menu_item_call>
- </menu>
+ </menu_item_check>
+ <menu_item_check
+ label="Do Not Disturb">
+ <menu_item_check.on_check
+ function="View.Status.CheckDoNotDisturb" />
+ <menu_item_check.on_click
+ function="World.SetDoNotDisturb"/>
+ </menu_item_check>
+
+ </menu>
<menu_item_separator/>
@@ -180,8 +183,7 @@
</menu_item_call>
<menu_item_call
label="Toolbar buttons..."
- name="Toolbars"
- shortcut="control|T">
+ name="Toolbars">
<menu_item_call.on_click
function="Floater.Toggle"
parameter="toybox" />
@@ -218,17 +220,28 @@
label="Communicate"
name="Communicate"
tear_off="true">
- <menu_item_check
- label="Chat..."
+ <menu_item_check
+ label="Conversations..."
+ name="Conversations"
+ shortcut="control|T">
+ <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" />
+ function="Floater.ToggleOrBringToFront"
+ parameter="nearby_chat" />
</menu_item_check>
<menu_item_check
label="Speak"
@@ -244,26 +257,47 @@
parameter="speak" />
</menu_item_check>
<menu_item_check
- label="Voice settings..."
- name="Nearby Voice">
+ label="Conversation Log...">
<menu_item_check.on_check
function="Floater.Visible"
- parameter="voice_controls" />
+ parameter="conversation" />
+ <menu_item_check.on_enable
+ function="Conversation.IsConversationLoggingAllowed" />
<menu_item_check.on_click
function="Floater.Toggle"
- parameter="voice_controls" />
+ parameter="conversation" />
</menu_item_check>
- <menu_item_check
- label="Voice morphing..."
- name="ShowVoice"
+ <menu_item_separator/>
+ <menu
+ label="Voice morphing"
+ name="VoiceMorphing"
visibility_control="VoiceMorphingEnabled">
- <menu_item_check.on_check
- function="Floater.Visible"
- parameter="voice_effect" />
- <menu_item_check.on_click
- function="Floater.Toggle"
- parameter="voice_effect" />
- </menu_item_check>
+ <menu_item_check
+ label="No voice morphing"
+ name="NoVoiceMorphing">
+ <menu_item_check.on_check
+ function="Communicate.VoiceMorphing.NoVoiceMorphing.Check" />
+ <menu_item_check.on_click
+ function="Communicate.VoiceMorphing.NoVoiceMorphing.Click" />
+ </menu_item_check>
+ <menu_item_separator/>
+ <menu_item_check
+ label="Preview..."
+ name="Preview">
+ <menu_item_check.on_check
+ function="Floater.Visible"
+ parameter="voice_effect" />
+ <menu_item_check.on_click
+ function="Floater.Toggle"
+ parameter="voice_effect" />
+ </menu_item_check>
+ <menu_item_call
+ label="Subscribe..."
+ name="Subscribe">
+ <menu_item_call.on_click
+ function="Communicate.VoiceMorphing.Subscribe" />
+ </menu_item_call>
+ </menu>
<menu_item_check
label="Gestures..."
name="Gestures"
@@ -313,8 +347,18 @@
label="Block List"
name="Block List">
<menu_item_call.on_click
- function="Communicate.BlockList" />
+ function="SideTray.PanelPeopleTab"
+ parameter="blocked_panel" />
</menu_item_call>
+ <menu_item_separator/>
+ <menu_item_check
+ label="Do Not Disturb">
+ <menu_item_check.on_check
+ function="View.Status.CheckDoNotDisturb" />
+ <menu_item_check.on_click
+ function="World.SetDoNotDisturb"/>
+ </menu_item_check>
+
</menu>
<menu
create_jump_keys="true"
@@ -1251,7 +1295,58 @@
function="Floater.Show"
parameter="hud" />
</menu_item_call>-->
-
+ <menu_item_separator/>
+
+ <menu_item_call
+ label="User’s guide"
+ name="User’s guide">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://community.secondlife.com/t5/English-Knowledge-Base/Second-Life-User-s-Guide/ta-p/1244857"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Knowledge Base"
+ name="Knowledge Base">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://community.secondlife.com/t5/tkb/communitypage"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Wiki"
+ name="Wiki">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://wiki.secondlife.com"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Community Forums"
+ name="Community Forums">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://community.secondlife.com/t5/Forums/ct-p/Forums"/>
+ </menu_item_call>
+ <menu_item_call
+ label="Support portal"
+ name="Support portal">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="https://support.secondlife.com/"/>
+ </menu_item_call>
+ <menu_item_separator/>
+ <menu_item_call
+ label="[SECOND_LIFE] News"
+ name="Second Life News">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://community.secondlife.com/t5/Featured-News/bg-p/blog_feature_news"/>
+ </menu_item_call>
+ <menu_item_call
+ label="[SECOND_LIFE] Blogs"
+ name="Second Life Blogs">
+ <menu_item_call.on_click
+ function="Advanced.ShowURL"
+ parameter="http://community.secondlife.com/t5/Blogs/ct-p/Blogs"/>
+ </menu_item_call>
<menu_item_separator/>
<menu_item_call
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index c8f5cbb2b0..c681e39002 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -3085,6 +3085,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>
@@ -3093,6 +3094,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]&apos;s objects has been revoked
</notification>
@@ -3726,12 +3728,15 @@ Cannot offer friendship at this time. Please try again in a moment.
<notification
icon="alert.tga"
- name="BusyModeSet"
+ name="DoNotDisturbModeSet"
type="alert">
-Busy mode is set.
-Chat and instant messages will be hidden. Instant messages will get your Busy mode response. All teleportation offers will be declined. All inventory offers will go to your Trash.
+Do Not Disturb is on. You will not be notified of incoming communications.
+
+- Other residents will receive your Do Not Disturb response (set in Preferences &gt; General).
+- Teleportation offers will be declined.
+- Voice calls will be rejected.
<usetemplate
- ignoretext="I change my status to Busy mode"
+ ignoretext="I change my status to Do Not Disturb mode"
name="okignore"
yestext="OK"/>
</notification>
@@ -4277,6 +4282,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.
@@ -4285,6 +4292,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.
@@ -4356,6 +4365,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>
@@ -4367,6 +4378,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>
@@ -4487,6 +4500,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>
@@ -4495,6 +4510,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.
@@ -4552,6 +4569,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>
@@ -4560,6 +4579,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.
@@ -5005,6 +5026,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/>
@@ -5172,25 +5207,25 @@ Do you want to replace it with the selected object?
<notification
icon="alert.tga"
- label="Busy Mode Warning"
- name="BusyModePay"
+ label="Do Not Disturb Mode Warning"
+ name="DoNotDisturbModePay"
type="alert">
-You are in Busy Mode, which means you will not receive any items offered in exchange for this payment.
+You have turned on Do Not Disturb. You will not receive any items offered in exchange for this payment.
-Would you like to leave Busy Mode before completing this transaction?
+Would you like to turn off Do Not Disturb before completing this transaction?
<tag>confirm</tag>
<form name="form">
<ignore name="ignore"
save_option="true"
- text="I am about to pay a person or object while I am in Busy mode"/>
+ text="I am about to pay a person or object while I am in Do Not Disturb mode"/>
<button
default="true"
- ignore="Always leave Busy Mode"
+ ignore="Always leave Do Not Disturb Mode"
index="0"
name="Yes"
text="OK"/>
<button
- ignore="Never leave Busy Mode"
+ ignore="Never leave Do Not Disturb Mode"
index="1"
name="No"
text="Cancel"/>
@@ -5501,6 +5536,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>
@@ -5544,18 +5581,14 @@ Topic: [SUBJECT], Message: [MESSAGE]
<notification
icon="notifytip.tga"
- name="FriendOnline"
+ name="FriendOnlineOffline"
+ log_to_chat="false"
type="notifytip">
<tag>friendship</tag>
-&lt;nolink&gt;[NAME]&lt;/nolink&gt; is Online
- </notification>
-
- <notification
- icon="notifytip.tga"
- name="FriendOffline"
- type="notifytip">
- <tag>friendship</tag>
-&lt;nolink&gt;[NAME]&lt;/nolink&gt; is Offline
+&lt;nolink&gt;[NAME]&lt;/nolink&gt; is [STATUS]
+ <unique combine="cancel_old">
+ <context>NAME</context>
+ </unique>
</notification>
<notification
@@ -5799,6 +5832,8 @@ You don&apos;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>
@@ -5806,6 +5841,8 @@ You don&apos;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>
@@ -5887,6 +5924,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>
@@ -5896,6 +5934,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>
@@ -6040,6 +6079,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]:
@@ -6438,7 +6478,9 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; has given you th
<notification
icon="notify.tga"
name="UserGiveItem"
- type="offer">
+ log_to_im ="true"
+ type="offer"
+ sound="UISndNewIncomingIMSession">
[NAME_SLURL] has given you this [OBJECTTYPE]:
[ITEM_SLURL]
<form name="form">
@@ -6493,7 +6535,10 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; has given you th
<notification
icon="notify.tga"
name="TeleportOffered"
- type="offer">
+ log_to_im="true"
+ log_to_chat="false"
+ type="offer"
+ sound="UISndNewIncomingIMSession">
[NAME_SLURL] has offered to teleport you to their location:
“[MESSAGE]”
@@ -6514,6 +6559,8 @@ Your object named &lt;nolink&gt;[OBJECTFROMNAME]&lt;/nolink&gt; 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:
@@ -6537,6 +6584,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:
@@ -6550,7 +6599,10 @@ However, this region contains content accessible to adults only.
<notification
icon="notify.tga"
name="TeleportOfferSent"
- type="offer">
+ log_to_im="true"
+ log_to_chat="false"
+ show_toast="false"
+ type="notify">
Teleport offer sent to [TO_NAME]
</notification>
@@ -6577,6 +6629,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>
@@ -6600,7 +6653,9 @@ However, this region contains content accessible to adults only.
<notification
icon="notify.tga"
name="FriendshipOffered"
- type="offer">
+ log_to_im="true"
+ show_toast="false"
+ type="notify">
<tag>friendship</tag>
You have offered friendship to [TO_NAME]
</notification>
@@ -6629,7 +6684,8 @@ However, this region contains content accessible to adults only.
<notification
icon="notify.tga"
name="FriendshipAccepted"
- type="offer">
+ log_to_im="true"
+ type="notify">
<tag>friendship</tag>
&lt;nolink&gt;[NAME]&lt;/nolink&gt; accepted your friendship offer.
</notification>
@@ -6637,6 +6693,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>
@@ -6646,7 +6703,9 @@ However, this region contains content accessible to adults only.
<notification
icon="notify.tga"
name="FriendshipAcceptedByMe"
- type="offer">
+ log_to_im="true"
+ show_toast="false"
+ type="notify">
<tag>friendship</tag>
Friendship offer accepted.
</notification>
@@ -6654,7 +6713,9 @@ Friendship offer accepted.
<notification
icon="notify.tga"
name="FriendshipDeclinedByMe"
- type="offer">
+ log_to_im="true"
+ show_toast="false"
+ type="notify">
<tag>friendship</tag>
Friendship offer declined.
</notification>
@@ -6703,6 +6764,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]?
@@ -6805,6 +6867,7 @@ Do not allow access if you do not fully understand why it wants access to your a
<notification
icon="notify.tga"
name="ScriptDialog"
+ show_toast="false"
type="notify">
[NAME]&apos;s &apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos;
[MESSAGE]
@@ -6823,6 +6886,7 @@ Do not allow access if you do not fully understand why it wants access to your a
<notification
icon="notify.tga"
name="ScriptDialogGroup"
+ show_toast="false"
type="notify">
<tag>group</tag>
[GROUPNAME]&apos;s &apos;&lt;nolink&gt;[TITLE]&lt;/nolink&gt;&apos;
@@ -9644,14 +9708,6 @@ No room to sit here, try another spot.
<notification
icon="alertmodal.tga"
- name="AutopilotCanceled"
- type="notify">
- <tag>fail</tag>
-Autopilot canceled
- </notification>
-
- <notification
- icon="alertmodal.tga"
name="ClaimObjectFailedNoPermission"
type="notify">
<tag>fail</tag>
@@ -9938,4 +9994,41 @@ An internal error prevented us from properly updating your viewer. The L$ balan
Cannot create large prims that intersect other players. Please re-try when other players have moved.
</notification>
+ <notification
+ icon="alertmodal.tga"
+ name="PreferenceChatClearLog"
+ type="alertmodal">
+ This will delete the logs of previous conversations, and any backups of that file.
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Confirm before I delete the log of previous conversations."
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="PreferenceChatDeleteTranscripts"
+ type="alertmodal">
+ This will delete the transcripts for all previous conversations. The list of past conversations will not be affected. All files with the suffixes .txt and txt.backup in the folder [FOLDER] will be deleted.
+ <tag>confirm</tag>
+ <usetemplate
+ ignoretext="Confirm before I delete transcripts."
+ name="okcancelignore"
+ notext="Cancel"
+ yestext="OK"/>
+ </notification>
+
+ <notification
+ icon="alert.tga"
+ name="PreferenceChatPathChanged"
+ type="alert">
+ Unable to move files. Restored previous path.
+ <usetemplate
+ ignoretext="Unable to move files. Restored previous path."
+ name="okignore"
+ yestext="OK"/>
+ </notification>
+
</notifications>
diff --git a/indra/newview/skins/default/xui/en/panel_activeim_row.xml b/indra/newview/skins/default/xui/en/panel_activeim_row.xml
deleted file mode 100644
index 9369d1b5cf..0000000000
--- a/indra/newview/skins/default/xui/en/panel_activeim_row.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<panel
- name="panel_activeim_row"
- layout="topleft"
- follows="left|right"
- top="0"
- left="0"
- height="35"
- width="318"
- background_opaque="false"
- background_visible="true"
- bg_alpha_color="0.0 0.0 0.0 0.0" >
- <chiclet_im_p2p
- name="p2p_chiclet"
- layout="topleft"
- follows="left"
- top="3"
- left="5"
- height="25"
- width="25"
- visible="false"
- speaker.name="speaker_p2p"
- speaker.width="20"
- speaker.height="25"
- speaker.left="25"
- speaker.top="25"
- speaker.auto_update="true"
- speaker.draw_border="false"
- speaker.visible="false">
- </chiclet_im_p2p>
- <chiclet_im_group
- name="group_chiclet"
- layout="topleft"
- follows="left"
- top="3"
- left="5"
- height="25"
- width="25"
- visible="false"
- speaker.name="speaker_grp"
- speaker.width="20"
- speaker.height="25"
- speaker.left="25"
- speaker.top="25"
- speaker.auto_update="true"
- speaker.draw_border="false"
- speaker.visible="false">
- </chiclet_im_group>
- <chiclet_im_adhoc
- name="adhoc_chiclet"
- layout="topleft"
- follows="left"
- top="3"
- left="5"
- height="25"
- width="25"
- visible="false"
- speaker.name="speaker_hoc"
- speaker.width="20"
- speaker.height="25"
- speaker.left="25"
- speaker.top="25"
- speaker.auto_update="true"
- speaker.draw_border="false"
- speaker.visible="false">
- </chiclet_im_adhoc>
- <text
- translate="false"
- type="string"
- name="contact_name"
- layout="topleft"
- top="10"
- left_pad="10"
- height="14"
- width="250"
- length="1"
- follows="right|left"
- parse_urls="false"
- use_ellipses="true"
- font="SansSerifBold">
- TestString PleaseIgnore
- </text>
- <button
- top="10"
- right="-5"
- width="17"
- height="17"
- layout="topleft"
- follows="right"
- name="hide_btn"
- mouse_opaque="true"
- label=""
- tab_stop="false"
- image_unselected="Toast_CloseBtn"
- image_selected="Toast_CloseBtn"
- />
-</panel> \ No newline at end of file
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_avatar_list_item.xml b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
index b7c58eb6ab..aa1b929412 100644
--- a/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_avatar_list_item.xml
@@ -129,6 +129,7 @@
left_pad="3"
right="-53"
name="info_btn"
+ tool_tip="More info"
tab_stop="false"
top_delta="0"
width="16" />
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_chiclet_bar.xml b/indra/newview/skins/default/xui/en/panel_chiclet_bar.xml
index ff0146490b..fc321fdd23 100644
--- a/indra/newview/skins/default/xui/en/panel_chiclet_bar.xml
+++ b/indra/newview/skins/default/xui/en/panel_chiclet_bar.xml
@@ -87,54 +87,6 @@
layout="topleft"
min_height="28"
min_width="37"
- name="im_well_panel"
- top="0"
- width="37">
- <chiclet_im_well
- follows="right"
- height="28"
- layout="topleft"
- left="0"
- max_displayed_count="99"
- name="im_well"
- top="0"
- width="35">
- <!--
-Emulate 4 states of button by background images, see details in EXT-3147. The same should be for notification_well button
-xml attribute Description
-image_unselected "Unlit" - there are no new messages
-image_selected "Unlit" + "Selected" - there are no new messages and the Well is open
-image_pressed "Lit" - there are new messages
-image_pressed_selected "Lit" + "Selected" - there are new messages and the Well is open
- -->
- <button
- auto_resize="false"
- follows="right"
- halign="center"
- height="23"
- image_overlay="Unread_IM"
- image_overlay_alignment="center"
- image_pressed="WellButton_Lit"
- image_pressed_selected="WellButton_Lit_Selected"
- image_selected="PushButton_Press"
- label_color="Black"
- left="0"
- name="Unread IM messages"
- tool_tip="Conversations"
- width="34">
- <init_callback
- function="Button.SetDockableFloaterToggle"
- parameter="im_well_window" />
- </button>
- </chiclet_im_well>
- </layout_panel>
- <layout_panel
- auto_resize="false"
- follows="right"
- height="28"
- layout="topleft"
- min_height="28"
- min_width="37"
name="notification_well_panel"
top="0"
width="37">
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..a054e71e34
--- /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="18"
+ follows="top|right|left"
+ image_name="Conv_toolbar_open_call"
+ layout="topleft"
+ left="0"
+ name="selected_icon"
+ top="3"
+ width="18" />
+ </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_indicator"
+ 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_list_item.xml b/indra/newview/skins/default/xui/en/panel_group_list_item.xml
index 12735026fa..cfe3aeb7c9 100644
--- a/indra/newview/skins/default/xui/en/panel_group_list_item.xml
+++ b/indra/newview/skins/default/xui/en/panel_group_list_item.xml
@@ -56,6 +56,7 @@
left_pad="3"
right="-31"
name="info_btn"
+ tool_tip="More info"
tab_stop="false"
top_delta="-2"
width="16" />
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..7ce2627be9 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"
+ label="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"
+ label="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 &amp; 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..bd096ebb88 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_chat.xml
@@ -1,242 +1,493 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- border="true"
- follows="left|top|right|bottom"
- height="408"
- label="Text Chat"
- layout="topleft"
- left="102"
- name="chat"
- top="1"
- width="517">
- <text
- follows="left|top"
- layout="topleft"
- left="30"
- height="12"
- name="font_size"
- width="120"
- top="10">
- Font size:
- </text>
- <radio_group
- height="30"
- layout="topleft"
- left="40"
- control_name="ChatFontSize"
- name="chat_font_size"
- top_pad="0"
- width="440">
- <radio_item
- height="16"
- label="Small"
- layout="topleft"
- left="0"
- name="radio"
- value="0"
- top="10"
- width="125" />
- <radio_item
- height="16"
- label="Medium"
- layout="topleft"
- left_delta="145"
- name="radio2"
- value="1"
- top_delta="0"
- width="125" />
- <radio_item
- height="16"
- label="Large"
- layout="topleft"
- left_delta="170"
- name="radio3"
- value="2"
- top_delta="0"
- width="125" />
- </radio_group>
-
+ border="true"
+ has_border="true"
+ height="408"
+ label="Text Chat"
+ layout="topleft"
+ left="102"
+ name="chat"
+ top="1"
+ width="517">
+
+ <panel
+ border="false"
+ height="60"
+ layout="topleft"
+ top="10"
+ left="13"
+ width="517">
+
<check_box
- control_name="PlayTypingAnim"
- height="16"
- initial_value="true"
- label="Play typing animation when chatting"
- layout="topleft"
- left="30"
- name="play_typing_animation"
- top_pad="10"
- width="400" />
+ control_name="PlayTypingAnim"
+ height="16"
+ initial_value="true"
+ label="Play typing animation when chatting"
+ layout="topleft"
+ top="0"
+ name="play_typing_animation"
+ width="330">
+ </check_box>
+
<check_box
- enabled="false"
- height="16"
- label="Email me IMs when I'm offline"
- layout="topleft"
- left_delta="0"
- name="send_im_to_email"
- top_pad="5"
- width="400" />
+ enabled="false"
+ height="16"
+ label="Email me IMs when I'm offline"
+ layout="topleft"
+ name="send_im_to_email"
+ top_pad="6"
+ width="330">
+ </check_box>
+
<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" />
+ control_name="VoiceCallsFriendsOnly"
+ height="16"
+ label="Only friends and groups can call or IM me"
+ layout="topleft"
+ name="voice_call_friends_only_check"
+ top_pad="6"
+ width="350">
+ </check_box>
+
+ <text
+ layout="topleft"
+ left="345"
+ height="12"
+ name="font_size"
+ width="120"
+ top="0">
+ Font size:
+ </text>
+
+ <combo_box
+ control_name="ChatFontSize"
+ height="23"
+ layout="topleft"
+ left="341"
+ name="chat_font_size"
+ top_pad="5"
+ width="100">
+ <item
+ label="Small"
+ name="Small"
+ value="0"/>
+ <item
+ label="Medium"
+ name="Medium"
+ value="1"/>
+ <item
+ label="Large"
+ name="Large"
+ value="2"/>
+ </combo_box>
+
<check_box
- control_name="UseChatBubbles"
- follows="left|top"
- height="16"
- label="Bubble Chat"
- layout="topleft"
- left_delta="0"
- top_pad="5"
- name="bubble_text_chat"
- width="150" />
+ control_name="UseChatBubbles"
+ height="16"
+ label="Bubble Chat"
+ layout="topleft"
+ top_pad="4"
+ name="bubble_text_chat"
+ width="330">
+ </check_box>
+
+ </panel>
+
+ <panel
+ border="false"
+ height="165"
+ layout="topleft"
+ left="13"
+ width="517">
+
<text
- name="show_ims_in_label"
- follows="left|top"
- layout="topleft"
- left="30"
- height="20"
- width="170"
- top_pad="15">
- Show IMs in:
+ layout="topleft"
+ height="12"
+ name="notifications"
+ left="0"
+ width="120">
+ Notifications
</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"
+ height="12"
+ name="friend_ims"
+ width="145"
+ left="0"
+ top_pad="13">
+ Friend IMs:
+ </text>
+ <combo_box
+ control_name="NotificationFriendIMOptions"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ top_delta="-6"
+ name="FriendIMOptions"
+ width="223">
+ <item
+ label="Open Conversations window"
+ name="OpenConversationsWindow"
+ value="openconversations"/>
+ <item
+ label="Pop up the message"
+ name="PopUpMessage"
+ value="toast"/>
+ <item
+ label="Flash toolbar button"
+ name="FlashToolbarButton"
+ value="flash"/>
+ <item
+ label="None"
+ name="None"
+ value="none"/>
+ </combo_box>
+ <text
+ layout="topleft"
+ height="12"
+ name="non_friend_ims"
+ width="145"
+ left="0"
+ top_pad="9">
+ Non-friend IMs:
+ </text>
+ <combo_box
+ control_name="NotificationNonFriendIMOptions"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ top_delta="-6"
+ name="NonFriendIMOptions"
+ width="223">
+ <item
+ label="Open Conversations window"
+ name="OpenConversationsWindow"
+ value="openconversations"/>
+ <item
+ label="Pop up the message"
+ name="PopUpMessage"
+ value="toast"/>
+ <item
+ label="Flash toolbar button"
+ name="FlashToolbarButton"
+ value="flash"/>
+ <item
+ label="None"
+ name="None"
+ value="none"/>
+ </combo_box>
+ <text
+ layout="topleft"
+ left="0"
+ height="13"
+ name="conference_ims"
+ width="145"
+ top_pad="9">
+ Conference IMs:
+ </text>
+ <combo_box
+ control_name="NotificationConferenceIMOptions"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ top_delta="-6"
+ name="ConferenceIMOptions"
+ width="223">
+ <item
+ label="Open Conversations window"
+ name="OpenConversationsWindow"
+ value="openconversations"/>
+ <item
+ label="Pop up the message"
+ name="PopUpMessage"
+ value="toast"/>
+ <item
+ label="Flash toolbar button"
+ name="FlashToolbarButton"
+ value="flash"/>
+ <item
+ label="None"
+ name="None"
+ value="none"/>
+ </combo_box>
+ <text
+ layout="topleft"
+ left="0"
+ height="13"
+ name="group_chat"
+ width="145"
+ top_pad="9">
+ Group chat:
+ </text>
+ <combo_box
+ control_name="NotificationGroupChatOptions"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ top_delta="-6"
+ name="GroupChatOptions"
+ width="223">
+ <item
+ label="Open Conversations window"
+ name="OpenConversationsWindow"
+ value="openconversations"/>
+ <item
+ label="Pop up the message"
+ name="PopUpMessage"
+ value="toast"/>
+ <item
+ label="Flash toolbar button"
+ name="FlashToolbarButton"
+ value="flash"/>
+ <item
+ label="None"
+ name="None"
+ value="none"/>
+ </combo_box>
+ <text
+ layout="topleft"
+ left="0"
+ height="12"
+ name="nearby_chat"
+ width="145"
+ top_pad="9">
+ Nearby chat:
+ </text>
+ <combo_box
+ control_name="NotificationNearbyChatOptions"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ top_delta="-6"
+ name="NearbyChatOptions"
+ width="223">
+ <item
+ label="Open Conversations window"
+ name="OpenConversationsWindow"
+ value="openconversations"/>
+ <item
+ label="Pop up the message"
+ name="PopUpMessage"
+ value="toast"/>
+ <item
+ label="Flash toolbar button"
+ name="FlashToolBarButton"
+ value="flash"/>
+ <item
+ label="None"
+ name="None"
+ value="none"/>
+ </combo_box>
+ <text
+ layout="topleft"
+ left="0"
+ height="13"
+ name="notifications_alert"
+ width="500"
+ top_pad="9"
+ visible="true"
+ text_color="DrYellow">
+ To temporarily stop all notifications, use Communicate &gt; Do Not Disturb.
+ </text>
+
+ </panel>
+
+ <panel
+ border="false"
+ height="50"
layout="topleft"
- left_delta="0"
- name="radio2"
- value="1"
- top_pad="5"
- width="150" />
- </radio_group>
+ left="13"
+ top_pad="10"
+ width="517">
+
<text
- name="disable_toast_label"
- follows="left|top"
- layout="topleft"
- top_pad="20"
- left="30"
- height="10"
- width="400">
- Enable incoming chat popups:
- </text>
+ layout="topleft"
+ left="0"
+ name="play_sound"
+ width="100"
+ top_pad="8"
+ visible="true">
+ Play sound:
+ </text>
+ <check_box
+ control_name="PlaySoundNewConversation"
+ height="16"
+ label="New conversation"
+ layout="topleft"
+ left_pad="15"
+ top_pad="-10"
+ name="new_conversation"
+ width="150" />
<check_box
- control_name="EnableGroupChatPopups"
- name="EnableGroupChatPopups"
- label="Group Chats"
- layout="topleft"
- top_pad="5"
- left_delta="10"
- height="20"
- tool_tip="Check to see popups when a Group Chat message arrives"
- width="400" />
+ control_name="PlaySoundIncomingVoiceCall"
+ height="16"
+ label="Incoming voice call"
+ layout="topleft"
+ top_pad="6"
+ name="incoming_voice_call"
+ width="150" />
<check_box
- control_name="EnableIMChatPopups"
- name="EnableIMChatPopups"
- label="IM Chats"
- layout="topleft"
- top_pad="5"
- height="16"
- tool_tip="Check to see popups when an instant message arrives"
- width="400" />
- <spinner
- control_name="NearbyToastLifeTime"
- decimal_digits="0"
- follows="left|top"
- height="23"
- increment="1"
- initial_value="23"
- label="Nearby chat toasts life time:"
- label_width="285"
- layout="topleft"
- left="45"
- max_val="60"
- min_val="1"
- name="nearby_toasts_lifetime"
- top_pad="10"
- width="325" />
- <spinner
- control_name="NearbyToastFadingTime"
- decimal_digits="0"
- follows="left|top"
- height="23"
- increment="1"
- initial_value="3"
- label="Nearby chat toasts fading time:"
- label_width="285"
- layout="topleft"
- left_delta="0"
- max_val="60"
- min_val="0"
- name="nearby_toasts_fadingtime"
- top_pad="3"
- width="325" />
+ control_name="PlaySoundTeleportOffer"
+ height="16"
+ label="Teleport offer"
+ layout="topleft"
+ left_pad="35"
+ top_pad="-38"
+ name="teleport_offer"
+ width="150" />
+ <check_box
+ control_name="PlaySoundInventoryOffer"
+ height="16"
+ label="Inventory offer"
+ layout="topleft"
+ top_pad="6"
+ name="inventory_offer"
+ width="150" />
+
+ </panel>
+
+ <view_border
+ bevel_style="none"
+ height="0"
+ layout="topleft"
+ left="13"
+ name="cost_text_border"
+ top_pad="5"
+ width="495"/>
+
+ <panel
+ height="50"
+ layout="topleft"
+ left="13"
+ top_pad="10"
+ width="505">
+
+ <text
+ layout="topleft"
+ left="0"
+ text_color="White"
+ height="12"
+ top="5"
+ width="55">
+ Save:
+ </text>
+
+ <combo_box
+ enabled="false"
+ control_name="KeepConversationLogTranscripts"
+ height="23"
+ layout="topleft"
+ left_pad="5"
+ name="chat_font_size"
+ top="0"
+ width="165">
+ <item
+ label="Log and transcripts"
+ value="2"/>
+ <item
+ label="Log only"
+ value="1"/>
+ <item
+ label="No log or transcripts"
+ value="0"/>
+ </combo_box>
+
+ <button
+ enabled="false"
+ height="23"
+ label="Clear log..."
+ layout="topleft"
+ left_pad="5"
+ top="0"
+ name="clear_log"
+ width="110">
+ <commit_callback
+ function="Pref.ClearLog" />
+ </button>
+
+ <button
+ enabled="false"
+ height="23"
+ label="Delete transcripts..."
+ layout="topleft"
+ left_pad="5"
+ top="0"
+ name="delete_transcripts"
+ width="147">
+ <button.commit_callback
+ function="Pref.DeleteTranscripts" />
+ </button>
+
+ <text
+ layout="topleft"
+ left="0"
+ text_color="White"
+ height="12"
+ top_pad="15"
+ width="55">
+ Location:
+ </text>
+
+ <line_editor
+ control_name="InstantMessageLogPath"
+ border_style="line"
+ border_thickness="1"
+ font="SansSerif"
+ height="23"
+ layout="topleft"
+ left_pad="55"
+ max_length="4096"
+ name="log_path_string"
+ top_delta="-5"
+ width="185">
+ </line_editor>
+
+ <button
+ enabled="false"
+ follows="left|top"
+ height="23"
+ label="Browse..."
+ label_selected="Browse"
+ layout="topleft"
+ left_pad="5"
+ name="log_path_button"
+ top_delta="0"
+ width="112">
+ <commit_callback function="Pref.LogPath" />
+ </button>
+
+ </panel>
+
<button
- follows="left|top"
- height="23"
- label="Translation..."
- layout="topleft"
- left="30"
- name="ok_btn"
- top="-50"
- width="170">
- <button.commit_callback
- function="Pref.TranslationSettings" />
+ height="23"
+ label="Translation..."
+ layout="topleft"
+ left="9"
+ name="ok_btn"
+ top="-29"
+ width="170">
+ <commit_callback
+ function="Pref.TranslationSettings" />
</button>
<button
- follows="top|left"
- height="23"
- layout="topleft"
- top_pad="-23"
- left_pad="5"
- name="autoreplace_showgui"
- commit_callback.function="Pref.AutoReplace"
- label="Auto-Replace..."
- width="150">
+ height="23"
+ layout="topleft"
+ top_pad="-23"
+ left_pad="5"
+ name="autoreplace_showgui"
+ commit_callback.function="Pref.AutoReplace"
+ label="Auto-Replace..."
+ width="150">
</button>
<button
- follows="top|left"
- height="23"
- layout="topleft"
- top_pad="-23"
- left_pad="5"
- name="spellcheck_showgui"
- commit_callback.function="Pref.SpellChecker"
- label="Spell Checking..."
- width="150">
+ height="23"
+ layout="topleft"
+ top_pad="-23"
+ left_pad="5"
+ name="spellcheck_showgui"
+ commit_callback.function="Pref.SpellChecker"
+ label="Spell Checking..."
+ width="150">
</button>
</panel>
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_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml
index 24882988b0..ea0f7d8593 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml
@@ -409,10 +409,10 @@
name="text_box3"
top_pad="3"
width="240">
- Busy mode response:
+ Do Not Disturb response:
</text>
<text_editor
- control_name="BusyModeResponse"
+ control_name="DoNotDisturbModeResponse"
text_readonly_color="LabelDisabledColor"
bg_writeable_color="LtGray"
use_ellipses="false"
@@ -421,7 +421,7 @@
height="29"
layout="topleft"
left="30"
- name="busy_response"
+ name="do_not_disturb_response"
width="470"
word_wrap="true">
log_in_to_change
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
index 587c461bee..78743d26bb 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_privacy.xml
@@ -1,72 +1,69 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
- border="true"
- follows="left|top|right|bottom"
- height="408"
- label="Communication"
- layout="topleft"
- left="102"
- name="im"
- top="1"
- width="517">
- <panel.string
- name="log_in_to_change">
- log in to change
- </panel.string>
- <button
- follows="left|bottom"
- height="23"
- label="Clear History"
- tool_tip="Clear login image, last location, teleport history, web, and texture cache"
- layout="topleft"
- left="30"
- name="clear_cache"
- top="10"
- width="145">
- <button.commit_callback
- function="Pref.WebClearCache" />
- </button>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_pad="10"
- mouse_opaque="false"
- name="cache_size_label_l"
- top_delta="3"
- text_color="LtGray_50"
- width="300">
- (Locations, images, web, search history)
- </text>
- <check_box
- height="16"
- enabled="false"
- label="Show me in Search results"
- layout="topleft"
- left="30"
- name="online_searchresults"
- top_pad="20"
- width="350" />
- <check_box
- height="16"
- enabled="false"
- label="Only friends and groups know I'm online"
- layout="topleft"
- left="30"
- name="online_visibility"
- top_pad="30"
- width="350" />
- <check_box
- control_name="VoiceCallsFriendsOnly"
- height="16"
- label="Only friends and groups can call or IM me"
- layout="topleft"
- left="30"
- name="voice_call_friends_only_check"
- top_pad="10"
- width="350" />
+ border="true"
+ follows="left|top|right|bottom"
+ height="408"
+ label="Communication"
+ layout="topleft"
+ left="102"
+ name="im"
+ top="1"
+ width="517">
+
+ <panel.string
+ name="log_in_to_change">
+ log in to change
+ </panel.string>
+
+ <button
+ follows="left|bottom"
+ height="23"
+ label="Clear History"
+ tool_tip="Clear login image, last location, teleport history, web, and texture cache"
+ layout="topleft"
+ left="30"
+ name="clear_cache"
+ top="10"
+ width="145">
+ <button.commit_callback
+ function="Pref.WebClearCache" />
+ </button>
+
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="10"
+ layout="topleft"
+ left_pad="10"
+ mouse_opaque="false"
+ name="cache_size_label_l"
+ top_delta="3"
+ text_color="LtGray_50"
+ width="300">
+ (Locations, images, web, search history)
+ </text>
+
+ <check_box
+ height="16"
+ enabled="false"
+ label="Show me in Search results"
+ layout="topleft"
+ left="30"
+ name="online_searchresults"
+ top_pad="20"
+ width="350" />
+
+ <check_box
+ height="16"
+ enabled="false"
+ label="Only friends and groups know I'm online"
+ layout="topleft"
+ left="30"
+ name="online_visibility"
+ top_pad="30"
+ width="350" />
+
<check_box
enabled_control="EnableVoiceChat"
control_name="AutoDisengageMic"
@@ -87,100 +84,7 @@
name="favorites_on_login_check"
top_pad="10"
width="350" />
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left="30"
- mouse_opaque="false"
- name="Logs:"
- top_pad="20"
- width="350">
- Chat Logs:
- </text>
- <check_box
- enabled="false"
- control_name="LogNearbyChat"
- height="16"
- label="Save nearby chat logs on my computer"
- layout="topleft"
- left="30"
- name="log_nearby_chat"
- top_pad="10"
- width="350">
- </check_box>
- <check_box
- enabled="false"
- control_name="LogInstantMessages"
- height="16"
- label="Save IM logs on my computer"
- layout="topleft"
- left="30"
- name="log_instant_messages"
- top_pad="10"
- width="350">
- </check_box>
- <check_box
- control_name="LogTimestamp"
- enabled="false"
- height="16"
- label="Add timestamp to each line in chat log"
- layout="topleft"
- left_delta="0"
- name="show_timestamps_check_im"
- top_pad="10"
- width="237" />
- <check_box
- control_name="LogFileNamewithDate"
- enabled="false"
- height="16"
- label="Add datestamp to log file name."
- layout="topleft"
- left_delta="5"
- name="logfile_name_datestamp"
- top_pad="10"
- width="350"/>
- <text
- type="string"
- length="1"
- follows="left|top"
- height="10"
- layout="topleft"
- left_delta="0"
- mouse_opaque="false"
- name="log_path_desc"
- top_pad="30"
- width="128">
- Location of logs:
- </text>
- <line_editor
- bottom="366"
- control_name="InstantMessageLogPath"
- follows="top|left|right"
- halign="right"
- height="23"
- layout="topleft"
- left_delta="0"
- mouse_opaque="false"
- name="log_path_string"
- top_pad="5"
- width="250"/>
- <button
- enabled="false"
- follows="right|bottom"
- height="23"
- label="Browse"
- label_selected="Browse"
- layout="topleft"
- left_pad="5"
- name="log_path_button"
- top_delta="0"
- width="145">
- <button.commit_callback
- function="Pref.LogPath" />
- </button>
+
<button
follows="left|bottom"
height="23"
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index 0f4424a7f9..3b1cbf6a58 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -295,7 +295,7 @@ Please try logging in again in a minute.</string>
<!-- llvoavatar. Displayed in the avatar chat bubble -->
<string name="AvatarEditingAppearance">(Editing Appearance)</string>
<string name="AvatarAway">Away</string>
- <string name="AvatarBusy">Busy</string>
+ <string name="AvatarDoNotDisturb">Do Not Disturb</string>
<string name="AvatarMuted">Blocked</string>
<!-- animations -->
@@ -384,6 +384,8 @@ 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>
@@ -405,8 +407,9 @@ Please try logging in again in a minute.</string>
<string name="ChangePermissions">Change its permissions</string>
<string name="TrackYourCamera">Track your camera</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>
- <string name="NotConnected">Not Connected</string>
<!-- Sim Access labels -->
<string name="SIM_ACCESS_PG">General</string>
@@ -2071,12 +2074,6 @@ For AI Character: Get the closest navigable point to the point provided.
</string>
- <!-- Avatar busy/away mode -->
- <string name="AvatarSetNotAway">Not Away</string>
- <string name="AvatarSetAway">Away</string>
- <string name="AvatarSetNotBusy">Not Busy</string>
- <string name="AvatarSetBusy">Busy</string>
-
<!-- Wearable Types -->
<string name="shape">Shape</string>
<string name="skin">Skin</string>
@@ -2270,7 +2267,8 @@ 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 favorites">My Favorites</string>
@@ -2284,6 +2282,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>
@@ -2526,7 +2525,7 @@ Drag folders to this area and click "Send to Marketplace" to list them for sale
<string name="PanelContentsNewScript">New Script</string>
<!-- panel preferences general -->
- <string name="BusyModeResponseDefault">The Resident you messaged is in &apos;busy mode&apos; which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing.</string>
+ <string name="DoNotDisturbModeResponseDefault">This resident has turned on &apos;Do Not Disturb&apos; and will see your message later.</string>
<!-- Mute -->
<string name="MuteByName">(By name)</string>
@@ -2585,9 +2584,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>
@@ -3383,12 +3379,15 @@ 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 &apos;Only friends and groups can call or IM me&apos; 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>
<string name="you_started_call">You started a voice call</string>
<string name="you_joined_call">You joined the voice call</string>
- <string name="name_started_call">[NAME] started a voice call</string>
+ <string name="you_auto_rejected_call-im">You automatically rejected the voice call while &apos;Do Not Disturb&apos; was on.</string>
+ <string name="name_started_call">[NAME] started a voice call</string>
<string name="ringing-im">
Joining voice call...
@@ -3403,7 +3402,7 @@ If you continue to receive this message, contact the [SUPPORT_SITE].
Connecting...
</string>
<string name="conference-title">
- Ad-hoc Conference
+ Multi-person chat
</string>
<string name="conference-title-incoming">
Conference with [AGENT_NAME]
@@ -3829,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>
@@ -3855,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>
@@ -3909,4 +3910,15 @@ Try enclosing path to the editor with double quotes.
<!-- Spell check settings floater -->
<string name="UserDictionary">[User]</string>
+ <!-- Conversation log messages -->
+ <string name="logging_calls_disabled_log_empty">
+ Conversations are not being logged. To begin keeping a log, choose "Save: Log only" or "Save: Log and transcripts" under Preferences > Chat.
+ </string>
+ <string name="logging_calls_disabled_log_not_empty">
+ No more conversations will be logged. To resume keeping a log, choose "Save: Log only" or "Save: Log and transcripts" under Preferences > Chat.
+ </string>
+ <string name="logging_calls_enabled_log_empty">
+ There are no logged conversations. After you contact someone, or someone contacts you, a log entry will be shown here.
+ </string>
+
</strings>
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/chiclet_im_adhoc.xml b/indra/newview/skins/default/xui/en/widgets/chiclet_im_adhoc.xml
deleted file mode 100644
index 0e29ed0d0b..0000000000
--- a/indra/newview/skins/default/xui/en/widgets/chiclet_im_adhoc.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<chiclet_im_adhoc
- height="23"
- name="im_adhoc_chiclet"
- show_speaker="false"
- width="25">
- <chiclet_im_adhoc.chiclet_button
- height="25"
- image_selected="PushButton_On"
- image_unselected="PushButton_Off"
- name="chiclet_button"
- tab_stop="false"
- width="25" />
- <chiclet_im_adhoc.speaker
- image_mute="Parcel_VoiceNo_Light"
- image_off="VoicePTT_Off_Dark"
- image_on="VoicePTT_On_Dark"
- image_level_1="VoicePTT_Lvl1_Dark"
- image_level_2="VoicePTT_Lvl2_Dark"
- image_level_3="VoicePTT_Lvl3_Dark"
- auto_update="true"
- draw_border="false"
- height="24"
- left="25"
- bottom="1"
- name="speaker"
- visible="false"
- width="20" />
- <chiclet_im_adhoc.avatar_icon
- bottom="3"
- follows="left|top|bottom"
- height="20"
- left="2"
- mouse_opaque="false"
- name="adhoc_icon"
- width="21" />
- <chiclet_im_adhoc.unread_notifications
- halign="center"
- height="23"
- left="25"
- mouse_opaque="false"
- name="unread"
- text_color="white"
- v_pad="3"
- visible="false"
- width="20" />
- <chiclet_im_adhoc.new_message_icon
- bottom="11"
- height="14"
- image_name="Unread_Chiclet"
- left="12"
- name="new_message_icon"
- visible="false"
- width="14" />
-</chiclet_im_adhoc> \ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/widgets/chiclet_im_group.xml b/indra/newview/skins/default/xui/en/widgets/chiclet_im_group.xml
deleted file mode 100644
index 77011139bf..0000000000
--- a/indra/newview/skins/default/xui/en/widgets/chiclet_im_group.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<chiclet_im_group
- height="23"
- name="im_group_chiclet"
- show_speaker="false"
- width="25">
- <chiclet_im_group.chiclet_button
- height="25"
- image_selected="PushButton_On"
- image_unselected="PushButton_Off"
- name="chiclet_button"
- tab_stop="false"
- width="25" />
- <chiclet_im_group.speaker
- image_mute="Parcel_VoiceNo_Light"
- image_off="VoicePTT_Off_Dark"
- image_on="VoicePTT_On_Dark"
- image_level_1="VoicePTT_Lvl1_Dark"
- image_level_2="VoicePTT_Lvl2_Dark"
- image_level_3="VoicePTT_Lvl3_Dark"
- auto_update="true"
- draw_border="false"
- height="24"
- left="25"
- bottom="1"
- name="speaker"
- visible="false"
- width="20" />
- <chiclet_im_group.group_icon
- bottom="3"
- default_icon="Generic_Group"
- follows="left|top|bottom"
- height="20"
- left="2"
- mouse_opaque="false"
- name="group_icon"
- width="21" />
- <chiclet_im_group.unread_notifications
- height="23"
- halign="center"
- left="25"
- mouse_opaque="false"
- name="unread"
- text_color="white"
- v_pad="3"
- visible="false"
- width="20"/>
- <chiclet_im_group.new_message_icon
-bottom="11"
- height="14"
- image_name="Unread_Chiclet"
- left="12"
- name="new_message_icon"
- visible="false"
- width="14" />
-</chiclet_im_group> \ No newline at end of file
diff --git a/indra/newview/skins/default/xui/en/widgets/chiclet_im_p2p.xml b/indra/newview/skins/default/xui/en/widgets/chiclet_im_p2p.xml
deleted file mode 100644
index 8b56a8f0f6..0000000000
--- a/indra/newview/skins/default/xui/en/widgets/chiclet_im_p2p.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<chiclet_im_p2p
- height="23"
- name="im_p2p_chiclet"
- show_speaker="false"
- width="25">
- <chiclet_im_p2p.chiclet_button
- height="25"
- image_selected="PushButton_On"
- image_unselected="PushButton_Off"
- name="chiclet_button"
- tab_stop="false"
- width="25"/>
- <chiclet_im_p2p.speaker
- image_mute="Parcel_VoiceNo_Light"
- image_off="VoicePTT_Off_Dark"
- image_on="VoicePTT_On_Dark"
- image_level_1="VoicePTT_Lvl1_Dark"
- image_level_2="VoicePTT_Lvl2_Dark"
- image_level_3="VoicePTT_Lvl3_Dark"
- auto_update="true"
- draw_border="false"
- height="24"
- left="25"
- bottom="1"
- name="speaker"
- visible="false"
- width="20" />
- <chiclet_im_p2p.avatar_icon
- bottom="3"
- color="white"
- follows="left|top|bottom"
- height="20"
- left="2"
- mouse_opaque="false"
- name="avatar_icon"
- width="21" />
- <chiclet_im_p2p.unread_notifications
- height="23"
- halign="center"
- left="25"
- mouse_opaque="false"
- name="unread"
- text_color="white"
- v_pad="3"
- visible="false"
- width="20"/>
- <chiclet_im_p2p.new_message_icon
- bottom="11"
- height="14"
- image_name="Unread_Chiclet"
- left="12"
- name="new_message_icon"
- visible="false"
- width="14" />
-</chiclet_im_p2p>
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..b83d9122f7
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/widgets/conversation_view_participant.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<conversation_view_participant
+ folder_arrow_image="Folder_Arrow"
+ 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"
+ 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/en/widgets/toolbar.xml b/indra/newview/skins/default/xui/en/widgets/toolbar.xml
index 0aa478ace9..0ace37a5dc 100644
--- a/indra/newview/skins/default/xui/en/widgets/toolbar.xml
+++ b/indra/newview/skins/default/xui/en/widgets/toolbar.xml
@@ -30,9 +30,9 @@
image_overlay_alignment="left"
use_ellipses="true"
auto_resize="true"
- button_flash_count="99999"
- button_flash_rate="1.0"
- flash_color="EmphasisColor"/>
+ button_flash_count="4"
+ button_flash_rate="0.5"
+ flash_color="BeaconColor"/>
<button_icon pad_left="10"
pad_right="10"
image_bottom_pad="10"
@@ -51,7 +51,7 @@
chrome="true"
use_ellipses="true"
auto_resize="true"
- button_flash_count="99999"
- button_flash_rate="1.0"
- flash_color="EmphasisColor"/>
+ button_flash_count="4"
+ button_flash_rate="0.5"
+ flash_color="BeaconColor"/>
</toolbar>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/es/menu_im_well_button.xml
deleted file mode 100644
index c8f6c217cc..0000000000
--- a/indra/newview/skins/default/xui/es/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Cerrar todo" name="Close All"/>
-</context_menu>
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_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/menu_im_well_button.xml b/indra/newview/skins/default/xui/fr/menu_im_well_button.xml
deleted file mode 100644
index 8ef1529e6b..0000000000
--- a/indra/newview/skins/default/xui/fr/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Tout fermer" name="Close All"/>
-</context_menu>
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/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/menu_im_well_button.xml b/indra/newview/skins/default/xui/it/menu_im_well_button.xml
deleted file mode 100644
index 9e471b771c..0000000000
--- a/indra/newview/skins/default/xui/it/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Chiudi tutto" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/ja/menu_im_well_button.xml
deleted file mode 100644
index 3397004bd7..0000000000
--- a/indra/newview/skins/default/xui/ja/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="すべて閉じる" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/pl/menu_im_well_button.xml
deleted file mode 100644
index 207bc2211b..0000000000
--- a/indra/newview/skins/default/xui/pl/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Zamknij wszystkie" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/pt/menu_im_well_button.xml
deleted file mode 100644
index 2d37cefd6f..0000000000
--- a/indra/newview/skins/default/xui/pt/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Fechar tudo" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/ru/menu_im_well_button.xml
deleted file mode 100644
index 5a5bde61b9..0000000000
--- a/indra/newview/skins/default/xui/ru/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Закрыть все" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/tr/menu_im_well_button.xml
deleted file mode 100644
index c3e559a723..0000000000
--- a/indra/newview/skins/default/xui/tr/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="Tümünü Kapat" name="Close All"/>
-</context_menu>
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/menu_im_well_button.xml b/indra/newview/skins/default/xui/zh/menu_im_well_button.xml
deleted file mode 100644
index 4b9b4b2758..0000000000
--- a/indra/newview/skins/default/xui/zh/menu_im_well_button.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="yes"?>
-<context_menu name="IM Well Button Context Menu">
- <menu_item_call label="全部關閉" name="Close All"/>
-</context_menu>
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 4361b588d8..9489113d09 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>