diff --git a/.hgpatchinfo/.RLVa.dep b/.hgpatchinfo/.RLVa.dep index db9f1b03b0..5898e71ba3 100644 --- a/.hgpatchinfo/.RLVa.dep +++ b/.hgpatchinfo/.RLVa.dep @@ -1,2 +1,2 @@ -d2202f2501db50084ca884bcb97a03c7b1c2ec4e -f501c2356b74bc8c5a48bdf42621a2d4f5bea808 \ No newline at end of file +a52aa93d9772c677a4594b22411e99d3ca7cb7c3 +c0b22ce8586c399ea4201ef08036782e06a78d13 \ No newline at end of file diff --git a/.hgpatchinfo/Appearance-Misc.dep b/.hgpatchinfo/Appearance-Misc.dep index a8722be32b..0056f0bc00 100644 --- a/.hgpatchinfo/Appearance-Misc.dep +++ b/.hgpatchinfo/Appearance-Misc.dep @@ -1 +1 @@ -336854c58a54210c0b5046e4dd8af49acc1a9159 \ No newline at end of file +23a6fee23f1d6a2432201906d41e80f3471e8700 \ No newline at end of file diff --git a/.hgpatchinfo/Appearance-MixedViewers.dep b/.hgpatchinfo/Appearance-MixedViewers.dep index a8722be32b..0056f0bc00 100644 --- a/.hgpatchinfo/Appearance-MixedViewers.dep +++ b/.hgpatchinfo/Appearance-MixedViewers.dep @@ -1 +1 @@ -336854c58a54210c0b5046e4dd8af49acc1a9159 \ No newline at end of file +23a6fee23f1d6a2432201906d41e80f3471e8700 \ No newline at end of file diff --git a/.hgpatchinfo/RLVa.dep b/.hgpatchinfo/RLVa.dep index 060eb10921..eecca8f76a 100644 --- a/.hgpatchinfo/RLVa.dep +++ b/.hgpatchinfo/RLVa.dep @@ -1 +1 @@ -9ea58fa9198ff25fa7ff9446a1bbe52b01076f16 \ No newline at end of file +ff000086493c960ef83586482bbcd3fd78cda094 \ No newline at end of file diff --git a/.hgpatchinfo/Viewer-Build.dep b/.hgpatchinfo/Viewer-Build.dep index 830028baa8..c69b7ad4e9 100644 --- a/.hgpatchinfo/Viewer-Build.dep +++ b/.hgpatchinfo/Viewer-Build.dep @@ -1 +1 @@ -230637f9ff30c055fed20e05cbcfb2420cdecef0 \ No newline at end of file +13149a524874b608aeb76325b35faff113a5ea53 \ No newline at end of file diff --git a/.hgtags b/.hgtags index 156166d868..02fa2bd659 100644 --- a/.hgtags +++ b/.hgtags @@ -453,475 +453,3 @@ b23419a2748483c98f3b84b630468a21c88feba5 DRTVWR-292 0a5d409161ef2a89b28c9a741051dd2dedc707d6 DRTVWR-297 852b69ef0b5fe6b13b69cc2217282cc64de6afab 3.4.5-beta5 a49c715243a36a8a380504d14cb7416b3039c956 3.4.5-release -0000000000000000000000000000000000000000 alpha-3 -0000000000000000000000000000000000000000 fork to viewer-2-0 -0000000000000000000000000000000000000000 alpha-4 -0000000000000000000000000000000000000000 fork to viewer-20qa -0000000000000000000000000000000000000000 alpha-5 -0000000000000000000000000000000000000000 howard-demo -0000000000000000000000000000000000000000 alpha-6 -0000000000000000000000000000000000000000 beta-3-5 -0000000000000000000000000000000000000000 beta-4 -0000000000000000000000000000000000000000 beta-5 -0000000000000000000000000000000000000000 viewer-2-0-0 -0000000000000000000000000000000000000000 viewer-2-0-1 -0000000000000000000000000000000000000000 2-1rn1 -0000000000000000000000000000000000000000 viewer-2-0-2-start -0000000000000000000000000000000000000000 viewer-2-1-0-start -0000000000000000000000000000000000000000 2-1-beta-1 -0000000000000000000000000000000000000000 2-1-beta-2 -0000000000000000000000000000000000000000 2-1-beta-3 -0000000000000000000000000000000000000000 2-1-beta-4 -0000000000000000000000000000000000000000 2-1-release -0000000000000000000000000000000000000000 2-1-1-beta-1 -0000000000000000000000000000000000000000 2-1-1-beta-2 -0000000000000000000000000000000000000000 2-1-1-release -0000000000000000000000000000000000000000 2.1.1-release -0000000000000000000000000000000000000000 last_sprint -0000000000000000000000000000000000000000 2.2.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-5_2.2.0-beta1 -0000000000000000000000000000000000000000 beta_2.1.3 -0000000000000000000000000000000000000000 2.2.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-3_2.2.0-beta2 -0000000000000000000000000000000000000000 2.2.0-beta3 -0000000000000000000000000000000000000000 DRTVWR-7_2.2.0-beta3 -0000000000000000000000000000000000000000 2.2.0-beta4 -0000000000000000000000000000000000000000 2.2.0-release -0000000000000000000000000000000000000000 DRTVWR-8_2.2.0-release -0000000000000000000000000000000000000000 2.3.0-start -0000000000000000000000000000000000000000 2.3.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-14_2.3.0-beta1 -0000000000000000000000000000000000000000 2.3.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-17_2.3.0-beta2 -0000000000000000000000000000000000000000 2.3.0-beta3 -0000000000000000000000000000000000000000 2.3.0-release -0000000000000000000000000000000000000000 DRTVWR-13_2.3.0-release -0000000000000000000000000000000000000000 DRTVWR-20_2.3.0-beta3 -0000000000000000000000000000000000000000 2.4.0-start -0000000000000000000000000000000000000000 2.4.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-26_2.4.0-beta1 -0000000000000000000000000000000000000000 2.4.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-27_2.4.0-beta2 -0000000000000000000000000000000000000000 2.4.0-release -0000000000000000000000000000000000000000 DRTVWR-25_2.4.0-release -0000000000000000000000000000000000000000 2.5.0-start -0000000000000000000000000000000000000000 2.5.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-32_2.5.0-beta1 -0000000000000000000000000000000000000000 2.5.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-33--2.5.0beta2 -0000000000000000000000000000000000000000 DRTVWR-33_2.5.0-beta2 -0000000000000000000000000000000000000000 2.5.0-beta3 -0000000000000000000000000000000000000000 2.5.0-release -0000000000000000000000000000000000000000 DRTVWR-31_2.5.0-release -0000000000000000000000000000000000000000 DRTVWR-34_2.5.0-beta3 -0000000000000000000000000000000000000000 nat-eventapi2-base -0000000000000000000000000000000000000000 2.5.1-start -0000000000000000000000000000000000000000 2.5.1-release -0000000000000000000000000000000000000000 DRTVWR-37_2.5.1-release -0000000000000000000000000000000000000000 2.5.2-release -0000000000000000000000000000000000000000 DRTVWR-38_2.5.2-release -0000000000000000000000000000000000000000 2.6.0-start -0000000000000000000000000000000000000000 2.6.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-40_2.6.0-beta1 -0000000000000000000000000000000000000000 2.6.0-beta1-tip -0000000000000000000000000000000000000000 2.6.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-41_2.6.0-beta2 -0000000000000000000000000000000000000000 2.6.1-beta1 -0000000000000000000000000000000000000000 2.6.1-start -0000000000000000000000000000000000000000 DRTVWR-43_2.6.1-beta1 -0000000000000000000000000000000000000000 2.6.2-start -0000000000000000000000000000000000000000 2.6.2-beta1 -0000000000000000000000000000000000000000 DRTVWR-45_2.6.2-beta1 -0000000000000000000000000000000000000000 2.6.0-release -0000000000000000000000000000000000000000 DRTVWR-39_2.6.0-release -0000000000000000000000000000000000000000 2.6.1-release -0000000000000000000000000000000000000000 DRTVWR-42_2.6.1-release -0000000000000000000000000000000000000000 end-of-develop.py -0000000000000000000000000000000000000000 2.6.3-start -0000000000000000000000000000000000000000 2.6.3-beta1 -0000000000000000000000000000000000000000 DRTVWR-47_2.6.3-beta1 -0000000000000000000000000000000000000000 2.6.3-beta2 -0000000000000000000000000000000000000000 DRTVWR-48_2.6.3-beta2 -0000000000000000000000000000000000000000 dons-headless-hackathon-work -0000000000000000000000000000000000000000 2.6.2-release -0000000000000000000000000000000000000000 DRTVWR-44_2.6.2-release -0000000000000000000000000000000000000000 2.6.5-beta1 -0000000000000000000000000000000000000000 DRTVWR-50_2.6.5-beta1 -0000000000000000000000000000000000000000 2.6.3-release -0000000000000000000000000000000000000000 DRTVWR-46_2.6.3-release -0000000000000000000000000000000000000000 2.6.6-start -0000000000000000000000000000000000000000 2.6.6-beta1 -0000000000000000000000000000000000000000 DRTVWR-52_2.6.6-beta1 -0000000000000000000000000000000000000000 2.6.8-start -0000000000000000000000000000000000000000 2.6.6-release -0000000000000000000000000000000000000000 DRTVWR-51_2.6.6-release -0000000000000000000000000000000000000000 2.6.8-beta1 -0000000000000000000000000000000000000000 DRTVWR-55_2.6.8-beta1 -0000000000000000000000000000000000000000 2.6.8-release -0000000000000000000000000000000000000000 DRTVWR-54_2.6.8-release -0000000000000000000000000000000000000000 2.6.9-start -0000000000000000000000000000000000000000 2.6.9-beta1 -0000000000000000000000000000000000000000 2.6.9-release -0000000000000000000000000000000000000000 DRTVWR-56_2.6.9-release -0000000000000000000000000000000000000000 DRTVWR-57_2.6.9-beta1 -0000000000000000000000000000000000000000 2.7.0-start -0000000000000000000000000000000000000000 2.7.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-59_2.7.0-beta1 -0000000000000000000000000000000000000000 2.7.1-start -0000000000000000000000000000000000000000 2.7.1-beta1 -0000000000000000000000000000000000000000 DRTVWR-61_2.7.1-beta1 -0000000000000000000000000000000000000000 2.7.2-start -0000000000000000000000000000000000000000 2.7.2-beta1 -0000000000000000000000000000000000000000 DRTVWR-63_2.7.2-beta1 -0000000000000000000000000000000000000000 2.7.3-start -0000000000000000000000000000000000000000 2.7.1-release -0000000000000000000000000000000000000000 2.7.2-release -0000000000000000000000000000000000000000 DRTVWR-60_2.7.1-release -0000000000000000000000000000000000000000 DRTVWR-62_2.7.2-release -0000000000000000000000000000000000000000 2.7.4-start -0000000000000000000000000000000000000000 2.7.4-beta1 -0000000000000000000000000000000000000000 DRTVWR-67_2.7.4-beta1 -0000000000000000000000000000000000000000 2.7.5-start -0000000000000000000000000000000000000000 2.7.5-beta1 -0000000000000000000000000000000000000000 DRTVWR-69_2.7.5-beta1 -0000000000000000000000000000000000000000 2.8.0-beta1 -0000000000000000000000000000000000000000 2.8.0-start -0000000000000000000000000000000000000000 DRTVWR-71_2.8.0-beta1 -0000000000000000000000000000000000000000 2.7.4-release -0000000000000000000000000000000000000000 DRTVWR-66_2.7.4-release -0000000000000000000000000000000000000000 2.7.5-release -0000000000000000000000000000000000000000 DRTVWR-68_2.7.5-release -0000000000000000000000000000000000000000 2.8.1-start -0000000000000000000000000000000000000000 2.8.1-beta1 -0000000000000000000000000000000000000000 DRTVWR-73_2.8.1-beta1 -0000000000000000000000000000000000000000 2.8.0-release -0000000000000000000000000000000000000000 DRTVWR-70_2.8.0-release -0000000000000000000000000000000000000000 2.8.2-start -0000000000000000000000000000000000000000 2.8.3-start -0000000000000000000000000000000000000000 2.8.1-release -0000000000000000000000000000000000000000 DRTVWR-72_2.8.1-release -0000000000000000000000000000000000000000 2.8.1-hotfix -0000000000000000000000000000000000000000 DRTVWR-76_2.8.1-hotfix -0000000000000000000000000000000000000000 2.8.3-beta1 -0000000000000000000000000000000000000000 DRTVWR-75_2.8.3-beta1 -0000000000000000000000000000000000000000 3.0.0-start -0000000000000000000000000000000000000000 3.0.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-78_3.0.0-beta1 -0000000000000000000000000000000000000000 2.8.3-release -0000000000000000000000000000000000000000 DRTVWR-74_2.8.3-release -0000000000000000000000000000000000000000 3.0.1-start -0000000000000000000000000000000000000000 3.0.1-beta1 -0000000000000000000000000000000000000000 DRTVWR-81_3.0.1-beta1 -0000000000000000000000000000000000000000 3.0.2-start -0000000000000000000000000000000000000000 3.0.2-beta1 -0000000000000000000000000000000000000000 DRTVWR-83_3.0.2-beta1 -0000000000000000000000000000000000000000 3.0.0-release -0000000000000000000000000000000000000000 DRTVWR-77_3.0.0-release -0000000000000000000000000000000000000000 3.0.2-beta2 -0000000000000000000000000000000000000000 DRTVWR-86_3.0.2-beta2 -0000000000000000000000000000000000000000 3.0.3-start -0000000000000000000000000000000000000000 3.0.3-beta1 -0000000000000000000000000000000000000000 DRTVWR-85_3.0.3-beta1 -0000000000000000000000000000000000000000 3.0.3-beta2 -0000000000000000000000000000000000000000 DRTVWR-89_3.0.3-beta2 -0000000000000000000000000000000000000000 3.0.4-start -0000000000000000000000000000000000000000 3.0.3-release -0000000000000000000000000000000000000000 DRTVWR-84_3.0.3-release -0000000000000000000000000000000000000000 3.0.5-start -0000000000000000000000000000000000000000 3.1.0-start -0000000000000000000000000000000000000000 3.1.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-93_3.1.0-beta1 -0000000000000000000000000000000000000000 3.1.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-96_3.1.0-beta2 -0000000000000000000000000000000000000000 3.1.0-release -0000000000000000000000000000000000000000 DRTVWR-92_3.1.0-release -0000000000000000000000000000000000000000 3.2.0-start -0000000000000000000000000000000000000000 3.2.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-95_3.2.0-beta1 -0000000000000000000000000000000000000000 3.2.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-97_3.2.0-beta2 -0000000000000000000000000000000000000000 3.2.0-beta3 -0000000000000000000000000000000000000000 DRTVWR-98_3.2.0-beta3 -0000000000000000000000000000000000000000 3.2.0-release -0000000000000000000000000000000000000000 DRTVWR-94_3.2.0-release -0000000000000000000000000000000000000000 3.2.1-start -0000000000000000000000000000000000000000 3.2.1-beta1 -0000000000000000000000000000000000000000 DRTVWR-100_3.2.1-beta1 -0000000000000000000000000000000000000000 3.2.2-start -0000000000000000000000000000000000000000 3.2.2-beta1 -0000000000000000000000000000000000000000 DRTVWR-102_3.2.2-beta1 -0000000000000000000000000000000000000000 3.2.1-release -0000000000000000000000000000000000000000 DRTVWR-99_3.2.1-release -0000000000000000000000000000000000000000 3.2.3-start -0000000000000000000000000000000000000000 3.2.4-beta1 -0000000000000000000000000000000000000000 3.2.4-start -0000000000000000000000000000000000000000 DRTVWR-104_3.2.4-beta1 -0000000000000000000000000000000000000000 3.2.4-release -0000000000000000000000000000000000000000 DRTVWR-103_3.2.4-release -0000000000000000000000000000000000000000 3.2.5-start -0000000000000000000000000000000000000000 3.2.5-beta1 -0000000000000000000000000000000000000000 DRTVWR-106_3.2.5-beta1 -0000000000000000000000000000000000000000 3.2.5-beta2 -0000000000000000000000000000000000000000 DRTVWR-107_3.2.5-beta2 -0000000000000000000000000000000000000000 3.2.5-release -0000000000000000000000000000000000000000 DRTVWR-105_3.2.5-release -0000000000000000000000000000000000000000 3.2.6-start -0000000000000000000000000000000000000000 3.2.6-beta1 -0000000000000000000000000000000000000000 DRTVWR-109_3.2.6-beta1 -0000000000000000000000000000000000000000 3.2.7-start -0000000000000000000000000000000000000000 3.2.7-beta1 -0000000000000000000000000000000000000000 DRTVWR-111_3.2.7-beta1 -0000000000000000000000000000000000000000 3.2.8-start -0000000000000000000000000000000000000000 3.2.8-beta1 -0000000000000000000000000000000000000000 DRTVWR-114_3.2.8-beta1 -0000000000000000000000000000000000000000 3.2.8-beta2 -0000000000000000000000000000000000000000 DRTVWR-115_3.2.8-beta2 -0000000000000000000000000000000000000000 3.2.8-release -0000000000000000000000000000000000000000 DRTVWR-113_3.2.8-release -0000000000000000000000000000000000000000 3.2.9-start -0000000000000000000000000000000000000000 3.2.9-beta1 -0000000000000000000000000000000000000000 DRTVWR-117_3.2.9-beta1 -0000000000000000000000000000000000000000 3.2.9-beta2 -0000000000000000000000000000000000000000 DRTVWR-118_3.2.9-beta2 -0000000000000000000000000000000000000000 3.3.0-beta1 -0000000000000000000000000000000000000000 3.3.0-start -0000000000000000000000000000000000000000 DRTVWR-119 -0000000000000000000000000000000000000000 3.3.0-release -0000000000000000000000000000000000000000 3.3.1-start -0000000000000000000000000000000000000000 DRTVWR-125 -0000000000000000000000000000000000000000 3.3.1-beta1 -0000000000000000000000000000000000000000 3.3.1-beta2 -0000000000000000000000000000000000000000 DRTVWR-139 -0000000000000000000000000000000000000000 viewer-beta-candidate -0000000000000000000000000000000000000000 3.3.1-release -0000000000000000000000000000000000000000 3.3.2-beta1 -0000000000000000000000000000000000000000 3.3.2-beta2 -0000000000000000000000000000000000000000 3.3.2-release -0000000000000000000000000000000000000000 viewer-release-candidate -0000000000000000000000000000000000000000 DRTVWR-156 -0000000000000000000000000000000000000000 DRTVWR-148 -0000000000000000000000000000000000000000 DRTVWR-155 -0000000000000000000000000000000000000000 DRTVWR-153 -0000000000000000000000000000000000000000 DRTVWR-160 -0000000000000000000000000000000000000000 3.3.3-beta1 -0000000000000000000000000000000000000000 DRTVWR-144 -0000000000000000000000000000000000000000 DRTVWR-164 -0000000000000000000000000000000000000000 DRTVWR-162 -0000000000000000000000000000000000000000 DRTVWR-165 -0000000000000000000000000000000000000000 3.3.3-beta2 -0000000000000000000000000000000000000000 DRTVWR-169 -0000000000000000000000000000000000000000 3.3.3-beta3 -0000000000000000000000000000000000000000 3.3.3-release -0000000000000000000000000000000000000000 DRTVWR-159 -0000000000000000000000000000000000000000 DRTVWR-157 -0000000000000000000000000000000000000000 DRTVWR-161 -0000000000000000000000000000000000000000 DRTVWR-163 -0000000000000000000000000000000000000000 DRTVWR-171 -0000000000000000000000000000000000000000 DRTVWR-168 -0000000000000000000000000000000000000000 DRTVWR-170 -0000000000000000000000000000000000000000 DRTVWR-158 -0000000000000000000000000000000000000000 3.3.4-beta1 -0000000000000000000000000000000000000000 DRTVWR-173 -0000000000000000000000000000000000000000 DRTVWR-174 -0000000000000000000000000000000000000000 3.3.4-beta2 -0000000000000000000000000000000000000000 DRTVWR-176 -0000000000000000000000000000000000000000 DRTVWR-177 -0000000000000000000000000000000000000000 3.3.4-beta3 -0000000000000000000000000000000000000000 DRTVWR-180 -0000000000000000000000000000000000000000 DRTVWR-183 -0000000000000000000000000000000000000000 3.3.4-beta4 -0000000000000000000000000000000000000000 DRTVWR-184 -0000000000000000000000000000000000000000 3.3.4-beta5 -0000000000000000000000000000000000000000 3.3.4-release -0000000000000000000000000000000000000000 DRTVWR-167 -0000000000000000000000000000000000000000 DRTVWR-175 -0000000000000000000000000000000000000000 DRTVWR-182 -0000000000000000000000000000000000000000 3.4.0-beta1 -0000000000000000000000000000000000000000 DRTVWR-189 -0000000000000000000000000000000000000000 DRTVWR-190 -0000000000000000000000000000000000000000 DRTVWR-192 -0000000000000000000000000000000000000000 DRTVWR-195 -0000000000000000000000000000000000000000 DRTVWR-193 -0000000000000000000000000000000000000000 DRTVWR-196 -0000000000000000000000000000000000000000 DRTVWR-198 -0000000000000000000000000000000000000000 DRTVWR-197 -0000000000000000000000000000000000000000 3.4.0-beta2 -0000000000000000000000000000000000000000 DRTVWR-200 -0000000000000000000000000000000000000000 3.4.0-beta3 -0000000000000000000000000000000000000000 DRTVWR-199 -0000000000000000000000000000000000000000 3.3.4-release2 -0000000000000000000000000000000000000000 DRTVWR-201 -0000000000000000000000000000000000000000 3.4.0-beta4 -0000000000000000000000000000000000000000 3.4.0-beta5 -0000000000000000000000000000000000000000 DRTVWR-207 -0000000000000000000000000000000000000000 3.3.4-release3 -0000000000000000000000000000000000000000 DRTVWR-212 -0000000000000000000000000000000000000000 DRTVWR-213 -0000000000000000000000000000000000000000 3.3.4-beta6 -0000000000000000000000000000000000000000 DRTVWR-215 -0000000000000000000000000000000000000000 3.4.0-beta7 -0000000000000000000000000000000000000000 3.4.0-release -0000000000000000000000000000000000000000 3.4.1-beta1 -0000000000000000000000000000000000000000 3.4.1-beta1a -0000000000000000000000000000000000000000 DRTVWR-220 -0000000000000000000000000000000000000000 DRTVWR-221 -0000000000000000000000000000000000000000 3.4.1-beta3 -0000000000000000000000000000000000000000 DRTVWR-222 -0000000000000000000000000000000000000000 3.4.1-beta4 -0000000000000000000000000000000000000000 DRTVWR-224 -0000000000000000000000000000000000000000 3.4.1-beta5 -0000000000000000000000000000000000000000 DRTVWR-226 -0000000000000000000000000000000000000000 DRTVWR-179 -0000000000000000000000000000000000000000 3.4.1-beta6 -0000000000000000000000000000000000000000 DRTVWR-227 -0000000000000000000000000000000000000000 3.4.1-beta7 -0000000000000000000000000000000000000000 DRTVWR-228 -0000000000000000000000000000000000000000 3.4.1-beta8 -0000000000000000000000000000000000000000 DRTVWR-231 -0000000000000000000000000000000000000000 DRTVWR-233 -0000000000000000000000000000000000000000 3.4.1-beta9 -0000000000000000000000000000000000000000 3.4.1-beta10 -0000000000000000000000000000000000000000 DRTVWR-235 -0000000000000000000000000000000000000000 DRTVWR-236 -0000000000000000000000000000000000000000 3.4.1-beta11 -0000000000000000000000000000000000000000 DRTVWR-237 -0000000000000000000000000000000000000000 3.4.1-beta12 -0000000000000000000000000000000000000000 3.4.1-release -0000000000000000000000000000000000000000 DRTVWR-186 -0000000000000000000000000000000000000000 DRTVWR-181 -0000000000000000000000000000000000000000 DRTVWR-188 -0000000000000000000000000000000000000000 DRTVWR-191 -0000000000000000000000000000000000000000 DRTVWR-194 -0000000000000000000000000000000000000000 DRTVWR-203 -0000000000000000000000000000000000000000 DRTVWR-202 -0000000000000000000000000000000000000000 oz-build-test-tag -0000000000000000000000000000000000000000 DRTVWR-205 -0000000000000000000000000000000000000000 DRTVWR-206 -0000000000000000000000000000000000000000 DRTVWR-219 -0000000000000000000000000000000000000000 DRTVWR-217 -0000000000000000000000000000000000000000 3.4.2-beta1 -0000000000000000000000000000000000000000 DRTVWR-216 -0000000000000000000000000000000000000000 3.4.2-beta2 -0000000000000000000000000000000000000000 DRTVWR-242 -0000000000000000000000000000000000000000 3.4.2-beta3 -0000000000000000000000000000000000000000 DRTVWR-243 -0000000000000000000000000000000000000000 3.4.2-beta4 -0000000000000000000000000000000000000000 3.4.2-release -0000000000000000000000000000000000000000 DRTVWR-208 -0000000000000000000000000000000000000000 DRTVWR-223 -0000000000000000000000000000000000000000 DRTVWR-240 -0000000000000000000000000000000000000000 DRTVWR-209 -0000000000000000000000000000000000000000 DRTVWR-238 -0000000000000000000000000000000000000000 DRTVWR-210 -0000000000000000000000000000000000000000 DRTVWR-241 -0000000000000000000000000000000000000000 DRTVWR-232 -0000000000000000000000000000000000000000 DRTVWR-245 -0000000000000000000000000000000000000000 3.4.3-beta1 -0000000000000000000000000000000000000000 DRTVWR-252 -0000000000000000000000000000000000000000 DRTVWR-251 -0000000000000000000000000000000000000000 DRTVWR-253 -0000000000000000000000000000000000000000 DRTVWR-254 -0000000000000000000000000000000000000000 DRTVWR-256 -0000000000000000000000000000000000000000 DRTVWR-258 -0000000000000000000000000000000000000000 DRTVWR-257 -0000000000000000000000000000000000000000 DRTVWR-259 -0000000000000000000000000000000000000000 DRTVWR-265 -0000000000000000000000000000000000000000 DRTVWR-266 -0000000000000000000000000000000000000000 DRTVWR-267 -0000000000000000000000000000000000000000 3.4.3-beta3 -0000000000000000000000000000000000000000 3.4.3-release -0000000000000000000000000000000000000000 DRTVWR-229 -0000000000000000000000000000000000000000 DRTVWR-246 -0000000000000000000000000000000000000000 DRTVWR-247 -0000000000000000000000000000000000000000 DRTVWR-249 -0000000000000000000000000000000000000000 DRTVWR-230 -0000000000000000000000000000000000000000 DRTVWR-255 -0000000000000000000000000000000000000000 DRTVWR-260 -0000000000000000000000000000000000000000 DRTVWR-244 -0000000000000000000000000000000000000000 3.4.4-beta1 -0000000000000000000000000000000000000000 DRTVWR-270 -0000000000000000000000000000000000000000 3.4.4-beta2 -0000000000000000000000000000000000000000 DRTVWR-271 -0000000000000000000000000000000000000000 3.4.4-beta3 -0000000000000000000000000000000000000000 DRTVWR-273 -0000000000000000000000000000000000000000 DRTVWR-275 -0000000000000000000000000000000000000000 3.4.4-beta4 -0000000000000000000000000000000000000000 3.4.4-release -0000000000000000000000000000000000000000 DRTVWR-268 -0000000000000000000000000000000000000000 DRTVWR-272 -0000000000000000000000000000000000000000 DRTVWR-269 -0000000000000000000000000000000000000000 DRTVWR-250 -0000000000000000000000000000000000000000 DRTVWR-277 -0000000000000000000000000000000000000000 DRTVWR-276 -0000000000000000000000000000000000000000 DRTVWR-279 -0000000000000000000000000000000000000000 DRTVWR-280 -0000000000000000000000000000000000000000 3.4.5-beta1 -0000000000000000000000000000000000000000 DRTVWR-281 -0000000000000000000000000000000000000000 DRTVWR-283 -0000000000000000000000000000000000000000 3.4.5-beta2 -0000000000000000000000000000000000000000 DRTVWR-286 -0000000000000000000000000000000000000000 DRTVWR-287 -0000000000000000000000000000000000000000 3.4.5-beta3 -0000000000000000000000000000000000000000 DRTVWR-289 -0000000000000000000000000000000000000000 DRTVWR-290 -0000000000000000000000000000000000000000 DRTVWR-291 -0000000000000000000000000000000000000000 DRTVWR-292 -0000000000000000000000000000000000000000 3.4.5-beta4 -0000000000000000000000000000000000000000 DRTVWR-296 -0000000000000000000000000000000000000000 DRTVWR-297 -0000000000000000000000000000000000000000 3.4.5-beta5 -4f777ffb99fefdc6497c61385c22688ff149c659 SL-2.0.0 -668851b2ef0f8cf8df07a0fba429e4a6c1e70abb SL-2.0.1 -b03065d018b8a2e28b7de85b293a4c992cb4c12d SL-2.1.0 -bb38ff1a763738609e1b3cada6d15fa61e5e84b9 SL-2.1.1 -1415e6538d54fd5d568ee88343424d57c6803c2c SL-2.2.0 -6ad3d6fa35a4e320e9ce442fce2bf9c7fc852556 SL-2.3.0 -1ed382c6a08ba3850b6ce9061bc551ddece0ea07 SL-2.4.0 -b723921b5c711bd24dbe77dc76ef488b544dac78 SL-2.5.0 -4dede9ae1ec74d41f6887719f6f1de7340d8578d SL-2.5.1 -b53a0576eec80614d7767ed72b40ed67aeff27c9 SL-2.5.2 -42f32494bac475d0737799346f6831558ae8bf5d SL-2.6.0 -c9182ed77d427c759cfacf49a7b71a2e20d522aa SL-2.6.1 -214180ad5714ce8392b82bbebcc92f4babd98300 SL-2.6.2 -8f2da1701c81a62352df2b8d413d27fb2cade9a6 SL-2.6.3 -dac76a711da5f1489a01c1fa62ec97d99c25736d SL-2.6.6 -be2000b946f8cb3de5f44b2d419287d4c48ec4eb SL-2.6.8 -77e5a08344c95738ab879f9671b7758cddd712a3 SL-2.6.9 -a9abb9633a266c8d2fe62411cfd1c86d32da72bf SL-2.7.1 -fe3a8e7973072ea62043c08b19b66626c1a720eb SL-2.7.2 -057f319dd8eccdf63a54d99686c68cdcb31b6abc SL-2.7.4 -6866d9df6efbd441c66451debd376d21211de39c SL-2.7.5 -493d9127ee50e84ba08a736a65a23ca86f7a5b01 SL-2.8.0 -29e93d7e19991011bd12b5748142b11a5dcb4370 SL-2.8.1 -fb85792b84bf28428889c4cc966469d92e5dac4c SL-2.8.3 -1778f26b6d0ae762dec3ca37140f66620f2485d9 SL-3.0.0 -0496d2f74043cf4e6058e76ac3db03d44cff42ce SL-3.0.3 -ae2de7b0b33c03dc5bdf3a7bfa54463b512221b2 SL-3.1.0 -3150219d229d628f0c15e58e8a51511cbd97e58d SL-3.2.0 -a8c7030d6845186fac7c188be4323a0e887b4184 SL-3.2.1 -bd6bcde2584491fd9228f1fa51c4575f4e764e19 SL-3.2.4 -c6175c955a19e9b9353d242889ec1779b5762522 SL-3.2.5 -51b2fd52e36aab8f670e0874e7e1472434ec4b4a SL-3.2.8 -5e8d2662f38a66eca6c591295f5880d47afc73f7 SL-3.3.0 -c623bbc854b6f7ee1b33a3718f76715046aa2937 SL-3.3.1 -675668bd24d3bea570814f71762a2a806f7e1b8d SL-3.3.2 -6428242e124b523813bfaf4c45b3d422f0298c81 SL-3.3.3 -28e100d0379a2b0710c57647a28fc5239d3d7b99 SL-3.3.4 -97977c67245f52db20eb15f1918cc0f24778cabc SL-3.4.0 -e59ffd3fe0838ae6b09b242a6e9df71761b88f41 SL-3.4.1 -16310aabccf315870f7cc9bf966926c0ad6954fa SL-3.4.2 -0a2ca6546b499239afeb66d17b2fadbcdbe36ab1 SL-3.4.3 -e1bb1ae7d8b12faeb37933a737c199cc9b9f89cc SL-3.4.4 -425f96b1e81e01644bf5e951961e7d1023bffb89 RLVa-1.2.0 -fc0cbb86f5bd6e7737159e35aea2c4cf9f619b62 RLVa-1.2.1 -43cb7dc1804de1a25c0b2b3f0715584af1f8b470 RLVa-1.2.2 -89532c8dfd5b6c29f1cb032665b44a74a52452e1 RLVa-1.3.0 -7bc5039ccf0b36eafbf6ce33a52b5e26332aa04c RLVa-1.3.1 -a563f7e215c7883c1cfd20908085687a0ed96284 RLVa-1.4.0 -40644beae9c4a617504163d5c9f195dc7bfff1b4 RLVa-1.4.1 -8787094c309a44ca32b7472acc9217a3c37f00c3 RLVa-1.4.2 -11c6c85ddd223bcbd6b3afc53f9a0f5fd349ba65 RLVa-1.4.3 -53b44208d44a601fe24e78c1a1909dc82356cded RLVa-1.4.4 -6522747820037cc11e5b7d0491a0a9808a958709 RLVa-1.4.5 -0c8f23b2c4cf96d1a08bd527b3ccb6339a9fdb60 RLVa-1.4.6 -674db463770b78f836fc9c87a1b2235e212a576c RLVa-1.4.7 diff --git a/BuildParams b/BuildParams index b020a52497..c929c2d90d 100644 --- a/BuildParams +++ b/BuildParams @@ -132,6 +132,18 @@ viewer-pathfinding.build_debug_release_separately = true viewer-pathfinding.build_CYGWIN_Debug = false viewer-pathfinding.build_viewer_update_version_manager = false +# ======================================== +# viewer-chui +# +# ======================================== + +viewer-chui.viewer_channel = "Project Viewer - CHUI" +viewer-chui.login_channel = "Project Viewer - CHUI" +viewer-chui.viewer_grid = agni +viewer-chui.build_debug_release_separately = true +viewer-chui.build_CYGWIN_Debug = false +viewer-chui.build_viewer_update_version_manager = false + # ================================================================= # asset delivery 2010 projects # ================================================================= diff --git a/autobuild.xml b/autobuild.xml index 84affb7ad3..312adcaafa 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -1334,9 +1334,9 @@ archive hash - 088a1c30e499289e94bc1738fd3d6a25 + 5bc44db15eb3cca021382e40e04a9a38 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-source/rev/270564/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130219.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearanceutility-source/rev/271972/arch/Linux/installer/llappearanceutility_source-0.1-linux-20130315.tar.bz2 name linux diff --git a/debian/rules b/debian/rules old mode 100755 new mode 100644 diff --git a/doc/contributions.txt b/doc/contributions.txt index f4629cb2e8..e983db8b59 100644 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -299,6 +299,7 @@ ChickyBabes Zuzu Christopher Organiser Ciaran Laval Cinder Roxley + BUG-2326 STORM-1703 Clara Young Coaldust Numbers @@ -745,10 +746,12 @@ Marc Claridge Marc2 Sands Marianne McCann Marine Kelley + CHUIBUG-134 STORM-281 MartinRJ Fayray STORM-1844 STORM-1845 + STORM-1934 Matthew Anthony Matthew Dowd VWR-1344 @@ -897,6 +900,7 @@ NickyD Nicky Dasmijn VWR-29228 MAINT-873 + SUN-72 Nicky Perian OPEN-1 STORM-1087 diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp old mode 100755 new mode 100644 index a83fa5df8b..184560c80f --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -467,7 +467,7 @@ void LLAvatarAppearance::computeBodySize() // [RLVa:KB] - Checked: 2013-03-03 (RLVa-1.4.8) mAvatarOffset.mV[VZ] = getAvatarOffset(); // [/RLVa:KB] -// mAvatarOffset.mV[VZ] = getVisualParamWeight(11001); +// mAvatarOffset.mV[VZ] = getVisualParamWeight(AVATAR_HOVER); mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] - knee.mV[VZ] * hip_scale.mV[VZ] - @@ -491,6 +491,25 @@ void LLAvatarAppearance::computeBodySize() mAvatarOffset.mV[VX] = 0.0f; mAvatarOffset.mV[VY] = 0.0f; + // Certain configurations of avatars can force the overall height (with offset) to go negative. + // Enforce a constraint to make sure we don't go below 0.1 meters. + // Camera positioning and other things start to break down when your avatar is "walking" while being fully underground + if (new_body_size.mV[VZ] + mAvatarOffset.mV[VZ] < 0.1f) + { + mAvatarOffset.mV[VZ] = -(new_body_size.mV[VZ] - 0.11f); // avoid floating point rounding making the above check continue to fail. + + llassert(new_body_size.mV[VZ] + mAvatarOffset.mV[VZ] >= 0.1f); + + if (mWearableData && isSelf()) + { + LLWearable* shape = mWearableData->getWearable(LLWearableType::WT_SHAPE, 0); + if (shape) + { + shape->setVisualParamWeight(AVATAR_HOVER, mAvatarOffset.mV[VZ], false); + } + } + } + if (new_body_size != mBodySize || old_offset != mAvatarOffset.mV[VZ]) { mBodySize = new_body_size; diff --git a/indra/llappearance/llavatarappearance.h b/indra/llappearance/llavatarappearance.h old mode 100755 new mode 100644 diff --git a/indra/llappearance/llavatarappearancedefines.h b/indra/llappearance/llavatarappearancedefines.h index 496f85c107..8a1d2c4707 100644 --- a/indra/llappearance/llavatarappearancedefines.h +++ b/indra/llappearance/llavatarappearancedefines.h @@ -41,6 +41,8 @@ extern const S32 SCRATCH_TEX_WIDTH; extern const S32 SCRATCH_TEX_HEIGHT; extern const S32 IMPOSTOR_PERIOD; +static const U32 AVATAR_HOVER = 11001; + //-------------------------------------------------------------------- // Enums //-------------------------------------------------------------------- diff --git a/indra/llappearance/llavatarjointmesh.cpp b/indra/llappearance/llavatarjointmesh.cpp old mode 100755 new mode 100644 diff --git a/indra/llappearance/llavatarjointmesh.h b/indra/llappearance/llavatarjointmesh.h old mode 100755 new mode 100644 diff --git a/indra/llappearance/lltexlayer.cpp b/indra/llappearance/lltexlayer.cpp index 56ad35b0cc..f027e066ca 100644 --- a/indra/llappearance/lltexlayer.cpp +++ b/indra/llappearance/lltexlayer.cpp @@ -38,7 +38,7 @@ #include "llvfs.h" #include "lltexlayerparams.h" #include "lltexturemanagerbridge.h" -#include "llrender2dutils.h" +#include "../llui/llui.h" #include "llwearable.h" #include "llwearabledata.h" #include "llvertexbuffer.h" diff --git a/indra/llappearance/lltexlayerparams.cpp b/indra/llappearance/lltexlayerparams.cpp old mode 100755 new mode 100644 index 64b3b62185..6aae9a8cc1 --- a/indra/llappearance/lltexlayerparams.cpp +++ b/indra/llappearance/lltexlayerparams.cpp @@ -33,7 +33,7 @@ #include "llquantize.h" #include "lltexlayer.h" #include "lltexturemanagerbridge.h" -#include "llrender2dutils.h" +#include "../llui/llui.h" #include "llwearable.h" //----------------------------------------------------------------------------- diff --git a/indra/llappearance/llwearable.cpp b/indra/llappearance/llwearable.cpp index 4028c1dfad..d86a460511 100644 --- a/indra/llappearance/llwearable.cpp +++ b/indra/llappearance/llwearable.cpp @@ -81,13 +81,13 @@ BOOL LLWearable::exportStream( std::ostream& output_stream ) const output_stream << mDescription << "\n"; // permissions - if( !mPermissions.exportStream( output_stream ) ) + if( !mPermissions.exportLegacyStream( output_stream ) ) { return FALSE; } // sale info - if( !mSaleInfo.exportStream( output_stream ) ) + if( !mSaleInfo.exportLegacyStream( output_stream ) ) { return FALSE; } @@ -263,7 +263,7 @@ LLWearable::EImportResult LLWearable::importStream( std::istream& input_stream, llwarns << "Bad Wearable asset: missing valid permissions" << llendl; return LLWearable::FAILURE; } - if( !mPermissions.importStream( input_stream ) ) + if( !mPermissions.importLegacyStream( input_stream ) ) { return LLWearable::FAILURE; } @@ -288,7 +288,7 @@ LLWearable::EImportResult LLWearable::importStream( std::istream& input_stream, // up the vast majority of the tasks. BOOL has_perm_mask = FALSE; U32 perm_mask = 0; - if( !mSaleInfo.importStream(input_stream, has_perm_mask, perm_mask) ) + if( !mSaleInfo.importLegacyStream(input_stream, has_perm_mask, perm_mask) ) { return LLWearable::FAILURE; } diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp old mode 100755 new mode 100644 diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h index 3817b27b3c..cee9592caa 100644 --- a/indra/llappearance/llwearabledata.h +++ b/indra/llappearance/llwearabledata.h @@ -2,7 +2,7 @@ * @file llwearabledata.h * @brief LLWearableData class header file * - * $LicenseInfo:firstyear=20012license=viewerlgpl$ + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index aef5bb3f1e..530549da31 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -848,6 +848,10 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i asp->play(audio_uuid); } +void LLAudioEngine::triggerSound(SoundData& soundData) +{ + triggerSound(soundData.audio_uuid, soundData.owner_id, soundData.gain, soundData.type, soundData.pos_global); +} void LLAudioEngine::setListenerPos(LLVector3 aVec) { diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index b1117a9c47..4422a622be 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -66,6 +66,7 @@ class LLAudioChannel; class LLAudioChannelOpenAL; class LLAudioBuffer; class LLStreamingAudioInterface; +struct SoundData; // @@ -145,8 +146,10 @@ public: void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, const LLVector3d &pos_global = LLVector3d::zero, - const LLUUID source_object = LLUUID::null); + const LLUUID source_object = LLUUID::null); // NaCl End + void triggerSound(SoundData& soundData); + bool preloadSound(const LLUUID &id); void addAudioSource(LLAudioSource *asp); @@ -485,6 +488,27 @@ protected: LLFrameTimer mLastUseTimer; }; +struct SoundData +{ + LLUUID audio_uuid; + LLUUID owner_id; + F32 gain; + S32 type; + LLVector3d pos_global; + + SoundData(const LLUUID &audio_uuid, + const LLUUID& owner_id, + const F32 gain, + const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, + const LLVector3d &pos_global = LLVector3d::zero) + { + this->audio_uuid = audio_uuid; + this->owner_id = owner_id; + this->gain = gain; + this->type = type; + this->pos_global = pos_global; + } +}; extern LLAudioEngine* gAudiop; diff --git a/indra/llcharacter/llanimationstates.cpp b/indra/llcharacter/llanimationstates.cpp index 155226cf17..c16cae1bbc 100644 --- a/indra/llcharacter/llanimationstates.cpp +++ b/indra/llcharacter/llanimationstates.cpp @@ -46,7 +46,7 @@ LLUUID const ANIM_AGENT_BLOW_KISS ("db84829b-462c-ee83-1e27-9bbee66b LLUUID const ANIM_AGENT_BORED ("b906c4ba-703b-1940-32a3-0c7f7d791510"); LLUUID const ANIM_AGENT_BOW ("82e99230-c906-1403-4d9c-3889dd98daba"); LLUUID const ANIM_AGENT_BRUSH ("349a3801-54f9-bf2c-3bd0-1ac89772af01"); -LLUUID const ANIM_AGENT_BUSY ("efcf670c-2d18-8128-973a-034ebc806b67"); +LLUUID const ANIM_AGENT_DO_NOT_DISTURB ("efcf670c-2d18-8128-973a-034ebc806b67"); LLUUID const ANIM_AGENT_CLAP ("9b0c1c4e-8ac7-7969-1494-28c874c4f668"); LLUUID const ANIM_AGENT_COURTBOW ("9ba1c942-08be-e43a-fb29-16ad440efc50"); LLUUID const ANIM_AGENT_CROUCH ("201f3fdf-cb1f-dbec-201f-7333e328ae7c"); @@ -211,7 +211,7 @@ LLAnimationLibrary::LLAnimationLibrary() : mAnimMap[ANIM_AGENT_BORED]= mAnimStringTable.addString("express_bored"); mAnimMap[ANIM_AGENT_BOW]= mAnimStringTable.addString("bow"); mAnimMap[ANIM_AGENT_BRUSH]= mAnimStringTable.addString("brush"); - mAnimMap[ANIM_AGENT_BUSY]= mAnimStringTable.addString("busy"); + mAnimMap[ANIM_AGENT_DO_NOT_DISTURB]= mAnimStringTable.addString("busy"); mAnimMap[ANIM_AGENT_CLAP]= mAnimStringTable.addString("clap"); mAnimMap[ANIM_AGENT_COURTBOW]= mAnimStringTable.addString("courtbow"); mAnimMap[ANIM_AGENT_CROUCH]= mAnimStringTable.addString("crouch"); diff --git a/indra/llcharacter/llanimationstates.h b/indra/llcharacter/llanimationstates.h index aa6579ac8e..84185c3f92 100644 --- a/indra/llcharacter/llanimationstates.h +++ b/indra/llcharacter/llanimationstates.h @@ -56,7 +56,7 @@ extern const LLUUID ANIM_AGENT_BLOW_KISS; extern const LLUUID ANIM_AGENT_BORED; extern const LLUUID ANIM_AGENT_BOW; extern const LLUUID ANIM_AGENT_BRUSH; -extern const LLUUID ANIM_AGENT_BUSY; +extern const LLUUID ANIM_AGENT_DO_NOT_DISTURB; extern const LLUUID ANIM_AGENT_CLAP; extern const LLUUID ANIM_AGENT_COURTBOW; extern const LLUUID ANIM_AGENT_CROUCH; diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp old mode 100755 new mode 100644 diff --git a/indra/llcharacter/llvisualparam.cpp b/indra/llcharacter/llvisualparam.cpp old mode 100755 new mode 100644 diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h old mode 100755 new mode 100644 diff --git a/indra/llcommon/imageids.cpp b/indra/llcommon/imageids.cpp old mode 100755 new mode 100644 diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h old mode 100755 new mode 100644 diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 5e566d6c7c..5ae2df3994 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -95,6 +95,7 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true)); addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false)); addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false)); + addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE)); }; diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index d538accbf7..69b01731e5 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -112,6 +112,9 @@ public: AT_WIDGET = 40, // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...) + AT_PERSON = 45, + // A user uuid which is not an inventory asset type, used in viewer only for adding a person to a chat via drag and drop. + AT_MESH = 49, // Mesh data in our proprietary SLM format diff --git a/indra/llcommon/llavatarname.cpp b/indra/llcommon/llavatarname.cpp index 3206843bf4..642bd82e90 100644 --- a/indra/llcommon/llavatarname.cpp +++ b/indra/llcommon/llavatarname.cpp @@ -30,6 +30,7 @@ #include "llavatarname.h" #include "lldate.h" +#include "llframetimer.h" #include "llsd.h" // Store these in pre-built std::strings to avoid memory allocations in @@ -42,6 +43,14 @@ static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default"); static const std::string DISPLAY_NAME_EXPIRES("display_name_expires"); static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update"); +bool LLAvatarName::sUseDisplayNames = true; + +// Minimum time-to-live (in seconds) for a name entry. +// Avatar name should always guarantee to expire reasonably soon by default +// so if the failure to get a valid expiration time was due to something temporary +// we will eventually request and get the right data. +const F64 MIN_ENTRY_LIFETIME = 60.0; + LLAvatarName::LLAvatarName() : mUsername(), mDisplayName(), @@ -61,6 +70,17 @@ bool LLAvatarName::operator<(const LLAvatarName& rhs) const return mUsername < rhs.mUsername; } +//static +void LLAvatarName::setUseDisplayNames(bool use) +{ + sUseDisplayNames = use; +} +//static +bool LLAvatarName::useDisplayNames() +{ + return sUseDisplayNames; +} + LLSD LLAvatarName::asLLSD() const { LLSD sd; @@ -85,21 +105,75 @@ void LLAvatarName::fromLLSD(const LLSD& sd) mExpires = expires.secondsSinceEpoch(); LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE]; mNextUpdate = next_update.secondsSinceEpoch(); + + // Some avatars don't have explicit display names set. Force a legible display name here. + if (mDisplayName.empty()) + { + mDisplayName = mUsername; + } +} + +// Transform a string (typically provided by the legacy service) into a decent +// avatar name instance. +void LLAvatarName::fromString(const std::string& full_name) +{ + mDisplayName = full_name; + std::string::size_type index = full_name.find(' '); + if (index != std::string::npos) + { + // The name is in 2 parts (first last) + mLegacyFirstName = full_name.substr(0, index); + mLegacyLastName = full_name.substr(index+1); + if (mLegacyLastName != "Resident") + { + mUsername = mLegacyFirstName + "." + mLegacyLastName; + mDisplayName = full_name; + LLStringUtil::toLower(mUsername); + } + else + { + // Very old names do have a dummy "Resident" last name + // that we choose to hide from users. + mUsername = mLegacyFirstName; + mDisplayName = mLegacyFirstName; + } + } + else + { + mLegacyFirstName = full_name; + mLegacyLastName = ""; + mUsername = full_name; + mDisplayName = full_name; + } + mIsDisplayNameDefault = true; + mIsTemporaryName = true; + setExpires(MIN_ENTRY_LIFETIME); +} + +void LLAvatarName::setExpires(F64 expires) +{ + mExpires = LLFrameTimer::getTotalSeconds() + expires; } std::string LLAvatarName::getCompleteName() const { std::string name; - if (mUsername.empty() || mIsDisplayNameDefault) - // If the display name feature is off - // OR this particular display name is defaulted (i.e. based on user name), - // then display only the easier to read instance of the person's name. + if (sUseDisplayNames) { - name = mDisplayName; + if (mUsername.empty() || mIsDisplayNameDefault) + { + // If this particular display name is defaulted (i.e. based on user name), + // then display only the easier to read instance of the person's name. + name = mDisplayName; + } + else + { + name = mDisplayName + " (" + mUsername + ")"; + } } else { - name = mDisplayName + " (" + mUsername + ")"; + name = getUserName(); } return name; } @@ -118,3 +192,48 @@ std::string LLAvatarName::getLegacyName() const name += mLegacyLastName; return name; } + +std::string LLAvatarName::getDisplayName() const +{ + if (sUseDisplayNames) + { + return mDisplayName; + } + else + { + return getUserName(); + } +} + +std::string LLAvatarName::getUserName() const +{ + std::string name; + if (mLegacyLastName.empty() || (mLegacyLastName == "Resident")) + { + if (mLegacyFirstName.empty()) + { + // If we cannot create a user name from the legacy strings, use the display name + name = mDisplayName; + } + else + { + // The last name might be empty if it defaulted to "Resident" + name = mLegacyFirstName; + } + } + else + { + name = mLegacyFirstName + " " + mLegacyLastName; + } + return name; +} + +void LLAvatarName::dump() const +{ + LL_DEBUGS("AvNameCache") << "LLAvatarName: " + << "user '" << mUsername << "' " + << "display '" << mDisplayName << "' " + << "expires in " << mExpires - LLFrameTimer::getTotalSeconds() << " seconds" + << LL_ENDL; +} + diff --git a/indra/llcommon/llavatarname.h b/indra/llcommon/llavatarname.h index ba258d6d52..7542a8dece 100644 --- a/indra/llcommon/llavatarname.h +++ b/indra/llcommon/llavatarname.h @@ -39,10 +39,27 @@ public: bool operator<(const LLAvatarName& rhs) const; + // Conversion to and from LLSD (cache file or server response) LLSD asLLSD() const; - void fromLLSD(const LLSD& sd); + // Used only in legacy mode when the display name capability is not provided server side + // or to otherwise create a temporary valid item. + void fromString(const std::string& full_name); + + // Set the name object to become invalid in "expires" seconds from now + void setExpires(F64 expires); + + // Set and get the display name flag set by the user in preferences. + static void setUseDisplayNames(bool use); + static bool useDisplayNames(); + + // A name object is valid if not temporary and not yet expired (default is expiration not checked) + bool isValidName(F64 max_unrefreshed = 0.0f) const { return !mIsTemporaryName && (mExpires >= max_unrefreshed); } + + // Return true if the name is made up from legacy or temporary data + bool isDisplayNameDefault() const { return mIsDisplayNameDefault; } + // For normal names, returns "James Linden (james.linden)" // When display names are disabled returns just "James Linden" std::string getCompleteName() const; @@ -51,11 +68,38 @@ public: // compatibility with systems like voice and muting // *TODO: Eliminate this in favor of username only std::string getLegacyName() const; + + // "José Sanchez" or "James Linden", UTF-8 encoded Unicode + // Takes the display name preference into account. This is truly the name that should + // be used for all UI where an avatar name has to be used unless we truly want something else (rare) + std::string getDisplayName() const; + + // Returns "James Linden" or "bobsmith123 Resident" + // Used where we explicitely prefer or need a non UTF-8 legacy (ASCII) name + // Also used for backwards compatibility with systems like voice and muting + std::string getUserName() const; + + // Returns "james.linden" or the legacy name for very old names + std::string getAccountName() const { return mUsername; } + // Debug print of the object + void dump() const; + + // Names can change, so need to keep track of when name was + // last checked. + // Unix time-from-epoch seconds for efficiency + F64 mExpires; + + // You can only change your name every N hours, so record + // when the next update is allowed + // Unix time-from-epoch seconds + F64 mNextUpdate; + +private: // "bobsmith123" or "james.linden", US-ASCII only std::string mUsername; - // "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode + // "José Sanchez" or "James Linden", UTF-8 encoded Unicode // Contains data whether or not user has explicitly set // a display name; may duplicate their username. std::string mDisplayName; @@ -81,15 +125,9 @@ public: // shown in UI, but are not serialized. bool mIsTemporaryName; - // Names can change, so need to keep track of when name was - // last checked. - // Unix time-from-epoch seconds for efficiency - F64 mExpires; - - // You can only change your name every N hours, so record - // when the next update is allowed - // Unix time-from-epoch seconds - F64 mNextUpdate; + // Global flag indicating if display name should be used or not + // This will affect the output of the high level "get" methods + static bool sUseDisplayNames; }; #endif diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 22606ad340..eaca45c576 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -864,12 +864,8 @@ llifstream::llifstream(const std::string& _Filename, #if LL_WINDOWS std::istream(&_M_filebuf) { - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - - // if (_M_filebuf.open(_Filename.c_str(), _Mode | ios_base::in) == 0) llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0) - // { _Myios::setstate(ios_base::failbit); } @@ -888,12 +884,8 @@ llifstream::llifstream(const char* _Filename, #if LL_WINDOWS std::istream(&_M_filebuf) { - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - - // if (_M_filebuf.open(_Filename, _Mode | ios_base::in) == 0) llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0) - // { _Myios::setstate(ios_base::failbit); } @@ -939,10 +931,6 @@ bool llifstream::is_open() const void llifstream::open(const char* _Filename, ios_base::openmode _Mode) { // open a C stream with specified mode - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - // if (_M_filebuf.open(_Filename, _Mode | ios_base::in) == 0) - // - #if LL_WINDOWS llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::in) == 0) @@ -997,12 +985,8 @@ llofstream::llofstream(const std::string& _Filename, #if LL_WINDOWS std::ostream(&_M_filebuf) { - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - - // if (_M_filebuf.open(_Filename.c_str(), _Mode | ios_base::out) == 0) llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0) - // { _Myios::setstate(ios_base::failbit); } @@ -1021,12 +1005,8 @@ llofstream::llofstream(const char* _Filename, #if LL_WINDOWS std::ostream(&_M_filebuf) { - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - - // if (_M_filebuf.open(_Filename, _Mode | ios_base::out) == 0) llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0) - // { _Myios::setstate(ios_base::failbit); } @@ -1070,11 +1050,6 @@ bool llofstream::is_open() const void llofstream::open(const char* _Filename, ios_base::openmode _Mode) { // open a C stream with specified mode - - // Need to convert to UTF16 for Win, Windows API does not use UTF8 - // if (_M_filebuf.open(_Filename, _Mode | ios_base::out) == 0) - // - #if LL_WINDOWS llutf16string wideName = utf8str_to_utf16str( _Filename ); if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0) diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 6af5e198d6..401e4d759a 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -194,13 +194,6 @@ public: return mHandle; } -protected: - typedef LLHandle handle_type_t; - LLHandleProvider() - { - // provided here to enforce T deriving from LLHandleProvider - } - template LLHandle getDerivedHandle(typename boost::enable_if< typename boost::is_convertible >::type* dummy = 0) const { @@ -209,6 +202,12 @@ protected: return downcast_handle; } +protected: + typedef LLHandle handle_type_t; + LLHandleProvider() + { + // provided here to enforce T deriving from LLHandleProvider + } private: mutable LLRootHandle mHandle; diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp index db72aa19b9..89c831d296 100644 --- a/indra/llcommon/llinitparam.cpp +++ b/indra/llcommon/llinitparam.cpp @@ -40,7 +40,9 @@ namespace LLInitParam { const U8* my_addr = reinterpret_cast(this); const U8* block_addr = reinterpret_cast(enclosing_block); - mEnclosingBlockOffset = 0x7FFFffff & (U32)(my_addr - block_addr); + U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr); + mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff; + mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16; } // @@ -112,6 +114,35 @@ namespace LLInitParam std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams)); } + void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name) + { + // create a copy of the param descriptor in mAllParams + // so other data structures can store a pointer to it + mAllParams.push_back(in_param); + ParamDescriptorPtr param(mAllParams.back()); + + std::string name(char_name); + if ((size_t)param->mParamHandle > mMaxParamOffset) + { + llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block" << llendl; + } + + if (name.empty()) + { + mUnnamedParams.push_back(param); + } + else + { + // don't use insert, since we want to overwrite existing entries + mNamedParams[name] = param; + } + + if (param->mValidationFunc) + { + mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); + } + } + BlockDescriptor::BlockDescriptor() : mMaxParamOffset(0), mInitializationState(UNINITIALIZED), @@ -150,7 +181,8 @@ namespace LLInitParam bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent) { - if (!deserializeBlock(p, std::make_pair(name_stack.begin(), name_stack.end()), true)) + Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end()); + if (!deserializeBlock(p, range, true)) { if (!silent) { @@ -196,12 +228,7 @@ namespace LLInitParam if (serialize_func) { const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; - // each param descriptor remembers its serial number - // so we can inspect the same param under different names - // and see that it has the same number - name_stack.push_back(std::make_pair("", true)); serialize_func(*param, parser, name_stack, diff_param); - name_stack.pop_back(); } } @@ -295,7 +322,7 @@ namespace LLInitParam return true; } - bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool ignored) + bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored) { BlockDescriptor& block_data = mostDerivedBlockDescriptor(); bool names_left = name_stack_range.first != name_stack_range.second; @@ -308,15 +335,12 @@ namespace LLInitParam { const std::string& top_name = name_stack_range.first->first; - ParamDescriptor::deserialize_func_t deserialize_func = NULL; - Param* paramp = NULL; - BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name); if (found_it != block_data.mNamedParams.end()) { // find pointer to member parameter from offset table - paramp = getParamFromHandle(found_it->second->mParamHandle); - deserialize_func = found_it->second->mDeserializeFunc; + Param* paramp = getParamFromHandle(found_it->second->mParamHandle); + ParamDescriptor::deserialize_func_t deserialize_func = found_it->second->mDeserializeFunc; Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second); ++new_name_stack.first; @@ -358,36 +382,6 @@ namespace LLInitParam return false; } - //static - void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptorPtr in_param, const char* char_name) - { - // create a copy of the param descriptor in mAllParams - // so other data structures can store a pointer to it - block_data.mAllParams.push_back(in_param); - ParamDescriptorPtr param(block_data.mAllParams.back()); - - std::string name(char_name); - if ((size_t)param->mParamHandle > block_data.mMaxParamOffset) - { - llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block" << llendl; - } - - if (name.empty()) - { - block_data.mUnnamedParams.push_back(param); - } - else - { - // don't use insert, since we want to overwrite existing entries - block_data.mNamedParams[name] = param; - } - - if (param->mValidationFunc) - { - block_data.mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); - } - } - void BaseBlock::addSynonym(Param& param, const std::string& synonym) { BlockDescriptor& block_data = mostDerivedBlockDescriptor(); @@ -460,7 +454,7 @@ namespace LLInitParam if (merge_func) { Param* paramp = getParamFromHandle((*it)->mParamHandle); - llassert(paramp->mEnclosingBlockOffset == (*it)->mParamHandle); + llassert(paramp->getEnclosingBlockOffset() == (*it)->mParamHandle); some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); } } diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index f1f8c3f72a..6cea21423d 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -38,6 +38,71 @@ #include "llerror.h" #include "llstl.h" +namespace LLTypeTags +{ + template + struct TypeTagBase + { + typedef void is_tag_t; + typedef INNER_TYPE inner_t; + static const int SORT_ORDER=_SORT_ORDER; + }; + + template + struct GreaterThan + { + static const bool value = VAL1 > VAL2; + }; + + template::value > + struct Swap + { + typedef typename ITEM::template Cons::value_t value_t; + }; + + template + struct Swap + { + typedef typename REST::template Cons::value_t>::value_t value_t; + }; + + template + struct IsSortable + { + static const bool value = false; + }; + + template + struct IsSortable + { + static const bool value = true; + }; + + template::value> + struct InsertInto + { + typedef typename ITEM::template Cons::value_t value_t; + }; + + template + struct InsertInto + { + typedef typename Swap::value_t value_t; + }; + + template::value> + struct Sorted + { + typedef T value_t; + }; + + template + struct Sorted + { + typedef typename InsertInto::value_t>::value_t value_t; + }; +} + namespace LLInitParam { // used to indicate no matching value to a given name when parsing @@ -45,6 +110,8 @@ namespace LLInitParam template const T& defaultValue() { static T value; return value; } + // wraps comparison operator between any 2 values of the same type + // specialize to handle cases where equality isn't defined well, or at all template ::value > struct ParamCompare { @@ -79,24 +146,123 @@ namespace LLInitParam // helper functions and classes typedef ptrdiff_t param_handle_t; + struct IS_A_BLOCK {}; + struct NOT_BLOCK {}; + + // these templates allow us to distinguish between template parameters + // that derive from BaseBlock and those that don't + template + struct IsBlock + { + typedef NOT_BLOCK value_t; + }; + + template + struct IsBlock + { + typedef IS_A_BLOCK value_t; + }; + + // ParamValue class directly manages the wrapped value + // by holding on to a copy (scalar params) + // or deriving from it (blocks) + // has specializations for custom value behavior + // and "tag" values like Lazy and Atomic + template::value_t> + class ParamValue + { + typedef ParamValue self_t; + + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue(): mValue() {} + ParamValue(const default_value_t& other) : mValue(other) {} + + void setValue(const value_t& val) + { + mValue = val; + } + + const value_t& getValue() const + { + return mValue; + } + + T& getValue() + { + return mValue; + } + + protected: + T mValue; + }; + + template + class ParamValue + : public T + { + typedef ParamValue self_t; + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue() + : T(), + mValidated(false) + {} + + ParamValue(const default_value_t& other) + : T(other), + mValidated(false) + {} + + void setValue(const value_t& val) + { + *this = val; + } + + const value_t& getValue() const + { + return *this; + } + + T& getValue() + { + return *this; + } + + protected: + mutable bool mValidated; // lazy validation flag + }; + // empty default implementation of key cache // leverages empty base class optimization template class TypeValues + : public ParamValue::value_t> { private: struct Inaccessable{}; public: typedef std::map value_name_map_t; typedef Inaccessable name_t; + typedef TypeValues type_value_t; + typedef ParamValue::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValues(const typename param_value_t::value_t& val) + : param_value_t(val) + {} void setValueName(const std::string& key) {} std::string getValueName() const { return ""; } - std::string calcValueName(const T& value) const { return ""; } + std::string calcValueName(const value_t& value) const { return ""; } void clearValueName() const {} - static bool getValueFromName(const std::string& name, T& value) + static bool getValueFromName(const std::string& name, value_t& value) { return false; } @@ -111,15 +277,39 @@ namespace LLInitParam return NULL; } + void assignNamedValue(const Inaccessable& name) + {} + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + static value_name_map_t* getValueNames() {return NULL;} }; - template > + // helper class to implement name value lookups + // and caching of last used name + template , bool IS_SPECIALIZED = true > class TypeValuesHelper + : public ParamValue::value_t> { + typedef TypeValuesHelper self_t; public: typedef typename std::map value_name_map_t; typedef std::string name_t; + typedef self_t type_value_t; + typedef ParamValue::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValuesHelper(const typename param_value_t::value_t& val) + : param_value_t(val) + {} //TODO: cache key by index to save on param block size void setValueName(const std::string& value_name) @@ -132,7 +322,7 @@ namespace LLInitParam return mValueName; } - std::string calcValueName(const T& value) const + std::string calcValueName(const value_t& value) const { value_name_map_t* map = getValueNames(); for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end(); @@ -153,7 +343,7 @@ namespace LLInitParam mValueName.clear(); } - static bool getValueFromName(const std::string& name, T& value) + static bool getValueFromName(const std::string& name, value_t& value) { value_name_map_t* map = getValueNames(); typename value_name_map_t::iterator found_it = map->find(name); @@ -195,18 +385,90 @@ namespace LLInitParam return &sValues; } - static void declare(const std::string& name, const T& value) + static void declare(const std::string& name, const value_t& value) { (*getValueNames())[name] = value; } + void operator ()(const std::string& name) + { + *this = name; + } + + void assignNamedValue(const std::string& name) + { + if (getValueFromName(name, param_value_t::getValue())) + { + setValueName(name); + } + } + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + protected: - static void getName(const std::string& name, const T& value) + static void getName(const std::string& name, const value_t& value) {} mutable std::string mValueName; }; + // string types can support custom named values, but need + // to disambiguate in code between a string that is a named value + // and a string that is a name + template + class TypeValuesHelper + : public TypeValuesHelper + { + public: + typedef TypeValuesHelper self_t; + typedef TypeValuesHelper base_t; + typedef std::string value_t; + typedef std::string name_t; + typedef self_t type_value_t; + + TypeValuesHelper(const std::string& val) + : TypeValuesHelper(val) + {} + + void operator ()(const std::string& name) + { + *this = name; + } + + self_t& operator =(const std::string& name) + { + if (base_t::getValueFromName(name, ParamValue::getValue())) + { + base_t::setValueName(name); + } + else + { + ParamValue::setValue(name); + } + return *this; + } + + operator const value_t&() const + { + return ParamValue::getValue(); + } + + const value_t& operator()() const + { + return ParamValue::getValue(); + } + + }; + + // parser base class with mechanisms for registering readers/writers/inspectors of different types class LL_COMMON_API Parser { LOG_CLASS(Parser); @@ -235,25 +497,25 @@ namespace LLInitParam virtual ~Parser(); template bool readValue(T& param, typename boost::disable_if >::type* dummy = 0) - { + { parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); if (found_it != mParserReadFuncs->end()) - { + { return found_it->second(*this, (void*)¶m); + } + + return false; } - return false; - } - template bool readValue(T& param, typename boost::enable_if >::type* dummy = 0) - { + { parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); if (found_it != mParserReadFuncs->end()) - { + { return found_it->second(*this, (void*)¶m); - } + } else - { + { found_it = mParserReadFuncs->find(&typeid(S32)); if (found_it != mParserReadFuncs->end()) { @@ -261,20 +523,20 @@ namespace LLInitParam bool parsed = found_it->second(*this, (void*)&int_value); param = (T)int_value; return parsed; + } } + return false; } - return false; - } template bool writeValue(const T& param, name_stack_t& name_stack) - { + { parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); if (found_it != mParserWriteFuncs->end()) - { + { return found_it->second(*this, (const void*)¶m, name_stack); + } + return false; } - return false; - } // dispatch inspection to registered inspection functions, for each parameter in a param block template bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) @@ -326,7 +588,7 @@ namespace LLInitParam }; typedef bool(*merge_func_t)(Param&, const Param&, bool); - typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, bool); + typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool); typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param); typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count); typedef bool(*validation_func_t)(const Param*); @@ -371,6 +633,7 @@ namespace LLInitParam } EInitializationState; void aggregateBlockData(BlockDescriptor& src_block_data); + void addParam(ParamDescriptorPtr param, const char* name); typedef boost::unordered_map param_map_t; typedef std::vector param_list_t; @@ -386,48 +649,58 @@ namespace LLInitParam class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed }; - class LL_COMMON_API BaseBlock - { - public: //TODO: implement in terms of owned_ptr template - class Lazy + class LazyValue { public: - Lazy() + LazyValue() : mPtr(NULL) {} - ~Lazy() + ~LazyValue() { delete mPtr; } - Lazy(const Lazy& other) + LazyValue(const T& value) { - if (other.mPtr) - { - mPtr = new T(*other.mPtr); - } - else - { - mPtr = NULL; - } - } + mPtr = new T(value); + } - Lazy& operator = (const Lazy& other) + LazyValue(const LazyValue& other) + : mPtr(NULL) + { + *this = other; + } + + LazyValue& operator = (const LazyValue& other) + { + if (!other.mPtr) { - if (other.mPtr) + delete mPtr; + mPtr = NULL; + } + else + { + if (!mPtr) { mPtr = new T(*other.mPtr); } else { - mPtr = NULL; + *mPtr = *(other.mPtr); + } } return *this; } + bool operator==(const LazyValue& other) const + { + if (empty() || other.empty()) return false; + return *mPtr == *other.mPtr; + } + bool empty() const { return mPtr == NULL; @@ -435,18 +708,29 @@ namespace LLInitParam void set(const T& other) { - delete mPtr; + if (!mPtr) + { mPtr = new T(other); } + else + { + *mPtr = other; + } + } const T& get() const { - return ensureInstance(); + return *ensureInstance(); } T& get() { - return ensureInstance(); + return *ensureInstance(); + } + + operator const T&() const + { + return get(); } private: @@ -461,13 +745,50 @@ namespace LLInitParam } private: - // if you get a compilation error with this, that means you are using a forward declared struct for T - // unfortunately, the type traits we rely on don't work with forward declared typed - //static const int dummy = sizeof(T); mutable T* mPtr; }; + // root class of all parameter blocks + + class LL_COMMON_API BaseBlock + { + public: + // lift block tags into baseblock namespace so derived classes do not need to qualify them + typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK; + typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK; + + template + struct Sequential : public LLTypeTags::TypeTagBase + { + template struct Cons { typedef Sequential > value_t; }; + template struct Cons > { typedef Sequential value_t; }; + }; + + template + struct Atomic : public LLTypeTags::TypeTagBase + { + template struct Cons { typedef Atomic > value_t; }; + template struct Cons > { typedef Atomic value_t; }; + }; + + template::value_t > + struct Lazy : public LLTypeTags::TypeTagBase + { + template struct Cons + { + typedef Lazy, BLOCK_T> value_t; + }; + template struct Cons > + { + typedef Lazy value_t; + }; + template struct Cons > + { + typedef Lazy value_t; + }; + }; + // "Multiple" constraint types, put here in root class to avoid ambiguity during use struct AnyAmount { @@ -533,12 +854,12 @@ namespace LLInitParam // Blocks can override this to do custom tracking of changes virtual void paramChanged(const Param& changed_param, bool user_provided) {} - bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name); + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const; - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } // take all provided params from other and apply to self bool overwriteFrom(const BaseBlock& other) @@ -552,10 +873,17 @@ namespace LLInitParam return false; } - static void addParam(BlockDescriptor& block_data, ParamDescriptorPtr param, const char* name); - ParamDescriptorPtr findParamDescriptor(const Param& param); + // take all provided params from other and apply to self + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); + + static BlockDescriptor& getBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + protected: void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); @@ -564,25 +892,11 @@ namespace LLInitParam { return mergeBlock(block_data, source, overwrite); } - // take all provided params from other and apply to self - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); - - static BlockDescriptor& selfBlockDescriptor() - { - static BlockDescriptor sBlockDescriptor; - return sBlockDescriptor; - } private: const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; }; - template - struct ParamCompare, false > - { - static bool equals(const BaseBlock::Lazy& a, const BaseBlock::Lazy& b) { return !a.empty() || !b.empty(); } - }; - class LL_COMMON_API Param { public: @@ -611,256 +925,68 @@ namespace LLInitParam // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class return *const_cast (reinterpret_cast - (my_addr - (ptrdiff_t)(S32)mEnclosingBlockOffset)); + (my_addr - (ptrdiff_t)getEnclosingBlockOffset())); + } + + U32 getEnclosingBlockOffset() const + { + return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow; } private: friend class BaseBlock; - U32 mEnclosingBlockOffset:31; - U32 mIsProvided:1; + //24 bits for member offset field and 1 bit for provided flag + U16 mEnclosingBlockOffsetLow; + U8 mEnclosingBlockOffsetHigh:7; + U8 mIsProvided:1; }; - // these templates allow us to distinguish between template parameters - // that derive from BaseBlock and those that don't - template - struct IsBlock - { - static const bool value = false; - struct EmptyBase {}; - typedef EmptyBase base_class_t; - }; - - template - struct IsBlock - { - static const bool value = true; - typedef BaseBlock base_class_t; - }; - - template - struct IsBlock, typename T::baseblock_base_class_t > - { - static const bool value = true; - typedef BaseBlock base_class_t; - }; - - template::value> - class ParamValue : public NAME_VALUE_LOOKUP - { - public: - typedef const T& value_assignment_t; - typedef T value_t; - typedef ParamValue self_t; - - ParamValue(): mValue() {} - ParamValue(value_assignment_t other) : mValue(other) {} - - void setValue(value_assignment_t val) - { - mValue = val; - } - - value_assignment_t getValue() const - { - return mValue; - } - - T& getValue() - { - return mValue; - } - - operator value_assignment_t() const - { - return mValue; - } - - value_assignment_t operator()() const - { - return mValue; - } - - void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) - { - *this = name; - } - - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) - { - if (NAME_VALUE_LOOKUP::getValueFromName(name, mValue)) - { - NAME_VALUE_LOOKUP::setValueName(name); - } - - return *this; - } - - protected: - T mValue; - }; - - template - class ParamValue - : public T, - public NAME_VALUE_LOOKUP - { - public: - typedef const T& value_assignment_t; - typedef T value_t; - typedef ParamValue self_t; - - ParamValue() - : T(), - mValidated(false) - {} - - ParamValue(value_assignment_t other) - : T(other), - mValidated(false) - {} - - void setValue(value_assignment_t val) - { - *this = val; - } - - value_assignment_t getValue() const - { - return *this; - } - - T& getValue() - { - return *this; - } - - operator value_assignment_t() const - { - return *this; - } - - value_assignment_t operator()() const - { - return *this; - } - - void operator ()(const typename NAME_VALUE_LOOKUP::name_t& name) - { - *this = name; - } - - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) - { - if (NAME_VALUE_LOOKUP::getValueFromName(name, *this)) - { - setValueName(name); - } - - return *this; - } - - protected: - mutable bool mValidated; // lazy validation flag - }; - - template - class ParamValue - : public NAME_VALUE_LOOKUP - { - public: - typedef const std::string& value_assignment_t; - typedef std::string value_t; - typedef ParamValue self_t; - - ParamValue(): mValue() {} - ParamValue(value_assignment_t other) : mValue(other) {} - - void setValue(value_assignment_t val) - { - if (NAME_VALUE_LOOKUP::getValueFromName(val, mValue)) - { - NAME_VALUE_LOOKUP::setValueName(val); - } - else - { - mValue = val; - } - } - - value_assignment_t getValue() const - { - return mValue; - } - - std::string& getValue() - { - return mValue; - } - - operator value_assignment_t() const - { - return mValue; - } - - value_assignment_t operator()() const - { - return mValue; - } - - protected: - std::string mValue; - }; - - template > struct ParamIterator { - typedef typename std::vector >::const_iterator const_iterator; - typedef typename std::vector >::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::iterator iterator; }; - // specialize for custom parsing/decomposition of specific classes - // e.g. TypedParam has left, top, right, bottom, etc... + // wrapper for parameter with a known type + // specialized to handle 4 cases: + // simple "scalar" value + // parameter that is itself a block + // multiple scalar values, stored in a vector + // multiple blocks, stored in a vector template, bool HAS_MULTIPLE_VALUES = false, - bool VALUE_IS_BLOCK = IsBlock >::value> + typename VALUE_IS_BLOCK = typename IsBlock::value_t> >::value_t> class TypedParam : public Param, - public ParamValue + public NAME_VALUE_LOOKUP::type_value_t { + protected: + typedef TypedParam self_t; + typedef ParamValue::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; public: - typedef TypedParam self_t; - typedef ParamValue param_value_t; - typedef typename param_value_t::value_assignment_t value_assignment_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; + typedef typename param_value_t::value_t value_t; - using param_value_t::operator(); + using named_value_t::operator(); - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + named_value_t(value) { if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } - - param_value_t::setValue(value); } bool isProvided() const { return Param::anyProvided(); } - static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { self_t& typed_param = static_cast(param); // no further names in stack, attempt to parse value now @@ -869,9 +995,9 @@ namespace LLInitParam std::string name; // try to parse a known named value - if(name_value_lookup_t::valueNamesExist() + if(named_value_t::valueNamesExist() && parser.readValue(name) - && name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + && named_value_t::getValueFromName(name, typed_param.getValue())) { typed_param.setValueName(name); typed_param.setProvided(); @@ -915,7 +1041,9 @@ namespace LLInitParam if (!parser.writeValue(typed_param.getValue(), name_stack)) { std::string calculated_key = typed_param.calcValueName(typed_param.getValue()); - if (!diff_param || !ParamCompare::equals(static_cast(diff_param)->getValueName(), calculated_key)) + if (calculated_key.size() + && (!diff_param + || !ParamCompare::equals(static_cast(diff_param)->getValueName(), calculated_key))) { parser.writeValue(calculated_key, name_stack); } @@ -928,22 +1056,23 @@ namespace LLInitParam // tell parser about our actual type parser.inspectValue(name_stack, min_count, max_count, NULL); // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) - if (name_value_lookup_t::getPossibleValues()) + if (named_value_t::getPossibleValues()) { - parser.inspectValue(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + parser.inspectValue(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } } - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const value_t& val, bool flag_as_provided = true) { - param_value_t::clearValueName(); + named_value_t::clearValueName(); param_value_t::setValue(val); setProvided(flag_as_provided); } - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + self_t& operator =(const typename named_value_t::name_t& name) { - return static_cast(param_value_t::operator =(name)); + named_value_t::assignNamedValue(name); + return *this; } protected: @@ -968,41 +1097,47 @@ namespace LLInitParam } return false; } + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; // parameter that is a block template - class TypedParam + class TypedParam : public Param, - public ParamValue + public NAME_VALUE_LOOKUP::type_value_t { + protected: + typedef ParamValue::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef TypedParam self_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; public: - typedef ParamValue param_value_t; - typedef typename param_value_t::value_assignment_t value_assignment_t; - typedef TypedParam self_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; + using named_value_t::operator(); + typedef typename param_value_t::value_t value_t; - using param_value_t::operator(); - - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr), - param_value_t(value) + named_value_t(value) { if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } } - static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { self_t& typed_param = static_cast(param); @@ -1010,9 +1145,9 @@ namespace LLInitParam { // try to parse a known named value std::string name; - if(name_value_lookup_t::valueNamesExist() + if(named_value_t::valueNamesExist() && parser.readValue(name) - && name_value_lookup_t::getValueFromName(name, typed_param.getValue())) + && named_value_t::getValueFromName(name, typed_param.getValue())) { typed_param.setValueName(name); typed_param.setProvided(); @@ -1044,9 +1179,9 @@ namespace LLInitParam std::string key = typed_param.getValueName(); if (!key.empty()) { - if (!parser.writeValue(key, name_stack)) + if (!diff_param || !ParamCompare::equals(static_cast(diff_param)->getValueName(), key)) { - return; + parser.writeValue(key, name_stack); } } else @@ -1057,8 +1192,16 @@ namespace LLInitParam static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { - // I am a param that is also a block, so just recurse into my contents const self_t& typed_param = static_cast(param); + + // tell parser about our actual type + parser.inspectValue(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + typed_param.inspectBlock(parser, name_stack, min_count, max_count); } @@ -1076,32 +1219,34 @@ namespace LLInitParam } // assign block contents to this param-that-is-a-block - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const value_t& val, bool flag_as_provided = true) { param_value_t::setValue(val); - param_value_t::clearValueName(); + named_value_t::clearValueName(); // force revalidation of block // next call to isProvided() will update provision status based on validity param_value_t::mValidated = false; setProvided(flag_as_provided); } - self_t& operator =(const typename NAME_VALUE_LOOKUP::name_t& name) + self_t& operator =(const typename named_value_t::name_t& name) { - return static_cast(param_value_t::operator =(name)); + named_value_t::assignNamedValue(name); + return *this; } // propagate changed status up to enclosing block /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) { param_value_t::paramChanged(changed_param, user_provided); + if (user_provided) { // a child param has been explicitly changed // so *some* aspect of this block is now provided param_value_t::mValidated = false; setProvided(); - param_value_t::clearValueName(); + named_value_t::clearValueName(); } else { @@ -1125,7 +1270,7 @@ namespace LLInitParam if (src_typed_param.anyProvided()) { - if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::selfBlockDescriptor(), src_typed_param, overwrite)) + if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite)) { dst_typed_param.clearValueName(); dst_typed_param.setProvided(true); @@ -1134,56 +1279,72 @@ namespace LLInitParam } return false; } + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; - // container of non-block parameters + // list of non-block parameters template - class TypedParam + class TypedParam : public Param { - public: - typedef TypedParam self_t; - typedef ParamValue param_value_t; - typedef typename std::vector container_t; - typedef const container_t& value_assignment_t; - - typedef typename param_value_t::value_t value_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; + protected: + typedef TypedParam self_t; + typedef ParamValue::value_t> param_value_t; + typedef typename std::vector container_t; + typedef container_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + public: + typedef typename param_value_t::value_t value_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr) { std::copy(value.begin(), value.end(), std::back_inserter(mValues)); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); + } } bool isProvided() const { return Param::anyProvided(); } - static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); self_t& typed_param = static_cast(param); value_t value; + + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + ++new_name_stack_range.first; + } + // no further names in stack, attempt to parse value now if (name_stack_range.first == name_stack_range.second) { std::string name; // try to parse a known named value - if(name_value_lookup_t::valueNamesExist() + if(named_value_t::valueNamesExist() && parser.readValue(name) - && name_value_lookup_t::getValueFromName(name, value)) + && named_value_t::getValueFromName(name, value)) { typed_param.add(value); typed_param.mValues.back().setValueName(name); @@ -1201,14 +1362,14 @@ namespace LLInitParam static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) { const self_t& typed_param = static_cast(param); - if (!typed_param.isProvided() || name_stack.empty()) return; + if (!typed_param.isProvided()) return; for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); it != end_it; ++it) { std::string key = it->getValueName(); - name_stack.back().second = true; + name_stack.push_back(std::make_pair(std::string(), true)); if(key.empty()) // not parsed via name values, write out value directly @@ -1230,19 +1391,21 @@ namespace LLInitParam break; } } + + name_stack.pop_back(); } } static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { parser.inspectValue(name_stack, min_count, max_count, NULL); - if (name_value_lookup_t::getPossibleValues()) + if (named_value_t::getPossibleValues()) { - parser.inspectValue(name_stack, min_count, max_count, name_value_lookup_t::getPossibleValues()); + parser.inspectValue(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } } - void set(value_assignment_t val, bool flag_as_provided = true) + void set(const container_t& val, bool flag_as_provided = true) { mValues = val; setProvided(flag_as_provided); @@ -1250,26 +1413,24 @@ namespace LLInitParam param_value_t& add() { - mValues.push_back(param_value_t(value_t())); + mValues.push_back(value_t()); Param::setProvided(); return mValues.back(); } self_t& add(const value_t& item) { - param_value_t param_value; - param_value.setValue(item); - mValues.push_back(param_value); + mValues.push_back(item); setProvided(); return *this; } - self_t& add(const typename name_value_lookup_t::name_t& name) + self_t& add(const typename named_value_t::name_t& name) { value_t value; // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) + if (named_value_t::getValueFromName(name, value)) { add(value); mValues.back().setValueName(name); @@ -1279,9 +1440,9 @@ namespace LLInitParam } // implicit conversion - operator value_assignment_t() const { return mValues; } + operator const container_t&() const { return mValues; } // explicit conversion - value_assignment_t operator()() const { return mValues; } + const container_t& operator()() const { return mValues; } typedef typename container_t::iterator iterator; typedef typename container_t::const_iterator const_iterator; @@ -1322,62 +1483,79 @@ namespace LLInitParam } container_t mValues; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; - // container of block parameters + // list of block parameters template - class TypedParam + class TypedParam : public Param { + protected: + typedef TypedParam self_t; + typedef ParamValue::value_t> param_value_t; + typedef typename std::vector container_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + typedef container_t default_value_t; + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; public: - typedef TypedParam self_t; - typedef ParamValue param_value_t; - typedef typename std::vector container_t; - typedef const container_t& value_assignment_t; typedef typename param_value_t::value_t value_t; - typedef NAME_VALUE_LOOKUP name_value_lookup_t; - TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) : Param(block_descriptor.mCurrentBlockPtr) { std::copy(value.begin(), value.end(), back_inserter(mValues)); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + init(block_descriptor, validate_func, min_count, max_count, name); } } bool isProvided() const { return Param::anyProvided(); } - static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); self_t& typed_param = static_cast(param); bool new_value = false; + bool new_array_value = false; - if (new_name || typed_param.mValues.empty()) + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + new_array_value = new_name_stack_range.first->second; + ++new_name_stack_range.first; + } + + if (new_name || new_array_value || typed_param.mValues.empty()) { new_value = true; typed_param.mValues.push_back(value_t()); } - param_value_t& value = typed_param.mValues.back(); if (name_stack_range.first == name_stack_range.second) { // try to parse a known named value std::string name; - if(name_value_lookup_t::valueNamesExist() + if(named_value_t::valueNamesExist() && parser.readValue(name) - && name_value_lookup_t::getValueFromName(name, value.getValue())) + && named_value_t::getValueFromName(name, value.getValue())) { typed_param.mValues.back().setValueName(name); typed_param.setProvided(); @@ -1386,9 +1564,13 @@ namespace LLInitParam } // attempt to parse block... - if(value.deserializeBlock(parser, name_stack_range, new_name)) + if(value.deserializeBlock(parser, new_name_stack_range, new_name)) { typed_param.setProvided(); + if (new_array_value) + { + name_stack_range.first->second = false; + } return true; } @@ -1404,13 +1586,13 @@ namespace LLInitParam static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param) { const self_t& typed_param = static_cast(param); - if (!typed_param.isProvided() || name_stack.empty()) return; + if (!typed_param.isProvided()) return; for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); it != end_it; ++it) { - name_stack.back().second = true; + name_stack.push_back(std::make_pair(std::string(), true)); std::string key = it->getValueName(); if (!key.empty()) @@ -1423,16 +1605,27 @@ namespace LLInitParam { it->serializeBlock(parser, name_stack, NULL); } + + name_stack.pop_back(); } } static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) { - // I am a vector of blocks, so describe my contents recursively - param_value_t(value_t()).inspectBlock(parser, name_stack, min_count, max_count); + const param_value_t& value_param = param_value_t(value_t()); + + // tell parser about our actual type + parser.inspectValue(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue(name_stack, min_count, max_count, named_value_t::getPossibleValues()); } - void set(value_assignment_t val, bool flag_as_provided = true) + value_param.inspectBlock(parser, name_stack, min_count, max_count); + } + + void set(const container_t& val, bool flag_as_provided = true) { mValues = val; setProvided(flag_as_provided); @@ -1452,12 +1645,12 @@ namespace LLInitParam return *this; } - self_t& add(const typename name_value_lookup_t::name_t& name) + self_t& add(const typename named_value_t::name_t& name) { value_t value; // try to parse a per type named value - if (name_value_lookup_t::getValueFromName(name, value)) + if (named_value_t::getValueFromName(name, value)) { add(value); mValues.back().setValueName(name); @@ -1466,12 +1659,10 @@ namespace LLInitParam } // implicit conversion - operator value_assignment_t() const { return mValues; } + operator const container_t&() const { return mValues; } // explicit conversion - value_assignment_t operator()() const { return mValues; } + const container_t& operator()() const { return mValues; } - typedef typename container_t::iterator iterator; - typedef typename container_t::const_iterator const_iterator; iterator begin() { return mValues.begin(); } iterator end() { return mValues.end(); } const_iterator begin() const { return mValues.begin(); } @@ -1518,6 +1709,20 @@ namespace LLInitParam } container_t mValues; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } }; template @@ -1532,13 +1737,13 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, true); + return static_cast(this)->mergeBlock(getBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, false); + return static_cast(this)->mergeBlock(getBlockDescriptor(), other, false); } bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) @@ -1556,7 +1761,7 @@ namespace LLInitParam bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite) { mCurChoice = other.mCurChoice; - return base_block_t::mergeBlock(selfBlockDescriptor(), other, overwrite); + return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite); } // clear out old choice when param has changed @@ -1577,38 +1782,38 @@ namespace LLInitParam base_block_t::paramChanged(changed_param, user_provided); } - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } protected: ChoiceBlock() : mCurChoice(0) { - BaseBlock::init(selfBlockDescriptor(), base_block_t::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // Alternatives are mutually exclusive wrt other Alternatives in the same block. // One alternative in a block will always have isChosen() == true. // At most one alternative in a block will have isProvided() == true. - template > + template ::type_value_t > class Alternative : public TypedParam { + typedef TypedParam super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: friend class ChoiceBlock; - typedef Alternative self_t; - typedef TypedParam >::value> super_t; - typedef typename super_t::value_assignment_t value_assignment_t; - using super_t::operator =; - explicit Alternative(const char* name = "", value_assignment_t val = defaultValue()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1), + explicit Alternative(const char* name = "", const default_value_t& val = defaultValue()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1), mOriginalValue(val) { // assign initial choice to first declared option - DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr); - if (LL_UNLIKELY(DERIVED_BLOCK::selfBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr); + if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) { if(blockp->mCurChoice == 0) { @@ -1622,27 +1827,27 @@ namespace LLInitParam static_cast(Param::enclosingBlock()).paramChanged(*this, true); } - void chooseAs(value_assignment_t val) + void chooseAs(const value_t& val) { super_t::set(val); } - void operator =(value_assignment_t val) + void operator =(const value_t& val) { super_t::set(val); } - void operator()(typename super_t::value_assignment_t val) + void operator()(const value_t& val) { super_t::set(val); } - operator value_assignment_t() const + operator const value_t&() const { return (*this)(); } - value_assignment_t operator()() const + const value_t& operator()() const { if (static_cast(Param::enclosingBlock()).getCurrentChoice() == this) { @@ -1657,11 +1862,11 @@ namespace LLInitParam } private: - T mOriginalValue; + default_value_t mOriginalValue; }; - protected: - static BlockDescriptor& selfBlockDescriptor() + public: + static BlockDescriptor& getBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; @@ -1681,6 +1886,8 @@ namespace LLInitParam : public BASE_BLOCK { typedef Block self_t; + + protected: typedef Block block_t; public: @@ -1689,80 +1896,82 @@ namespace LLInitParam // take all provided params from other and apply to self bool overwriteFrom(const self_t& other) { - return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, true); + return static_cast(this)->mergeBlock(getBlockDescriptor(), other, true); } // take all provided params that are not already provided, and apply to self bool fillFrom(const self_t& other) { - return static_cast(this)->mergeBlock(selfBlockDescriptor(), other, false); + return static_cast(this)->mergeBlock(getBlockDescriptor(), other, false); } - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return selfBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return selfBlockDescriptor(); } + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } protected: Block() { //#pragma message("Parsing LLInitParam::Block") - BaseBlock::init(selfBlockDescriptor(), BASE_BLOCK::selfBlockDescriptor(), sizeof(DERIVED_BLOCK)); + BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); } // // Nested classes for declaring parameters // - template > + template ::type_value_t > class Optional : public TypedParam { - public: - typedef TypedParam >::value> super_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef TypedParam super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: using super_t::operator(); using super_t::operator =; - explicit Optional(const char* name = "", value_assignment_t val = defaultValue()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, NULL, 0, 1) + explicit Optional(const char* name = "", const default_value_t& val = defaultValue()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1) { //#pragma message("Parsing LLInitParam::Block::Optional") } - Optional& operator =(value_assignment_t val) + Optional& operator =(const value_t& val) { super_t::set(val); return *this; } - DERIVED_BLOCK& operator()(value_assignment_t val) + DERIVED_BLOCK& operator()(const value_t& val) { super_t::set(val); return static_cast(Param::enclosingBlock()); } }; - template > + template ::type_value_t > class Mandatory : public TypedParam { - public: - typedef TypedParam >::value> super_t; + typedef TypedParam super_t; typedef Mandatory self_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + public: using super_t::operator(); using super_t::operator =; // mandatory parameters require a name to be parseable - explicit Mandatory(const char* name = "", value_assignment_t val = defaultValue()) - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, val, &validate, 1, 1) + explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1) {} - Mandatory& operator =(value_assignment_t val) + Mandatory& operator =(const value_t& val) { super_t::set(val); return *this; } - DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + DERIVED_BLOCK& operator()(const value_t& val) { super_t::set(val); return static_cast(Param::enclosingBlock()); @@ -1776,28 +1985,29 @@ namespace LLInitParam }; - template > + template ::type_value_t > class Multiple : public TypedParam { - public: - typedef TypedParam >::value> super_t; + typedef TypedParam super_t; typedef Multiple self_t; typedef typename super_t::container_t container_t; - typedef typename super_t::value_assignment_t value_assignment_t; + typedef typename super_t::value_t value_t; + + public: typedef typename super_t::iterator iterator; typedef typename super_t::const_iterator const_iterator; explicit Multiple(const char* name = "") - : super_t(DERIVED_BLOCK::selfBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) {} - Multiple& operator =(value_assignment_t val) + Multiple& operator =(const container_t& val) { super_t::set(val); return *this; } - DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val) + DERIVED_BLOCK& operator()(const container_t& val) { super_t::set(val); return static_cast(Param::enclosingBlock()); @@ -1810,13 +2020,15 @@ namespace LLInitParam } }; - class Deprecated : public Param + // can appear in data files, but will ignored during parsing + // cannot read or write in code + class Ignored : public Param { public: - explicit Deprecated(const char* name) - : Param(DERIVED_BLOCK::selfBlockDescriptor().mCurrentBlockPtr) + explicit Ignored(const char* name) + : Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr) { - BlockDescriptor& block_descriptor = DERIVED_BLOCK::selfBlockDescriptor(); + BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor(); if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) { ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( @@ -1827,11 +2039,11 @@ namespace LLInitParam NULL, NULL, 0, S32_MAX)); - BaseBlock::addParam(block_descriptor, param_descriptor, name); + block_descriptor.addParam(param_descriptor, name); } } - static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name) + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { if (name_stack_range.first == name_stack_range.second) { @@ -1844,19 +2056,46 @@ namespace LLInitParam } }; - // different semantics for documentation purposes, but functionally identical - typedef Deprecated Ignored; + // can appear in data files, or be written to in code, but data will be ignored + // cannot be read in code + class Deprecated : public Ignored + { + public: + explicit Deprecated(const char* name) : Ignored(name) {} - protected: - static BlockDescriptor& selfBlockDescriptor() + // dummy writer interfaces + template + Deprecated& operator =(const T& val) + { + // do nothing + return *this; + } + + template + DERIVED_BLOCK& operator()(const T& val) + { + // do nothing + return static_cast(Param::enclosingBlock()); + } + + template + void set(const T& val, bool flag_as_provided = true) + { + // do nothing + } + }; + + public: + static BlockDescriptor& getBlockDescriptor() { static BlockDescriptor sBlockDescriptor; return sBlockDescriptor; } - template + protected: + template void changeDefault(TypedParam& param, - typename TypedParam::value_assignment_t value) + const typename TypedParam::value_t& value) { if (!param.isProvided()) { @@ -1866,204 +2105,420 @@ namespace LLInitParam }; - template - class BatchBlock - : public Block + template + struct IsBlock, BLOCK_T >, void> { - public: - typedef BatchBlock self_t; - typedef Block super_t; + typedef IS_A_BLOCK value_t; + }; - BatchBlock() + template + struct IsBlock, BLOCK_T >, void> + { + typedef NOT_BLOCK value_t; + }; + + template + struct IsBlock, typename IsBlock >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock::value_t value_t; + }; + + template + struct IsBlock, typename IsBlock >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock::value_t value_t; + }; + + + template + struct InnerMostType + { + typedef T value_t; + }; + + template + struct InnerMostType > + { + typedef typename InnerMostType::value_t value_t; + }; + + template + struct InnerMostType > + { + typedef typename InnerMostType::value_t value_t; + }; + + template + class ParamValue , BLOCK_T> + { + typedef ParamValue , BLOCK_T> self_t; + + public: + typedef typename InnerMostType::value_t value_t; + typedef T default_value_t; + + ParamValue() + : mValue(), + mValidated(false) {} - bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) + ParamValue(const default_value_t& value) + : mValue(value), + mValidated(false) + {} + + void setValue(const value_t& val) + { + mValue.setValue(val); + } + + const value_t& getValue() const + { + return mValue.getValue(); + } + + value_t& getValue() + { + return mValue.getValue(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) { if (new_name) { - // reset block - *static_cast(this) = defaultBatchValue(); + resetToDefault(); } - return super_t::deserializeBlock(p, name_stack_range, new_name); + return mValue.deserializeBlock(p, name_stack_range, new_name); } - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const { - if (overwrite) + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + mValue.serializeBlock(p, name_stack, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const { - *static_cast(this) = defaultBatchValue(); - // merge individual parameters into destination - return super_t::mergeBlock(super_t::selfBlockDescriptor(), other, overwrite); + return mValue.inspectBlock(p, name_stack, min_count, max_count); } - return false; - } - protected: - static const DERIVED_BLOCK& defaultBatchValue() + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) { - static DERIVED_BLOCK default_value; - return default_value; + if ((overwrite && source_provided) // new values coming in on top or... + || (!overwrite && !dst_provided)) // values being pushed under with nothing already there + { + // clear away what is there and take the new stuff as a whole + resetToDefault(); + return mValue.mergeBlock(block_data, source.getValue(), overwrite); } + + + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.validateBlock(emit_errors); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + + mutable bool mValidated; // lazy validation flag + + private: + void resetToDefault() + { + static T default_value; + mValue = default_value; + } + + T mValue; }; - // FIXME: this specialization is not currently used, as it only matches against the BatchBlock base class - // and not the derived class with the actual params - template - class ParamValue , - NAME_VALUE_LOOKUP, - true> - : public NAME_VALUE_LOOKUP, - protected BatchBlock + template + class ParamValue , IS_A_BLOCK> { + typedef ParamValue , IS_A_BLOCK> self_t; + public: - typedef BatchBlock block_t; - typedef const BatchBlock& value_assignment_t; - typedef block_t value_t; + typedef typename InnerMostType::value_t value_t; + typedef T default_value_t; ParamValue() - : block_t(), + : mValue(), + mValidated(false) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + + ParamValue(const default_value_t& value) + : mValue(value), + mValidated(false) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + + void setValue(const value_t& val) + { + mValue.setValue(val); + } + + const value_t& getValue() const + { + return mValue.getValue(); + } + + value_t& getValue() + { + return mValue.getValue(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + if (new_name) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + if (name_stack_range.first == name_stack_range.second + && mCurParam != getBlockDescriptor().mAllParams.end()) + { + // deserialize to mCurParam + ParamDescriptor& pd = *(*mCurParam); + ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc; + Param* paramp = mValue.getParamFromHandle(pd.mParamHandle); + + if (deserialize_func + && paramp + && deserialize_func(*paramp, p, name_stack_range, new_name)) + { + ++mCurParam; + return true; + } + else + { + return false; + } + } + else + { + return mValue.deserializeBlock(p, name_stack_range, new_name); + } + } + + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const + { + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + mValue.serializeBlock(p, name_stack, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.validateBlock(emit_errors); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + mutable bool mValidated; // lazy validation flag + + private: + + BlockDescriptor::all_params_list_t::iterator mCurParam; + T mValue; + }; + + template + class ParamValue , NOT_BLOCK> + : public T + { + typedef ParamValue , NOT_BLOCK> self_t; + + public: + typedef typename InnerMostType::value_t value_t; + typedef T default_value_t; + + ParamValue() + : T(), + mValidated(false) + {} + + ParamValue(const default_value_t& value) + : T(value.getValue()), mValidated(false) {} - ParamValue(value_assignment_t other) - : block_t(other), - mValidated(false) - { - } - - void setValue(value_assignment_t val) - { - *this = val; - } - - value_assignment_t getValue() const - { - return *this; - } - - BatchBlock& getValue() - { - return *this; - } - - operator value_assignment_t() const - { - return *this; - } - - value_assignment_t operator()() const - { - return *this; - } - - protected: mutable bool mValidated; // lazy validation flag }; - template - class ParamValue , - TypeValues, - IS_BLOCK> - : public IsBlock::base_class_t + template + class ParamValue , BLOCK_T> { + typedef ParamValue , BLOCK_T> self_t; + public: - typedef ParamValue , TypeValues, false> self_t; - typedef const T& value_assignment_t; - typedef T value_t; + typedef typename InnerMostType::value_t value_t; + typedef LazyValue default_value_t; ParamValue() : mValue(), mValidated(false) {} - ParamValue(value_assignment_t other) + ParamValue(const default_value_t& other) : mValue(other), mValidated(false) {} - void setValue(value_assignment_t val) + ParamValue(const T& value) + : mValue(value), + mValidated(false) + {} + + void setValue(const value_t& val) { mValue.set(val); } - value_assignment_t getValue() const + const value_t& getValue() const { - return mValue.get(); + return mValue.get().getValue(); } - T& getValue() + value_t& getValue() { - return mValue.get(); + return mValue.get().getValue(); } - operator value_assignment_t() const - { - return mValue.get(); - } - - value_assignment_t operator()() const - { - return mValue.get(); - } - - bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name) + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) { return mValue.get().deserializeBlock(p, name_stack_range, new_name); } - void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const + void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const { if (mValue.empty()) return; - mValue.get().serializeBlock(p, name_stack, diff_block); + const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty()) + ? &(diff_block->mValue.get().getValue()) + : NULL; + mValue.get().serializeBlock(p, name_stack, base_block); } bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const { - if (mValue.empty()) return false; - return mValue.get().inspectBlock(p, name_stack, min_count, max_count); } - protected: + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.empty() || mValue.get().validateBlock(emit_errors); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + mutable bool mValidated; // lazy validation flag private: - BaseBlock::Lazy mValue; + LazyValue mValue; + }; + + template + class ParamValue , BLOCK_T> + { + typedef ParamValue , BLOCK_T> self_t; + + public: + typedef typename InnerMostType::value_t value_t; + typedef LazyValue default_value_t; + + ParamValue() + : mValue(), + mValidated(false) + {} + + ParamValue(const default_value_t& other) + : mValue(other), + mValidated(false) + {} + + ParamValue(const T& value) + : mValue(value), + mValidated(false) + {} + + void setValue(const value_t& val) + { + mValue.set(val); + } + + const value_t& getValue() const + { + return mValue.get().getValue(); + } + + value_t& getValue() + { + return mValue.get().getValue(); + } + + mutable bool mValidated; // lazy validation flag + + private: + LazyValue mValue; }; template <> - class ParamValue , - false> - : public TypeValues, - public BaseBlock + class ParamValue + : public BaseBlock { public: - typedef ParamValue, false> self_t; - typedef const LLSD& value_assignment_t; + typedef LLSD value_t; + typedef LLSD default_value_t; ParamValue() : mValidated(false) {} - ParamValue(value_assignment_t other) + ParamValue(const default_value_t& other) : mValue(other), mValidated(false) {} - void setValue(value_assignment_t val) { mValue = val; } + void setValue(const value_t& val) { mValue = val; } - value_assignment_t getValue() const { return mValue; } + const value_t& getValue() const { return mValue; } LLSD& getValue() { return mValue; } - operator value_assignment_t() const { return mValue; } - value_assignment_t operator()() const { return mValue; } - - // block param interface - LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name); + LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const; bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const { @@ -2082,8 +2537,7 @@ namespace LLInitParam template class CustomParamValue - : public Block > >, - public TypeValues + : public Block > { public: typedef enum e_value_age @@ -2093,20 +2547,21 @@ namespace LLInitParam BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative } EValueAge; - typedef ParamValue > derived_t; + typedef ParamValue derived_t; typedef CustomParamValue self_t; typedef Block block_t; - typedef const T& value_assignment_t; + typedef T default_value_t; typedef T value_t; + typedef void baseblock_base_class_t; - CustomParamValue(const T& value = T()) + CustomParamValue(const default_value_t& value = T()) : mValue(value), mValueAge(VALUE_AUTHORITATIVE), mValidated(false) {} - bool deserializeBlock(Parser& parser, Parser::name_stack_range_t name_stack_range, bool new_name) + bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) { derived_t& typed_param = static_cast(*this); // try to parse direct value T @@ -2117,8 +2572,6 @@ namespace LLInitParam typed_param.mValueAge = VALUE_AUTHORITATIVE; typed_param.updateBlockFromValue(false); - typed_param.clearValueName(); - return true; } } @@ -2132,18 +2585,8 @@ namespace LLInitParam const derived_t& typed_param = static_cast(*this); const derived_t* diff_param = static_cast(diff_block); - std::string key = typed_param.getValueName(); - - // first try to write out name of name/value pair - if (!key.empty()) - { - if (!diff_param || !ParamCompare::equals(diff_param->getValueName(), key)) - { - parser.writeValue(key, name_stack); - } - } // then try to serialize value directly - else if (!diff_param || !ParamCompare::equals(typed_param.getValue(), diff_param->getValue())) + if (!diff_param || !ParamCompare::equals(typed_param.getValue(), diff_param->getValue())) { if (!parser.writeValue(typed_param.getValue(), name_stack)) @@ -2173,19 +2616,6 @@ namespace LLInitParam } } - bool inspectBlock(Parser& parser, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - // first, inspect with actual type... - parser.inspectValue(name_stack, min_count, max_count, NULL); - if (TypeValues::getPossibleValues()) - { - //...then inspect with possible string values... - parser.inspectValue(name_stack, min_count, max_count, TypeValues::getPossibleValues()); - } - // then recursively inspect contents... - return block_t::inspectBlock(parser, name_stack, min_count, max_count); - } - bool validateBlock(bool emit_errors = true) const { if (mValueAge == VALUE_NEEDS_UPDATE) @@ -2193,7 +2623,6 @@ namespace LLInitParam if (block_t::validateBlock(emit_errors)) { // clear stale keyword associated with old value - TypeValues::clearValueName(); mValueAge = BLOCK_AUTHORITATIVE; static_cast(const_cast(this))->updateValueFromBlock(); return true; @@ -2223,17 +2652,15 @@ namespace LLInitParam } } - void setValue(value_assignment_t val) + void setValue(const value_t& val) { - derived_t& typed_param = static_cast(*this); // set param version number to be up to date, so we ignore block contents mValueAge = VALUE_AUTHORITATIVE; mValue = val; - typed_param.clearValueName(); static_cast(this)->updateBlockFromValue(false); } - value_assignment_t getValue() const + const value_t& getValue() const { validateBlock(true); return mValue; @@ -2245,20 +2672,10 @@ namespace LLInitParam return mValue; } - operator value_assignment_t() const - { - return getValue(); - } - - value_assignment_t operator()() const - { - return getValue(); - } - protected: // use this from within updateValueFromBlock() to set the value without making it authoritative - void updateValue(value_assignment_t value) + void updateValue(const value_t& value) { mValue = value; } diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 353ef20e53..19864ee2ef 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -43,7 +43,7 @@ * semantics: one instance per process, rather than one instance per module as * sometimes happens with data simply declared static. */ -class LL_COMMON_API LLInstanceTrackerBase : public boost::noncopyable +class LL_COMMON_API LLInstanceTrackerBase { protected: /// Get a process-unique void* pointer slot for the specified type_info @@ -245,6 +245,9 @@ protected: virtual const KEY& getKey() const { return mInstanceKey; } private: + LLInstanceTracker( const LLInstanceTracker& ); + const LLInstanceTracker& operator=( const LLInstanceTracker& ); + void add_(KEY key) { mInstanceKey = key; diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 053347804b..195f9b4027 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -27,6 +27,7 @@ #define LLREFCOUNT_H #include +#include #include "nd/ndintrin.h" // For FAA/FAD @@ -104,4 +105,22 @@ private: #endif }; +/** + * intrusive pointer support + * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type + */ +namespace boost +{ + inline void intrusive_ptr_add_ref(LLRefCount* p) + { + p->ref(); + } + + inline void intrusive_ptr_release(LLRefCount* p) + { + p->unref(); + } +}; + + #endif diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 853c427a13..bb0d60247e 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -307,6 +307,10 @@ public: virtual ~StaticRegistrar() {} StaticRegistrar(ref_const_key_t key, ref_const_value_t value) { + if (singleton_t::instance().exists(key)) + { + llerrs << "Duplicate registry entry under key \"" << key << "\"" << llendl; + } singleton_t::instance().mStaticScope->add(key, value); } }; diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp index 0e29873bb0..9f4460a988 100644 --- a/indra/llcommon/llsdparam.cpp +++ b/indra/llcommon/llsdparam.cpp @@ -223,10 +223,14 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser: { bool new_traversal = it->second; - LLSD* child_sd = it->first.empty() ? sd_to_write : &(*sd_to_write)[it->first]; - - if (child_sd->isArray()) + LLSD* child_sd; + if (it->first.empty()) { + child_sd = sd_to_write; + if (child_sd->isUndefined()) + { + *child_sd = LLSD::emptyArray(); + } if (new_traversal) { // write to new element at end @@ -240,22 +244,7 @@ LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser: } else { - if (new_traversal - && child_sd->isDefined() - && !child_sd->isArray()) - { - // copy child contents into first element of an array - LLSD new_array = LLSD::emptyArray(); - new_array.append(*child_sd); - // assign array to slot that previously held the single value - *child_sd = new_array; - // return next element in that array - sd_to_write = &((*child_sd)[1]); - } - else - { - sd_to_write = child_sd; - } + sd_to_write = &(*sd_to_write)[it->first]; } it->second = false; } @@ -283,8 +272,9 @@ void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLI it != sd.endArray(); ++it) { - stack.back().second = true; + stack.push_back(make_pair(std::string(), true)); readSDValues(cb, *it, stack); + stack.pop_back(); } } else if (sd.isUndefined()) @@ -313,8 +303,14 @@ namespace LLInitParam { // LLSD specialization // block param interface - bool ParamValue, false>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name) + bool ParamValue::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name) { + if (name_stack.first == name_stack.second + && p.readValue(mValue)) + { + return true; + } + LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); LLSD::String string; @@ -328,15 +324,18 @@ namespace LLInitParam } //static - void ParamValue, false>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) + void ParamValue::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) { p.writeValue(sd.asString(), name_stack); } - void ParamValue, false>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const + void ParamValue::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const { - // read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) - Parser::name_stack_t stack; - LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, stack); + // attempt to write LLSD out directly + if (!p.writeValue(mValue, name_stack)) + { + // otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) + LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack); + } } } diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 5c8bbca2ca..0fb89c5613 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -30,6 +30,7 @@ #include "llapp.h" #include "llapr.h" #include "apr_thread_cond.h" +#include "boost/intrusive_ptr.hpp" class LLThread; class LLMutex; @@ -284,6 +285,22 @@ private: S32 mRef; }; +/** + * intrusive pointer support for LLThreadSafeRefCount + * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type + */ +namespace boost +{ + inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p) + { + p->ref(); + } + + inline void intrusive_ptr_release(LLThreadSafeRefCount* p) + { + p->unref(); + } +}; //============================================================================ // Simple responder for self destructing callbacks diff --git a/indra/llcommon/llversionviewer.h.in b/indra/llcommon/llversionviewer.h.in index 652d9bc68f..9e54c5a231 100644 --- a/indra/llcommon/llversionviewer.h.in +++ b/indra/llcommon/llversionviewer.h.in @@ -28,8 +28,8 @@ #define LL_LLVERSIONVIEWER_H const S32 LL_VERSION_MAJOR = 3; -const S32 LL_VERSION_MINOR = 4; -const S32 LL_VERSION_PATCH = 6; +const S32 LL_VERSION_MINOR = 5; +const S32 LL_VERSION_PATCH = 1; const S32 LL_VERSION_BUILD = 264760; const char * const LL_CHANNEL = "Firestorm-private"; diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h index 40b3364b36..efcbe76795 100644 --- a/indra/llcommon/stdenums.h +++ b/indra/llcommon/stdenums.h @@ -51,7 +51,8 @@ enum EDragAndDropType DAD_LINK = 14, DAD_MESH = 15, DAD_WIDGET = 16, - DAD_COUNT = 17, // number of types in this enum + DAD_PERSON = 17, + DAD_COUNT = 18, // number of types in this enum }; // Reasons for drags to be denied. diff --git a/indra/llimage/CMakeLists.txt b/indra/llimage/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/indra/llinventory/CMakeLists.txt b/indra/llinventory/CMakeLists.txt index 242bac6ccb..3aa22ca2c5 100644 --- a/indra/llinventory/CMakeLists.txt +++ b/indra/llinventory/CMakeLists.txt @@ -21,10 +21,6 @@ include_directories(SYSTEM ${LLCOMMON_SYSTEM_INCLUDE_DIRS} ${LLXML_SYSTEM_INCLUDE_DIRS} ) -include_directories(SYSTEM - ${LLCOMMON_SYSTEM_INCLUDE_DIRS} - ${LLXML_SYSTEM_INCLUDE_DIRS} - ) set(llinventory_SOURCE_FILES llcategory.cpp @@ -68,12 +64,6 @@ list(APPEND llinventory_SOURCE_FILES ${llinventory_HEADER_FILES}) add_library (llinventory ${llinventory_SOURCE_FILES}) -target_link_libraries(llinventory - ${LLCOMMON_LIBRARIES} - ${LLMATH_LIBRARIES} - ${LLMESSAGE_LIBRARIES} - ${LLXML_LIBRARIES} - ) #add unit tests diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 41ebe3998e..90246d23e9 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -75,13 +75,15 @@ LLInventoryObject::LLInventoryObject(const LLUUID& uuid, mUUID(uuid), mParentUUID(parent_uuid), mType(type), - mName(name) + mName(name), + mCreationDate(0) { correctInventoryName(mName); } LLInventoryObject::LLInventoryObject() : - mType(LLAssetType::AT_NONE) + mType(LLAssetType::AT_NONE), + mCreationDate(0) { } @@ -275,6 +277,26 @@ void LLInventoryObject::correctInventoryName(std::string& name) LLStringUtil::truncate(name, DB_INV_ITEM_NAME_STR_LEN); } +time_t LLInventoryObject::getCreationDate() const +{ + return mCreationDate; +} + +void LLInventoryObject::setCreationDate(time_t creation_date_utc) +{ + mCreationDate = creation_date_utc; +} + + +const std::string& LLInventoryItem::getDescription() const +{ + return mDescription; +} + +const std::string& LLInventoryItem::getActualDescription() const +{ + return mDescription; +} ///---------------------------------------------------------------------------- /// Class LLInventoryItem @@ -297,9 +319,10 @@ LLInventoryItem::LLInventoryItem(const LLUUID& uuid, mDescription(desc), mSaleInfo(sale_info), mInventoryType(inv_type), - mFlags(flags), - mCreationDate(creation_date_utc) + mFlags(flags) { + mCreationDate = creation_date_utc; + LLStringUtil::replaceNonstandardASCII(mDescription, ' '); LLStringUtil::replaceChar(mDescription, '|', ' '); mPermissions.initMasks(inv_type); @@ -312,9 +335,9 @@ LLInventoryItem::LLInventoryItem() : mDescription(), mSaleInfo(), mInventoryType(LLInventoryType::IT_NONE), - mFlags(0), - mCreationDate(0) + mFlags(0) { + mCreationDate = 0; } LLInventoryItem::LLInventoryItem(const LLInventoryItem* other) : @@ -374,21 +397,6 @@ void LLInventoryItem::setAssetUUID(const LLUUID& asset_id) } -const std::string& LLInventoryItem::getDescription() const -{ - return mDescription; -} - -const std::string& LLInventoryItem::getActualDescription() const -{ - return mDescription; -} - -time_t LLInventoryItem::getCreationDate() const -{ - return mCreationDate; -} - U32 LLInventoryItem::getCRC32() const { // *FIX: Not a real crc - more of a checksum. @@ -445,11 +453,6 @@ void LLInventoryItem::setFlags(U32 flags) mFlags = flags; } -void LLInventoryItem::setCreationDate(time_t creation_date_utc) -{ - mCreationDate = creation_date_utc; -} - // Currently only used in the Viewer to handle calling cards // where the creator is actually used to store the target. void LLInventoryItem::setCreator(const LLUUID& creator) @@ -511,6 +514,12 @@ U32 LLInventoryItem::getFlags() const return mFlags; } +time_t LLInventoryItem::getCreationDate() const +{ + return mCreationDate; +} + + // virtual void LLInventoryItem::packMessage(LLMessageSystem* msg) const { @@ -933,7 +942,7 @@ BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) } else if(0 == strcmp("permissions", keyword)) { - success = mPermissions.importStream(input_stream); + success = mPermissions.importLegacyStream(input_stream); } else if(0 == strcmp("sale_info", keyword)) { @@ -943,7 +952,7 @@ BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) // should pick up the vast majority of the tasks. BOOL has_perm_mask = FALSE; U32 perm_mask = 0; - success = mSaleInfo.importStream(input_stream, has_perm_mask, perm_mask); + success = mSaleInfo.importLegacyStream(input_stream, has_perm_mask, perm_mask); if(has_perm_mask) { if(perm_mask == PERM_NONE) @@ -1059,7 +1068,7 @@ BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL inclu output_stream << "\t\titem_id\t" << uuid_str << "\n"; mParentUUID.toString(uuid_str); output_stream << "\t\tparent_id\t" << uuid_str << "\n"; - mPermissions.exportStream(output_stream); + mPermissions.exportLegacyStream(output_stream); // Check for permissions to see the asset id, and if so write it // out as an asset id. Otherwise, apply our cheesy encryption. @@ -1093,7 +1102,7 @@ BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL inclu std::string buffer; buffer = llformat( "\t\tflags\t%08x\n", mFlags); output_stream << buffer; - mSaleInfo.exportStream(output_stream); + mSaleInfo.exportLegacyStream(output_stream); output_stream << "\t\tname\t" << mName.c_str() << "|\n"; output_stream << "\t\tdesc\t" << mDescription.c_str() << "|\n"; output_stream << "\t\tcreation_date\t" << mCreationDate << "\n"; diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index a88894da92..99716ed7be 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -73,6 +73,7 @@ public: virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items BOOL getIsLinkType() const; + virtual time_t getCreationDate() const; //-------------------------------------------------------------------- // Mutators @@ -83,6 +84,7 @@ public: virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); void setType(LLAssetType::EType type); + virtual void setCreationDate(time_t creation_date_utc); // only stored for items private: // in place correction for inventory name string @@ -111,6 +113,7 @@ protected: LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL. LLAssetType::EType mType; std::string mName; + time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,7 +179,6 @@ public: void setPermissions(const LLPermissions& perm); void setInventoryType(LLInventoryType::EType inv_type); void setFlags(U32 flags); - void setCreationDate(time_t creation_date_utc); void setCreator(const LLUUID& creator); // only used for calling cards // Check for changes in permissions masks and sale info @@ -222,7 +224,6 @@ protected: LLSaleInfo mSaleInfo; LLInventoryType::EType mInventoryType; U32 mFlags; - time_t mCreationDate; // seconds from 1/1/1970, UTC }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/indra/llinventory/llinventorytype.cpp b/indra/llinventory/llinventorytype.cpp index 8282d79b67..8807b36117 100644 --- a/indra/llinventory/llinventorytype.cpp +++ b/indra/llinventory/llinventorytype.cpp @@ -85,6 +85,7 @@ LLInventoryDictionary::LLInventoryDictionary() addEntry(LLInventoryType::IT_GESTURE, new InventoryEntry("gesture", "gesture", 1, LLAssetType::AT_GESTURE)); addEntry(LLInventoryType::IT_MESH, new InventoryEntry("mesh", "mesh", 1, LLAssetType::AT_MESH)); addEntry(LLInventoryType::IT_WIDGET, new InventoryEntry("widget", "widget", 1, LLAssetType::AT_WIDGET)); + addEntry(LLInventoryType::IT_PERSON, new InventoryEntry("person", "person", 1, LLAssetType::AT_PERSON)); } @@ -140,7 +141,7 @@ DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] = LLInventoryType::IT_NONE, // 42 AT_NONE LLInventoryType::IT_NONE, // 43 AT_NONE LLInventoryType::IT_NONE, // 44 AT_NONE - LLInventoryType::IT_NONE, // 45 AT_NONE + LLInventoryType::IT_PERSON, // 45 AT_PERSON LLInventoryType::IT_NONE, // 46 AT_NONE LLInventoryType::IT_NONE, // 47 AT_NONE LLInventoryType::IT_NONE, // 48 AT_NONE diff --git a/indra/llinventory/llinventorytype.h b/indra/llinventory/llinventorytype.h index 078b773932..fc3c78cf50 100644 --- a/indra/llinventory/llinventorytype.h +++ b/indra/llinventory/llinventorytype.h @@ -63,7 +63,8 @@ public: IT_GESTURE = 20, IT_MESH = 22, IT_WIDGET = 23, - IT_COUNT = 24, + IT_PERSON = 24, + IT_COUNT = 25, IT_NONE = -1 }; diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp index be297ffbc1..4b6d0f74d4 100644 --- a/indra/llinventory/llpermissions.cpp +++ b/indra/llinventory/llpermissions.cpp @@ -577,17 +577,143 @@ int splitCacheLine( char *aBuffer, char *&aKeyword, char *&aValue ); BOOL LLPermissions::importFile(LLFILE* fp) { - llifstream ifs(fp); - return importStream(ifs); + init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); + const S32 BUFSIZE = 16384; + + // *NOTE: Changing the buffer size will require changing the scanf + // calls below. + char buffer[BUFSIZE]; /* Flawfinder: ignore */ + char keyword[256]; /* Flawfinder: ignore */ + char valuestr[256]; /* Flawfinder: ignore */ + char uuid_str[256]; /* Flawfinder: ignore */ + U32 mask; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + + while (!feof(fp)) + { + if (fgets(buffer, BUFSIZE, fp) == NULL) + { + buffer[0] = '\0'; + } + + sscanf( /* Flawfinder: ignore */ + buffer, + " %255s %255s", + keyword, valuestr); + if (!strcmp("{", keyword)) + { + continue; + } + if (!strcmp("}",keyword)) + { + break; + } + else if (!strcmp("creator_mask", keyword)) + { + // legacy support for "creator" masks + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + fixFairUse(); + } + else if (!strcmp("base_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskBase = mask; + //fixFairUse(); + } + else if (!strcmp("owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskOwner = mask; + } + else if (!strcmp("group_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskGroup = mask; + } + else if (!strcmp("everyone_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskEveryone = mask; + } + else if (!strcmp("next_owner_mask", keyword)) + { + sscanf(valuestr, "%x", &mask); + mMaskNextOwner = mask; + } + else if (!strcmp("creator_id", keyword)) + { + sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ + mCreator.set(uuid_str); + } + else if (!strcmp("owner_id", keyword)) + { + sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ + mOwner.set(uuid_str); + } + else if (!strcmp("last_owner_id", keyword)) + { + sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ + mLastOwner.set(uuid_str); + } + else if (!strcmp("group_id", keyword)) + { + sscanf(valuestr, "%255s", uuid_str); /* Flawfinder: ignore */ + mGroup.set(uuid_str); + } + else if (!strcmp("group_owned", keyword)) + { + sscanf(valuestr, "%d", &mask); + if(mask) mIsGroupOwned = true; + else mIsGroupOwned = false; + } + else + { + llinfos << "unknown keyword " << keyword << " in permissions import" << llendl; + } + } + fix(); + return TRUE; } + BOOL LLPermissions::exportFile(LLFILE* fp) const { - llofstream ofs(fp); - return exportStream(ofs); + std::string uuid_str; + + fprintf(fp, "\tpermissions 0\n"); + fprintf(fp, "\t{\n"); + + fprintf(fp, "\t\tbase_mask\t%08x\n", mMaskBase); + fprintf(fp, "\t\towner_mask\t%08x\n", mMaskOwner); + fprintf(fp, "\t\tgroup_mask\t%08x\n", mMaskGroup); + fprintf(fp, "\t\teveryone_mask\t%08x\n", mMaskEveryone); + fprintf(fp, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner); + + mCreator.toString(uuid_str); + fprintf(fp, "\t\tcreator_id\t%s\n", uuid_str.c_str()); + + mOwner.toString(uuid_str); + fprintf(fp, "\t\towner_id\t%s\n", uuid_str.c_str()); + + mLastOwner.toString(uuid_str); + fprintf(fp, "\t\tlast_owner_id\t%s\n", uuid_str.c_str()); + + mGroup.toString(uuid_str); + fprintf(fp, "\t\tgroup_id\t%s\n", uuid_str.c_str()); + + if(mIsGroupOwned) + { + fprintf(fp, "\t\tgroup_owned\t1\n"); + } + fprintf(fp,"\t}\n"); + return TRUE; } -BOOL LLPermissions::importStream(std::istream& input_stream) + +BOOL LLPermissions::importLegacyStream(std::istream& input_stream) { init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null); const S32 BUFSIZE = 16384; @@ -606,18 +732,6 @@ BOOL LLPermissions::importStream(std::istream& input_stream) while (input_stream.good()) { input_stream.getline(buffer, BUFSIZE); - if (input_stream.eof()) - { - llwarns << "Bad permissions: early end of input stream" - << llendl; - return FALSE; - } - if (input_stream.fail()) - { - llwarns << "Bad permissions: failed to read from input stream" - << llendl; - return FALSE; - } sscanf( /* Flawfinder: ignore */ buffer, " %255s %255s", @@ -691,8 +805,7 @@ BOOL LLPermissions::importStream(std::istream& input_stream) } else { - llwarns << "unknown keyword " << keyword - << " in permissions import" << llendl; + llinfos << "unknown keyword " << keyword << " in permissions import" << llendl; } } fix(); @@ -700,26 +813,36 @@ BOOL LLPermissions::importStream(std::istream& input_stream) } -BOOL LLPermissions::exportStream(std::ostream& output_stream) const +BOOL LLPermissions::exportLegacyStream(std::ostream& output_stream) const { - if (!output_stream.good()) return FALSE; + std::string uuid_str; + output_stream << "\tpermissions 0\n"; output_stream << "\t{\n"; - char prev_fill = output_stream.fill('0'); - output_stream << std::hex; - output_stream << "\t\tbase_mask\t" << std::setw(8) << mMaskBase << "\n"; - output_stream << "\t\towner_mask\t" << std::setw(8) << mMaskOwner << "\n"; - output_stream << "\t\tgroup_mask\t" << std::setw(8) << mMaskGroup << "\n"; - output_stream << "\t\teveryone_mask\t" << std::setw(8) << mMaskEveryone << "\n"; - output_stream << "\t\tnext_owner_mask\t" << std::setw(8) << mMaskNextOwner << "\n"; - output_stream << std::dec; - output_stream.fill(prev_fill); + std::string buffer; + buffer = llformat( "\t\tbase_mask\t%08x\n", mMaskBase); + output_stream << buffer; + buffer = llformat( "\t\towner_mask\t%08x\n", mMaskOwner); + output_stream << buffer; + buffer = llformat( "\t\tgroup_mask\t%08x\n", mMaskGroup); + output_stream << buffer; + buffer = llformat( "\t\teveryone_mask\t%08x\n", mMaskEveryone); + output_stream << buffer; + buffer = llformat( "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner); + output_stream << buffer; - output_stream << "\t\tcreator_id\t" << mCreator << "\n"; - output_stream << "\t\towner_id\t" << mOwner << "\n"; - output_stream << "\t\tlast_owner_id\t" << mLastOwner << "\n"; - output_stream << "\t\tgroup_id\t" << mGroup << "\n"; + mCreator.toString(uuid_str); + output_stream << "\t\tcreator_id\t" << uuid_str << "\n"; + + mOwner.toString(uuid_str); + output_stream << "\t\towner_id\t" << uuid_str << "\n"; + + mLastOwner.toString(uuid_str); + output_stream << "\t\tlast_owner_id\t" << uuid_str << "\n"; + + mGroup.toString(uuid_str); + output_stream << "\t\tgroup_id\t" << uuid_str << "\n"; if(mIsGroupOwned) { diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h index 7d3a68d353..3ecc922370 100644 --- a/indra/llinventory/llpermissions.h +++ b/indra/llinventory/llpermissions.h @@ -316,8 +316,8 @@ public: BOOL importFile(LLFILE* fp); BOOL exportFile(LLFILE* fp) const; - BOOL importStream(std::istream& input_stream); - BOOL exportStream(std::ostream& output_stream) const; + BOOL importLegacyStream(std::istream& input_stream); + BOOL exportLegacyStream(std::ostream& output_stream) const; bool operator==(const LLPermissions &rhs) const; bool operator!=(const LLPermissions &rhs) const; diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp index a829667933..dd408a8efe 100644 --- a/indra/llinventory/llsaleinfo.cpp +++ b/indra/llinventory/llsaleinfo.cpp @@ -81,13 +81,15 @@ U32 LLSaleInfo::getCRC32() const BOOL LLSaleInfo::exportFile(LLFILE* fp) const { - llofstream ofs(fp); - return exportStream(ofs); + fprintf(fp, "\tsale_info\t0\n\t{\n"); + fprintf(fp, "\t\tsale_type\t%s\n", lookup(mSaleType)); + fprintf(fp, "\t\tsale_price\t%d\n", mSalePrice); + fprintf(fp,"\t}\n"); + return TRUE; } -BOOL LLSaleInfo::exportStream(std::ostream& output_stream) const +BOOL LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const { - if (!output_stream.good()) return FALSE; output_stream << "\tsale_info\t0\n\t{\n"; output_stream << "\t\tsale_type\t" << lookup(mSaleType) << "\n"; output_stream << "\t\tsale_price\t" << mSalePrice << "\n"; @@ -131,40 +133,25 @@ bool LLSaleInfo::fromLLSD(const LLSD& sd, BOOL& has_perm_mask, U32& perm_mask) // because I can't find any non-test code references to it. 2009-05-04 JC BOOL LLSaleInfo::importFile(LLFILE* fp, BOOL& has_perm_mask, U32& perm_mask) -{ - llifstream ifs(fp); - return importStream(ifs, has_perm_mask, perm_mask); -} - -BOOL LLSaleInfo::importStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask) { has_perm_mask = FALSE; - const S32 BUFSIZE = 16384; - // *NOTE: Changing the buffer size will require changing the scanf // calls below. - char buffer[BUFSIZE]; /* Flawfinder: ignore */ - char keyword[255]; /* Flawfinder: ignore */ - char valuestr[255]; /* Flawfinder: ignore */ + char buffer[MAX_STRING]; /* Flawfinder: ignore */ + char keyword[MAX_STRING]; /* Flawfinder: ignore */ + char valuestr[MAX_STRING]; /* Flawfinder: ignore */ + BOOL success = TRUE; keyword[0] = '\0'; valuestr[0] = '\0'; - while(input_stream.good()) + while(success && (!feof(fp))) { - input_stream.getline(buffer, MAX_STRING); - if (input_stream.eof()) + if (fgets(buffer, MAX_STRING, fp) == NULL) { - llwarns << "Bad sale info: early end of input stream" - << llendl; - return FALSE; - } - if (input_stream.fail()) - { - llwarns << "Bad sale info: failed to read from input stream" - << llendl; - return FALSE; + buffer[0] = '\0'; } + sscanf( /* Flawfinder: ignore */ buffer, " %254s %254s", @@ -202,7 +189,63 @@ BOOL LLSaleInfo::importStream(std::istream& input_stream, BOOL& has_perm_mask, U << "' in sale info import" << llendl; } } - return TRUE; + return success; +} + +BOOL LLSaleInfo::importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask) +{ + has_perm_mask = FALSE; + + // *NOTE: Changing the buffer size will require changing the scanf + // calls below. + char buffer[MAX_STRING]; /* Flawfinder: ignore */ + char keyword[MAX_STRING]; /* Flawfinder: ignore */ + char valuestr[MAX_STRING]; /* Flawfinder: ignore */ + BOOL success = TRUE; + + keyword[0] = '\0'; + valuestr[0] = '\0'; + while(success && input_stream.good()) + { + input_stream.getline(buffer, MAX_STRING); + sscanf( /* Flawfinder: ignore */ + buffer, + " %254s %254s", + keyword, valuestr); + if(!keyword[0]) + { + continue; + } + if(0 == strcmp("{",keyword)) + { + continue; + } + if(0 == strcmp("}", keyword)) + { + break; + } + else if(0 == strcmp("sale_type", keyword)) + { + mSaleType = lookup(valuestr); + } + else if(0 == strcmp("sale_price", keyword)) + { + sscanf(valuestr, "%d", &mSalePrice); + mSalePrice = llclamp(mSalePrice, 0, S32_MAX); + } + else if (!strcmp("perm_mask", keyword)) + { + //llinfos << "found deprecated keyword perm_mask" << llendl; + has_perm_mask = TRUE; + sscanf(valuestr, "%x", &perm_mask); + } + else + { + llwarns << "unknown keyword '" << keyword + << "' in sale info import" << llendl; + } + } + return success; } void LLSaleInfo::setSalePrice(S32 price) diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h index f7f3f9269c..4e98ccf6ff 100644 --- a/indra/llinventory/llsaleinfo.h +++ b/indra/llinventory/llsaleinfo.h @@ -89,11 +89,11 @@ public: BOOL exportFile(LLFILE* fp) const; BOOL importFile(LLFILE* fp, BOOL& has_perm_mask, U32& perm_mask); - BOOL exportStream(std::ostream& output_stream) const; + BOOL exportLegacyStream(std::ostream& output_stream) const; LLSD asLLSD() const; operator LLSD() const { return asLLSD(); } bool fromLLSD(const LLSD& sd, BOOL& has_perm_mask, U32& perm_mask); - BOOL importStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask); + BOOL importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask); LLSD packMessage() const; void unpackMessage(LLSD sales); diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index 16de14099d..5e110f8b49 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -44,10 +44,6 @@ namespace LLAvatarNameCache { use_display_name_signal_t mUseDisplayNamesSignal; - // Manual override for display names - can disable even if the region - // supports it. - bool sUseDisplayNames = true; - // [RLVa:KB] - Checked: 2010-12-08 (RLVa-1.4.0a) | Added: RLVa-1.2.2c // RLVa override for display names bool sForceDisplayNames = false; @@ -57,18 +53,22 @@ namespace LLAvatarNameCache // current region supports display names. bool sRunning = false; + // Use the People API (modern) for fetching name if true. Use the old legacy protocol if false. + // For testing, there's a UsePeopleAPI setting that can be flipped (must restart viewer). + bool sUsePeopleAPI = true; + // Base lookup URL for name service. // On simulator, loaded from indra.xml // On viewer, usually a simulator capability (at People API team's request) // Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/" std::string sNameLookupURL; - // accumulated agent IDs for next query against service + // Accumulated agent IDs for next query against service typedef std::set ask_queue_t; ask_queue_t sAskQueue; - // agent IDs that have been requested, but with no reply - // maps agent ID to frame time request was made + // Agent IDs that have been requested, but with no reply. + // Maps agent ID to frame time request was made. typedef std::map pending_queue_t; pending_queue_t sPendingQueue; @@ -79,21 +79,21 @@ namespace LLAvatarNameCache typedef std::map signal_map_t; signal_map_t sSignalMap; - // names we know about + // The cache at last, i.e. avatar names we know about. typedef std::map cache_t; cache_t sCache; - // Send bulk lookup requests a few times a second at most - // only need per-frame timing resolution + // Send bulk lookup requests a few times a second at most. + // Only need per-frame timing resolution. LLFrameTimer sRequestTimer; - /// Maximum time an unrefreshed cache entry is allowed + // Maximum time an unrefreshed cache entry is allowed. const F64 MAX_UNREFRESHED_TIME = 20.0 * 60.0; - /// Time when unrefreshed cached names were checked last + // Time when unrefreshed cached names were checked last. static F64 sLastExpireCheck; - /// Time-to-live for a temp cache entry. + // Time-to-live for a temp cache entry. const F64 TEMP_CACHE_ENTRY_LIFETIME = 60.0; //----------------------------------------------------------------------- @@ -101,26 +101,21 @@ namespace LLAvatarNameCache //----------------------------------------------------------------------- // Handle name response off network. - // Optionally skip adding to cache, used when this is a fallback to the - // legacy name system. void processName(const LLUUID& agent_id, - const LLAvatarName& av_name, - bool add_to_cache); + const LLAvatarName& av_name); void requestNamesViaCapability(); - // Legacy name system callback + // Legacy name system callbacks void legacyNameCallback(const LLUUID& agent_id, const std::string& full_name, - bool is_group - ); - + bool is_group); + void legacyNameFetch(const LLUUID& agent_id, + const std::string& full_name, + bool is_group); + void requestNamesViaLegacy(); - // Fill in an LLAvatarName with the legacy name data - void buildLegacyName(const std::string& full_name, - LLAvatarName* av_name); - // Do a single callback to a given slot void fireSignal(const LLUUID& agent_id, const callback_slot_t& slot, @@ -215,20 +210,11 @@ public: // Use expiration time from header av_name.mExpires = expires; - // Some avatars don't have explicit display names set - if (av_name.mDisplayName.empty()) - { - av_name.mDisplayName = av_name.mUsername; - } - - LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << " " - << "user '" << av_name.mUsername << "' " - << "display '" << av_name.mDisplayName << "' " - << "expires in " << expires - now << " seconds" - << LL_ENDL; + LL_DEBUGS("AvNameCache") << "LLAvatarNameResponder::result for " << agent_id << LL_ENDL; + av_name.dump(); // cache it and fire signals - LLAvatarNameCache::processName(agent_id, av_name, true); + LLAvatarNameCache::processName(agent_id, av_name); } // Same logic as error response case @@ -294,8 +280,7 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) LL_WARNS("AvNameCache") << "LLAvatarNameCache get legacy for agent " << agent_id << LL_ENDL; gCacheName->get(agent_id, false, // legacy compatibility - boost::bind(&LLAvatarNameCache::legacyNameCallback, - _1, _2, _3)); + boost::bind(&LLAvatarNameCache::legacyNameFetch, _1, _2, _3)); } else { @@ -306,28 +291,23 @@ void LLAvatarNameCache::handleAgentError(const LLUUID& agent_id) LLAvatarNameCache::sPendingQueue.erase(agent_id); LLAvatarName& av_name = existing->second; - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " - << agent_id - << "user '" << av_name.mUsername << "' " - << "display '" << av_name.mDisplayName << "' " - << "expires in " << av_name.mExpires - LLFrameTimer::getTotalSeconds() << " seconds" - << LL_ENDL; - av_name.mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; // reset expiry time so we don't constantly rerequest. + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache use cache for agent " << agent_id << LL_ENDL; + av_name.dump(); + + // Reset expiry time so we don't constantly rerequest. + av_name.setExpires(TEMP_CACHE_ENTRY_LIFETIME); } } -void LLAvatarNameCache::processName(const LLUUID& agent_id, - const LLAvatarName& av_name, - bool add_to_cache) +void LLAvatarNameCache::processName(const LLUUID& agent_id, const LLAvatarName& av_name) { - if (add_to_cache) - { - sCache[agent_id] = av_name; - } + // Add to the cache + sCache[agent_id] = av_name; + // Suppress request from the queue sPendingQueue.erase(agent_id); - // signal everyone waiting on this name + // Signal everyone waiting on this name signal_map_t::iterator sig_it = sSignalMap.find(agent_id); if (sig_it != sSignalMap.end()) { @@ -404,22 +384,33 @@ void LLAvatarNameCache::legacyNameCallback(const LLUUID& agent_id, const std::string& full_name, bool is_group) { - // Construct a dummy record for this name. By convention, SLID is blank - // Never expires, but not written to disk, so lasts until end of session. - LLAvatarName av_name; - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::legacyNameCallback " - << "agent " << agent_id << " " - << "full name '" << full_name << "'" - << ( is_group ? " [group]" : "" ) - << LL_ENDL; - buildLegacyName(full_name, &av_name); + // Put the received data in the cache + legacyNameFetch(agent_id, full_name, is_group); + + // Retrieve the name and set it to never (or almost never...) expire: when we are using the legacy + // protocol, we do not get an expiration date for each name and there's no reason to ask the + // data again and again so we set the expiration time to the largest value admissible. + std::map::iterator av_record = sCache.find(agent_id); + LLAvatarName& av_name = av_record->second; + av_name.setExpires(MAX_UNREFRESHED_TIME); +} - // Add to cache, because if we don't we'll keep rerequesting the - // same record forever. buildLegacyName should always guarantee - // that these records expire reasonably soon - // (in TEMP_CACHE_ENTRY_LIFETIME seconds), so if the failure was due - // to something temporary we will eventually request and get the right data. - processName(agent_id, av_name, true); +void LLAvatarNameCache::legacyNameFetch(const LLUUID& agent_id, + const std::string& full_name, + bool is_group) +{ + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::legacyNameFetch " + << "agent " << agent_id << " " + << "full name '" << full_name << "'" + << ( is_group ? " [group]" : "" ) + << LL_ENDL; + + // Construct an av_name record from this name. + LLAvatarName av_name; + av_name.fromString(full_name); + + // Add to cache: we're still using the new cache even if we're using the old (legacy) protocol. + processName(agent_id, av_name); } void LLAvatarNameCache::requestNamesViaLegacy() @@ -441,20 +432,19 @@ void LLAvatarNameCache::requestNamesViaLegacy() LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::requestNamesViaLegacy agent " << agent_id << LL_ENDL; gCacheName->get(agent_id, false, // legacy compatibility - boost::bind(&LLAvatarNameCache::legacyNameCallback, - _1, _2, _3)); + boost::bind(&LLAvatarNameCache::legacyNameCallback, _1, _2, _3)); } } -void LLAvatarNameCache::initClass(bool running) +void LLAvatarNameCache::initClass(bool running, bool usePeopleAPI) { sRunning = running; - //[Caching DN : SJ] Check if caching names is running - llinfos << "Caching DN : sRunning = " << sRunning << llendl; + sUsePeopleAPI = usePeopleAPI; } void LLAvatarNameCache::cleanupClass() { + sCache.clear(); } void LLAvatarNameCache::importFile(std::istream& istr) @@ -495,7 +485,7 @@ void LLAvatarNameCache::exportFile(std::ostream& ostr) const LLUUID& agent_id = it->first; const LLAvatarName& av_name = it->second; // Do not write temporary or expired entries to the stored cache - if (!av_name.mIsTemporaryName && av_name.mExpires >= max_unrefreshed) + if (av_name.isValidName(max_unrefreshed)) { // key must be a string agents[agent_id.asString()] = av_name.asLLSD(); @@ -516,6 +506,11 @@ bool LLAvatarNameCache::hasNameLookupURL() return !sNameLookupURL.empty(); } +bool LLAvatarNameCache::usePeopleAPI() +{ + return hasNameLookupURL() && sUsePeopleAPI; +} + void LLAvatarNameCache::idle() { // By convention, start running at first idle() call @@ -532,13 +527,12 @@ void LLAvatarNameCache::idle() if (!sAskQueue.empty()) { - if (useDisplayNames()) + if (usePeopleAPI()) { requestNamesViaCapability(); } else { - // ...fall back to legacy name cache system requestNamesViaLegacy(); } } @@ -586,7 +580,7 @@ void LLAvatarNameCache::eraseUnrefreshed() if (av_name.mExpires < max_unrefreshed) { LL_DEBUGS("AvNameCache") << it->first - << " user '" << av_name.mUsername << "' " + << " user '" << av_name.getAccountName() << "' " << "expired " << now - av_name.mExpires << " secs ago" << LL_ENDL; sCache.erase(it++); @@ -600,46 +594,6 @@ void LLAvatarNameCache::eraseUnrefreshed() } } -void LLAvatarNameCache::buildLegacyName(const std::string& full_name, - LLAvatarName* av_name) -{ - llassert(av_name); - av_name->mUsername = ""; - av_name->mDisplayName = full_name; - av_name->mIsDisplayNameDefault = true; - av_name->mIsTemporaryName = true; - av_name->mExpires = LLFrameTimer::getTotalSeconds() + TEMP_CACHE_ENTRY_LIFETIME; - - // Please leave this when merging! In case no display names - // are used, you would have to retrieve mDisplayName to get - // the legacy name - something that absolutely makes no - // sense. And getLegacyName(), which is supposed to return - // the legacy name, returns an empty string. By adding the - // next two lines, getLegacyName() will function properly - // in either display name or no display name case. - std::istringstream fname(full_name); - fname >> av_name->mLegacyFirstName >> av_name->mLegacyLastName; - // - - // Also fill username for in case of disabled display names. - // We need this for instance for FIRE-4297 - if (LLCacheName::sDontTrimLegacyNames && av_name->mLegacyLastName == "Resident") - { - av_name->mUsername = av_name->mLegacyFirstName; - } - else - { - av_name->mUsername = full_name; - } - LLStringUtil::toLower(av_name->mUsername); - LLStringUtil::replaceChar(av_name->mUsername, ' ', '.'); - // - - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::buildLegacyName " - << full_name - << LL_ENDL; -} - // fills in av_name if it has it in the cache, even if expired (can check expiry time) // returns bool specifying if av_name was filled, false otherwise bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) @@ -647,45 +601,31 @@ bool LLAvatarNameCache::get(const LLUUID& agent_id, LLAvatarName *av_name) if (sRunning) { // ...only do immediate lookups when cache is running - if (useDisplayNames()) + std::map::iterator it = sCache.find(agent_id); + if (it != sCache.end()) { - // ...use display names cache - std::map::iterator it = sCache.find(agent_id); - if (it != sCache.end()) + *av_name = it->second; + if(LGGContactSets::getInstance()->hasPseudonym(agent_id)) { - *av_name = it->second; - if(LGGContactSets::getInstance()->hasPseudonym(agent_id)) - { - LLSD info = av_name->asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id)?(info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) : LGGContactSets::getInstance()->getPseudonym(agent_id); - av_name->fromLLSD(info); - } + LLSD info = av_name->asLLSD(); + info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); + info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id)?(info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) : LGGContactSets::getInstance()->getPseudonym(agent_id); + av_name->fromLLSD(info); + } - // re-request name if entry is expired - if (av_name->mExpires < LLFrameTimer::getTotalSeconds()) - { - if (!isRequestPending(agent_id)) - { - LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get " - << "refresh agent " << agent_id - << LL_ENDL; - sAskQueue.insert(agent_id); - } - } - - return true; - } - } - else - { - // ...use legacy names cache - std::string full_name; - if (gCacheName->getFullName(agent_id, full_name)) + // re-request name if entry is expired + if (av_name->mExpires < LLFrameTimer::getTotalSeconds()) { - buildLegacyName(full_name, av_name); - return true; + if (!isRequestPending(agent_id)) + { + LL_DEBUGS("AvNameCache") << "LLAvatarNameCache::get " + << "refresh agent " << agent_id + << LL_ENDL; + sAskQueue.insert(agent_id); + } } + + return true; } } @@ -716,68 +656,37 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& ag if (sRunning) { // ...only do immediate lookups when cache is running - if (useDisplayNames()) + std::map::iterator it = sCache.find(agent_id); + if (it != sCache.end()) { - LL_DEBUGS("AvNameCache") << "DN cache lookup for " << agent_id << llendl; - // ...use new cache - std::map::iterator it = sCache.find(agent_id); - if (it != sCache.end()) - { - LLAvatarName& av_name = it->second; - LLSD test = av_name.asLLSD(); - LL_DEBUGS("AvNameCache") << "DN cache hit for :" << test["display_name"].asString()+" (" + test["legacy_first_name"].asString()+" "+test["legacy_last_name"].asString()+")" << llendl; - - if(LGGContactSets::getInstance()->hasPseudonym(agent_id)) - { - LL_DEBUGS("AvNameCache") << "DN cache hit via alias" << llendl; - LLSD info = av_name.asLLSD(); - info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); - info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id)?(info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) : LGGContactSets::getInstance()->getPseudonym(agent_id); - av_name.fromLLSD(info); - } - - if (av_name.mExpires > LLFrameTimer::getTotalSeconds()) - { - // ...name already exists in cache, fire callback now - LL_DEBUGS("AvNameCache") << "DN cache hit valid" << llendl; - fireSignal(agent_id, slot, av_name); - return connection; - } + LLAvatarName& av_name = it->second; + LLSD test = av_name.asLLSD(); + LL_DEBUGS("AvNameCache") << "DN cache hit for :" << test["display_name"].asString()+" (" + test["legacy_first_name"].asString()+" "+test["legacy_last_name"].asString()+")" << llendl; - // If we get here, our DN is expired. - //Consider it a cache miss, handle as if sRunning was false. - LL_DEBUGS("AvNameCache") << "DN cache hit expired. " << av_name.mExpires << " vs. " << LLFrameTimer::getTotalSeconds() << llendl; - } - //If we get here, cache miss. We'll schedule it below - - } - else - { - // ...use old name system - std::string full_name; - if (gCacheName->getFullName(agent_id, full_name)) + if(LGGContactSets::getInstance()->hasPseudonym(agent_id)) { - LLAvatarName av_name; - buildLegacyName(full_name, &av_name); + LL_DEBUGS("AvNameCache") << "DN cache hit via alias" << llendl; + LLSD info = av_name.asLLSD(); + info["is_display_name_default"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id); + info["display_name"] = LGGContactSets::getInstance()->hasDisplayNameRemoved(agent_id)?(info["legacy_first_name"].asString() + " " + info["legacy_last_name"].asString()) : LGGContactSets::getInstance()->getPseudonym(agent_id); + av_name.fromLLSD(info); + } + + if (av_name.mExpires > LLFrameTimer::getTotalSeconds()) + { + // ...name already exists in cache, fire callback now fireSignal(agent_id, slot, av_name); return connection; } } - } - + // schedule a request if (!isRequestPending(agent_id)) { - LL_DEBUGS("AvNameCache") << "DN scheduling lookup for" << agent_id << llendl; sAskQueue.insert(agent_id); } - else - { - LL_DEBUGS("AvNameCache") << "DN lookup for " << agent_id << " already in progress. Returning." << llendl; - } - // always store additional callback, even if request is pending signal_map_t::iterator sig_it = sSignalMap.find(agent_id); if (sig_it == sSignalMap.end()) @@ -806,9 +715,9 @@ bool LLAvatarNameCache::getForceDisplayNames() void LLAvatarNameCache::setForceDisplayNames(bool force) { sForceDisplayNames = force; - if ( (!sUseDisplayNames) && (force) ) + if ( (!LLAvatarName::useDisplayNames()) && (force) ) { - setUseDisplayNames(true); + LLAvatarName::setUseDisplayNames(true); } } // [/RLVa:KB] @@ -819,22 +728,14 @@ void LLAvatarNameCache::setUseDisplayNames(bool use) // We need to force the use of the "display names" cache when @shownames=n restricted (and disallow toggling it) use |= getForceDisplayNames(); // [/RLVa:KB] - if (use != sUseDisplayNames) - { - sUseDisplayNames = use; - // flush our cache - sCache.clear(); + if (use != LLAvatarName::useDisplayNames()) + { + LLAvatarName::setUseDisplayNames(use); mUseDisplayNamesSignal(); } } -bool LLAvatarNameCache::useDisplayNames() -{ - // Must be both manually set on and able to look up names. - return sUseDisplayNames && !sNameLookupURL.empty(); -} - // FIRE-6659: Legacy "Resident" name toggle void LLAvatarNameCache::clear() { diff --git a/indra/llmessage/llavatarnamecache.h b/indra/llmessage/llavatarnamecache.h index 92f93284ed..1609bff582 100644 --- a/indra/llmessage/llavatarnamecache.h +++ b/indra/llmessage/llavatarnamecache.h @@ -37,33 +37,33 @@ class LLUUID; namespace LLAvatarNameCache { - typedef boost::signals2::signal use_display_name_signal_t; // Until the cache is set running, immediate lookups will fail and // async lookups will be queued. This allows us to block requests // until we know if the first region supports display names. - void initClass(bool running); + void initClass(bool running, bool usePeopleAPI); void cleanupClass(); + // Import/export the name cache to file. void importFile(std::istream& istr); void exportFile(std::ostream& ostr); - // On the viewer, usually a simulator capabilitity - // If empty, name cache will fall back to using legacy name - // lookup system + // On the viewer, usually a simulator capabilitity. + // If empty, name cache will fall back to using legacy name lookup system. void setNameLookupURL(const std::string& name_lookup_url); - // Do we have a valid lookup URL, hence are we trying to use the - // new display name lookup system? + // Do we have a valid lookup URL, i.e. are we trying to use the + // more recent display name lookup system? bool hasNameLookupURL(); + bool usePeopleAPI(); // Periodically makes a batch request for display names not already in - // cache. Call once per frame. + // cache. Called once per frame. void idle(); // If name is in cache, returns true and fills in provided LLAvatarName - // otherwise returns false + // otherwise returns false. bool get(const LLUUID& agent_id, LLAvatarName *av_name); // Callback types for get() below @@ -73,13 +73,19 @@ namespace LLAvatarNameCache typedef callback_signal_t::slot_type callback_slot_t; typedef boost::signals2::connection callback_connection_t; - // Fetches name information and calls callback. - // If name information is in cache, callback will be called immediately. + // Fetches name information and calls callbacks. + // If name information is in cache, callbacks will be called immediately. callback_connection_t get(const LLUUID& agent_id, callback_slot_t slot); - // Allow display names to be explicitly disabled for testing. + // Set display name: flips the switch and triggers the callbacks. void setUseDisplayNames(bool use); - bool useDisplayNames(); + + void insert(const LLUUID& agent_id, const LLAvatarName& av_name); + +// [RLVa:KB] - Checked: 2010-12-08 (RLVa-1.4.0a) | Added: RLVa-1.2.2c + bool getForceDisplayNames(); + void setForceDisplayNames(bool force); +// [/RLVa:KB] // [RLVa:KB] - Checked: 2010-12-08 (RLVa-1.4.0a) | Added: RLVa-1.2.2c bool getForceDisplayNames(); @@ -91,15 +97,13 @@ namespace LLAvatarNameCache // FIRE-6659: Legacy "Resident" name toggle void erase(const LLUUID& agent_id); - /// Provide some fallback for agents that return errors + /// Provide some fallback for agents that return errors. void handleAgentError(const LLUUID& agent_id); // Force a re-fetch of the most recent data, but keep the current // data in cache void fetch(const LLUUID& agent_id); // FS:TM used in LGGContactSets - void insert(const LLUUID& agent_id, const LLAvatarName& av_name); - // Compute name expiration time from HTTP Cache-Control header, // or return default value, in seconds from epoch. F64 nameExpirationFromHeaders(LLSD headers); diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 749315c1c8..86245cc14c 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -566,6 +566,7 @@ std::string LLCacheName::cleanFullName(const std::string& full_name) } //static +// Transform hard-coded name provided by server to a more legible username std::string LLCacheName::buildUsername(const std::string& full_name) { // rare, but handle hard-coded error names returned from server @@ -605,8 +606,9 @@ std::string LLCacheName::buildUsername(const std::string& full_name) return username; } - // if the input wasn't a correctly formatted legacy name just return it unchanged - return full_name; + // if the input wasn't a correctly formatted legacy name, just return it + // cleaned up from a potential terminal "Resident" + return cleanFullName(full_name); } //static diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h index b06c68d612..04c2b6ea13 100644 --- a/indra/llmessage/llcachename.h +++ b/indra/llmessage/llcachename.h @@ -40,7 +40,7 @@ typedef boost::signals2::signal LLCacheNameSignal; typedef LLCacheNameSignal::slot_type LLCacheNameCallback; -// Old callback with user data for compatability +// Old callback with user data for compatibility typedef void (*old_callback_t)(const LLUUID&, const std::string&, bool, void*); // Here's the theory: diff --git a/indra/llmessage/lldbstrings.h b/indra/llmessage/lldbstrings.h index 9bf1b3eda4..e23d17d5b6 100644 --- a/indra/llmessage/lldbstrings.h +++ b/indra/llmessage/lldbstrings.h @@ -156,18 +156,6 @@ const S32 DB_USER_SKILLS_BUF_SIZE = 255; const S32 DB_NV_NAME_STR_LEN = 128; const S32 DB_NV_NAME_BUF_SIZE = 129; -// votes.vote_text varchar(254) -const S32 DB_VOTE_TEXT_STR_LEN = 254; -const S32 DB_VOTE_TEXT_BUF_SIZE = 255; - -// vpte type text varchar(9) -const S32 DB_VOTE_TYPE_STR_LEN = 9; -const S32 DB_VOTE_TYPE_BUF_SIZE = 10; - -// vote result text -const S32 DB_VOTE_RESULT_BUF_LEN = 8; -const S32 DB_VOTE_RESULT_BUF_SIZE = 9; - // user_start_location.location_name varchar(254) const S32 DB_START_LOCATION_STR_LEN = 254; const S32 DB_START_LOCATION_BUF_SIZE = 255; diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 6fad67ba50..5f08f9490e 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -223,7 +223,11 @@ static void request( { if (!LLHTTPClient::hasPump()) { - responder->completed(U32_MAX, "No pump", LLSD()); + if (responder) + { + responder->completed(U32_MAX, "No pump", LLSD()); + } + delete body_injector; return; } LLPumpIO::chain_t chain; @@ -231,8 +235,13 @@ static void request( LLURLRequest* req = new LLURLRequest(method, url); if(!req->isValid())//failed { - delete req ; - return ; + if (responder) + { + responder->completed(498, "Internal Error - curl failure", LLSD()); + } + delete req; + delete body_injector; + return; } req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req); diff --git a/indra/llmessage/llhttpclientadapter.cpp b/indra/llmessage/llhttpclientadapter.cpp index f5d7a9abb6..0b59209af1 100644 --- a/indra/llmessage/llhttpclientadapter.cpp +++ b/indra/llmessage/llhttpclientadapter.cpp @@ -43,8 +43,11 @@ void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr respo void LLHTTPClientAdapter::get(const std::string& url, LLCurl::ResponderPtr responder, const LLSD& headers) { LLSD empty_pragma_header = headers; - // as above - empty_pragma_header["Pragma"] = " "; + if (!empty_pragma_header.has("Pragma")) + { + // as above + empty_pragma_header["Pragma"] = " "; + } LLHTTPClient::get(url, responder, empty_pragma_header); } diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp index 66a02b1ddb..77c78296bb 100644 --- a/indra/llmessage/llinstantmessage.cpp +++ b/indra/llmessage/llinstantmessage.cpp @@ -43,14 +43,6 @@ const U8 IM_ONLINE = 0; const U8 IM_OFFLINE = 1; -const S32 VOTE_YES = 1; -const S32 VOTE_NO = 0; -const S32 VOTE_ABSTAIN = -1; - -const S32 VOTE_MAJORITY = 0; -const S32 VOTE_SUPER_MAJORITY = 1; -const S32 VOTE_UNANIMOUS = 2; - const char EMPTY_BINARY_BUCKET[] = ""; const S32 EMPTY_BINARY_BUCKET_SIZE = 1; const U32 NO_TIMESTAMP = 0; @@ -72,7 +64,6 @@ LLIMInfo::LLIMInfo() : mViewerThinksToIsOnline(false), mIMType(IM_NOTHING_SPECIAL), mTimeStamp(0), - mSource(IM_FROM_SIM), mTTL(IM_TTL) { } @@ -91,7 +82,6 @@ LLIMInfo::LLIMInfo( LLSD data, U8 offline, U32 timestamp, - EIMSource source, S32 ttl) : mFromID(from_id), mFromGroup(from_group), @@ -107,14 +97,12 @@ LLIMInfo::LLIMInfo( mName(name), mMessage(message), mData(data), - mSource(source), mTTL(ttl) { } -LLIMInfo::LLIMInfo(LLMessageSystem* msg, EIMSource source, S32 ttl) : +LLIMInfo::LLIMInfo(LLMessageSystem* msg, S32 ttl) : mViewerThinksToIsOnline(false), - mSource(source), mTTL(ttl) { unpackMessageBlock(msg); @@ -329,7 +317,6 @@ LLSD im_info_to_llsd(LLPointer im_info) param_message["region_id"] = im_info->mRegionID; param_message["position"] = ll_sd_from_vector3(im_info->mPosition); param_message["data"] = im_info->mData; - param_message["source"]= im_info->mSource; param_message["ttl"] = im_info->mTTL; LLSD param_agent; @@ -362,7 +349,6 @@ LLPointer llsd_to_im_info(const LLSD& im_info_sd) param_message["data"], (U8) param_message["offline"].asInteger(), (U32) param_message["timestamp"].asInteger(), - (EIMSource)param_message["source"].asInteger(), param_message["ttl"].asInteger()); return im_info; @@ -384,7 +370,6 @@ LLPointer LLIMInfo::clone() mData, mOffline, mTimeStamp, - mSource, mTTL); } diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h index c4b65062bf..f040e9b1b8 100644 --- a/indra/llmessage/llinstantmessage.h +++ b/indra/llmessage/llinstantmessage.h @@ -115,8 +115,8 @@ enum EInstantMessage // viewer, since you can't IM an object yet. IM_FROM_TASK = 19, - // sent an IM to a busy user, this is the auto response - IM_BUSY_AUTO_RESPONSE = 20, + // sent an IM to a do not disturb user, this is the auto response + IM_DO_NOT_DISTURB_AUTO_RESPONSE = 20, // Shows the message in the console and chat history IM_CONSOLE_AND_CHAT_HISTORY = 21, @@ -164,57 +164,9 @@ enum EInstantMessage }; -// Hooks for quickly hacking in experimental admin debug messages -// without needing to recompile the viewer -// *NOTE: This functionality has been moved to be a string based -// operation so that we don't even have to do a full recompile. This -// enumeration will be phased out soon. -enum EGodlikeRequest -{ - GOD_WANTS_NOTHING, - - // for requesting physics information about an object - GOD_WANTS_PHYSICS_INFO, - - // two unused requests that can be appropriated for debug - // purposes (no viewer recompile necessary) - GOD_WANTS_FOO, - GOD_WANTS_BAR, - - // to dump simulator terrain data to terrain.raw file - GOD_WANTS_TERRAIN_SAVE, - // to load simulator terrain data from terrain.raw file - GOD_WANTS_TERRAIN_LOAD, - - GOD_WANTS_TOGGLE_AVATAR_GEOMETRY, // HACK for testing new avatar geom - - // real-time telehub operations - GOD_WANTS_TELEHUB_INFO, - GOD_WANTS_CONNECT_TELEHUB, - GOD_WANTS_DELETE_TELEHUB, - GOD_WANTS_ADD_TELEHUB_SPAWNPOINT, - GOD_WANTS_REMOVE_TELEHUB_SPAWNPOINT, - -}; - -enum EIMSource -{ - IM_FROM_VIEWER, - IM_FROM_DATASERVER, - IM_FROM_SIM -}; - extern const U8 IM_ONLINE; extern const U8 IM_OFFLINE; -extern const S32 VOTE_YES; -extern const S32 VOTE_NO; -extern const S32 VOTE_ABSTAIN; - -extern const S32 VOTE_MAJORITY; -extern const S32 VOTE_SUPER_MAJORITY; -extern const S32 VOTE_UNANIMOUS; - extern const char EMPTY_BINARY_BUCKET[]; extern const S32 EMPTY_BINARY_BUCKET_SIZE; @@ -237,7 +189,6 @@ protected: public: LLIMInfo(LLMessageSystem* msg, - EIMSource source = IM_FROM_SIM, S32 ttl = IM_TTL); LLIMInfo( @@ -254,7 +205,6 @@ public: LLSD data, U8 offline, U32 timestamp, - EIMSource source, S32 ttl = IM_TTL); void packInstantMessage(LLMessageSystem* msg) const; @@ -277,7 +227,6 @@ public: std::string mMessage; LLSD mData; - EIMSource mSource; S32 mTTL; }; diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp old mode 100755 new mode 100644 diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h old mode 100755 new mode 100644 diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp old mode 100755 new mode 100644 diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 5809a0fa45..2418073f0e 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -598,6 +598,11 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); + + if (NULL == fgi) + { + return 0; + } } // account for glyphs that run beyond the starting point for the next glyphs diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index fc9307b7a2..185c3833dd 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -57,8 +57,13 @@ BOOL gDebugSession = FALSE; BOOL gDebugGL = FALSE; BOOL gClothRipple = FALSE; +BOOL gHeadlessClient = FALSE; BOOL gGLActive = FALSE; +static const std::string HEADLESS_VENDOR_STRING("Linden Lab"); +static const std::string HEADLESS_RENDERER_STRING("Headless"); +static const std::string HEADLESS_VERSION_STRING("1.0"); + std::ofstream gFailLog; #if GL_ARB_debug_output @@ -388,7 +393,7 @@ PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL; PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL; PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements = NULL; #endif // LL_LINUX_NV_GL_HEADERS -#endif // (LL_WINDOWS || LL_LINUX || LL_SOLARIS) && !LL_MESA_HEADLESS +#endif LLGLManager gGLManager; @@ -592,12 +597,6 @@ bool LLGLManager::initGL() if (mGLVendor.substr(0,4) == "ATI ") { mGLVendorShort = "ATI"; - // "mobile" appears to be unused, and this code was causing warnings. - //BOOL mobile = FALSE; - //if (mGLRenderer.find("MOBILITY") != std::string::npos) - //{ - // mobile = TRUE; - //} mIsATI = TRUE; #if LL_WINDOWS && !LL_MESA_HEADLESS @@ -786,9 +785,19 @@ void LLGLManager::setToDebugGPU() void LLGLManager::getGLInfo(LLSD& info) { - info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR)); - info["GLInfo"]["GLRenderer"] = std::string((const char *)glGetString(GL_RENDERER)); - info["GLInfo"]["GLVersion"] = std::string((const char *)glGetString(GL_VERSION)); + if (gHeadlessClient) + { + info["GLInfo"]["GLVendor"] = HEADLESS_VENDOR_STRING; + info["GLInfo"]["GLRenderer"] = HEADLESS_RENDERER_STRING; + info["GLInfo"]["GLVersion"] = HEADLESS_VERSION_STRING; + return; + } + else + { + info["GLInfo"]["GLVendor"] = std::string((const char *)glGetString(GL_VENDOR)); + info["GLInfo"]["GLRenderer"] = std::string((const char *)glGetString(GL_RENDERER)); + info["GLInfo"]["GLVersion"] = std::string((const char *)glGetString(GL_VERSION)); + } #if !LL_MESA_HEADLESS std::string all_exts = ll_safe_string((const char *)gGLHExts.mSysExts); @@ -805,9 +814,18 @@ std::string LLGLManager::getGLInfoString() { std::string info_str; - info_str += std::string("GL_VENDOR ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n"); - info_str += std::string("GL_RENDERER ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n"); - info_str += std::string("GL_VERSION ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n"); + if (gHeadlessClient) + { + info_str += std::string("GL_VENDOR ") + HEADLESS_VENDOR_STRING + std::string("\n"); + info_str += std::string("GL_RENDERER ") + HEADLESS_RENDERER_STRING + std::string("\n"); + info_str += std::string("GL_VERSION ") + HEADLESS_VERSION_STRING + std::string("\n"); + } + else + { + info_str += std::string("GL_VENDOR ") + ll_safe_string((const char *)glGetString(GL_VENDOR)) + std::string("\n"); + info_str += std::string("GL_RENDERER ") + ll_safe_string((const char *)glGetString(GL_RENDERER)) + std::string("\n"); + info_str += std::string("GL_VERSION ") + ll_safe_string((const char *)glGetString(GL_VERSION)) + std::string("\n"); + } #if !LL_MESA_HEADLESS std::string all_exts= ll_safe_string(((const char *)gGLHExts.mSysExts)); @@ -820,9 +838,18 @@ std::string LLGLManager::getGLInfoString() void LLGLManager::printGLInfoString() { - LL_INFOS("RenderInit") << "GL_VENDOR: " << ((const char *)glGetString(GL_VENDOR)) << LL_ENDL; - LL_INFOS("RenderInit") << "GL_RENDERER: " << ((const char *)glGetString(GL_RENDERER)) << LL_ENDL; - LL_INFOS("RenderInit") << "GL_VERSION: " << ((const char *)glGetString(GL_VERSION)) << LL_ENDL; + if (gHeadlessClient) + { + LL_INFOS("RenderInit") << "GL_VENDOR: " << HEADLESS_VENDOR_STRING << LL_ENDL; + LL_INFOS("RenderInit") << "GL_RENDERER: " << HEADLESS_RENDERER_STRING << LL_ENDL; + LL_INFOS("RenderInit") << "GL_VERSION: " << HEADLESS_VERSION_STRING << LL_ENDL; + } + else + { + LL_INFOS("RenderInit") << "GL_VENDOR: " << ((const char *)glGetString(GL_VENDOR)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_RENDERER: " << ((const char *)glGetString(GL_RENDERER)) << LL_ENDL; + LL_INFOS("RenderInit") << "GL_VERSION: " << ((const char *)glGetString(GL_VERSION)) << LL_ENDL; + } #if !LL_MESA_HEADLESS std::string all_exts= ll_safe_string(((const char *)gGLHExts.mSysExts)); @@ -834,7 +861,14 @@ void LLGLManager::printGLInfoString() std::string LLGLManager::getRawGLString() { std::string gl_string; - gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER)); + if (gHeadlessClient) + { + gl_string = HEADLESS_VENDOR_STRING + " " + HEADLESS_RENDERER_STRING; + } + else + { + gl_string = ll_safe_string((char*)glGetString(GL_VENDOR)) + " " + ll_safe_string((char*)glGetString(GL_RENDERER)); + } return gl_string; } @@ -900,7 +934,7 @@ void LLGLManager::initExtensions() mHasCubeMap = FALSE; mHasOcclusionQuery = FALSE; mHasPointParameters = FALSE; - mHasShaderObjects = TRUE; + mHasShaderObjects = FALSE; mHasVertexShader = FALSE; mHasFragmentShader = FALSE; mHasTextureRectangle = FALSE; @@ -1452,8 +1486,8 @@ void assert_glerror() void clear_glerror() { - // Create or update texture to be used with this data - glGetError(); + glGetError(); + glGetError(); } /////////////////////////////////////////////////////////////// @@ -1952,10 +1986,7 @@ LLGLState::LLGLState(LLGLenum state, S32 enabled) : if (mState) { mWasEnabled = sStateMap[state]; - if (gDebugGL) - { - llassert(mWasEnabled == glIsEnabled(state)); - } + llassert(mWasEnabled == glIsEnabled(state)); setEnabled(enabled); stop_glerror(); } diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index da5361d7b1..d70e764769 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -457,6 +457,7 @@ void init_glstates(); void parse_gl_version( S32* major, S32* minor, S32* release, std::string* vendor_specific, std::string* version_string ); extern BOOL gClothRipple; +extern BOOL gHeadlessClient; extern BOOL gGLActive; // Deal with changing glext.h definitions for newer SDK versions, specifically diff --git a/indra/llrender/llrender2dutils.cpp b/indra/llrender/llrender2dutils.cpp index 8eeaea5d63..17a7304fc7 100644 --- a/indra/llrender/llrender2dutils.cpp +++ b/indra/llrender/llrender2dutils.cpp @@ -913,31 +913,31 @@ void gl_rect_2d_checkerboard(const LLRect& rect, GLfloat alpha) { if (!LLGLSLShader::sNoFixedFunction) { - // Initialize the first time this is called. - const S32 PIXELS = 32; - static GLubyte checkerboard[PIXELS * PIXELS]; - static BOOL first = TRUE; - if( first ) + // Initialize the first time this is called. + const S32 PIXELS = 32; + static GLubyte checkerboard[PIXELS * PIXELS]; + static BOOL first = TRUE; + if( first ) + { + for( S32 i = 0; i < PIXELS; i++ ) { - for( S32 i = 0; i < PIXELS; i++ ) + for( S32 j = 0; j < PIXELS; j++ ) { - for( S32 j = 0; j < PIXELS; j++ ) - { - checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF; - } + checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF; } - first = FALSE; } + first = FALSE; + } - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - // ...white squares - gGL.color4f( 1.f, 1.f, 1.f, alpha ); - gl_rect_2d(rect); + // ...white squares + gGL.color4f( 1.f, 1.f, 1.f, alpha ); + gl_rect_2d(rect); - // ...gray squares - gGL.color4f( .7f, .7f, .7f, alpha ); - gGL.flush(); + // ...gray squares + gGL.color4f( .7f, .7f, .7f, alpha ); + gGL.flush(); glPolygonStipple( checkerboard ); @@ -1413,145 +1413,132 @@ void gl_segmented_rect_2d_fragment_tex(const S32 left, gGL.popUIMatrix(); } -void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, - const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, - const U32 edges) +void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, + const LLVector3& width_vec, const LLVector3& height_vec) { - LLVector3 left_border_width = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? border_width : LLVector3::zero; - LLVector3 right_border_width = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? border_width : LLVector3::zero; - - LLVector3 top_border_height = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? border_height : LLVector3::zero; - LLVector3 bottom_border_height = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? border_height : LLVector3::zero; - - gGL.begin(LLRender::QUADS); { // draw bottom left - gGL.texCoord2f(0.f, 0.f); + gGL.texCoord2f(clip_rect.mLeft, clip_rect.mBottom); gGL.vertex3f(0.f, 0.f, 0.f); - gGL.texCoord2f(border_scale.mV[VX], 0.f); - gGL.vertex3fv(left_border_width.mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(0.f, border_scale.mV[VY]); - gGL.vertex3fv(bottom_border_height.mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV); // draw bottom middle - gGL.texCoord2f(border_scale.mV[VX], 0.f); - gGL.vertex3fv(left_border_width.mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f); - gGL.vertex3fv((width_vec - right_border_width).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); // draw bottom right - gGL.texCoord2f(1.f - border_scale.mV[VX], 0.f); - gGL.vertex3fv((width_vec - right_border_width).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec).mV); - gGL.texCoord2f(1.f, 0.f); + gGL.texCoord2f(clip_rect.mRight, clip_rect.mBottom); gGL.vertex3fv(width_vec.mV); - gGL.texCoord2f(1.f, border_scale.mV[VY]); - gGL.vertex3fv((width_vec + bottom_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); // draw left - gGL.texCoord2f(0.f, border_scale.mV[VY]); - gGL.vertex3fv(bottom_border_height.mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); // draw middle - gGL.texCoord2f(border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); // draw right - gGL.texCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + bottom_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f, border_scale.mV[VY]); - gGL.vertex3fv((width_vec + bottom_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mBottom); + gGL.vertex3fv((width_vec + center_draw_rect.mBottom * height_vec).mV); - gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec + height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); // draw top left - gGL.texCoord2f(0.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f); - gGL.vertex3fv((left_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); - gGL.texCoord2f(0.f, 1.f); + gGL.texCoord2f(clip_rect.mLeft, clip_rect.mTop); gGL.vertex3fv((height_vec).mV); // draw top middle - gGL.texCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((left_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mLeft, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f); - gGL.vertex3fv((width_vec - right_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV); - gGL.texCoord2f(border_scale.mV[VX], 1.f); - gGL.vertex3fv((left_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mLeft, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mLeft * width_vec + height_vec).mV); // draw top right - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV); + gGL.texCoord2f(center_uv_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f, 1.f - border_scale.mV[VY]); - gGL.vertex3fv((width_vec + height_vec - top_border_height).mV); + gGL.texCoord2f(clip_rect.mRight, center_uv_rect.mTop); + gGL.vertex3fv((width_vec + center_draw_rect.mTop * height_vec).mV); - gGL.texCoord2f(1.f, 1.f); + gGL.texCoord2f(clip_rect.mRight, clip_rect.mTop); gGL.vertex3fv((width_vec + height_vec).mV); - gGL.texCoord2f(1.f - border_scale.mV[VX], 1.f); - gGL.vertex3fv((width_vec - right_border_width + height_vec).mV); + gGL.texCoord2f(center_uv_rect.mRight, clip_rect.mTop); + gGL.vertex3fv((center_draw_rect.mRight * width_vec + height_vec).mV); } gGL.end(); } -void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec) -{ - gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP); -} - // static void LLRender2D::initClass(LLImageProviderInterface* image_provider, const LLVector2* scale_factor) diff --git a/indra/llrender/llrender2dutils.h b/indra/llrender/llrender2dutils.h index 4ea2d06d99..4884422c58 100644 --- a/indra/llrender/llrender2dutils.h +++ b/indra/llrender/llrender2dutils.h @@ -106,8 +106,7 @@ typedef enum e_rounded_edge void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL); void gl_segmented_rect_2d_fragment_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL); -void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, U32 edges = ROUNDED_RECT_ALL); -void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec); +void gl_segmented_rect_3d_tex(const LLRectf& clip_rect, const LLRectf& center_uv_rect, const LLRectf& center_draw_rect, const LLVector3& width_vec, const LLVector3& height_vec); inline void gl_rect_2d( const LLRect& rect, BOOL filled ) { diff --git a/indra/llrender/lluiimage.cpp b/indra/llrender/lluiimage.cpp index a632e7ed2f..b954b66350 100644 --- a/indra/llrender/lluiimage.cpp +++ b/indra/llrender/lluiimage.cpp @@ -112,6 +112,50 @@ void LLUIImage::drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& drawSolid(border_rect, color); } +void LLUIImage::draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, + const LLRect& rect, const LLColor4& color) +{ + F32 border_scale = 1.f; + F32 border_height = (1.f - mScaleRegion.getHeight()) * getHeight(); + F32 border_width = (1.f - mScaleRegion.getWidth()) * getWidth(); + if (rect.getHeight() < border_height || rect.getWidth() < border_width) + { + if(border_height - rect.getHeight() > border_width - rect.getWidth()) + { + border_scale = (F32)rect.getHeight() / border_height; + } + else + { + border_scale = (F32)rect.getWidth() / border_width; + } + } + + LLRender2D::pushMatrix(); + { + LLVector3 rect_origin = origin_agent + (rect.mLeft * x_axis) + (rect.mBottom * y_axis); + LLRender2D::translate(rect_origin.mV[VX], + rect_origin.mV[VY], + rect_origin.mV[VZ]); + gGL.getTexUnit(0)->bind(getImage()); + gGL.color4fv(color.mV); + + LLRectf center_uv_rect(mClipRegion.mLeft + mScaleRegion.mLeft * mClipRegion.getWidth(), + mClipRegion.mBottom + mScaleRegion.mTop * mClipRegion.getHeight(), + mClipRegion.mLeft + mScaleRegion.mRight * mClipRegion.getWidth(), + mClipRegion.mBottom + mScaleRegion.mBottom * mClipRegion.getHeight()); + gl_segmented_rect_3d_tex(mClipRegion, + center_uv_rect, + LLRectf(border_width * border_scale * 0.5f / (F32)rect.getWidth(), + (rect.getHeight() - (border_height * border_scale * 0.5f)) / (F32)rect.getHeight(), + (rect.getWidth() - (border_width * border_scale * 0.5f)) / (F32)rect.getWidth(), + (border_height * border_scale * 0.5f) / (F32)rect.getHeight()), + rect.getWidth() * x_axis, + rect.getHeight() * y_axis); + + } LLRender2D::popMatrix(); +} + + S32 LLUIImage::getWidth() const { // return clipped dimensions of actual image area @@ -155,7 +199,7 @@ void LLUIImage::onImageLoaded() namespace LLInitParam { - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() { // The keyword "none" is specifically requesting a null image // do not default to current value. Used to overwrite template images. @@ -172,7 +216,7 @@ namespace LLInitParam } } - void ParamValue >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue::updateBlockFromValue(bool make_block_authoritative) { if (getValue() == NULL) { diff --git a/indra/llrender/lluiimage.h b/indra/llrender/lluiimage.h index f07e8fa746..7817ba1c7b 100644 --- a/indra/llrender/lluiimage.h +++ b/indra/llrender/lluiimage.h @@ -64,7 +64,9 @@ public: void drawBorder(S32 x, S32 y, S32 width, S32 height, const LLColor4& color, S32 border_width) const; void drawBorder(const LLRect& rect, const LLColor4& color, S32 border_width) const { drawBorder(rect.mLeft, rect.mBottom, rect.getWidth(), rect.getHeight(), color, border_width); } void drawBorder(S32 x, S32 y, const LLColor4& color, S32 border_width) const { drawBorder(x, y, getWidth(), getHeight(), color, border_width); } - + + void draw3D(const LLVector3& origin_agent, const LLVector3& x_axis, const LLVector3& y_axis, const LLRect& rect, const LLColor4& color); + const std::string& getName() const { return mName; } virtual S32 getWidth() const; @@ -92,7 +94,7 @@ protected: namespace LLInitParam { template<> - class ParamValue > + class ParamValue : public CustomParamValue { typedef boost::add_reference::type>::type T_const_ref; @@ -100,7 +102,7 @@ namespace LLInitParam public: Optional name; - ParamValue(LLUIImage* const& image) + ParamValue(LLUIImage* const& image = NULL) : super_t(image) { updateBlockFromValue(false); diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 95263c5554..4f27dbea03 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -43,6 +43,7 @@ set(llui_SOURCE_FILES llbadgeholder.cpp llbadgeowner.cpp llbutton.cpp + llchatentry.cpp llcheckboxctrl.cpp llclipboard.cpp llcombobox.cpp @@ -56,12 +57,16 @@ set(llui_SOURCE_FILES lleditmenuhandler.cpp llf32uictrl.cpp llfiltereditor.cpp + llflashtimer.cpp llflatlistview.cpp llfloater.cpp llfloaterreg.cpp llfloaterreglistener.cpp llflyoutbutton.cpp llfocusmgr.cpp + llfolderview.cpp + llfolderviewitem.cpp + llfolderviewmodel.cpp llfunctorregistry.cpp lliconctrl.cpp llkeywords.cpp @@ -76,7 +81,6 @@ set(llui_SOURCE_FILES llmultislider.cpp llmultisliderctrl.cpp llnotifications.cpp - llnotificationslistener.cpp llnotificationsutil.cpp llpanel.cpp llprogressbar.cpp @@ -144,6 +148,7 @@ set(llui_HEADER_FILES llbadgeowner.h llbutton.h llcallbackmap.h + llchatentry.h llcheckboxctrl.h llclipboard.h llcombobox.h @@ -157,12 +162,16 @@ set(llui_HEADER_FILES lleditmenuhandler.h llf32uictrl.h llfiltereditor.h + llflashtimer.h llflatlistview.h llfloater.h llfloaterreg.h llfloaterreglistener.h llflyoutbutton.h llfocusmgr.h + llfolderview.h + llfolderviewitem.h + llfolderviewmodel.h llfunctorregistry.h llhelp.h lliconctrl.h @@ -180,7 +189,6 @@ set(llui_HEADER_FILES llmultislider.h llnotificationptr.h llnotifications.h - llnotificationslistener.h llnotificationsutil.h llnotificationtemplate.h llnotificationvisibilityrule.h diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index c025cd7939..43462bd244 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -184,7 +184,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string if (mHeaderTextbox) { std::string text = mHeaderTextbox->getText(); - mStyleParams.font(mHeaderTextbox->getDefaultFont()); + mStyleParams.font(mHeaderTextbox->getFont()); mStyleParams.font.style(style); mHeaderTextbox->setText(text, mStyleParams); } diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 11d6266aa2..a8149a9a1d 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -50,8 +50,6 @@ #include "llhelp.h" #include "lldockablefloater.h" -#include "llcheckboxctrl.h" // Add checkbox control toggle - static LLDefaultChildRegistry::Register r("button"); // Compiler optimization, generate extern template @@ -62,9 +60,6 @@ template class LLButton* LLView::getChild( S32 LLBUTTON_H_PAD = 0; S32 BTN_HEIGHT_SMALL= 0; S32 BTN_HEIGHT = 0; -// [SL:KB] - Patch: UI-DndButtonCommit | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c -F32 DELAY_DRAG_HOVER_COMMIT = 0.5; -// [/SL:KB] LLButton::Params::Params() : label_selected("label_selected"), // requires is_toggle true @@ -110,12 +105,9 @@ LLButton::Params::Params() badge("badge"), handle_right_mouse("handle_right_mouse"), held_down_delay("held_down_delay"), + button_flash_enable("button_flash_enable", false), button_flash_count("button_flash_count"), - // Add checkbox control toggle - // button_flash_rate("button_flash_rate") - button_flash_rate("button_flash_rate"), - checkbox_control("checkbox_control") - // + button_flash_rate("button_flash_rate") { addSynonym(is_toggle, "toggle"); changeDefault(initial_value, LLSD(false)); @@ -180,14 +172,24 @@ LLButton::LLButton(const LLButton::Params& p) mHeldDownSignal(NULL), mUseDrawContextAlpha(p.use_draw_context_alpha), mHandleRightMouse(p.handle_right_mouse), - mButtonFlashCount(p.button_flash_count), - // Add checkbox control toggle - // mButtonFlashRate(p.button_flash_rate) - mButtonFlashRate(p.button_flash_rate), - mCheckboxControl(p.checkbox_control), - mCheckboxControlPanel(NULL) - // + mFlashingTimer(NULL) { + if (p.button_flash_enable) + { + // If optional parameter "p.button_flash_count" is not provided, LLFlashTimer will be + // used instead it a "default" value from gSavedSettings.getS32("FlashCount")). + // Likewise, missing "p.button_flash_rate" is replaced by gSavedSettings.getF32("FlashPeriod"). + // Note: flashing should be allowed in settings.xml (boolean key "EnableButtonFlashing"). + S32 flash_count = p.button_flash_count.isProvided()? p.button_flash_count : 0; + F32 flash_rate = p.button_flash_rate.isProvided()? p.button_flash_rate : 0.0; + mFlashingTimer = new LLFlashTimer ((LLFlashTimer::callback_t)NULL, flash_count, flash_rate); + } + else + { + mButtonFlashCount = p.button_flash_count; + mButtonFlashRate = p.button_flash_rate; + } + static LLUICachedControl llbutton_orig_h_pad ("UIButtonOrigHPad", 0); static Params default_params(LLUICtrlFactory::getDefaultParams()); @@ -281,6 +283,11 @@ LLButton::~LLButton() delete mMouseDownSignal; delete mMouseUpSignal; delete mHeldDownSignal; + + if (mFlashingTimer) + { + mFlashingTimer->unset(); + } } // HACK: Committing a button is the same as instantly clicking it. @@ -376,31 +383,6 @@ BOOL LLButton::postBuild() { autoResize(); - // Add checkbox control toggle - if(!mCheckboxControl.empty()) - { - mCheckboxControlPanel=LLUICtrlFactory::createFromFile("panel_button_checkbox.xml",this,LLDefaultChildRegistry::instance()); - if(mCheckboxControlPanel) - { - mCheckboxControlPanel->reshape(getRect().getWidth(),getRect().getHeight()); - - LLCheckBoxCtrl* check=mCheckboxControlPanel->findChild("check_control"); - if(check) - { - check->setControlName(mCheckboxControl,NULL); - } - else - { - llwarns << "Could not find checkbox control for button " << getName() << llendl; - } - } - else - { - llwarns << "Could not create checkbox panel for button " << getName() << llendl; - } - } - // - addBadgeToParentPanel(); return LLUICtrl::postBuild(); @@ -630,40 +612,6 @@ void LLButton::draw() { static LLCachedControl sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true); F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); - bool flash = FALSE; - - if( mFlashing) - { - if ( sEnableButtonFlashing) - { - // Global flash rate override - //F32 elapsed = mFlashingTimer.getElapsedTimeF32(); - //S32 flash_count = S32(elapsed * mButtonFlashRate * 2.f); - //// flash on or off? - //flash = (flash_count % 2 == 0) || flash_count > S32((F32)mButtonFlashCount * 2.f); - - S32 buttonFlashCount = mButtonFlashCount; - F32 buttonFlashRate = mButtonFlashRate; - static LLCachedControl fsGlobalButtonFlashOverride(*LLUI::sSettingGroups["config"], "FSGlobalButtonFlashOverride", true); - static LLCachedControl fsButtonFlashCount(*LLUI::sSettingGroups["config"], "FSButtonFlashCount", true); - static LLCachedControl fsButtonFlashRate(*LLUI::sSettingGroups["config"], "FSButtonFlashRate", true); - if (fsGlobalButtonFlashOverride) - { - buttonFlashCount = (S32)fsButtonFlashCount; - buttonFlashRate = (F32)fsButtonFlashRate; - } - - F32 elapsed = mFlashingTimer.getElapsedTimeF32(); - S32 flash_count = S32(elapsed * buttonFlashRate * 2.f); - // flash on or off? - flash = (flash_count % 2 == 0) || flash_count > S32((F32)buttonFlashCount * 2.f); - // Global flash rate override - } - else - { // otherwise just highlight button in flash color - flash = true; - } - } bool pressed_by_keyboard = FALSE; if (hasFocus()) @@ -688,9 +636,21 @@ void LLButton::draw() bool selected = getToggleState(); bool use_glow_effect = FALSE; - LLColor4 glow_color = LLColor4::white; + LLColor4 highlighting_color = LLColor4::white; + LLColor4 glow_color; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; LLUIImage* imagep = NULL; + + // Cancel sticking of color, if the button is pressed, + // or when a flashing of the previously selected button is ended + if (mFlashingTimer + && ((selected && !mFlashingTimer->isFlashingInProgress()) || pressed)) + { + mFlashing = false; + } + + bool flash = mFlashing && sEnableButtonFlashing; + if (pressed && mDisplayPressedState) { imagep = selected ? mImagePressedSelected : mImagePressed; @@ -756,15 +716,20 @@ void LLButton::draw() imagep = mImageFlash; } // else use usual flashing via flash_color - else + else if (mFlashingTimer) { LLColor4 flash_color = mFlashBgColor.get(); use_glow_effect = TRUE; glow_type = LLRender::BT_ALPHA; // blend the glow - if (mNeedsHighlight) // highlighted AND flashing - glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity - else + + if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress()) + { glow_color = flash_color; + } + else if (mNeedsHighlight) + { + glow_color = highlighting_color; + } } } @@ -813,8 +778,7 @@ void LLButton::draw() if (use_glow_effect) { mCurGlowStrength = lerp(mCurGlowStrength, - mFlashing ? (flash? 1.0 : 0.0) - : mHoverGlowStrength, + mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.0 : 0.0) : mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f)); } else @@ -974,15 +938,6 @@ void LLButton::draw() NULL, mUseEllipses); } - // Add checkbox control toggle - if(mCheckboxControlPanel) - { - mCheckboxControlPanel->setOrigin(0,0); - mCheckboxControlPanel->reshape(getRect().getWidth(),getRect().getHeight()); - mCheckboxControlPanel->draw(); - } - // - LLUICtrl::draw(); } @@ -1010,21 +965,26 @@ void LLButton::setToggleState(BOOL b) { setControlValue(b); // will fire LLControlVariable callbacks (if any) setValue(b); // may or may not be redundant + setFlashing(false); // stop flash state whenever the selected/unselected state if reset // Unselected label assignments autoResize(); } } -void LLButton::setFlashing( BOOL b ) +void LLButton::setFlashing(bool b) { - if ((bool)b != mFlashing) + if (mFlashingTimer) { mFlashing = b; - mFlashingTimer.reset(); + (b ? mFlashingTimer->startFlashing() : mFlashingTimer->stopFlashing()); + } + else if (b != mFlashing) + { + mFlashing = b; + mFrameTimer.reset(); } } - BOOL LLButton::toggleState() { bool flipped = ! getToggleState(); diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 9a9e401023..d65bf3829e 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -30,6 +30,7 @@ #include "lluuid.h" #include "llbadgeowner.h" #include "llcontrol.h" +#include "llflashtimer.h" #include "lluictrl.h" #include "v4color.h" #include "llframetimer.h" @@ -46,9 +47,6 @@ extern S32 LLBUTTON_H_PAD; extern S32 BTN_HEIGHT_SMALL; extern S32 BTN_HEIGHT; -// [SL:KB] - Patch: UI-DndButtonCommit | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c -extern F32 DELAY_DRAG_HOVER_COMMIT; -// [/SL:KB] // // Helpful functions @@ -136,6 +134,7 @@ public: Optional handle_right_mouse; + Optional button_flash_enable; Optional button_flash_count; Optional button_flash_rate; @@ -204,8 +203,9 @@ public: void setToggleState(BOOL b); void setHighlight(bool b); - void setFlashing( BOOL b ); + void setFlashing( bool b ); BOOL getFlashing() const { return mFlashing; } + LLFlashTimer* getFlashTimer() {return mFlashingTimer;} void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; } LLFontGL::HAlign getHAlign() const { return mHAlign; } @@ -378,7 +378,8 @@ protected: bool mForcePressedState; bool mDisplayPressedState; - LLFrameTimer mFlashingTimer; + LLFrameTimer mFrameTimer; + LLFlashTimer * mFlashingTimer; bool mHandleRightMouse; diff --git a/indra/llui/llchatentry.cpp b/indra/llui/llchatentry.cpp new file mode 100644 index 0000000000..6a1b48a08a --- /dev/null +++ b/indra/llui/llchatentry.cpp @@ -0,0 +1,257 @@ +/** + * @file llchatentry.cpp + * @brief LLChatEntry implementation + * + * $LicenseInfo:firstyear=2001&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 "linden_common.h" +#include "llscrollcontainer.h" + +#include "llchatentry.h" + +static LLDefaultChildRegistry::Register r("chat_editor"); + +LLChatEntry::Params::Params() +: has_history("has_history", true), + is_expandable("is_expandable", false), + expand_lines_count("expand_lines_count", 1) +{} + +LLChatEntry::LLChatEntry(const Params& p) +: LLTextEditor(p), + mTextExpandedSignal(NULL), + mHasHistory(p.has_history), + mIsExpandable(p.is_expandable), + mExpandLinesCount(p.expand_lines_count), + mPrevLinesCount(0), + mSingleLineMode(false), + mPrevExpandedLineCount(S32_MAX) +{ + // Initialize current history line iterator + mCurrentHistoryLine = mLineHistory.begin(); + + mAutoIndent = false; +} + +LLChatEntry::~LLChatEntry() +{ + delete mTextExpandedSignal; +} + +void LLChatEntry::draw() +{ + if(mIsExpandable) + { + expandText(); + } + + LLTextEditor::draw(); +} + +void LLChatEntry::onCommit() +{ + updateHistory(); + LLTextEditor::onCommit(); +} + +boost::signals2::connection LLChatEntry::setTextExpandedCallback(const commit_signal_t::slot_type& cb) +{ + if (!mTextExpandedSignal) + { + mTextExpandedSignal = new commit_signal_t(); + } + return mTextExpandedSignal->connect(cb); +} + +void LLChatEntry::expandText() +{ + S32 line_count = mSingleLineMode ? 1 : mExpandLinesCount; + + int visible_lines_count = llabs(getVisibleLines(true).first - getVisibleLines(true).second); + bool can_changed = getLineCount() <= line_count || line_count < mPrevExpandedLineCount ; + mPrevExpandedLineCount = line_count; + + // true if pasted text has more lines than expand height limit and expand limit is not reached yet + bool text_pasted = (getLineCount() > line_count) && (visible_lines_count < line_count); + + if (mIsExpandable && (can_changed || text_pasted || mSingleLineMode) && getLineCount() != mPrevLinesCount) + { + int lines_height = 0; + if (text_pasted) + { + // text is pasted and now mLineInfoList.size() > mExpandLineCounts and mLineInfoList is not empty, + // so lines_height is the sum of the last 'expanded_line_count' lines height + lines_height = (mLineInfoList.end() - line_count)->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + else + { + lines_height = mLineInfoList.begin()->mRect.mTop - mLineInfoList.back().mRect.mBottom; + } + + int height = mVPad * 2 + lines_height; + + LLRect doc_rect = getRect(); + doc_rect.setOriginAndSize(doc_rect.mLeft, doc_rect.mBottom, doc_rect.getWidth(), height); + setShape(doc_rect); + + mPrevLinesCount = getLineCount(); + + if (mTextExpandedSignal) + { + (*mTextExpandedSignal)(this, LLSD() ); + } + + needsReflow(); + } +} + +// line history support +void LLChatEntry::updateHistory() +{ + // On history enabled, remember committed line and + // reset current history line number. + // Be sure only to remember lines that are not empty and that are + // different from the last on the list. + if (mHasHistory && getLength()) + { + // Add text to history, ignoring duplicates + if (mLineHistory.empty() || getText() != mLineHistory.back()) + { + mLineHistory.push_back(getText()); + } + + mCurrentHistoryLine = mLineHistory.end(); + } +} + +void LLChatEntry::beforeValueChange() +{ + if(this->getLength() == 0 && !mLabel.empty()) + { + this->clearSegments(); + } +} + +void LLChatEntry::onValueChange(S32 start, S32 end) +{ + //Internally resetLabel() must meet a condition before it can reset the label + resetLabel(); +} + +bool LLChatEntry::useLabel() +{ + return !getLength() && !mLabel.empty(); +} + +void LLChatEntry::onFocusReceived() +{ + +} + +void LLChatEntry::onFocusLost() +{ + +} + +BOOL LLChatEntry::handleSpecialKey(const KEY key, const MASK mask) +{ + BOOL handled = FALSE; + + // In the case of a chat entry, pressing RETURN when something is selected + // should NOT erase the selection (unlike a notecard, for example) + if (key == KEY_RETURN) + { + endOfDoc(); + startSelection(); + endSelection(); + } + + LLTextEditor::handleSpecialKey(key, mask); + + switch(key) + { + case KEY_RETURN: + if (MASK_NONE == mask) + { + needsReflow(); + } + break; + + case KEY_UP: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine > mLineHistory.begin()) + { + setText(*(--mCurrentHistoryLine)); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + case KEY_DOWN: + if (mHasHistory && MASK_CONTROL == mask) + { + if (!mLineHistory.empty() && mCurrentHistoryLine < (mLineHistory.end() - 1) ) + { + setText(*(++mCurrentHistoryLine)); + endOfDoc(); + } + else if (!mLineHistory.empty() && mCurrentHistoryLine == (mLineHistory.end() - 1) ) + { + mCurrentHistoryLine++; + std::string empty(""); + setText(empty); + needsReflow(); + endOfDoc(); + } + else + { + LLUI::reportBadKeystroke(); + } + handled = TRUE; + } + break; + + default: + break; + } + + return handled; +} + +void LLChatEntry::enableSingleLineMode(bool single_line_mode) +{ + if (mScroller) + { + mScroller->setSize(single_line_mode ? 0 : -1); + } + + mSingleLineMode = single_line_mode; + mPrevLinesCount = -1; + setWordWrap(!single_line_mode); +} diff --git a/indra/llui/llchatentry.h b/indra/llui/llchatentry.h new file mode 100644 index 0000000000..49c8d21450 --- /dev/null +++ b/indra/llui/llchatentry.h @@ -0,0 +1,106 @@ +/** + * @file llchatentry.h + * @author Paul Guslisty + * @brief Text editor widget which is used for user input + * + * Features: + * Optional line history so previous entries can be recalled by CTRL UP/DOWN + * Optional auto-resize behavior on input chat field + * + * $LicenseInfo:firstyear=2001&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 LLCHATENTRY_H_ +#define LLCHATENTRY_H_ + +#include "lltexteditor.h" + +class LLChatEntry : public LLTextEditor +{ +public: + + struct Params : public LLInitParam::Block + { + Optional has_history, + is_expandable; + + Optional expand_lines_count; + + Params(); + }; + + virtual ~LLChatEntry(); + +protected: + + friend class LLUICtrlFactory; + LLChatEntry(const Params& p); + /*virtual*/ void beforeValueChange(); + /*virtual*/ void onValueChange(S32 start, S32 end); + /*virtual*/ bool useLabel(); + +public: + + virtual void draw(); + virtual void onCommit(); + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + + void enableSingleLineMode(bool single_line_mode); + boost::signals2::connection setTextExpandedCallback(const commit_signal_t::slot_type& cb); + +private: + + /** + * Implements auto-resize behavior. + * When user's typing reaches the right edge of the chat field + * the chat field expands vertically by one line. The bottom of + * the chat field remains bottom-justified. The chat field does + * not expand beyond mExpandLinesCount. + */ + void expandText(); + + /** + * Implements line history so previous entries can be recalled by CTRL UP/DOWN + */ + void updateHistory(); + + BOOL handleSpecialKey(const KEY key, const MASK mask); + + + // Fired when text height expanded to mExpandLinesCount + commit_signal_t* mTextExpandedSignal; + + // line history support: + typedef std::vector line_history_t; + line_history_t::iterator mCurrentHistoryLine; // currently browsed history line + line_history_t mLineHistory; // line history storage + bool mHasHistory; // flag for enabled/disabled line history + bool mIsExpandable; + bool mSingleLineMode; + + S32 mExpandLinesCount; + S32 mPrevLinesCount; + S32 mPrevExpandedLineCount; +}; + +#endif /* LLCHATENTRY_H_ */ diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index 9ce5e56369..ed69843b7f 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -110,7 +110,7 @@ LLCheckBoxCtrl::LLCheckBoxCtrl(const LLCheckBoxCtrl::Params& p) LLButton::Params params = p.check_button; params.rect(btn_rect); //params.control_name(p.control_name); - params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onButtonPress, this, _2)); + params.click_callback.function(boost::bind(&LLCheckBoxCtrl::onCommit, this)); params.commit_on_return(false); // Checkboxes only allow boolean initial values, but buttons can // take any LLSD. @@ -133,18 +133,6 @@ LLCheckBoxCtrl::~LLCheckBoxCtrl() // Children all cleaned up by default view destructor. } - -// static -void LLCheckBoxCtrl::onButtonPress( const LLSD& data ) -{ - //if (mRadioStyle) - //{ - // setValue(TRUE); - //} - - onCommit(); -} - void LLCheckBoxCtrl::onCommit() { if( getEnabled() ) diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h index 7a903ad432..7d1b6e1420 100644 --- a/indra/llui/llcheckboxctrl.h +++ b/indra/llui/llcheckboxctrl.h @@ -107,8 +107,6 @@ public: virtual void setControlName(const std::string& control_name, LLView* context); - void onButtonPress(const LLSD& data); - virtual BOOL isDirty() const; // Returns TRUE if the user has modified this control. virtual void resetDirty(); // Clear dirty state diff --git a/indra/llui/llcommandmanager.cpp b/indra/llui/llcommandmanager.cpp index a2c6c26ff9..e2ba5b96b1 100644 --- a/indra/llui/llcommandmanager.cpp +++ b/indra/llui/llcommandmanager.cpp @@ -85,6 +85,7 @@ LLCommand::LLCommand(const LLCommand::Params& p) , mIsRunningParameters(p.is_running_parameters) , mIsStartingFunction(p.is_starting_function) , mIsStartingParameters(p.is_starting_parameters) + , mIsFlashingAllowed(p.is_flashing_allowed) , mControlVariable(p.control_name) // Add control_name to toolbar buttons , mCheckboxControlVariable(p.checkbox_control) // Add checkbox control toggle { diff --git a/indra/llui/llcommandmanager.h b/indra/llui/llcommandmanager.h index 1b3ca19313..b4165d4198 100644 --- a/indra/llui/llcommandmanager.h +++ b/indra/llui/llcommandmanager.h @@ -97,7 +97,7 @@ public: Mandatory tooltip_ref; // Add control_name to toolbar buttons - // so we can have control_name instead of aall these functions. + // so we can have control_name instead of all these functions. // Mandatory execute_function; Optional execute_function; // @@ -115,6 +115,8 @@ public: Optional is_starting_function; Optional is_starting_parameters; + Optional is_flashing_allowed; + Optional control_name; // Add control_name to toolbar buttons Optional checkbox_control; // Add checkbox control toggle @@ -145,6 +147,8 @@ public: const std::string& isStartingFunctionName() const { return mIsStartingFunction; } const LLSD& isStartingParameters() const { return mIsStartingParameters; } + bool isFlashingAllowed() const { return mIsFlashingAllowed; } + private: LLCommandId mIdentifier; @@ -169,6 +173,8 @@ private: std::string mIsStartingFunction; LLSD mIsStartingParameters; + bool mIsFlashingAllowed; + // Add control_variable ald checkbox_control to commands in toolbar std::string mControlVariable; std::string mCheckboxControlVariable; diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp old mode 100755 new mode 100644 diff --git a/indra/llui/lldockcontrol.cpp b/indra/llui/lldockcontrol.cpp index 32fbb2c02b..59a03aee95 100644 --- a/indra/llui/lldockcontrol.cpp +++ b/indra/llui/lldockcontrol.cpp @@ -31,7 +31,6 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, const LLUIImagePtr& dockTongue, DocAt dockAt, get_allowed_rect_callback_t get_allowed_rect_callback) : - mDockWidget(dockWidget), mDockableFloater(dockableFloater), mDockTongue(dockTongue), mDockTongueX(0), @@ -39,10 +38,10 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, { mDockAt = dockAt; - // FIRE-9134; Fix crash on logout. USe handle to detect if mDockWidget is already dead (To simplify the amount of code changed for future merged, use a LLHandle + the old LLView*) - if (mDockWidget != NULL) - mDockHandle = mDockWidget->getHandle(); - // + if (dockWidget != NULL) + { + mDockWidgetHandle = dockWidget->getHandle(); + } if (dockableFloater->isDocked()) { @@ -67,7 +66,7 @@ LLDockControl::LLDockControl(LLView* dockWidget, LLFloater* dockableFloater, repositionDockable(); } - if (mDockWidget != NULL) + if (getDock() != NULL) { mDockWidgetVisible = isDockVisible(); } @@ -83,23 +82,15 @@ LLDockControl::~LLDockControl() void LLDockControl::setDock(LLView* dockWidget) { - mDockWidget = dockWidget; - - // FIRE-9134; Fix crash on logout. USe handle to detect if mDockWidget is already dead (To simplify the amount of code changed for future merged, use a LLHandle + the old LLView*) - mDockHandle = LLHandle< LLView >(); - // - - if (mDockWidget != NULL) + if (dockWidget != NULL) { - // FIRE-9134; Fix crash on logout. USe handle to detect if mDockWidget is already dead (To simplify the amount of code changed for future merged, use a LLHandle + the old LLView*) - mDockHandle = mDockWidget->getHandle(); - // - + mDockWidgetHandle = dockWidget->getHandle(); repositionDockable(); mDockWidgetVisible = isDockVisible(); } else { + mDockWidgetHandle = LLHandle(); mDockWidgetVisible = false; } } @@ -112,8 +103,8 @@ void LLDockControl::getAllowedRect(LLRect& rect) void LLDockControl::repositionDockable() { - if (!mDockWidget) return; - LLRect dockRect = mDockWidget->calcScreenRect(); + if (!getDock()) return; + LLRect dockRect = getDock()->calcScreenRect(); LLRect rootRect; LLRect floater_rect = mDockableFloater->calcScreenRect(); mGetAllowedRectCallback(rootRect); @@ -165,13 +156,13 @@ bool LLDockControl::isDockVisible() { bool res = true; - if (mDockWidget != NULL) + if (getDock() != NULL) { //we should check all hierarchy - res = mDockWidget->isInVisibleChain(); + res = getDock()->isInVisibleChain(); if (res) { - LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect dockRect = getDock()->calcScreenRect(); switch (mDockAt) { @@ -184,7 +175,7 @@ bool LLDockControl::isDockVisible() // assume that parent for all dockable floaters // is the root view LLRect dockParentRect = - mDockWidget->getRootView()->calcScreenRect(); + getDock()->getRootView()->calcScreenRect(); if (dockRect.mRight <= dockParentRect.mLeft || dockRect.mLeft >= dockParentRect.mRight) { @@ -204,7 +195,7 @@ bool LLDockControl::isDockVisible() void LLDockControl::moveDockable() { // calculate new dockable position - LLRect dockRect = mDockWidget->calcScreenRect(); + LLRect dockRect = getDock()->calcScreenRect(); LLRect rootRect; mGetAllowedRectCallback(rootRect); @@ -278,7 +269,7 @@ void LLDockControl::moveDockable() // calculate dock tongue position - dockParentRect = mDockWidget->getParent()->calcScreenRect(); + dockParentRect = getDock()->getParent()->calcScreenRect(); if (dockRect.getCenterX() < dockParentRect.mLeft) { mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; @@ -314,7 +305,7 @@ void LLDockControl::moveDockable() } // calculate dock tongue position - dockParentRect = mDockWidget->getParent()->calcScreenRect(); + dockParentRect = getDock()->getParent()->calcScreenRect(); if (dockRect.getCenterX() < dockParentRect.mLeft) { mDockTongueX = dockParentRect.mLeft - mDockTongue->getWidth() / 2; diff --git a/indra/llui/lldockcontrol.h b/indra/llui/lldockcontrol.h index b5888d5409..e06198e22c 100644 --- a/indra/llui/lldockcontrol.h +++ b/indra/llui/lldockcontrol.h @@ -63,15 +63,7 @@ public: void setDock(LLView* dockWidget); LLView* getDock() { - // FIRE-9134; Fix crash on logout. USe handle to detect if mDockWidget is already dead (To simplify the amount of code changed for future merged, use a LLHandle + the old LLView*) - - // return mDockWidget; - - if( mDockWidget && !mDockHandle.isDead() ) - return mDockWidget; - return 0; - - // + return mDockWidgetHandle.get(); } void repositionDockable(); void drawToungue(); @@ -91,7 +83,7 @@ private: bool mRecalculateDockablePosition; bool mDockWidgetVisible; DocAt mDockAt; - LLView* mDockWidget; + LLHandle mDockWidgetHandle; LLRect mPrevDockRect; LLRect mRootRect; LLRect mFloaterRect; @@ -101,7 +93,7 @@ private: S32 mDockTongueY; // FIRE-9134; Fix crash on logout. USe handle to detect if mDockWidget is already dead (To simplify the amount of code changed for future merged, use a LLHandle + the old LLView*) - LLHandle< LLView > mDockHandle; + // LLHandle< LLView > mDockHandle; check CHUI merge // }; diff --git a/indra/llui/llflashtimer.cpp b/indra/llui/llflashtimer.cpp new file mode 100644 index 0000000000..e49628acd5 --- /dev/null +++ b/indra/llui/llflashtimer.cpp @@ -0,0 +1,100 @@ +/** + * @file llflashtimer.cpp + * @brief LLFlashTimer class implementation + * + * $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 "../newview/llviewerprecompiledheaders.h" + +#include "llflashtimer.h" +#include "../newview/llviewercontrol.h" +#include "lleventtimer.h" + +LLFlashTimer::LLFlashTimer(callback_t cb, S32 count, F32 period) + : LLEventTimer(period) + , mCallback(cb) + , mCurrentTickCount(0) + , mIsFlashingInProgress(false) + , mIsCurrentlyHighlighted(false) + , mUnset(false) +{ + mEventTimer.stop(); + + // By default 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. + mFlashCount = 2 * ((count > 0) ? count : gSavedSettings.getS32("FlashCount")); + if (mPeriod <= 0) + { + mPeriod = gSavedSettings.getF32("FlashPeriod"); + } +} + +void LLFlashTimer::unset() +{ + mUnset = true; + mCallback = NULL; +} + +BOOL LLFlashTimer::tick() +{ + mIsCurrentlyHighlighted = !mIsCurrentlyHighlighted; + + if (mCallback) + { + mCallback(mIsCurrentlyHighlighted); + } + + if (++mCurrentTickCount >= mFlashCount) + { + stopFlashing(); + } + + return mUnset; +} + +void LLFlashTimer::startFlashing() +{ + mIsFlashingInProgress = true; + mIsCurrentlyHighlighted = true; + mEventTimer.start(); +} + +void LLFlashTimer::stopFlashing() +{ + mEventTimer.stop(); + mIsFlashingInProgress = false; + mIsCurrentlyHighlighted = false; + mCurrentTickCount = 0; +} + +bool LLFlashTimer::isFlashingInProgress() +{ + return mIsFlashingInProgress; +} + +bool LLFlashTimer::isCurrentlyHighlighted() +{ + return mIsCurrentlyHighlighted; +} + + diff --git a/indra/llui/llflashtimer.h b/indra/llui/llflashtimer.h new file mode 100644 index 0000000000..c60f99a8ea --- /dev/null +++ b/indra/llui/llflashtimer.h @@ -0,0 +1,73 @@ +/** + * @file llflashtimer.h + * @brief LLFlashTimer class implementation + * + * $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 LL_FLASHTIMER_H +#define LL_FLASHTIMER_H + +#include "lleventtimer.h" + +class LLFlashTimer : public LLEventTimer +{ +public: + + typedef boost::function 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 + */ + LLFlashTimer(callback_t cb = NULL, S32 count = 0, F32 period = 0.0); + ~LLFlashTimer() {}; + + /*virtual*/ BOOL tick(); + + void startFlashing(); + void stopFlashing(); + + bool isFlashingInProgress(); + bool isCurrentlyHighlighted(); + /* + * Use this instead of deleting this object. + * The next call to tick() will return true and that will destroy this object. + */ + void unset(); + +private: + callback_t mCallback; + /** + * How many times parent will blink. + */ + S32 mFlashCount; + S32 mCurrentTickCount; + bool mIsCurrentlyHighlighted; + bool mIsFlashingInProgress; + bool mUnset; +}; + +#endif /* LL_FLASHTIMER_H */ diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 46c2320cfd..d6455d2211 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -63,6 +63,8 @@ // use this to control "jumping" behavior when Ctrl-Tabbing const S32 TABBED_FLOATER_OFFSET = 0; +extern LLControlGroup gSavedSettings; + namespace LLInitParam { void TypeValues::declareValues() @@ -176,9 +178,6 @@ LLFloater::Params::Params() save_dock_state("save_dock_state", false), save_rect("save_rect", false), save_visibility("save_visibility", false), -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - save_tearoff_state("save_tearoff_state", false), -// [/SL:KB] can_dock("can_dock", false), show_title("show_title", true), positioning("positioning", LLFloaterEnums::POSITIONING_RELATIVE), @@ -268,8 +267,6 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mHasBeenDraggedWhileMinimized(FALSE), mPreviousMinimizedBottom(0), mPreviousMinimizedLeft(0), - mTearOffSignal(NULL), -// [/SL:KB] mMinimizeSignal(NULL) // mNotificationContext(NULL) { @@ -555,9 +552,6 @@ LLFloater::~LLFloater() storeDockStateControl(); delete mMinimizeSignal; -// [SL:KB] - Patch: UI-FloaterTearOffSignal | Checked: 2011-11-12 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - delete mTearOffSignal; -// [/SL:KB] } void LLFloater::storeRectControl() @@ -599,16 +593,6 @@ void LLFloater::storeDockStateControl() } } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a -void LLFloater::storeTearOffStateControl() -{ - if ( (!sQuitting) && (mCanTearOff) && (mTearOffStateControl.size() > 1) ) - { - getControlGroup()->setBOOL(mTearOffStateControl, isTornOff()); - } -} -// [/SL:KB] - // static std::string LLFloater::getControlName(const std::string& name, const LLSD& key) { @@ -668,6 +652,17 @@ void LLFloater::setVisible( BOOL visible ) storeVisibilityControl(); } + +void LLFloater::setIsSingleInstance(BOOL is_single_instance) +{ + mSingleInstance = is_single_instance; + if (!mIsReuseInitialized) + { + mReuseInstance = is_single_instance; // reuse single-instance floaters by default + } +} + + // virtual void LLFloater::handleVisibilityChange ( BOOL new_visibility ) { @@ -683,14 +678,20 @@ void LLFloater::openFloater(const LLSD& key) { llinfos << "Opening floater " << getName() << llendl; mKey = key; // in case we need to open ourselves again - + if (getSoundFlags() != SILENT // don't play open sound for hosted (tabbed) windows && !getHost() && !getFloaterHost() && (!getVisible() || isMinimized())) { - make_ui_sound("UISndWindowOpen"); + //Don't play a sound for incoming voice call based upon chat preference setting + bool playSound = !(getName() == "incoming call" && gSavedSettings.getBOOL("PlaySoundIncomingVoiceCall") == FALSE); + + if(playSound) + { + make_ui_sound("UISndWindowOpen"); + } } //RN: for now, we don't allow rehosting from one multifloater to another @@ -767,6 +768,33 @@ void LLFloater::closeFloater(bool app_quitting) make_ui_sound("UISndWindowClose"); } + gFocusMgr.clearLastFocusForGroup(this); + + if (hasFocus()) + { + // Do this early, so UI controls will commit before the + // window is taken down. + releaseFocus(); + + // give focus to dependee floater if it exists, and we had focus first + if (isDependent()) + { + LLFloater* dependee = mDependeeHandle.get(); + if (dependee && !dependee->isDead()) + { + dependee->setFocus(TRUE); + } + } + } + + + //If floater is a dependent, remove it from parent (dependee) + LLFloater* dependee = mDependeeHandle.get(); + if (dependee) + { + dependee->removeDependentFloater(this); + } + // now close dependent floater for(handle_set_iter_t dependent_it = mDependents.begin(); dependent_it != mDependents.end(); ) @@ -785,28 +813,6 @@ void LLFloater::closeFloater(bool app_quitting) } cleanupHandles(); - gFocusMgr.clearLastFocusForGroup(this); - - if (hasFocus()) - { - // Do this early, so UI controls will commit before the - // window is taken down. - releaseFocus(); - - // give focus to dependee floater if it exists, and we had focus first - if (isDependent()) - { - LLFloater* dependee = mDependeeHandle.get(); - if (dependee && !dependee->isDead()) - { - dependee->setFocus(TRUE); - } - } - - // STORM-1879: since this floater has focus, treat the closeFloater- call - // like a click on the close-button, and close gear- and contextmenus - LLMenuGL::sMenuContainer->hideMenus(); - } dirtyRect(); @@ -842,6 +848,20 @@ void LLFloater::closeFloater(bool app_quitting) } } +/*virtual*/ +void LLFloater::closeHostedFloater() +{ + // When toggling *visibility*, close the host instead of the floater when hosted + if (getHost()) + { + getHost()->closeFloater(); + } + else + { + closeFloater(); + } +} + /*virtual*/ void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent) { @@ -902,38 +922,19 @@ LLMultiFloater* LLFloater::getHost() return (LLMultiFloater*)mHostHandle.get(); } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-11-25 (Catznip-3.2.0b) | Added: Catznip-3.2.0b -LLMultiFloater* LLFloater::getLastHost() const -{ - return (LLMultiFloater*)mLastHostHandle.get(); -} -// [/SL:KB] - void LLFloater::applyControlsAndPosition(LLFloater* other) { // if (!applyDockState()) // Don't apply dock state and forget about the undocked values { -// if (!applyRectControl()) -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-10-27 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - if ( (!applyRectControl()) && ((!getHost()) || (mTornOff)) ) -// [/SL:KB] + // Don't apply position to undocked IM floater (FIRE-5459) + //applyPositioning(other); CHUI Merge old + //if (!applyRectControl()) CHUI Merge new + if ((strcmp(getName().c_str(), "panel_im") != 0)) // CHUI Merge do we still want this? { - // Don't apply position to undocked IM floater (FIRE-5459) - //applyPositioning(other); - if ((strcmp(getName().c_str(), "panel_im") != 0)) - { - applyPositioning(other, true); - } - // Don't apply position to undocked IM floater (FIRE-5459) + applyPositioning(other, true); } + // Don't apply position to undocked IM floater (FIRE-5459) } - -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - if (getHost()) - { - applyTearOffState(); - } -// [/SL:KB] applyDockState(); // Only now apply docked state so floaters don't forget their positions } @@ -1088,17 +1089,6 @@ void LLFloater::applyPositioning(LLFloater* other, bool on_open) } } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a -void LLFloater::applyTearOffState() -{ - if ( (mCanTearOff) && (mTearOffStateControl.size() > 1) ) - { - bool tearoffState = getControlGroup()->getBOOL(mTearOffStateControl); - setTornOff(tearoffState); - } -} -// [/SL:KB] - void LLFloater::applyTitle() { if (!mDragHandle) @@ -1290,7 +1280,6 @@ void LLFloater::setMinimized(BOOL minimize) { // minimized flag should be turned on before release focus mMinimized = TRUE; - mExpandedRect = getRect(); // If the floater has been dragged while minimized in the @@ -1364,7 +1353,6 @@ void LLFloater::setMinimized(BOOL minimize) } setOrigin( mExpandedRect.mLeft, mExpandedRect.mBottom ); - if (mButtonsEnabled[BUTTON_RESTORE]) { mButtonsEnabled[BUTTON_MINIMIZE] = TRUE; @@ -1400,7 +1388,6 @@ void LLFloater::setMinimized(BOOL minimize) // Reshape *after* setting mMinimized reshape( mExpandedRect.getWidth(), mExpandedRect.getHeight(), TRUE ); - applyPositioning(NULL, false); } make_ui_sound("UISndWindowClose"); @@ -1522,7 +1509,6 @@ void LLFloater::setHost(LLMultiFloater* host) mButtonScale = 1.f; //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE; } - updateTitleButtons(); if (host) { mHostHandle = host->getHandle(); @@ -1532,6 +1518,8 @@ void LLFloater::setHost(LLMultiFloater* host) { mHostHandle.markDead(); } + + updateTitleButtons(); } void LLFloater::moveResizeHandlesToFront() @@ -1688,10 +1676,19 @@ void LLFloater::bringToFront( S32 x, S32 y ) // virtual -void LLFloater::setVisibleAndFrontmost(BOOL take_focus) +void LLFloater::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key) { - setVisible(TRUE); - setFrontmost(take_focus); + LLMultiFloater* hostp = getHost(); + if (hostp) + { + hostp->setVisible(TRUE); + hostp->setFrontmost(take_focus); + } + else + { + setVisible(TRUE); + setFrontmost(take_focus); + } } void LLFloater::setFrontmost(BOOL take_focus) @@ -1759,128 +1756,54 @@ void LLFloater::onClickMinimize(LLFloater* self) self->setMinimized( !self->isMinimized() ); } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a -void LLFloater::setTornOff(bool torn_off) +void LLFloater::onClickTearOff(LLFloater* self) { - if ( (!mCanTearOff) || (mTornOff == torn_off) ) + if (!self) return; - - LLMultiFloater* host_floater = getHost(); - if ( (torn_off) && (host_floater) ) // Tear off + S32 floater_header_size = self->mHeaderHeight; + LLMultiFloater* host_floater = self->getHost(); + if (host_floater) //Tear off { -// [SL:KB] - Patch: UI-FloaterTearSignal | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - if (mTearOffSignal) - (*mTearOffSignal)(this, LLSD(true)); -// [/SL:KB] - LLRect new_rect; - host_floater->removeFloater(this); + host_floater->removeFloater(self); // reparent to floater view - gFloaterView->addChild(this); + gFloaterView->addChild(self); - openFloater(getKey()); - - // get floater rect size from either the rect control or the saved rect -Zi - if (this->mRectControl.empty()) - // restore old size and position -Zi - new_rect= getExpandedRect(); - else - new_rect= getControlGroup()->getRect(mRectControl); - - // only force position for floaters that have an empty rect -Zi - if(new_rect.isEmpty()) + self->openFloater(self->getKey()); + if (self->mSaveRect && !self->mRectControl.empty()) { - llwarns << "no rect saved yet, so reshape the floater" << llendl; - new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - mHeaderHeight - 5, getRect().getWidth(), getRect().getHeight()); - setRect(new_rect); + self->applyRectControl(); } - setShape(new_rect); - storeRectControl(); - - gFloaterView->adjustToFitScreen(this, FALSE); + else + { // only force position for floaters that don't have that data saved + new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight()); + self->setRect(new_rect); + } + gFloaterView->adjustToFitScreen(self, FALSE); // give focus to new window to keep continuity for the user - setFocus(TRUE); - mTornOff = true;; + self->setFocus(TRUE); + self->setTornOff(true); } - else if (!torn_off) // Attach to parent. + else //Attach to parent. { - storeRectControl(); - // Mark floater as hosted here already, so any storeRectControl() call - // will know NOT to save floater sizes. - mTornOff = false; // - - // save the current size and position -Zi - setExpandedRect( getRect() ); - LLMultiFloater* new_host = (LLMultiFloater*)mLastHostHandle.get(); + LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get(); if (new_host) { -// [SL:KB] - Patch: UI-FloaterTearSignal | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - if (mTearOffSignal) - (*mTearOffSignal)(this, LLSD(false)); -// [/SL:KB] - - setMinimized(FALSE); // to reenable minimize button if it was minimized - new_host->showFloater(this); + if (self->mSaveRect) + { + self->storeRectControl(); + } + self->setMinimized(FALSE); // to reenable minimize button if it was minimized + new_host->showFloater(self); // make sure host is visible new_host->openFloater(new_host->getKey()); } - // mTornOff = false; // This would be too late, so we moved it up + self->setTornOff(false); } - updateTitleButtons(); - - storeTearOffStateControl(); - storeVisibilityControl(); // Make sure to update visibility, too + self->updateTitleButtons(); + self->setOpenPositioning(LLFloaterEnums::POSITIONING_RELATIVE); } -void LLFloater::onClickTearOff(LLFloater* self) -{ - if ( (self) && (self->mCanTearOff) ) - { - self->setTornOff(!self->mTornOff); - } -} -// [/SL:KB] -//void LLFloater::onClickTearOff(LLFloater* self) -//{ -// if (!self) -// return; -// S32 floater_header_size = self->mHeaderHeight; -// LLMultiFloater* host_floater = self->getHost(); -// if (host_floater) //Tear off -// { -// LLRect new_rect; -// host_floater->removeFloater(self); -// // reparent to floater view -// gFloaterView->addChild(self); -// -// self->openFloater(self->getKey()); -// -// // only force position for floaters that don't have that data saved -// if (self->mRectControl.size() <= 1) -// { -// new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - floater_header_size - 5, self->getRect().getWidth(), self->getRect().getHeight()); -// self->setRect(new_rect); -// } -// gFloaterView->adjustToFitScreen(self, FALSE); -// // give focus to new window to keep continuity for the user -// self->setFocus(TRUE); -// self->setTornOff(true); -// } -// else //Attach to parent. -// { -// LLMultiFloater* new_host = (LLMultiFloater*)self->mLastHostHandle.get(); -// if (new_host) -// { -// self->setMinimized(FALSE); // to reenable minimize button if it was minimized -// new_host->showFloater(self); -// // make sure host is visible -// new_host->openFloater(new_host->getKey()); -// } -// self->setTornOff(false); -// } -// self->updateTitleButtons(); -//} - // static void LLFloater::onClickDock(LLFloater* self) { @@ -1904,6 +1827,18 @@ void LLFloater::onClickHelp( LLFloater* self ) } } +void LLFloater::initRectControl() +{ + // save_rect and save_visibility only apply to registered floaters + if (mSaveRect) + { + std::string ctrl_name = getControlName(mInstanceName, mKey); + mRectControl = LLFloaterReg::declareRectControl(ctrl_name); + mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name); + mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name); + } +} + // static void LLFloater::closeFrontmostFloater() { @@ -2357,7 +2292,8 @@ LLFloaterView::LLFloaterView (const Params& p) mFocusCycleMode(FALSE), mMinimizePositionVOffset(0), mSnapOffsetBottom(0), - mSnapOffsetRight(0) + mSnapOffsetRight(0), + mFrontChild(NULL) { mSnapView = getHandle(); } @@ -2507,6 +2443,17 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) { + if (mFrontChild == child) + { + if (give_focus && !gFocusMgr.childHasKeyboardFocus(child)) + { + child->setFocus(TRUE); + } + return; + } + + mFrontChild = child; + // *TODO: make this respect floater's mAutoFocus value, instead of // using parameter if (child->getHost()) @@ -2514,15 +2461,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) // this floater is hosted elsewhere and hence not one of our children, abort return; } - std::vector floaters_to_move; + std::vector floaters_to_move; // Look at all floaters...tab - for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it) + for (child_list_const_iter_t child_it = beginChild(); child_it != endChild(); ++child_it) { - LLView* viewp = *child_it; - LLFloater *floater = (LLFloater *)viewp; + LLFloater* floater = dynamic_cast(*child_it); // ...but if I'm a dependent floater... - if (child->isDependent()) + if (floater && child->isDependent()) { // ...look for floaters that have me as a dependent... LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle()); @@ -2530,15 +2476,14 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) if (found_dependent != floater->mDependents.end()) { // ...and make sure all children of that floater (including me) are brought to front... - for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); - dependent_it != floater->mDependents.end(); ) + for (LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin(); + dependent_it != floater->mDependents.end(); ++dependent_it) { LLFloater* sibling = dependent_it->get(); if (sibling) { floaters_to_move.push_back(sibling); } - ++dependent_it; } //...before bringing my parent to the front... floaters_to_move.push_back(floater); @@ -2546,10 +2491,10 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) } } - std::vector::iterator view_it; - for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it) + std::vector::iterator floater_it; + for(floater_it = floaters_to_move.begin(); floater_it != floaters_to_move.end(); ++floater_it) { - LLFloater* floaterp = (LLFloater*)(*view_it); + LLFloater* floaterp = *floater_it; sendChildToFront(floaterp); // always unminimize dependee, but allow dependents to stay minimized @@ -2561,23 +2506,19 @@ void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus) floaters_to_move.clear(); // ...then bringing my own dependents to the front... - for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin(); - dependent_it != child->mDependents.end(); ) + for (LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin(); + dependent_it != child->mDependents.end(); ++dependent_it) { LLFloater* dependent = dependent_it->get(); if (dependent) { sendChildToFront(dependent); - //don't un-minimize dependent windows automatically - // respect user's wishes - //dependent->setMinimized(FALSE); } - ++dependent_it; } // ...and finally bringing myself to front // (do this last, so that I'm left in front at end of this call) - if( *getChildList()->begin() != child ) + if (*beginChild() != child) { sendChildToFront(child); } @@ -3129,21 +3070,14 @@ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list) void LLFloater::setInstanceName(const std::string& name) { - if (name == mInstanceName) - return; + if (name != mInstanceName) + { llassert_always(mInstanceName.empty()); mInstanceName = name; if (!mInstanceName.empty()) { std::string ctrl_name = getControlName(mInstanceName, mKey); - - // save_rect and save_visibility only apply to registered floaters - if (mSaveRect) - { - mRectControl = LLFloaterReg::declareRectControl(ctrl_name); - mPosXControl = LLFloaterReg::declarePosXControl(ctrl_name); - mPosYControl = LLFloaterReg::declarePosYControl(ctrl_name); - } + initRectControl(); if (!mVisibilityControl.empty()) { mVisibilityControl = LLFloaterReg::declareVisibilityControl(ctrl_name); @@ -3152,14 +3086,9 @@ void LLFloater::setInstanceName(const std::string& name) { mDocStateControl = LLFloaterReg::declareDockStateControl(ctrl_name); } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - if(!mTearOffStateControl.empty()) - { - mTearOffStateControl = LLFloaterReg::declareTearOffStateControl(ctrl_name); - } -// [/SL:KB] } } +} void LLFloater::setKey(const LLSD& newkey) { @@ -3243,12 +3172,6 @@ void LLFloater::initFromParams(const LLFloater::Params& p) { mDocStateControl = "t"; // flag to build mDocStateControl name once mInstanceName is set } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - if (p.save_tearoff_state) - { - mTearOffStateControl = "t"; // flag to build mTearOffStateControl name once mInstanceName is set - } -// [/SL:KB] // open callback if (p.open_callback.isProvided()) @@ -3273,14 +3196,6 @@ boost::signals2::connection LLFloater::setMinimizeCallback( const commit_signal_ return mMinimizeSignal->connect(cb); } -// [SL:KB] - Patch: UI-FloaterTearOffSignal | Checked: 2011-11-12 (Catznip-3.2.0a) | Added: Catznip-3.2.0a -boost::signals2::connection LLFloater::setTearOffCallback( const commit_signal_t::slot_type& cb ) -{ - if (!mTearOffSignal) mTearOffSignal = new commit_signal_t(); - return mTearOffSignal->connect(cb); -} -// [/SL:KB] - boost::signals2::connection LLFloater::setOpenCallback( const commit_signal_t::slot_type& cb ) { return mOpenSignal.connect(cb); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 921a8ff925..dfc786bf14 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -161,9 +161,6 @@ public: save_rect, save_visibility, save_dock_state, -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - save_tearoff_state, -// [/SL:KB] can_dock, show_title; @@ -210,9 +207,6 @@ public: bool buildFromFile(const std::string &filename); boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb ); -// [SL:KB] - Patch: UI-FloaterTearOffSignal | Checked: 2011-11-12 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - boost::signals2::connection setTearOffCallback( const commit_signal_t::slot_type& cb ); -// [/SL:KB] boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setCloseCallback( const commit_signal_t::slot_type& cb ); @@ -225,13 +219,17 @@ public: /*virtual*/ void setFocus( BOOL b ); /*virtual*/ void setIsChrome(BOOL is_chrome); /*virtual*/ void setRect(const LLRect &rect); + void setIsSingleInstance(BOOL is_single_instance); void initFloater(const Params& p); void openFloater(const LLSD& key = LLSD()); // If allowed, close the floater cleanly, releasing focus. - void closeFloater(bool app_quitting = false); + virtual void closeFloater(bool app_quitting = false); + + // Close the floater or its host. Use when hidding or toggling a floater instance. + virtual void closeHostedFloater(); /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); @@ -242,9 +240,6 @@ public: void center(); LLMultiFloater* getHost(); -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-11-25 (Catznip-3.2.0b) | Added: Catznip-3.2.0b - LLMultiFloater* getLastHost() const; -// [/SL:KB] void applyTitle(); std::string getCurrentTitle() const; @@ -312,6 +307,7 @@ public: /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); // do not override void setFrontmost(BOOL take_focus = TRUE); + virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); // Defaults to false. virtual BOOL canSaveAs() const { return FALSE; } @@ -334,10 +330,9 @@ public: bool isDocked() const { return mDocked; } virtual void setDocked(bool docked, bool pop_on_undock = true); -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - bool isTornOff() const { return mTornOff; } - virtual void setTornOff(bool torn_off); -// [/SL:KB] + virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } + bool isTornOff() {return mTornOff;} + void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mPositioning = pos;} // Close the floater returned by getFrontmostClosableFloater() and @@ -368,26 +363,20 @@ protected: void stackWith(LLFloater& other); + virtual void initRectControl(); virtual bool applyRectControl(); bool applyDockState(); void applyPositioning(LLFloater* other, bool on_open); void applyRelativePosition(); -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - void applyTearOffState(); -// [/SL:KB] void storeRectControl(); void storeVisibilityControl(); void storeDockStateControl(); -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - void storeTearOffStateControl(); -// [/SL:KB] void setKey(const LLSD& key); void setInstanceName(const std::string& name); virtual void bringToFront(S32 x, S32 y); - virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE); void setExpandedRect(const LLRect& rect) { mExpandedRect = rect; } // size when not minimized const LLRect& getExpandedRect() const { return mExpandedRect; } @@ -440,9 +429,6 @@ public: commit_signal_t mCloseSignal; commit_signal_t* mMinimizeSignal; -// [SL:KB] - Patch: UI-FloaterTearOffSignal | Checked: 2011-11-12 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - commit_signal_t* mTearOffSignal; -// [/SL:KB] protected: bool mSaveRect; @@ -451,10 +437,6 @@ protected: std::string mPosYControl; std::string mVisibilityControl; std::string mDocStateControl; -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - std::string mTearOffStateControl; -// [/SL:KB] - LLSD mKey; // Key used for retrieving instances; set (for now) by LLFLoaterReg LLDragHandle* mDragHandle; @@ -468,9 +450,10 @@ private: LLUIString mTitle; LLUIString mShortTitle; - BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater - bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it - std::string mInstanceName; // Store the instance name so we can remove ourselves from the list + BOOL mSingleInstance; // TRUE if there is only ever one instance of the floater + bool mReuseInstance; // true if we want to hide the floater when we close it instead of destroying it + bool mIsReuseInitialized; // true if mReuseInstance already set from parameters + std::string mInstanceName; // Store the instance name so we can remove ourselves from the list BOOL mDropShadow; // ## Zi: Optional Drop Shadows S32 mLabelVPadding; // Make vertical label padding a per-skin option @@ -599,6 +582,7 @@ private: S32 mMinimizePositionVOffset; typedef std::vector, boost::signals2::connection> > hidden_floaters_t; hidden_floaters_t mHiddenFloaters; + LLFloater * mFrontChild; }; // diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index eb7b92c320..a082d61757 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -296,17 +296,9 @@ bool LLFloaterReg::hideInstance(const std::string& name, const LLSD& key) LLFloater* instance = findInstance(name, key); if (instance) { - // When toggling *visibility*, close the host instead of the floater when hosted - if (instance->getHost()) - instance->getHost()->closeFloater(); - else - instance->closeFloater(); - return true; - } - else - { - return false; + instance->closeHostedFloater(); } + return (instance != NULL); } //static @@ -316,11 +308,7 @@ bool LLFloaterReg::toggleInstance(const std::string& name, const LLSD& key) LLFloater* instance = findInstance(name, key); if (LLFloater::isShown(instance)) { - // When toggling *visibility*, close the host instead of the floater when hosted - if (instance->getHost()) - instance->getHost()->closeFloater(); - else - instance->closeFloater(); + instance->closeHostedFloater(); return false; } else @@ -471,24 +459,6 @@ std::string LLFloaterReg::getDockStateControlName(const std::string& name) return res; } -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a -//static -std::string LLFloaterReg::declareTearOffStateControl(const std::string& name) -{ - std::string controlname = getTearOffStateControlName(name); - LLFloater::getControlGroup()->declareBOOL(controlname, TRUE, llformat("Window Tear Off state for %s", name.c_str()), TRUE); - return controlname; -} - -//static -std::string LLFloaterReg::getTearOffStateControlName(const std::string& name) -{ - std::string res = std::string("floater_tearoff_") + name; - LLStringUtil::replaceChar(res, ' ', '_'); - return res; -} -// [/SL:KB] - //static void LLFloaterReg::registerControlVariables() { @@ -536,51 +506,59 @@ void LLFloaterReg::toggleInstanceOrBringToFront(const LLSD& sdname, const LLSD& // * Also, if it is not on top, bring it forward when focus is given. // * Else the target floater is open, close it. // - std::string name = sdname.asString(); LLFloater* instance = getInstance(name, key); + if (!instance) { lldebugs << "Unable to get instance of floater '" << name << "'" << llendl; + return; } - else if (instance->isMinimized()) + + // If hosted, we need to take that into account + LLFloater* host = instance->getHost(); + + if (host) { - instance->setMinimized(FALSE); - instance->setVisibleAndFrontmost(); - } - else if (!instance->isShown()) - { -// [RLVa:KB] - Checked: 2011-12-17 (RLVa-1.4.5a) | Added: RLVa-1.4.5a - // [See LLFloaterReg::showInstance()] - if ( ((!sBlockShowFloaters) || (sAlwaysShowableList.find(name) != sAlwaysShowableList.end())) && (mValidateSignal(name, key)) ) + if (host->isMinimized() || !host->isShown() || !host->isFrontmost()) + { + host->setMinimized(FALSE); + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->getVisible()) { instance->openFloater(key); - instance->setVisibleAndFrontmost(); + instance->setVisibleAndFrontmost(true, key); + instance->setFocus(TRUE); + } + else + { + instance->closeHostedFloater(); } -// [/RLVa:KB] -// instance->openFloater(key); -// instance->setVisibleAndFrontmost(); } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-11-23 (Catznip-3.2.0b) | Modified: Catznip-3.2.0b else { - // Give focus to, or close, the host rather than the floater when hosted - LLFloater* floaterp = (!instance->getHost()) ? instance : instance->getHost(); - if (!floaterp->isFrontmost() || !floaterp->hasFocus()) - floaterp->setVisibleAndFrontmost(); + if (instance->isMinimized()) + { + instance->setMinimized(FALSE); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isShown()) + { + instance->openFloater(key); + instance->setVisibleAndFrontmost(true, key); + } + else if (!instance->isFrontmost()) + { + instance->setVisibleAndFrontmost(true, key); + } else - floaterp->closeFloater(); + { + instance->closeHostedFloater(); + } } -// [/SL:KB] -// else if (!instance->isFrontmost()) -// { -// instance->setVisibleAndFrontmost(); -// } -// else -// { -// instance->closeFloater(); -// } } // static diff --git a/indra/llui/llfloaterreg.h b/indra/llui/llfloaterreg.h index 51d9a84cc2..fe80db69e3 100644 --- a/indra/llui/llfloaterreg.h +++ b/indra/llui/llfloaterreg.h @@ -151,11 +151,6 @@ public: static std::string declareDockStateControl(const std::string& name); static std::string getDockStateControlName(const std::string& name); -// [SL:KB] - Patch: UI-FloaterTearOffState | Checked: 2011-09-30 (Catznip-3.2.0a) | Added: Catznip-3.0.0a - static std::string declareTearOffStateControl(const std::string& name); - static std::string getTearOffStateControlName(const std::string& name); -// [/SL:KB] - static void registerControlVariables(); // Callback wrappers diff --git a/indra/newview/llfolderview.cpp b/indra/llui/llfolderview.cpp similarity index 62% rename from indra/newview/llfolderview.cpp rename to indra/llui/llfolderview.cpp index bf01bebbb3..99a848b749 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -24,39 +24,20 @@ * $/LicenseInfo$ */ -#include "llviewerprecompiledheaders.h" +#include "linden_common.h" #include "llfolderview.h" - -#include "llcallbacklist.h" -#include "llinventorybridge.h" +#include "llfolderviewmodel.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 "lltextbox.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" @@ -64,7 +45,6 @@ #include "llfontgl.h" #include "llgl.h" #include "llrender.h" -#include "llinventory.h" // Third-party library includes #include @@ -76,11 +56,7 @@ 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. @@ -94,42 +70,6 @@ enum { 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 @@ -154,7 +94,6 @@ public: }; -// Set the sort order. void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder) { folder->setOpenArrangeRecursively(mOpen); @@ -177,7 +116,7 @@ const LLRect LLFolderViewScrollContainer::getScrolledViewRect() const LLFolderView* folder_view = dynamic_cast(mScrolledView); if (folder_view) { - S32 height = folder_view->mRunningHeight; + S32 height = folder_view->getRect().getHeight(); rect = mScrolledView->getRect(); rect.setLeftTopAndSize(rect.mLeft, rect.mTop, rect.getWidth(), height); @@ -195,27 +134,25 @@ LLFolderViewScrollContainer::LLFolderViewScrollContainer(const LLScrollContainer /// Class LLFolderView ///---------------------------------------------------------------------------- LLFolderView::Params::Params() -: task_id("task_id"), - title("title"), +: 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) + use_ellipses("use_ellipses", false), + options_menu("options_menu", "") { + folder_indentation = -4; } // 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), @@ -223,9 +160,6 @@ LLFolderView::LLFolderView(const Params& p) 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), @@ -237,33 +171,27 @@ LLFolderView::LLFolderView(const Params& p) mUseEllipses(p.use_ellipses), mDraggingOverItem(NULL), mStatusTextBox(NULL), - mSelectionChanged(false) + mShowItemLinkOverlays(p.show_item_link_overlays), + mViewModel(p.view_model) { + mViewModel->setFolderView(this); 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(); - S32 indentation = item_params.folder_indentation(); - mIndentation = -indentation; // children start at indentation 0 - gIdleCallbacks.addFunction(idle, this); + mIndentation = p.folder_indentation; //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"); @@ -280,10 +208,11 @@ LLFolderView::LLFolderView(const Params& p) // Textbox LLTextBox::Params text_p; LLFontGL* font = getLabelFontForStyle(mLabelStyle); - LLRect new_r = LLRect(rect.mLeft + ICON_PAD, - rect.mTop - TEXT_PAD, + //mIconPad, mTextPad are set in folder_view_item.xml + LLRect new_r = LLRect(rect.mLeft + mIconPad, + rect.mTop - mTextPad, rect.mRight, - rect.mTop - TEXT_PAD - font->getLineHeight()); + rect.mTop - mTextPad - font->getLineHeight()); text_p.rect(new_r); text_p.name(std::string(p.name)); text_p.font(font); @@ -300,7 +229,7 @@ LLFolderView::LLFolderView(const Params& p) // make the popup menu available - LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile(p.options_menu, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); if (!menu) { menu = LLUICtrlFactory::getDefaultWidget("inventory_menu"); @@ -308,7 +237,7 @@ LLFolderView::LLFolderView(const Params& p) menu->setBackgroundColor(LLUIColorTable::instance().getColor("MenuPopupBgColor")); mPopupMenuHandle = menu->getHandle(); - mListener->openItem(); + mViewModelItem->openItem(); mFolderviewProxy = 0; // FIRE-4702; } @@ -334,7 +263,6 @@ LLFolderView::~LLFolderView( void ) mStatusTextBox = NULL; mAutoOpenItems.removeAllNodes(); - gIdleCallbacks.deleteFunction(idle, this); if (mPopupMenuHandle.get()) mPopupMenuHandle.get()->die(); @@ -343,10 +271,7 @@ LLFolderView::~LLFolderView( void ) mItems.clear(); mFolders.clear(); - mItemMap.clear(); - - delete mFilter; - mFilter = NULL; + mViewModel = NULL; } BOOL LLFolderView::canFocusChildren() const @@ -354,46 +279,9 @@ BOOL LLFolderView::canFocusChildren() const return FALSE; } -static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); - -void LLFolderView::setSortOrder(U32 order) +void LLFolderView::addFolder( LLFolderViewFolder* folder) { - 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; + LLFolderViewFolder::addFolder(folder); } void LLFolderView::closeAllFolders() @@ -413,145 +301,39 @@ void LLFolderView::openTopLevelFolders() } } -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()) +// *width should be 0 +// conform show folder state works +S32 LLFolderView::arrange( S32* unused_width, S32* unused_height ) { - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - } - - LLFastTimer t2(FTM_ARRANGE); - - filter_generation = mFilter->getMinRequiredGeneration(); mMinWidth = 0; + S32 target_height; - mHasVisibleChildren = hasFilteredDescendants(filter_generation); - // arrange always finishes, so optimistically set the arrange generation to the most current - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); + LLFolderViewFolder::arrange(&mMinWidth, &target_height); - LLInventoryFilter::EFolderShow show_folder_state = - getRoot()->getFilter()->getShowFolderState(); + LLRect scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); + reshape( llmax(scroll_rect.getWidth(), mMinWidth), llround(mCurHeight) ); - 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(); + LLRect new_scroll_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) { - reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); + reshape( llmax(scroll_rect.getWidth(), mMinWidth), llround(mCurHeight) ); } // 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 Folder View"); -static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory"); - -void LLFolderView::filter( LLInventoryFilter& filter ) +void LLFolderView::filter( LLFolderViewFilter& filter ) { LLFastTimer t2(FTM_FILTER); - filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); + filter.setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000)); - if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) - { - mPassedFilter = FALSE; - mMinWidth = 0; - LLFolderViewFolder::filter(filter); - } - else - { - mPassedFilter = TRUE; - } + getViewModelItem()->filter(filter); } void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) @@ -563,7 +345,7 @@ void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) scroll_rect = mScrollContainer->getContentWindowRect(); } width = llmax(mMinWidth, scroll_rect.getWidth()); - height = llmax(mRunningHeight, scroll_rect.getHeight()); + height = llmax(llround(mCurHeight), scroll_rect.getHeight()); // Restrict width within scroll container's width if (mUseEllipses && mScrollContainer) @@ -626,6 +408,10 @@ LLFolderViewItem* LLFolderView::getCurSelectedItem( void ) return NULL; } +LLFolderView::selected_items_t& LLFolderView::getSelectedItems( void ) +{ + return mSelectedItems; +} // Record the selected item and pass it down the hierachy. BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, @@ -663,30 +449,6 @@ BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL openitem, 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; @@ -744,9 +506,6 @@ void LLFolderView::sanitizeSelection() // 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 items_to_remove; selected_items_t::iterator item_iter; for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter) @@ -754,24 +513,19 @@ void LLFolderView::sanitizeSelection() 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 + BOOL visible = false; + if(item->getViewModelItem() != NULL) + { + visible = item->getViewModelItem()->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 + // Move up through parent folders and see what's visible while(parent_folder) { - visible = visible && parent_folder->isOpen() && parent_folder->potentiallyVisible(); + visible = visible && parent_folder->isOpen() && parent_folder->getViewModelItem()->potentiallyVisible(); parent_folder = parent_folder->getParentFolder(); } - } - } // deselect item if any ancestor is closed or didn't pass filter requirements. if (!visible) @@ -821,7 +575,7 @@ void LLFolderView::sanitizeSelection() parent_folder; parent_folder = parent_folder->getParentFolder()) { - if (parent_folder->potentiallyVisible()) + if (parent_folder->getViewModelItem() && parent_folder->getViewModelItem()->potentiallyVisible()) { // give initial selection to first ancestor folder that potentially passes the filter if (!new_selection) @@ -860,43 +614,31 @@ void LLFolderView::clearSelection() } mSelectedItems.clear(); - mSelectThisID.setNull(); mSelectionChanged = true; } -std::set LLFolderView::getSelectionList() const +std::set LLFolderView::getSelectionList() const { - std::set selection; - for (selected_items_t::const_iterator item_it = mSelectedItems.begin(); - item_it != mSelectedItems.end(); - ++item_it) - { - selection.insert((*item_it)->getListener()->getUUID()); - } + std::set selection; + std::copy(mSelectedItems.begin(), mSelectedItems.end(), std::inserter(selection, selection.begin())); return selection; } -BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source) +bool LLFolderView::startDrag() { - std::vector types; - uuid_vec_t cargo_ids; + std::vector selected_items; 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); + selected_items.push_back((*item_it)->getViewModelItem()); } - LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, source, mSourceID); + return getFolderViewModel()->startDrag(selected_items); } - return can_drag; + return false; } void LLFolderView::commitRename( const LLSD& data ) @@ -906,15 +648,6 @@ void LLFolderView::commitRename( const LLSD& data ) void LLFolderView::draw() { - 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 @@ -924,52 +657,18 @@ void LLFolderView::draw() 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()) + if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout") || !mSearchString.size()) { mSearchString.clear(); } - if (hasVisibleChildren() - || mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS) + if (hasVisibleChildren()) { - 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->setValue(getFolderViewModel()->getStatusText()); mStatusTextBox->setVisible( TRUE ); // firstly reshape message textbox with current size. This is necessary to @@ -986,7 +685,11 @@ void LLFolderView::draw() // 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(); + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); + LLUI::popMatrix(); LLUI::pushMatrix(); LLUI::translate((F32)getRect().mLeft, (F32)getRect().mBottom); @@ -1013,7 +716,7 @@ void LLFolderView::finishRenamingItem( void ) closeRenamer(); - // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible. + // List is re-sorted alphabetically, so scroll to make sure the selected item is visible. scrollToShowSelection(); } @@ -1022,76 +725,12 @@ void LLFolderView::closeRenamer( void ) if (mRenamer && mRenamer->getVisible()) { // Triggers onRenamerLost() that actually closes the renamer. - gViewerWindow->removePopup(mRenamer); + LLUI::removePopup(mRenamer); } } -void LLFolderView::removeSelectedItems( void ) +void LLFolderView::removeSelectedItems() { - if (mSelectedItems.empty()) return; - LLSD args; - args["QUESTION"] = LLTrans::getString(mSelectedItems.size() > 1 ? "DeleteItems" : "DeleteItem"); - - // FIRE-4702; - - // LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLFolderView::onItemsRemovalConfirmation, this, _1, _2)); - - mFolderviewProxy = new FolderviewProxy(); - mFolderviewProxy->mFolderview = this; - LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&FolderviewProxy::onItemsRemovalConfirmation, mFolderviewProxy, _1, _2)); - // -} - -bool isDescendantOfASelectedItem(LLFolderViewItem* item, const std::vector& selectedItems) -{ - LLFolderViewItem* item_parent = dynamic_cast(item->getParent()); - - if (item_parent) - { - for(std::vector::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(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 objects; - LLClipboard::instance().pasteFromClipboard(objects); - for (LLDynamicArray::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. @@ -1122,68 +761,38 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL // iterate through the new container. count = items.size(); LLUUID new_selection_id; + LLFolderViewItem* item_to_select = getNextUnselectedItem(); + 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)) + if (item_to_delete->remove()) { // change selection on successful delete - if (new_selection) - { - setSelectionFromRoot(new_selection, new_selection->isOpen(), mParentPanel->hasFocus()); - } - else - { - setSelectionFromRoot(NULL, mParentPanel->hasFocus()); - } + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel->hasFocus()); } } arrangeAll(); } else if (count > 1) { - LLDynamicArray 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()); - } + LLDynamicArray listeners; + LLFolderViewModelItem* listener; + + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel->hasFocus()); for(S32 i = 0; i < count; ++i) { - listener = items[i]->getListener(); - if(listener && (listeners.find(listener) == LLDynamicArray::FAIL)) + listener = items[i]->getViewModelItem(); + if(listener && (listeners.find(listener) == LLDynamicArray::FAIL)) { listeners.put(listener); } } - listener = listeners.get(0); + listener = static_cast(listeners.get(0)); if(listener) { listener->removeBatch(listeners); @@ -1194,82 +803,6 @@ void LLFolderView::onItemsRemovalConfirmation(const LLSD& notification, const LL } } -// 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) || @@ -1293,7 +826,7 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); item->setOpen(TRUE); - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); scrollToShowItem(item, constraint_rect); } @@ -1354,7 +887,7 @@ BOOL LLFolderView::canCopy() const 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()) + if (!item->getViewModelItem()->isItemCopyable()) { return FALSE; } @@ -1370,11 +903,11 @@ void LLFolderView::copy() S32 count = mSelectedItems.size(); if(getVisible() && getEnabled() && (count > 0)) { - LLFolderViewEventListener* listener = NULL; + LLFolderViewModelItem* listener = NULL; selected_items_t::iterator item_it; for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) { - listener = (*item_it)->getListener(); + listener = (*item_it)->getViewModelItem(); if(listener) { listener->copyToClipboard(); @@ -1394,7 +927,7 @@ BOOL LLFolderView::canCut() const 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(); + const LLFolderViewModelItem* listener = item->getViewModelItem(); if (!listener || !listener->isItemRemovable()) { @@ -1408,22 +941,28 @@ void LLFolderView::cut() { // clear the inventory clipboard LLClipboard::instance().reset(); - S32 count = mSelectedItems.size(); - if(getVisible() && getEnabled() && (count > 0)) + if(getVisible() && getEnabled() && (mSelectedItems.size() > 0)) { - LLFolderViewEventListener* listener = NULL; - selected_items_t::iterator item_it; - for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + // Find out which item will be selected once the selection will be cut + LLFolderViewItem* item_to_select = getNextUnselectedItem(); + + // Get the selection: removeItem() modified mSelectedItems and makes iterating on it unwise + std::set inventory_selected = getSelectionList(); + + // Move each item to the clipboard and out of their folder + for (std::set::iterator item_it = inventory_selected.begin(); item_it != inventory_selected.end(); ++item_it) { - listener = (*item_it)->getListener(); - if(listener) + LLFolderViewItem* item_to_cut = *item_it; + LLFolderViewModelItem* listener = item_to_cut->getViewModelItem(); + if (listener) { listener->cutToClipboard(); + listener->removeItem(); } } - // FIRE-6714: Don't move objects to trash during cut&paste - //LLFolderView::removeCutItems(); - // FIRE-6714: Don't move objects to trash during cut&paste + + // Update the selection + setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, mParentPanel->hasFocus()); } mSearchString.clear(); } @@ -1444,7 +983,7 @@ BOOL LLFolderView::canPaste() const { // *TODO: only check folders and parent folders of items const LLFolderViewItem* item = (*item_it); - const LLFolderViewEventListener* listener = item->getListener(); + const LLFolderViewModelItem* listener = item->getViewModelItem(); // Nicky D.; Only run each isClipboardPasteable once for each type of listeners. // isClipboardPasteable is a very expensive operation, and it will always process the whole clipboards contents. @@ -1458,7 +997,7 @@ BOOL LLFolderView::canPaste() const if(!listener || !listener->isClipboardPasteable()) { const LLFolderViewFolder* folderp = item->getParentFolder(); - listener = folderp->getListener(); + listener = folderp->getViewModelItem(); if (!listener || !listener->isClipboardPasteable()) { return FALSE; @@ -1476,24 +1015,24 @@ void LLFolderView::paste() if(getVisible() && getEnabled()) { // find set of unique folders to paste into - std::set folder_set; + std::set 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) + LLFolderViewFolder* folder = dynamic_cast(item); + if (folder == NULL) { - item = item->getParentFolder(); + folder = item->getParentFolder(); } - folder_set.insert(item); + folder_set.insert(folder); } - std::set::iterator set_iter; + std::set::iterator set_iter; for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter) { - LLFolderViewEventListener* listener = (*set_iter)->getListener(); + LLFolderViewModelItem* listener = (*set_iter)->getViewModelItem(); if(listener && listener->isClipboardPasteable()) { listener->pasteFromClipboard(); @@ -1515,8 +1054,8 @@ void LLFolderView::startRenamingSelectedItem( void ) { item = mSelectedItems.front(); } - if(getVisible() && getEnabled() && (count == 1) && item && item->getListener() && - item->getListener()->isItemRenameable()) + if(getVisible() && getEnabled() && (count == 1) && item && item->getViewModelItem() && + item->getViewModelItem()->isItemRenameable()) { mRenameItem = item; @@ -1529,7 +1068,7 @@ void LLFolderView::startRenamingSelectedItem( void ) // set focus will fail unless item is visible mRenamer->setFocus( TRUE ); mRenamer->setTopLostCallback(boost::bind(&LLFolderView::onRenamerLost, this)); - gViewerWindow->addPopup(mRenamer); + LLUI::addPopup(mRenamer); } } @@ -1562,11 +1101,6 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) mSearchString.clear(); handled = TRUE; } - else - { - LLFolderView::openSelectedItems(); - handled = TRUE; - } } break; @@ -1581,25 +1115,37 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) case KEY_PAGE_UP: mSearchString.clear(); + if (mScrollContainer) + { mScrollContainer->pageUp(30); + } handled = TRUE; break; case KEY_PAGE_DOWN: mSearchString.clear(); + if (mScrollContainer) + { mScrollContainer->pageDown(30); + } handled = TRUE; break; case KEY_HOME: mSearchString.clear(); + if (mScrollContainer) + { mScrollContainer->goToTop(); + } handled = TRUE; break; case KEY_END: mSearchString.clear(); + if (mScrollContainer) + { mScrollContainer->goToBottom(); + } break; case KEY_DOWN: @@ -1623,12 +1169,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (next->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == next->getParentFolder()) { // grow selection - changeSelectionFromRoot(next, TRUE); + changeSelection(next, TRUE); } } } @@ -1687,12 +1233,12 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if (prev->isSelected()) { // shrink selection - changeSelectionFromRoot(last_selected, FALSE); + changeSelection(last_selected, FALSE); } else if (last_selected->getParentFolder() == prev->getParentFolder()) { // grow selection - changeSelectionFromRoot(prev, TRUE); + changeSelection(prev, TRUE); } } } @@ -1752,20 +1298,6 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) 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; } @@ -1795,7 +1327,7 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) } //do text search - if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout")) + if (mSearchTimer.getElapsedTimeF32() > LLUI::sSettingGroups["config"]->getF32("TypeAheadTimeout")) { mSearchString.clear(); } @@ -1813,29 +1345,6 @@ BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char) } -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; @@ -1891,7 +1400,7 @@ BOOL LLFolderView::search(LLFolderViewItem* first_item, const std::string &searc // FIRE-8253 } - const std::string current_item_label(search_item->getSearchableLabel()); + const std::string current_item_label(search_item->getViewModelItem()->getSearchableName()); 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)) { @@ -1935,19 +1444,23 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) 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 + && ( count > 0 && (hasVisibleChildren()) ) // 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()) @@ -2010,24 +1523,8 @@ BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // 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; } @@ -2071,17 +1568,16 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr if(item) { LLRect local_rect = item->getLocalRect(); - LLRect item_scrolled_rect; // item position relative to display area of scroller - 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(); + S32 max_height_to_show = item->isOpen() && mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + item->getIconPad()) : 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()), + //+40 is supposed to include few first characters + llmin(item->getLabelXPos() - item->getIndentation() + 40, local_rect.getWidth()), llmax(0, local_rect.getHeight() - max_height_to_show)); LLRect item_doc_rect; @@ -2095,8 +1591,8 @@ void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constr LLRect LLFolderView::getVisibleRect() { - S32 visible_height = mScrollContainer->getRect().getHeight(); - S32 visible_width = mScrollContainer->getRect().getWidth(); + S32 visible_height = (mScrollContainer ? mScrollContainer->getRect().getHeight() : 0); + S32 visible_width = (mScrollContainer ? mScrollContainer->getRect().getWidth() : 0); LLRect visible_rect; visible_rect.setLeftTopAndSize(-getRect().mLeft, visible_height - getRect().mBottom, visible_width, visible_height); return visible_rect; @@ -2125,159 +1621,28 @@ void LLFolderView::setShowSingleSelection(BOOL 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::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 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::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() +void LLFolderView::update() { // 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(mParentPanel); - if (inventory_panel && !inventory_panel->getIsViewsInitialized()) - { - return; - } - LLFastTimer t2(FTM_INVENTORY); - BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); - if (debug_filters != getDebugFilters()) + if (getFolderViewModel()->getFilter().isModified() && getFolderViewModel()->getFilter().isNotDefault()) { - mDebugFilters = debug_filters; - arrangeAll(); + mNeedsAutoSelect = TRUE; } - 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(); + filter(getFolderViewModel()->getFilter()); + // Clear the modified setting on the filter only if the filter count is non-zero after running the filter process + // Note: if the filter count is zero, then the filter most likely halted before completing the entire set of items + if (getFolderViewModel()->getFilter().isModified() && (getFolderViewModel()->getFilter().getFilterCount() > 0)) + { + getFolderViewModel()->getFilter().clearModified(); + } // automatically show matching items, and select first one if we had a selection if (mNeedsAutoSelect) @@ -2285,7 +1650,7 @@ void LLFolderView::doIdle() 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())) + if (!mAutoSelectOverride && (!selected_itemp || !selected_itemp->getViewModelItem()->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 @@ -2295,7 +1660,7 @@ void LLFolderView::doIdle() // Open filtered folders for folder views with mAutoSelectOverride=TRUE. // Used by LLPlacesFolderView. - if (!mFilter->getFilterSubString().empty()) + if (getFolderViewModel()->getFilter().showAllResults()) { // 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 @@ -2306,16 +1671,30 @@ void LLFolderView::doIdle() scrollToShowSelection(); } - BOOL filter_finished = mCompletedFilterGeneration >= mFilter->getCurrentGeneration() - && !LLInventoryModelBackgroundFetch::instance().folderFetchActive(); + BOOL filter_finished = getViewModelItem()->passedFilter() + && mViewModel->contentsReady(); if (filter_finished - || gFocusMgr.childHasKeyboardFocus(inventory_panel) - || gFocusMgr.childHasMouseCapture(inventory_panel)) + || gFocusMgr.childHasKeyboardFocus(mParentPanel) + || gFocusMgr.childHasMouseCapture(mParentPanel)) { // finishing the filter process, giving focus to the folder view, or dragging the scrollbar all stop the auto select process mNeedsAutoSelect = FALSE; } + BOOL is_visible = isInVisibleChain(); + + //Puts folders/items in proper positions + if ( is_visible ) + { + sanitizeSelection(); + if( needsArrange() ) + { + S32 height = 0; + S32 width = 0; + S32 total_height = arrange( &width, &height ); + notifyParent(LLSD().with("action", "size_changes").with("height", total_height)); + } + } // during filtering process, try to pin selected item's location on screen // this will happen when searching your inventory and when new items arrive @@ -2327,22 +1706,30 @@ void LLFolderView::doIdle() // lets pin it! mPinningSelectedItem = TRUE; - LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); + //Computes visible area + const LLRect visible_content_rect = (mScrollContainer ? mScrollContainer->getVisibleContentRect() : LLRect()); LLFolderViewItem* selected_item = mSelectedItems.back(); + //Computes location of selected content, content outside visible area will be scrolled to using below code 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)) + + //Computes intersected region of the selected content and visible area + LLRect overlap_rect(item_rect); + overlap_rect.intersectWith(visible_content_rect); + + //Don't scroll when the selected content exists within the visible area + if (overlap_rect.getHeight() >= selected_item->getItemHeight()) { // then attempt to keep it in same place on screen mScrollConstraintRect = item_rect; mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); } + //Scroll because the selected content is outside the visible area else { // otherwise we just want it onscreen somewhere - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); } } @@ -2365,22 +1752,10 @@ void LLFolderView::doIdle() else { // during normal use (page up/page down, etc), just try to fit item on screen - LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); 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); @@ -2401,17 +1776,6 @@ void LLFolderView::doIdle() 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; @@ -2429,13 +1793,13 @@ void LLFolderView::updateRenamerPosition() if(mRenameItem) { // See also LLFolderViewItem::draw() - S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mRenameItem->getIndentation(); + S32 x = mRenameItem->getLabelXPos(); 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); + LLRect scroller_rect(0, 0, (S32)LLUI::getWindowSize().mV[VX], 0); if (mScrollContainer) { scroller_rect = mScrollContainer->getContentWindowRect(); @@ -2462,14 +1826,15 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu) // Successively filter out invalid options - U32 flags = FIRST_SELECTED_ITEM; + U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0); + U32 flags = multi_select_flag | 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; + flags = multi_select_flag; } addNoOptions(menu); @@ -2582,7 +1947,7 @@ void LLFolderView::onRenamerLost() if( mRenameItem ) { - setSelectionFromRoot( mRenameItem, TRUE ); + setSelection( mRenameItem, TRUE ); mRenameItem = NULL; } } @@ -2606,74 +1971,14 @@ LLFolderViewItem* LLFolderView::getNextUnselectedItem() return new_selection; } -LLInventoryFilter* LLFolderView::getFilter() +S32 LLFolderView::getItemHeight() { - return mFilter; + if(!hasVisibleChildren()) +{ + //We need to display status textbox, let's reserve some place for it + return llmax(0, mStatusTextBox->getTextPixelHeight()); } - -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(user_data); - fv->removeSelectedItems(); - } -} - -void copy_selected_item(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->copy(); - } -} - -void paste_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->paste(); - } -} - -void open_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->openSelectedItems(); - } -} - -void properties_selected_items(void* user_data) -{ - if(user_data) - { - LLFolderView* fv = reinterpret_cast(user_data); - fv->propertiesSelectedItems(); - } + return 0; } // FIRE-4702; diff --git a/indra/newview/llfolderview.h b/indra/llui/llfolderview.h similarity index 71% rename from indra/newview/llfolderview.h rename to indra/llui/llfolderview.h index e074b680a9..6e6aa1f173 100644 --- a/indra/newview/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -39,19 +39,16 @@ #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 LLFolderViewModelInterface; class LLFolderViewFolder; class LLFolderViewItem; -class LLInventoryModel; +class LLFolderViewFilter; class LLPanel; class LLLineEditor; class LLMenuGL; @@ -91,136 +88,104 @@ public: struct Params : public LLInitParam::Block { Mandatory parent_panel; - Optional task_id; Optional title; Optional use_label_suffix, allow_multiselect, show_empty_message, - show_load_status, - use_ellipses; + use_ellipses, + show_item_link_overlays; + Mandatory view_model; + Mandatory options_menu; + Params(); }; friend class LLFolderViewScrollContainer; + typedef std::deque selected_items_t; LLFolderView(const Params&); virtual ~LLFolderView( void ); virtual BOOL canFocusChildren() const; + virtual const LLFolderView* getRoot() const { return this; } 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); - + LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; } + const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; } + typedef boost::signals2::signal& 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); + virtual void 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 ); + virtual S32 arrange( S32* width, S32* height ); + virtual S32 getItemHeight(); void arrangeAll() { mArrangeGeneration++; } S32 getArrangeGeneration() { return mArrangeGeneration; } - // Apply filters to control visibility of inventory items - virtual void filter( LLInventoryFilter& filter); + // applies filters to control visibility of items + virtual void filter( LLFolderViewFilter& filter); // Get the last selected item virtual LLFolderViewItem* getCurSelectedItem( void ); + selected_items_t& getSelectedItems( void ); // Record the selected item and pass it down the hierarchy. virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, - BOOL take_keyboard_focus); + BOOL take_keyboard_focus = TRUE); - // 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. + // 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 getSelectionList() const; + virtual std::set getSelectionList() const; - // Make sure if ancestor is selected, descendents are not + // Make sure if ancestor is selected, descendants are not void sanitizeSelection(); - void clearSelection(); + virtual void clearSelection(); void addToSelectionList(LLFolderViewItem* item); void removeFromSelectionList(LLFolderViewItem* item); - BOOL startDrag(LLToolDragAndDrop::ESource source); + bool startDrag(); 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); + BOOL isOpen() const { return TRUE; } // root folder always open // Copy & paste - virtual void copy(); virtual BOOL canCopy() const; + virtual void copy(); - virtual void cut(); virtual BOOL canCut() const; + virtual void cut(); - virtual void paste(); virtual BOOL canPaste() const; - - virtual void doDelete(); - virtual BOOL canDoDelete() const; + virtual void paste(); 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 ); @@ -251,16 +216,9 @@ public: BOOL getShowSingleSelection() { return mShowSingleSelection; } F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); } bool getUseEllipses() { return mUseEllipses; } + S32 getSelectedCount() { return (S32)mSelectedItems.size(); } - 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() + void update(); // needs to be called periodically (e.g. once per frame) BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; } BOOL needsAutoRename() { return mNeedsAutoRename; } @@ -268,9 +226,9 @@ public: void setPinningSelectedItem(BOOL val) { mPinningSelectedItem = val; } void setAutoSelectOverride(BOOL val) { mAutoSelectOverride = val; } - void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } + bool showItemLinkOverlays() { return mShowItemLinkOverlays; } - BOOL getDebugFilters() { return mDebugFilters; } + void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } LLPanel* getParentPanel() { return mParentPanel; } // DEBUG only @@ -299,18 +257,15 @@ protected: BOOL addNoOptions(LLMenuGL* menu) const; - void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response); protected: LLHandle mPopupMenuHandle; - typedef std::deque 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 @@ -323,15 +278,13 @@ protected: BOOL mAutoSelectOverride; BOOL mNeedsAutoRename; bool mUseLabelSuffix; + bool mShowItemLinkOverlays; - BOOL mDebugFilters; - U32 mSortOrder; LLDepthStack mAutoOpenItems; LLFolderViewFolder* mAutoOpenCandidate; LLFrameTimer mAutoOpenTimer; LLFrameTimer mSearchTimer; std::string mSearchString; - LLInventoryFilter* mFilter; BOOL mShowSelectionContext; BOOL mShowSingleSelection; LLFrameTimer mMultiSelectionFadeTimer; @@ -341,13 +294,11 @@ protected: signal_t mReshapeSignal; S32 mSignalSelectCallback; S32 mMinWidth; - S32 mRunningHeight; - std::map mItemMap; BOOL mDragAndDropThisFrame; - LLUUID mSelectThisID; // if non null, select this item - LLPanel* mParentPanel; + + LLFolderViewModelInterface* mViewModel; /** * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. @@ -389,11 +340,82 @@ private: }; -bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b); -bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b); + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// 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 LLSelectFirstFilteredItem +// +// This will select the first *item* found in the hierarchy. If no item can be +// selected, the first matching folder will. +// Since doFolder() is done first but we prioritize item selection, we let the +// first filtered folder set the selection and raise a folder flag. +// The selection might be overridden by the first filtered item in doItem() +// which checks an item flag. Since doFolder() checks the item flag too, the first +// item will still be selected if items were to be done first and folders second. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLSelectFirstFilteredItem : public LLFolderViewFunctor +{ +public: + LLSelectFirstFilteredItem() : mItemSelected(FALSE), mFolderSelected(FALSE) {} + virtual ~LLSelectFirstFilteredItem() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); + BOOL wasItemSelected() { return mItemSelected || mFolderSelected; } +protected: + BOOL mItemSelected; + BOOL mFolderSelected; +}; + +class LLOpenFilteredFolders : public LLFolderViewFunctor +{ +public: + LLOpenFilteredFolders() {} + virtual ~LLOpenFilteredFolders() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; + +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 mOpenFolders; + BOOL mApply; +}; + +class LLOpenFoldersWithSelection : public LLFolderViewFunctor +{ +public: + LLOpenFoldersWithSelection() {} + virtual ~LLOpenFoldersWithSelection() {} + virtual void doFolder(LLFolderViewFolder* folder); + virtual void doItem(LLFolderViewItem* item); +}; // Flags for buildContextMenu() const U32 SUPPRESS_OPEN_ITEM = 0x1; const U32 FIRST_SELECTED_ITEM = 0x2; +const U32 ITEM_IN_MULTI_SELECTION = 0x4; #endif // LL_LLFOLDERVIEW_H diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp new file mode 100644 index 0000000000..95e1939130 --- /dev/null +++ b/indra/llui/llfolderviewitem.cpp @@ -0,0 +1,2145 @@ +/** +* @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 "../newview/llviewerprecompiledheaders.h" + +#include "llflashtimer.h" + +#include "linden_common.h" +#include "../newview/aoengine.h" // ## Zi: Animation Overrider +#include "../newview/fslslbridge.h" //-TT Client LSL Bridge +#include "llfolderviewitem.h" +#include "llfolderview.h" +#include "llfolderviewmodel.h" +#include "llpanel.h" +#include "llcriticaldamp.h" +#include "llclipboard.h" +#include "llfocusmgr.h" // gFocusMgr +#include "lltrans.h" +#include "llwindow.h" + +//#include "llinventorypanel.h" CHUI merge check + +///---------------------------------------------------------------------------- +/// Class LLFolderViewItem +///---------------------------------------------------------------------------- + +static LLDefaultChildRegistry::Register r("folder_view_item"); + +// statics +std::map LLFolderViewItem::sFonts; // map of styles to fonts + +bool LLFolderViewItem::sColorSetInitialized = false; +LLUIColor LLFolderViewItem::sFgColor; +LLUIColor LLFolderViewItem::sHighlightBgColor; +LLUIColor LLFolderViewItem::sFlashBgColor; +LLUIColor LLFolderViewItem::sFocusOutlineColor; +LLUIColor LLFolderViewItem::sMouseOverColor; +LLUIColor LLFolderViewItem::sFilterBGColor; +LLUIColor LLFolderViewItem::sFilterTextColor; +LLUIColor LLFolderViewItem::sSuffixColor; +LLUIColor LLFolderViewItem::sSearchStatusColor; + +// 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() +: 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(), + allow_open("allow_open", true), + font_color("font_color"), + font_highlight_color("font_highlight_color"), + left_pad("left_pad", 0), + icon_pad("icon_pad", 0), + icon_width("icon_width", 0), + text_pad("text_pad", 0), + text_pad_right("text_pad_right", 0), + arrow_size("arrow_size", 0), + max_folder_item_overlap("max_folder_item_overlap", 0) + +{ + static LLCachedControl FolderViewItemHeight(gSavedSettings, "FSFolderViewItemHeight"); + item_height = (S32)FolderViewItemHeight; +} + +// Default constructor +LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) +: LLView(p), + mLabelWidth(0), + mLabelWidthDirty(false), + mLabelPaddingRight(DEFAULT_LABEL_PADDING_RIGHT), + mParentFolder( NULL ), + mIsSelected( FALSE ), + mIsCurSelection( FALSE ), + mSelectPending(FALSE), + mLabelStyle( LLFontGL::NORMAL ), + mHasVisibleChildren(FALSE), + mLocalIndentation(p.folder_indentation), + mIndentation(0), + mItemHeight(p.item_height), + mControlLabelRotation(0.f), + mDragAndDropTarget(FALSE), + mLabel(p.name), + mRoot(p.root), + mViewModelItem(p.listener), + mIsMouseOverTitle(false), + mAllowOpen(p.allow_open), + mFontColor(p.font_color), + mFontHighlightColor(p.font_highlight_color), + mLeftPad(p.left_pad), + mIconPad(p.icon_pad), + mIconWidth(p.icon_width), + mTextPad(p.text_pad), + mTextPadRight(p.text_pad_right), + mArrowSize(p.arrow_size), + mMaxFolderItemOverlap(p.max_folder_item_overlap) +{ + if (!sColorSetInitialized) + { + sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); + sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); + sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); + sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); + sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); + sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); + sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); + sColorSetInitialized = true; + } + + if (mViewModelItem) + { + mViewModelItem->setFolderViewItem(this); + } +} + +// Destroys the object +LLFolderViewItem::~LLFolderViewItem() +{ + mViewModelItem = NULL; +} + +BOOL LLFolderViewItem::postBuild() +{ + refresh(); + return TRUE; +} + + +LLFolderView* LLFolderViewItem::getRoot() +{ + return mRoot; +} + +const LLFolderView* LLFolderViewItem::getRoot() const +{ + 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; +} + +BOOL LLFolderViewItem::passedFilter(S32 filter_generation) +{ + return getViewModelItem()->passedFilter(filter_generation); +} + +void LLFolderViewItem::refresh() +{ + LLFolderViewModelItem& vmi = *getViewModelItem(); + + mLabel = vmi.getDisplayName(); + + setToolTip(vmi.getName()); + mIcon = vmi.getIcon(); + mIconOpen = vmi.getIconOpen(); + mIconOverlay = vmi.getIconOverlay(); + + if (mRoot->useLabelSuffix()) + { + mLabelStyle = vmi.getLabelStyle(); + mLabelSuffix = vmi.getLabelSuffix(); + } + + mLabelWidthDirty = true; + vmi.dirtyFilter(); +} + +// Utility function for LLFolderView +void LLFolderViewItem::arrangeAndSet(BOOL set_selection, + BOOL take_keyboard_focus) +{ + LLFolderView* root = getRoot(); + if (getParentFolder()) + { + getParentFolder()->requestArrange(); + } + if(set_selection) + { + getRoot()->setSelection(this, TRUE, take_keyboard_focus); + if(root) + { + root->scrollToShowSelection(); + } + } +} + + +std::set LLFolderViewItem::getSelectionList() const +{ + std::set selection; + return selection; +} + +// addToFolder() returns TRUE if it succeeds. FALSE otherwise +void LLFolderViewItem::addToFolder(LLFolderViewFolder* folder) +{ + 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 ) +{ + // Only indent deeper items in hierarchy + mIndentation = (getParentFolder()) + ? getParentFolder()->getIndentation() + mLocalIndentation + : 0; + if (mLabelWidthDirty) + { + mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; + mLabelWidthDirty = false; + } + + *width = llmax(*width, mLabelWidth); + + // 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; +} + +S32 LLFolderViewItem::getLabelXPos() +{ + return getIndentation() + mArrowSize + mTextPad + mIconWidth + mIconPad; +} + +S32 LLFolderViewItem::getIconPad() +{ + return mIconPad; +} + +S32 LLFolderViewItem::getTextPad() +{ + return mTextPad; +} + +// *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) + { + mIsSelected = TRUE; + getViewModelItem()->selectItem(); + } +} + +BOOL LLFolderViewItem::isMovable() +{ + return getViewModelItem()->isItemMovable(); +} + +BOOL LLFolderViewItem::isRemovable() +{ + return getViewModelItem()->isItemRemovable(); +} + +void LLFolderViewItem::destroyView() +{ + getRoot()->removeFromSelectionList(this); + + if (mParentFolder) + { + // removeView deletes me + mParentFolder->extractItem(this); + } + delete 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; + } + return getViewModelItem()->removeItem(); +} + +// Build an appropriate context menu for the item. +void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags) +{ + getViewModelItem()->buildContextMenu(menu, flags); +} + +void LLFolderViewItem::openItem( void ) +{ + if (mAllowOpen) + { + getViewModelItem()->openItem(); + } +} + +void LLFolderViewItem::rename(const std::string& new_name) +{ + if( !new_name.empty() ) + { + getViewModelItem()->renameItem(new_name); + } +} + +const std::string& LLFolderViewItem::getName( void ) const +{ + static const std::string noName(""); + return getViewModelItem() ? getViewModelItem()->getName() : noName; +} + +// LLView functionality +BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask ) +{ + if(!mIsSelected) + { + getRoot()->setSelection(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) + { + getRoot()->changeSelection(this, !mIsSelected); + } + else if (mask & MASK_SHIFT) + { + getParentFolder()->extendSelectionTo(this); + } + else + { + getRoot()->setSelection(this, FALSE); + } + make_ui_sound("UISndClick"); + } + else + { + // If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. + // This is necessary so we maintain selection consistent when starting a drag. + mSelectPending = TRUE; + } + + mDragStartX = x; + mDragStartY = y; + return TRUE; +} + +BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) +{ + static LLCachedControl drag_and_drop_threshold(*LLUI::sSettingGroups["config"],"DragAndDropDistanceThreshold"); + + mIsMouseOverTitle = (y > (getRect().getHeight() - mItemHeight)); + + if( hasMouseCapture() && isMovable() ) + { + LLFolderView* root = getRoot(); + + if( (x - mDragStartX) * (x - mDragStartX) + (y - mDragStartY) * (y - mDragStartY) > drag_and_drop_threshold() * drag_and_drop_threshold() + && root->getCurSelectedItem() + && root->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); + + getWindow()->setCursor(UI_CURSOR_ARROW); + } + else if (x != mDragStartX || y != mDragStartY) + { + getWindow()->setCursor(UI_CURSOR_NOLOCKED); + } + + return TRUE; + } + else + { + getRoot()->setShowSelectionContext(FALSE); + getWindow()->setCursor(UI_CURSOR_ARROW); + // let parent handle this then... + return FALSE; + } +} + + +BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + openItem(); + 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) + { + getRoot()->changeSelection(this, !mIsSelected); + } + else if (mask & MASK_SHIFT) + { + getParentFolder()->extendSelectionTo(this); + } + else + { + getRoot()->setSelection(this, FALSE); + } + } + + mSelectPending = FALSE; + + if( hasMouseCapture() ) + { + if (getRoot()) + { + 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 handled = FALSE; + BOOL accepted = getViewModelItem()->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::drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color) +{ + //--------------------------------------------------------------------------------// + // Draw open folder arrow + // + const S32 TOP_PAD = default_params.item_top_pad; + + if (hasVisibleChildren() || getViewModelItem()->hasChildren()) + { + LLUIImage* arrow_image = default_params.folder_arrow_image; + gl_draw_scaled_rotated_image( + mIndentation, getRect().getHeight() - mArrowSize - mTextPad - TOP_PAD, + mArrowSize, mArrowSize, mControlLabelRotation, arrow_image->getImage(), fg_color); + } +} + +/*virtual*/ bool LLFolderViewItem::isHighlightAllowed() +{ + return mIsSelected; +} + +/*virtual*/ bool LLFolderViewItem::isHighlightActive() +{ + return mIsCurSelection; +} + +void LLFolderViewItem::drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, + const LLUIColor &focusOutlineColor, const LLUIColor &mouseOverColor) +{ + const S32 focus_top = getRect().getHeight(); + const S32 focus_bottom = getRect().getHeight() - mItemHeight; + const bool folder_open = (getRect().getHeight() > mItemHeight + 4); + const S32 FOCUS_LEFT = 1; + + // Determine which background color to use for highlighting + LLUIColor bgColor = (isFlashing() ? flashColor : selectColor); + + //--------------------------------------------------------------------------------// + // Draw highlight for selected items + // Note: Always render "current" item or flashing item, only render other selected + // items if mShowSingleSelection is FALSE. + // + if (isHighlightAllowed()) + + { + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + // Highlight for selected but not current items + if (!isHighlightActive() && !isFlashing()) + { + LLColor4 bg_color = bgColor; + // do time-based fade of extra objects + F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); + if (getRoot() && 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, hasKeyboardFocus); + } + + // Highlight for currently selected or flashing item + if (isHighlightActive()) + { + // Background + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + bgColor, hasKeyboardFocus); + // Outline + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + focusOutlineColor, FALSE); + } + + if (folder_open) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, + focusOutlineColor, FALSE); + if (showContent && !isFlashing()) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, + getRect().getWidth() - 2, + 0, + bgColor, TRUE); + } + } + } + else if (mIsMouseOverTitle) + { + gl_rect_2d(FOCUS_LEFT, + focus_top, + getRect().getWidth() - 2, + focus_bottom, + mouseOverColor, 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, + bgColor, FALSE); + if (folder_open) + { + gl_rect_2d(FOCUS_LEFT, + focus_bottom + 1, // overlap with bottom edge of above rect + getRect().getWidth() - 2, + 0, + bgColor, FALSE); + } + mDragAndDropTarget = FALSE; + } +} + +void LLFolderViewItem::drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x) +{ + //--------------------------------------------------------------------------------// + // Draw the actual label text + // + font->renderUTF8(mLabel, 0, x, y, color, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, getRect().getWidth() - (S32) x - mLabelPaddingRight, &right_x, TRUE); +} + +void LLFolderViewItem::draw() +{ + const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); + const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled + + const Params& default_params = LLUICtrlFactory::getDefaultParams(); + const S32 TOP_PAD = default_params.item_top_pad; + + const LLFontGL* font = getLabelFontForStyle(mLabelStyle); + + getViewModelItem()->update(); + + drawOpenFolderArrow(default_params, sFgColor); + + drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); + + //--------------------------------------------------------------------------------// + // Draw open icon + // + const S32 icon_x = mIndentation + mArrowSize + mTextPad; + 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 (mIconOverlay && getRoot()->showItemLinkOverlays()) + { + mIconOverlay->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1); + } + + //--------------------------------------------------------------------------------// + // Exit if no label to draw + // + if (mLabel.empty()) + { + return; + } + + std::string::size_type filter_string_length = mViewModelItem->hasFilterStringMatch() ? mViewModelItem->getFilterStringSize() : 0; + F32 right_x = 0; + F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + F32 text_left = (F32)getLabelXPos(); + std::string combined_string = mLabel + mLabelSuffix; + + if (filter_string_length > 0) + { + S32 left = llround(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; + S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), 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); + } + + LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; + drawLabel(font, text_left, y, color, right_x); + + // ## Zi: Animation Overrider + //--------------------------------------------------------------------------------// + // Draw "protected" indicator + // + if((mListener->getUUID()==AOEngine::instance().getAOFolder() && gSavedPerAccountSettings.getBOOL("ProtectAOFolders")) +//-TT Client LSL Bridge + ||(mListener->getUUID()==FSLSLBridge::instance().getBridgeFolder() && gSavedPerAccountSettings.getBOOL("ProtectBridgeFolder")) +//-TT + ) + { + std::string locked_string = " (" + LLTrans::getString("ProtectedFolder") + ") "; + font->renderUTF8(locked_string, 0, right_x, y, sProtectedColor, + LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + S32_MAX, S32_MAX, &right_x, FALSE); + } + // ## Zi: Animation Overrider + + //--------------------------------------------------------------------------------// + // 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 (filter_string_length > 0) + { + F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mViewModelItem->getFilterStringOffset()); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + font->renderUTF8( combined_string, mViewModelItem->getFilterStringOffset(), match_string_left, yy, + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, + filter_string_length, S32_MAX, &right_x, FALSE ); + } + + //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to + //be distorted...oddly. I initially added this in but didn't need it after all. So removing to prevent unnecessary bug. + //LLView::draw(); +} + +const LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) const +{ + return getRoot()->getFolderViewModel(); +} + +LLFolderViewModelInterface* LLFolderViewItem::getFolderViewModel( void ) +{ + return getRoot()->getFolderViewModel(); +} + +bool LLFolderViewItem::isInSelection() const +{ + return mIsSelected || (mParentFolder && mParentFolder->isInSelection()); +} +// Don't bother with unneeded tooltips in inventor + +BOOL LLFolderViewItem::handleToolTip(S32 x, S32 y, MASK mask) +{ + if( childrenHandleToolTip( x, y, mask ) ) + return TRUE; + + int nStart = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation; + int nWidth = getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + nStart; + + if( getRoot()->getParentPanel()->getRect().getWidth() < nWidth ) // Label is truncated, display tooltip + { + setToolTip( mLabel ); + return LLView::handleToolTip( x, y, mask ); + } + else + setToolTip( LLStringExplicit("") ); + + // In case of root we always want to return TRUE, otherwise tooltip handling gets propagated one level up and we end with a tooltip like 'All Items'. + if( this == getRoot() ) + return TRUE; + + return FALSE; +} +// + + + +///---------------------------------------------------------------------------- +/// Class LLFolderViewFolder +///---------------------------------------------------------------------------- + +LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): + LLFolderViewItem( p ), + mIsOpen(FALSE), + mExpanderHighlighted(FALSE), + mCurHeight(0.f), + mTargetHeight(0.f), + mAutoOpenCountdown(0.f), + mLastArrangeGeneration( -1 ), + mLastCalculatedWidth(0) +{ +} + +void LLFolderViewFolder::updateLabelRotation() +{ + if (mAutoOpenCountdown != 0.f) + { + mControlLabelRotation = mAutoOpenCountdown * -90.f; + } + else if (isOpen()) + { + mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); + } + else + { + mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); + } +} + +// 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() +} + +// addToFolder() returns TRUE if it succeeds. FALSE otherwise +void LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder) +{ + folder->addFolder(this); +} + +static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); + +// 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 ) +{ + // sort before laying out contents + getRoot()->getFolderViewModel()->sort(this); + + LLFastTimer t2(FTM_ARRANGE); + + // evaluate mHasVisibleChildren + mHasVisibleChildren = false; + if (getViewModelItem()->descendantsPassedFilter()) + { + // 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->passedFilter(); + 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->passedFilter(); + if (found) + break; + } + } + + mHasVisibleChildren = found; + } + + // calculate height as a single item (without any children), and reshapes rectangle to match + LLFolderViewItem::arrange( width, height ); + + // 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 + 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 (isOpen()) + { + // Add sizes of children + S32 parent_item_height = getRect().getHeight(); + + for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) + { + LLFolderViewFolder* folderp = (*fit); + folderp->setVisible(folderp->passedFilter()); // 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 ); + + 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); + itemp->setVisible(itemp->passedFilter()); + + 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 ); + // 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(isOpen() ? 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) + mMaxFolderItemOverlap) + { + // 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) + mMaxFolderItemOverlap) + { + (*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(); +} + +// 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 item_a_ancestors; + + LLFolderViewFolder* parent = item_a->getParentFolder(); + while(parent) + { + item_a_ancestors.push_back(parent); + parent = parent->getParentFolder(); + } + + std::deque 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& 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 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 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::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::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() +{ + while (!mItems.empty()) + { + LLFolderViewItem *itemp = mItems.back(); + itemp->destroyView(); // LLFolderViewItem::destroyView() removes entry from mItems + } + + while (!mFolders.empty()) + { + LLFolderViewFolder *folderp = mFolders.back(); + folderp->destroyView(); // LLFolderVievFolder::destroyView() removes entry from mFolders + } + + LLFolderViewItem::destroyView(); +} + +// extractItem() removes the specified item from the folder, but +// doesn't delete it. +void LLFolderViewFolder::extractItem( LLFolderViewItem* item ) +{ + if (item->isSelected()) + getRoot()->clearSelection(); + 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(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 + getViewModelItem()->removeChild(item->getViewModelItem()); + //because an item is going away regardless of filter status, force rearrange + requestArrange(); + removeChild(item); +} + +BOOL LLFolderViewFolder::isMovable() +{ + if( !(getViewModelItem()->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( !(getViewModelItem()->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. +void LLFolderViewFolder::addItem(LLFolderViewItem* item) +{ + if (item->getParentFolder()) + { + item->getParentFolder()->extractItem(item); + } + item->setParentFolder(this); + + mItems.push_back(item); + + item->setRect(LLRect(0, 0, getRect().getWidth(), 0)); + item->setVisible(FALSE); + + addChild(item); + + // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it + // Note: this happens when models are created before views or shared between views + if (!item->getViewModelItem()->hasParent()) + { + getViewModelItem()->addChild(item->getViewModelItem()); + } +} + +// this is an internal method used for adding items to folders. +void LLFolderViewFolder::addFolder(LLFolderViewFolder* folder) +{ + if (folder->mParentFolder) + { + folder->mParentFolder->extractItem(folder); + } + folder->mParentFolder = this; + mFolders.push_back(folder); + folder->setOrigin(0, 0); + folder->reshape(getRect().getWidth(), 0); + folder->setVisible(FALSE); + // rearrange all descendants too, as our indentation level might have changed + //folder->requestArrange(); + //requestSort(); + + addChild(folder); + + // When the model is already hooked into a hierarchy (i.e. has a parent), do not reparent it + // Note: this happens when models are created before views or shared between views + if (!folder->getViewModelItem()->hasParent()) + { + getViewModelItem()->addChild(folder->getViewModelItem()); + } +} + +void LLFolderViewFolder::requestArrange() +{ + if ( mLastArrangeGeneration != -1) + { + mLastArrangeGeneration = -1; + // flag all items up to root + if (mParentFolder) + { + mParentFolder->requestArrange(); + } + } +} + +void LLFolderViewFolder::toggleOpen() +{ + setOpen(!isOpen()); +} + +// Force a folder open or closed +void LLFolderViewFolder::setOpen(BOOL openitem) +{ + setOpenArrangeRecursively(openitem); +} + +void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) +{ + BOOL was_open = isOpen(); + mIsOpen = openitem; + if(!was_open && openitem) + { + getViewModelItem()->openItem(); + } + else if(was_open && !openitem) + { + getViewModelItem()->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 != isOpen()) + { + requestArrange(); + } +} + +BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask, + BOOL drop, + EDragAndDropType c_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + BOOL accepted = mViewModelItem->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)); + } +} + +// 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 (isOpen()) + { + 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 = getViewModelItem()->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; + + if( isOpen() ) + { + 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( isOpen() ) + { + handled = childrenHandleMouseDown(x,y,mask) != NULL; + } + if( !handled ) + { + if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + { + 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 ) +{ + BOOL handled = FALSE; + if( isOpen() ) + { + handled = childrenHandleDoubleClick( x, y, mask ) != NULL; + } + if( !handled ) + { + if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + { + // don't select when user double-clicks plus sign + // so as not to contradict single-click behavior + toggleOpen(); + } + else + { + getRoot()->setSelection(this, FALSE); + toggleOpen(); + } + handled = TRUE; + } + return handled; +} + +void LLFolderViewFolder::draw() +{ + updateLabelRotation(); + + LLFolderViewItem::draw(); + + // draw children if root folder, or any other folder that is open or animating to closed state + if( getRoot() == this || (isOpen() || mCurHeight != mTargetHeight )) + { + LLView::draw(); + } + + mExpanderHighlighted = FALSE; +} + +// 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() && include_children) + { + 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; +} + diff --git a/indra/newview/llfolderviewitem.h b/indra/llui/llfolderviewitem.h similarity index 51% rename from indra/newview/llfolderviewitem.h rename to indra/llui/llfolderviewitem.h index cf29f222fd..8815aa2f34 100644 --- a/indra/newview/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -26,55 +26,16 @@ #ifndef LLFOLDERVIEWITEM_H #define LLFOLDERVIEWITEM_H +#include "llflashtimer.h" #include "llview.h" -#include "lldarray.h" // *TODO: Eliminate, forward declare #include "lluiimage.h" -class LLFontGL; class LLFolderView; -class LLFolderViewEventListener; +class LLFolderViewModelItem; 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 LLFolderViewFilter; +class LLFolderViewModelInterface; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFolderViewItem @@ -86,153 +47,130 @@ private: class LLFolderViewItem : public LLView { public: - static void initClass(); - static void cleanupClass(); - struct Params : public LLInitParam::Block { - Optional icon; - Optional icon_open; // used for folders - Optional icon_overlay; // for links - Optional root; - Mandatory listener; + Optional folder_arrow_image, + selection_image; + Mandatory root; + Mandatory listener; - Optional folder_arrow_image; - Optional folder_indentation; // pixels - Optional selection_image; - Optional item_height; // pixels - Optional item_top_pad; // pixels + Optional folder_indentation, // pixels + item_height, + item_top_pad; - Optional creation_date; //UTC seconds + Optional creation_date; + Optional allow_open; + Optional font_color; + Optional font_highlight_color; + + Optional left_pad, + icon_pad, + icon_width, + text_pad, + text_pad_right, + arrow_size, + max_folder_item_overlap; 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; + + static const S32 DEFAULT_LABEL_PADDING_RIGHT = 4; // animation parameters - static const F32 FOLDER_CLOSE_TIME_CONSTANT; - static const F32 FOLDER_OPEN_TIME_CONSTANT; - - // replaced by getSearchableLabel() const - - // Mostly for debugging printout purposes. - - // const std::string& getSearchableLabel() { return mSearchableLabel; } - - // - - BOOL isLoading() const { return mIsLoading; } - -private: - BOOL mIsSelected; + static const F32 FOLDER_CLOSE_TIME_CONSTANT, + FOLDER_OPEN_TIME_CONSTANT; protected: friend class LLUICtrlFactory; - friend class LLFolderViewEventListener; + friend class LLFolderViewModelItem; LLFolderViewItem(const Params& p); std::string mLabel; - - // Reduce memory load for users with huge inventory by only creating member we really need - // std::string mSearchableLabel; - // - S32 mLabelWidth; bool mLabelWidthDirty; - time_t mCreationDate; + S32 mLabelPaddingRight; LLFolderViewFolder* mParentFolder; - LLFolderViewEventListener* mListener; - BOOL mIsCurSelection; - BOOL mSelectPending; + LLPointer mViewModelItem; LLFontGL::StyleFlags mLabelStyle; std::string mLabelSuffix; - LLUIImagePtr mIcon; - -#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Reduce memory load for users with huge inventory by only creating member we really need - std::string mStatusText; -#endif // - - LLUIImagePtr mIconOpen; - LLUIImagePtr mIconOverlay; - BOOL mHasVisibleChildren; + LLUIImagePtr mIcon, + mIconOpen, + mIconOverlay; + S32 mLocalIndentation; S32 mIndentation; S32 mItemHeight; - BOOL mPassedFilter; - S32 mLastFilterGeneration; - std::string::size_type mStringMatchOffset; + S32 mDragStartX, + mDragStartY; - // Begin Multi-substring inventory search - std::vector mStringMatchOffsets; - std::vector mStringMatchSizes; - // End Multi-substring inventory search + S32 mLeftPad, + mIconPad, + mIconWidth, + mTextPad, + mTextPadRight, + mArrowSize, + mMaxFolderItemOverlap; F32 mControlLabelRotation; LLFolderView* mRoot; - BOOL mDragAndDropTarget; - BOOL mIsLoading; - LLTimer mTimeSinceRequestStart; - bool mShowLoadStatus; - bool mIsMouseOverTitle; + bool mHasVisibleChildren, + mIsCurSelection, + mDragAndDropTarget, + mIsMouseOverTitle, + mAllowOpen, + mSelectPending; + + LLUIColor mFontColor; + LLUIColor mFontHighlightColor; - // helper function to change the selection from the root. - void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected); + // For now assuming all colors are the same in derived classes. + static bool sColorSetInitialized; + static LLUIColor sFgColor; + static LLUIColor sFgDisabledColor; + static LLUIColor sHighlightBgColor; + static LLUIColor sFlashBgColor; + static LLUIColor sFocusOutlineColor; + static LLUIColor sMouseOverColor; + static LLUIColor sFilterBGColor; + static LLUIColor sFilterTextColor; + static LLUIColor sSuffixColor; + static LLUIColor sSearchStatusColor; // 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; } + virtual void addItem(LLFolderViewItem*) { } + virtual void addFolder(LLFolderViewFolder*) { } + virtual bool isHighlightAllowed(); + virtual bool isHighlightActive(); + virtual bool isFlashing() { return false; } + virtual void setFlashState(bool) { } static LLFontGL* getLabelFontForStyle(U8 style); - virtual void setCreationDate(time_t creation_date_utc) { mCreationDate = creation_date_utc; } + BOOL mIsSelected; public: + static void initClass(); + static void cleanupClass(); + 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); + virtual void openItem( void ); - // 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; + virtual void addToFolder(LLFolderViewFolder* folder); // 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 arrange( S32* width, S32* height ); 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(); + virtual S32 getLabelXPos(); + S32 getIconPad(); + S32 getTextPad(); // If 'selection' is 'this' then note that otherwise ignore. // Returns TRUE if this item ends up being selected. @@ -250,7 +188,7 @@ public: virtual void selectItem(); // gets multiple-element selection - virtual std::set getSelectionList() const; + virtual std::set getSelectionList() const; // Returns true is this object and all of its children can be removed (deleted by user) virtual BOOL isRemovable(); @@ -272,86 +210,54 @@ public: 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); + void buildContextMenu(class 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; - std::string getSearchableLabel( void ) const; - - // ## Zi: Extended Inventory Search - std::string getSearchableCreator( void ) const; - std::string getSearchableDescription( void ) const; - std::string getSearchableUUID( void ) const; - std::string getSearchableAll( void ) const; - - LLViewerInventoryItem * getInventoryItem(void) const; - - // ## Zi: Extended Inventory Search - // 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; } + void setParentFolder(LLFolderViewFolder* parent) { mParentFolder = parent; } + 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); + const LLFolderViewModelItem* getViewModelItem( void ) const { return mViewModelItem; } + LLFolderViewModelItem* getViewModelItem( void ) { return mViewModelItem; } + + const LLFolderViewModelInterface* getFolderViewModel( void ) const; + LLFolderViewModelInterface* getFolderViewModel( 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") + // Show children virtual void setOpen(BOOL open = TRUE) {}; - virtual BOOL isOpen() const { return FALSE; } virtual LLFolderView* getRoot(); + virtual const LLFolderView* getRoot() const; 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); + virtual BOOL passedFilter(S32 filter_generation = -1); // 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 ); @@ -361,15 +267,18 @@ public: virtual void onMouseLeave(S32 x, S32 y, MASK mask); - virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return NULL; } + //virtual LLView* findChildView(const std::string& name, BOOL recurse) const { return LLView::findChildView(name, recurse); } // virtual void handleDropped(); virtual void draw(); + void drawOpenFolderArrow(const Params& default_params, const LLUIColor& fg_color); + void drawHighlight(const BOOL showContent, const BOOL hasKeyboardFocus, const LLUIColor &selectColor, const LLUIColor &flashColor, const LLUIColor &outlineColor, const LLUIColor &mouseOverColor); + void drawLabel(const LLFontGL * font, const F32 x, const F32 y, const LLColor4& color, F32 &right_x); virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); // Don't bother with unneeded tooltips in inventor /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); @@ -380,11 +289,6 @@ private: static std::map sFonts; // map of styles to fonts }; - -// function used for sorting. -typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b); - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFolderViewFolder // @@ -399,33 +303,26 @@ protected: LLFolderViewFolder( const LLFolderViewItem::Params& ); friend class LLUICtrlFactory; -public: - typedef enum e_trash - { - UNKNOWN, TRASH, NOT_TRASH - } ETrash; + void updateLabelRotation(); + virtual bool isCollapsed() { return FALSE; } +public: typedef std::list items_t; typedef std::list 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 @@ -439,48 +336,25 @@ public: 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); + virtual void addToFolder(LLFolderViewFolder* folder); // 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 arrange( S32* width, S32* height ); 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); + bool descendantsPassedFilter(S32 filter_generation = -1); // 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); + virtual BOOL setSelection(LLFolderViewItem* selection, BOOL openitem, BOOL take_keyboard_focus = TRUE); // This method is used to change the selection of an item. // Recursively traverse all children; if 'selection' is 'this' then change @@ -500,31 +374,13 @@ public: // 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 ); + virtual 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 @@ -535,8 +391,7 @@ public: 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); + virtual void requestArrange(); // internal method which doesn't update the entire view. This // method was written because the list iterators destroy the state @@ -549,65 +404,60 @@ public: // 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); + 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); + // apply this functor to the folder's descendants. + void applyFunctorRecursively(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, + 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::iterator getFoldersBegin() { return mFolders.begin(); } + folders_t::iterator getFoldersEnd() { 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& items); + + // internal functions for tracking folders and items separately + // use addToFolder() virtual method to ensure folders are always added to mFolders + // and not mItems + void addItem(LLFolderViewItem* item); + void addFolder( LLFolderViewFolder* folder); + + //WARNING: do not call directly...use the appropriate LLFolderViewModel-derived class instead + template void sortFolders(const SORT_FUNC& func) { mFolders.sort(func); } + template void sortItems(const SORT_FUNC& func) { mItems.sort(func); } }; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// 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/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp new file mode 100644 index 0000000000..3593804554 --- /dev/null +++ b/indra/llui/llfolderviewmodel.cpp @@ -0,0 +1,68 @@ +/** + * @file llfolderviewmodel.cpp + * @brief Implementation of the view model 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 "linden_common.h" + +#include "llfolderviewmodel.h" +#include "lltrans.h" + +bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item) +{ + return item->getSortVersion() < mTargetSortVersion; +} + +std::string LLFolderViewModelCommon::getStatusText() +{ + if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration()) + { + return LLTrans::getString("Searching"); + } + else + { + return getFilter().getEmptyLookupMessage(); + } +} + +void LLFolderViewModelCommon::filter() +{ + getFilter().setFilterCount(llclamp(LLUI::sSettingGroups["config"]->getS32("FilterItemsPerFrame"), 1, 5000)); + mFolderView->getViewModelItem()->filter(getFilter()); +} + +bool LLFolderViewModelItemCommon::hasFilterStringMatch() +{ + return mStringMatchOffsetFilter != std::string::npos; +} + +std::string::size_type LLFolderViewModelItemCommon::getFilterStringOffset() +{ + return mStringMatchOffsetFilter; +} + +std::string::size_type LLFolderViewModelItemCommon::getFilterStringSize() +{ + return mRootViewModel.getFilter().getFilterStringSize(); +} diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h new file mode 100644 index 0000000000..1b61212c0e --- /dev/null +++ b/indra/llui/llfolderviewmodel.h @@ -0,0 +1,444 @@ +/** + * @file llfolderviewmodel.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 LLFOLDERVIEWMODEL_H +#define LLFOLDERVIEWMODEL_H + +#include "llfontgl.h" // just for StyleFlags enum +#include "llfolderview.h" + +// 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 +}; + +class LLFontGL; +class LLInventoryModel; +class LLMenuGL; +class LLUIImage; +class LLUUID; +class LLFolderViewItem; +class LLFolderViewFolder; + +class LLFolderViewFilter +{ +public: + enum EFilterModified + { + 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 + }; + +public: + + LLFolderViewFilter() {} + virtual ~LLFolderViewFilter() {} + + // +-------------------------------------------------------------------+ + // + Execution And Results + // +-------------------------------------------------------------------+ + virtual bool check(const LLFolderViewModelItem* item) = 0; + virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0; + + virtual void setEmptyLookupMessage(const std::string& message) = 0; + virtual std::string getEmptyLookupMessage() const = 0; + + virtual bool showAllResults() const = 0; + + virtual std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const = 0; + virtual std::string::size_type getFilterStringSize() const = 0; + // +-------------------------------------------------------------------+ + // + Status + // +-------------------------------------------------------------------+ + virtual bool isActive() const = 0; + virtual bool isModified() const = 0; + virtual void clearModified() = 0; + virtual const std::string& getName() const = 0; + virtual const std::string& getFilterText() = 0; + //RN: this is public to allow system to externally force a global refilter + virtual void setModified(EFilterModified behavior = FILTER_RESTART) = 0; + + // +-------------------------------------------------------------------+ + // + Count + // +-------------------------------------------------------------------+ + virtual void setFilterCount(S32 count) = 0; + virtual S32 getFilterCount() const = 0; + virtual void decrementFilterCount() = 0; + + // +-------------------------------------------------------------------+ + // + Default + // +-------------------------------------------------------------------+ + virtual bool isDefault() const = 0; + virtual bool isNotDefault() const = 0; + virtual void markDefault() = 0; + virtual void resetDefault() = 0; + + // +-------------------------------------------------------------------+ + // + Generation + // +-------------------------------------------------------------------+ + virtual S32 getCurrentGeneration() const = 0; + virtual S32 getFirstSuccessGeneration() const = 0; + virtual S32 getFirstRequiredGeneration() const = 0; +}; + +class LLFolderViewModelInterface +{ +public: + virtual ~LLFolderViewModelInterface() {} + virtual void requestSortAll() = 0; + + virtual void sort(class LLFolderViewFolder*) = 0; + virtual void filter() = 0; + + virtual bool contentsReady() = 0; + virtual void setFolderView(LLFolderView* folder_view) = 0; + virtual LLFolderViewFilter& getFilter() = 0; + virtual const LLFolderViewFilter& getFilter() const = 0; + virtual std::string getStatusText() = 0; + + virtual bool startDrag(std::vector& items) = 0; +}; + +// This is an abstract base class that users of the folderview classes +// would use to bridge the folder view with the underlying data +class LLFolderViewModelItem : public LLRefCount +{ +public: + LLFolderViewModelItem() { } + virtual ~LLFolderViewModelItem() { } + + virtual void update() {} //called when drawing + virtual const std::string& getName() const = 0; + virtual const std::string& getDisplayName() const = 0; + virtual const std::string& getSearchableName() const = 0; + + virtual LLPointer getIcon() const = 0; + virtual LLPointer getIconOpen() const { return getIcon(); } + virtual LLPointer getIconOverlay() const { return NULL; } + + virtual LLFontGL::StyleFlags getLabelStyle() const = 0; + virtual std::string getLabelSuffix() const = 0; + + virtual void openItem( void ) = 0; + virtual void closeItem( void ) = 0; + virtual void selectItem(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 void move( LLFolderViewModelItem* parent_listener ) = 0; + + virtual BOOL isItemRemovable( void ) const = 0; // Can be destroyed + virtual BOOL removeItem() = 0; + virtual void removeBatch(std::vector& batch) = 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 potentiallyVisible() = 0; // is the item definitely visible or we haven't made up our minds yet? + + virtual bool filter( LLFolderViewFilter& filter) = 0; + virtual bool passedFilter(S32 filter_generation = -1) = 0; + virtual bool descendantsPassedFilter(S32 filter_generation = -1) = 0; + virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) = 0; + virtual void setPassedFolderFilter(bool passed, S32 filter_generation) = 0; + virtual void dirtyFilter() = 0; + virtual bool hasFilterStringMatch() = 0; + virtual std::string::size_type getFilterStringOffset() = 0; + virtual std::string::size_type getFilterStringSize() = 0; + + virtual S32 getLastFilterGeneration() const = 0; + + virtual bool hasChildren() const = 0; + virtual void addChild(LLFolderViewModelItem* child) = 0; + virtual void removeChild(LLFolderViewModelItem* child) = 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; + + virtual void requestSort() = 0; + virtual S32 getSortVersion() = 0; + virtual void setSortVersion(S32 version) = 0; + virtual void setParent(LLFolderViewModelItem* parent) = 0; + virtual bool hasParent() = 0; + +protected: + + friend class LLFolderViewItem; + virtual void setFolderViewItem(LLFolderViewItem* folder_view_item) = 0; + +}; + + +class LLFolderViewModelItemCommon : public LLFolderViewModelItem +{ +public: + LLFolderViewModelItemCommon(LLFolderViewModelInterface& root_view_model) + : mSortVersion(-1), + mPassedFilter(true), + mPassedFolderFilter(true), + mStringMatchOffsetFilter(std::string::npos), + mStringFilterSize(0), + mFolderViewItem(NULL), + mLastFilterGeneration(-1), + mLastFolderFilterGeneration(-1), + mMostFilteredDescendantGeneration(-1), + mParent(NULL), + mRootViewModel(root_view_model) + { + mChildren.clear(); + } + + void requestSort() { mSortVersion = -1; } + S32 getSortVersion() { return mSortVersion; } + void setSortVersion(S32 version) { mSortVersion = version;} + + S32 getLastFilterGeneration() const { return mLastFilterGeneration; } + S32 getLastFolderFilterGeneration() const { return mLastFolderFilterGeneration; } + void dirtyFilter() + { + mLastFilterGeneration = -1; + mLastFolderFilterGeneration = -1; + + // bubble up dirty flag all the way to root + if (mParent) + { + mParent->dirtyFilter(); + } + } + bool hasFilterStringMatch(); + std::string::size_type getFilterStringOffset(); + std::string::size_type getFilterStringSize(); + + typedef std::list child_list_t; + + virtual void addChild(LLFolderViewModelItem* child) + { + // Avoid duplicates: bail out if that child is already present in the list + // Note: this happens when models are created before views + child_list_t::const_iterator iter; + for (iter = mChildren.begin(); iter != mChildren.end(); iter++) + { + if (child == *iter) + { + return; + } + } + mChildren.push_back(child); + child->setParent(this); + dirtyFilter(); + requestSort(); + } + virtual void removeChild(LLFolderViewModelItem* child) + { + mChildren.remove(child); + child->setParent(NULL); + dirtyFilter(); + } + + virtual void clearChildren() + { + // As this is cleaning the whole list of children wholesale, we do need to delete the pointed objects + // This is different and not equivalent to calling removeChild() on each child + std::for_each(mChildren.begin(), mChildren.end(), DeletePointer()); + mChildren.clear(); + dirtyFilter(); + } + + child_list_t::const_iterator getChildrenBegin() const { return mChildren.begin(); } + child_list_t::const_iterator getChildrenEnd() const { return mChildren.end(); } + child_list_t::size_type getChildrenCount() const { return mChildren.size(); } + + void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) + { + mPassedFilter = passed; + mLastFilterGeneration = filter_generation; + mStringMatchOffsetFilter = string_offset; + mStringFilterSize = string_size; + } + + void setPassedFolderFilter(bool passed, S32 filter_generation) + { + mPassedFolderFilter = passed; + mLastFolderFilterGeneration = filter_generation; + } + + virtual bool potentiallyVisible() + { + return passedFilter() // we've passed the filter + || getLastFilterGeneration() < mRootViewModel.getFilter().getFirstSuccessGeneration() // or we don't know yet + || descendantsPassedFilter(); + } + + virtual bool passedFilter(S32 filter_generation = -1) + { + if (filter_generation < 0) + filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); + + bool passed_folder_filter = mPassedFolderFilter && mLastFolderFilterGeneration >= filter_generation; + bool passed_filter = mPassedFilter && mLastFilterGeneration >= filter_generation; + return passed_folder_filter + && (descendantsPassedFilter(filter_generation) + || passed_filter); + } + + virtual bool descendantsPassedFilter(S32 filter_generation = -1) + { + if (filter_generation < 0) filter_generation = mRootViewModel.getFilter().getFirstSuccessGeneration(); + return mMostFilteredDescendantGeneration >= filter_generation; + } + + +protected: + virtual void setParent(LLFolderViewModelItem* parent) { mParent = parent; } + virtual bool hasParent() { return mParent != NULL; } + + S32 mSortVersion; + bool mPassedFilter; + bool mPassedFolderFilter; + std::string::size_type mStringMatchOffsetFilter; + std::string::size_type mStringFilterSize; + + S32 mLastFilterGeneration; + S32 mLastFolderFilterGeneration; + S32 mMostFilteredDescendantGeneration; + + child_list_t mChildren; + LLFolderViewModelItem* mParent; + LLFolderViewModelInterface& mRootViewModel; + + void setFolderViewItem(LLFolderViewItem* folder_view_item) { mFolderViewItem = folder_view_item;} + LLFolderViewItem* mFolderViewItem; +}; + + + +class LLFolderViewModelCommon : public LLFolderViewModelInterface +{ +public: + LLFolderViewModelCommon() + : mTargetSortVersion(0), + mFolderView(NULL) + {} + + virtual void requestSortAll() + { + // sort everything + mTargetSortVersion++; + } + virtual std::string getStatusText(); + virtual void filter(); + + void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} + +protected: + bool needsSort(class LLFolderViewModelItem* item); + + S32 mTargetSortVersion; + LLFolderView* mFolderView; + +}; + +template +class LLFolderViewModel : public LLFolderViewModelCommon +{ +public: + LLFolderViewModel(){} + virtual ~LLFolderViewModel() {} + + typedef SORT_TYPE SortType; + typedef ITEM_TYPE ItemType; + typedef FOLDER_TYPE FolderType; + typedef FILTER_TYPE FilterType; + + virtual SortType& getSorter() { return mSorter; } + virtual const SortType& getSorter() const { return mSorter; } + virtual void setSorter(const SortType& sorter) { mSorter = sorter; requestSortAll(); } + + virtual FilterType& getFilter() { return mFilter; } + virtual const FilterType& getFilter() const { return mFilter; } + virtual void setFilter(const FilterType& filter) { mFilter = filter; } + + // By default, we assume the content is available. If a network fetch mechanism is implemented for the model, + // this method needs to be overloaded and return the relevant fetch status. + virtual bool contentsReady() { return true; } + + + struct ViewModelCompare + { + ViewModelCompare(const SortType& sorter) + : mSorter(sorter) + {} + + bool operator () (const LLFolderViewItem* a, const LLFolderViewItem* b) const + { + return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); + } + + bool operator () (const LLFolderViewFolder* a, const LLFolderViewFolder* b) const + { + return mSorter(static_cast(a->getViewModelItem()), static_cast(b->getViewModelItem())); + } + + const SortType& mSorter; + }; + + void sort(LLFolderViewFolder* folder) + { + if (needsSort(folder->getViewModelItem())) + { + folder->sortFolders(ViewModelCompare(getSorter())); + folder->sortItems(ViewModelCompare(getSorter())); + folder->getViewModelItem()->setSortVersion(mTargetSortVersion); + folder->requestArrange(); + } + } + +protected: + SortType mSorter; + FilterType mFilter; +}; + +#endif // LLFOLDERVIEWMODEL_H diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index f6a7a89ba3..4bfdddc9b3 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -32,11 +32,10 @@ #include "lllocalcliprect.h" #include "llpanel.h" -#include "llresizebar.h" #include "llcriticaldamp.h" #include "boost/foreach.hpp" -static const F32 MIN_FRACTIONAL_SIZE = 0.0f; +static const F32 MIN_FRACTIONAL_SIZE = 0.00001f; static const F32 MAX_FRACTIONAL_SIZE = 1.f; static LLDefaultChildRegistry::Register register_layout_stack("layout_stack"); @@ -71,7 +70,7 @@ LLLayoutPanel::LLLayoutPanel(const Params& p) mCollapseAmt(0.f), mVisibleAmt(1.f), // default to fully visible mResizeBar(NULL), - mFractionalSize(MIN_FRACTIONAL_SIZE), + mFractionalSize(0.f), mTargetDim(0), mIgnoreReshape(false), mOrientation(LLLayoutStack::HORIZONTAL) @@ -608,7 +607,7 @@ void LLLayoutStack::updateFractionalSizes() { if (panelp->mAutoResize) { - total_resizable_dim += llmax(0, panelp->getLayoutDim() - panelp->getRelevantMinDim()); + total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); } } @@ -759,12 +758,12 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& S32 new_dim = (mOrientation == HORIZONTAL) ? new_rect.getWidth() : new_rect.getHeight(); - S32 delta_dim = new_dim - resized_panel->getVisibleDim(); - if (delta_dim == 0) return; + S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim(); + if (delta_panel_dim == 0) return; F32 total_visible_fraction = 0.f; F32 delta_auto_resize_headroom = 0.f; - F32 original_auto_resize_headroom = 0.f; + F32 old_auto_resize_headroom = 0.f; LLLayoutPanel* other_resize_panel = NULL; LLLayoutPanel* following_panel = NULL; @@ -773,7 +772,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& { if (panelp->mAutoResize) { - original_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); + old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); if (panelp->getVisible() && !panelp->mCollapsed) { total_visible_fraction += panelp->mFractionalSize; @@ -791,25 +790,24 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& } } - if (resized_panel->mAutoResize) { if (!other_resize_panel || !other_resize_panel->mAutoResize) { - delta_auto_resize_headroom += delta_dim; + delta_auto_resize_headroom += delta_panel_dim; } } else { if (!other_resize_panel || other_resize_panel->mAutoResize) { - delta_auto_resize_headroom -= delta_dim; + delta_auto_resize_headroom -= delta_panel_dim; } } F32 fraction_given_up = 0.f; F32 fraction_remaining = 1.f; - F32 updated_auto_resize_headroom = original_auto_resize_headroom + delta_auto_resize_headroom; + F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; enum { @@ -821,7 +819,14 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) { - if (!panelp->getVisible() || panelp->mCollapsed) continue; + if (!panelp->getVisible() || panelp->mCollapsed) + { + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + } + continue; + } if (panelp == resized_panel) { @@ -833,9 +838,9 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& case BEFORE_RESIZED_PANEL: if (panelp->mAutoResize) { // freeze current size as fraction of overall auto_resize space - F32 fractional_adjustment_factor = updated_auto_resize_headroom == 0.f + F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f ? 1.f - : original_auto_resize_headroom / updated_auto_resize_headroom; + : old_auto_resize_headroom / new_auto_resize_headroom; F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); @@ -852,9 +857,9 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& case RESIZED_PANEL: if (panelp->mAutoResize) { // freeze new size as fraction - F32 new_fractional_size = (updated_auto_resize_headroom == 0.f) + F32 new_fractional_size = (new_auto_resize_headroom == 0.f) ? MAX_FRACTIONAL_SIZE - : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / updated_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); fraction_given_up -= new_fractional_size - panelp->mFractionalSize; fraction_remaining -= panelp->mFractionalSize; panelp->mFractionalSize = new_fractional_size; @@ -877,29 +882,22 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& } else { - // Don't divide by zero! updated_auto_resize_headroom - // will be zero if the next panel after the resized - // panel reaches it minimum size. Set the new - // fractional size to MAX_FRACTIONAL_SIZE because - // it seems to be always MAX_FRACTIONAL_SIZE during - // resizing. - //F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) - // / updated_auto_resize_headroom, - // MIN_FRACTIONAL_SIZE, - // MAX_FRACTIONAL_SIZE); - F32 new_fractional_size = (updated_auto_resize_headroom == 0.f) ? MAX_FRACTIONAL_SIZE - : llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) - / updated_auto_resize_headroom, + if (new_auto_resize_headroom < 1.f) + { + new_auto_resize_headroom = 1.f; + } + + F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) + / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); - // fraction_given_up -= new_fractional_size - panelp->mFractionalSize; panelp->mFractionalSize = new_fractional_size; } } else { - panelp->mTargetDim -= delta_dim; + panelp->mTargetDim -= delta_panel_dim; } which_panel = AFTER_RESIZED_PANEL; break; @@ -915,7 +913,7 @@ void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& } } updateLayout(); - normalizeFractionalSizes(); + //normalizeFractionalSizes(); } void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index 5c078e225b..f3d55cd0b9 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -29,6 +29,7 @@ #define LL_LLLAYOUTSTACK_H #include "llpanel.h" +#include "llresizebar.h" class LLLayoutPanel; @@ -189,6 +190,9 @@ public: F32 getAutoResizeFactor() const; F32 getVisibleAmount() const; S32 getVisibleDim() const; + LLResizeBar* getResizeBar() { return mResizeBar; } + + bool isCollapsed() const { return mCollapsed;} void setOrientation(LLLayoutStack::ELayoutOrientation orientation); void storeOriginalDim(); diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index c500ddae43..c321b94317 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -157,8 +157,7 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) mHighlightColor(p.highlight_color()), mPreeditBgColor(p.preedit_bg_color()), mGLFont(p.font), - mContextMenuHandle(), - mAutoreplaceCallback() + mContextMenuHandle() { llassert( mMaxLengthBytes > 0 ); @@ -203,6 +202,14 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p) LLLineEditor::~LLLineEditor() { mCommitOnFocusLost = FALSE; + + // Make sure no context menu linger around once the widget is deleted + LLContextMenu* menu = static_cast(mContextMenuHandle.get()); + if (menu) + { + menu->hide(); + } + setContextMenu(NULL); // calls onCommit() while LLLineEditor still valid gFocusMgr.releaseFocusIfNeeded( this ); @@ -973,12 +980,6 @@ void LLLineEditor::addChar(const llwchar uni_char) LLUI::reportBadKeystroke(); } - if (!mReadOnly && mAutoreplaceCallback != NULL) - { - // call callback - mAutoreplaceCallback(mText, mCursorPos); - } - getWindow()->hideCursorUntilMouseMove(); } diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index ad077b1255..fcffe67d25 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -189,9 +189,6 @@ public: virtual BOOL setTextArg( const std::string& key, const LLStringExplicit& text ); virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); - typedef boost::function autoreplace_callback_t; - autoreplace_callback_t mAutoreplaceCallback; - void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; } void setLabel(const LLStringExplicit &new_label) { mLabel = new_label; } const std::string& getLabel() { return mLabel.getString(); } diff --git a/indra/llui/llloadingindicator.cpp b/indra/llui/llloadingindicator.cpp index 6ac38f5ad4..1ede5b706f 100644 --- a/indra/llui/llloadingindicator.cpp +++ b/indra/llui/llloadingindicator.cpp @@ -52,7 +52,7 @@ LLLoadingIndicator::LLLoadingIndicator(const Params& p) void LLLoadingIndicator::initFromParams(const Params& p) { - BOOST_FOREACH(LLUIImage* image, p.images.image) + BOOST_FOREACH(LLUIImage* image, p.images().image) { mImages.push_back(image); } diff --git a/indra/llui/llloadingindicator.h b/indra/llui/llloadingindicator.h index c1f979c111..ffcb329f42 100644 --- a/indra/llui/llloadingindicator.h +++ b/indra/llui/llloadingindicator.h @@ -51,7 +51,7 @@ class LLLoadingIndicator LOG_CLASS(LLLoadingIndicator); public: - struct Images : public LLInitParam::BatchBlock + struct Images : public LLInitParam::Block { Multiple image; @@ -62,8 +62,8 @@ public: struct Params : public LLInitParam::Block { - Optional images_per_sec; - Optional images; + Optional images_per_sec; + Optional > images; Params() : images_per_sec("images_per_sec", 1.0f), diff --git a/indra/llui/lllocalcliprect.cpp b/indra/llui/lllocalcliprect.cpp index f3a526faeb..0620e0f52d 100644 --- a/indra/llui/lllocalcliprect.cpp +++ b/indra/llui/lllocalcliprect.cpp @@ -33,7 +33,7 @@ LLScreenClipRect::LLScreenClipRect(const LLRect& rect, BOOL enabled) -: mScissorState(GL_SCISSOR_TEST), + : mScissorState(GL_SCISSOR_TEST), mEnabled(enabled) { if (mEnabled) @@ -100,10 +100,10 @@ void LLScreenClipRect::updateScissorRegion() // LLLocalClipRect //--------------------------------------------------------------------------- LLLocalClipRect::LLLocalClipRect(const LLRect& rect, BOOL enabled /* = TRUE */) -: LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, - rect.mTop + LLFontGL::sCurOrigin.mY, - rect.mRight + LLFontGL::sCurOrigin.mX, - rect.mBottom + LLFontGL::sCurOrigin.mY), enabled) + : LLScreenClipRect(LLRect(rect.mLeft + LLFontGL::sCurOrigin.mX, + rect.mTop + LLFontGL::sCurOrigin.mY, + rect.mRight + LLFontGL::sCurOrigin.mX, + rect.mBottom + LLFontGL::sCurOrigin.mY), enabled) {} LLLocalClipRect::~LLLocalClipRect() diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 50d59f79f4..746ade4648 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -44,33 +44,27 @@ void LLMenuButton::MenuPositions::declareValues() LLMenuButton::Params::Params() : menu_filename("menu_filename"), - position("position", MP_BOTTOM_LEFT) + position("menu_position", MP_BOTTOM_LEFT) { + addSynonym(position, "position"); } LLMenuButton::LLMenuButton(const LLMenuButton::Params& p) : LLButton(p), mIsMenuShown(false), - mMenuPosition(p.position) + mMenuPosition(p.position), + mOwnMenu(false) { std::string menu_filename = p.menu_filename; - if (!menu_filename.empty()) - { - LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); - if (!menu) - { - llwarns << "Error loading menu_button menu" << llendl; - return; - } + setMenu(menu_filename, mMenuPosition); + updateMenuOrigin(); +} - menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); - - mMenuHandle = menu->getHandle(); - - updateMenuOrigin(); - } +LLMenuButton::~LLMenuButton() +{ + cleanup(); } boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_signal_t::slot_type& cb ) @@ -80,9 +74,7 @@ boost::signals2::connection LLMenuButton::setMouseDownCallback( const mouse_sign void LLMenuButton::hideMenu() { - if(mMenuHandle.isDead()) return; - - LLToggleableMenu* menu = dynamic_cast(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (menu) { menu->setVisible(FALSE); @@ -94,19 +86,39 @@ LLToggleableMenu* LLMenuButton::getMenu() return dynamic_cast(mMenuHandle.get()); } -void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/) +void LLMenuButton::setMenu(const std::string& menu_filename, EMenuPosition position /*MP_TOP_LEFT*/) +{ + if (menu_filename.empty()) + { + return; + } + + LLToggleableMenu* menu = LLUICtrlFactory::getInstance()->createFromFile(menu_filename, LLMenuGL::sMenuContainer, LLMenuHolderGL::child_registry_t::instance()); + if (!menu) + { + llwarns << "Error loading menu_button menu" << llendl; + return; + } + + setMenu(menu, position, true); +} + +void LLMenuButton::setMenu(LLToggleableMenu* menu, EMenuPosition position /*MP_TOP_LEFT*/, bool take_ownership /*false*/) { if (!menu) return; + cleanup(); // destroy the previous memnu if we own it + mMenuHandle = menu->getHandle(); mMenuPosition = position; + mOwnMenu = take_ownership; menu->setVisibilityChangeCallback(boost::bind(&LLMenuButton::onMenuVisibilityChange, this, _2)); } BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) { - if (mMenuHandle.isDead()) return FALSE; + if (!getMenu()) return FALSE; if( KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key)) { @@ -118,7 +130,7 @@ BOOL LLMenuButton::handleKeyHere(KEY key, MASK mask ) return TRUE; } - LLToggleableMenu* menu = dynamic_cast(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (menu && menu->getVisible() && key == KEY_ESCAPE && mask == MASK_NONE) { menu->setVisible(FALSE); @@ -139,9 +151,12 @@ BOOL LLMenuButton::handleMouseDown(S32 x, S32 y, MASK mask) void LLMenuButton::toggleMenu() { - if(mMenuHandle.isDead()) return; + if (mValidateSignal && !(*mValidateSignal)(this, LLSD())) + { + return; + } - LLToggleableMenu* menu = dynamic_cast(mMenuHandle.get()); + LLToggleableMenu* menu = getMenu(); if (!menu) return; // Store the button rectangle to toggle menu visibility if a mouse event @@ -170,7 +185,8 @@ void LLMenuButton::toggleMenu() void LLMenuButton::updateMenuOrigin() { - if (mMenuHandle.isDead()) return; + LLToggleableMenu* menu = getMenu(); + if (!menu) return; LLRect rect = getRect(); @@ -179,12 +195,12 @@ void LLMenuButton::updateMenuOrigin() case MP_TOP_LEFT: { mX = rect.mLeft; - mY = rect.mTop + mMenuHandle.get()->getRect().getHeight(); + mY = rect.mTop + menu->getRect().getHeight(); break; } case MP_TOP_RIGHT: { - const LLRect& menu_rect = mMenuHandle.get()->getRect(); + const LLRect& menu_rect = menu->getRect(); mX = rect.mRight - menu_rect.getWidth(); mY = rect.mTop + menu_rect.getHeight(); break; @@ -211,3 +227,11 @@ void LLMenuButton::onMenuVisibilityChange(const LLSD& param) mIsMenuShown = false; } } + +void LLMenuButton::cleanup() +{ + if (mMenuHandle.get() && mOwnMenu) + { + mMenuHandle.get()->die(); + } +} diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index e2396e7fb2..67ec1983b3 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -34,6 +34,8 @@ class LLToggleableMenu; class LLMenuButton : public LLButton { + LOG_CLASS(LLMenuButton); + public: typedef enum e_menu_position { @@ -53,7 +55,7 @@ public: { // filename for it's toggleable menu Optional menu_filename; - Optional position; + Optional position; Params(); }; @@ -68,13 +70,15 @@ public: void hideMenu(); LLToggleableMenu* getMenu(); - void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT); + void setMenu(const std::string& menu_filename, EMenuPosition position = MP_TOP_LEFT); + void setMenu(LLToggleableMenu* menu, EMenuPosition position = MP_TOP_LEFT, bool take_ownership = false); void setMenuPosition(EMenuPosition position) { mMenuPosition = position; } protected: friend class LLUICtrlFactory; LLMenuButton(const Params&); + ~LLMenuButton(); void toggleMenu(); void updateMenuOrigin(); @@ -82,11 +86,14 @@ protected: void onMenuVisibilityChange(const LLSD& param); private: + void cleanup(); + LLHandle mMenuHandle; bool mIsMenuShown; EMenuPosition mMenuPosition; S32 mX; S32 mY; + bool mOwnMenu; // true if we manage the menu lifetime }; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index a471461d57..0a8d186d8d 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -589,48 +589,32 @@ void LLMenuItemSeparatorGL::draw( void ) BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); -// [SL:KB] - Patch: UI-Misc | Checked: 2012-08-03 (Catznip-3.3) - LLMenuItemGL* menu_item = (parent_menu) ? parent_menu->getHighlightedItem() : NULL; - if ( (menu_item) && (this != menu_item) ) + if (y > getRect().getHeight() / 2) { - return menu_item->handleMouseDown(x, y, mask); + // the menu items are in the child list in bottom up order + LLView* prev_menu_item = parent_menu->findNextSibling(this); + return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; + } + else + { + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE; } - return LLMenuItemGL::handleMouseDown(x, y, mask); -// [/SL:KB] -// if (y > getRect().getHeight() / 2) -// { -// // the menu items are in the child list in bottom up order -// LLView* prev_menu_item = parent_menu->findNextSibling(this); -// return prev_menu_item ? prev_menu_item->handleMouseDown(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; -// } -// else -// { -// LLView* next_menu_item = parent_menu->findPrevSibling(this); -// return next_menu_item ? next_menu_item->handleMouseDown(x, 0, mask) : FALSE; -// } } BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); -// [SL:KB] - Patch: UI-Misc | Checked: 2012-08-03 (Catznip-3.3) - LLMenuItemGL* menu_item = (parent_menu) ? parent_menu->getHighlightedItem() : NULL; - if ( (menu_item) && (this != menu_item) ) + if (y > getRect().getHeight() / 2) { - return menu_item->handleMouseUp(x, y, mask); + LLView* prev_menu_item = parent_menu->findNextSibling(this); + return (prev_menu_item && prev_menu_item->getVisible() && prev_menu_item->getEnabled()) ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; + } + else + { + LLView* next_menu_item = parent_menu->findPrevSibling(this); + return (next_menu_item && next_menu_item->getVisible() && next_menu_item->getEnabled()) ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE; } - return LLMenuItemGL::handleMouseUp(x, y, mask); -// [/SL:KB] -// if (y > getRect().getHeight() / 2) -// { -// LLView* prev_menu_item = parent_menu->findNextSibling(this); -// return prev_menu_item ? prev_menu_item->handleMouseUp(x, prev_menu_item->getRect().getHeight(), mask) : FALSE; -// } -// else -// { -// LLView* next_menu_item = parent_menu->findPrevSibling(this); -// return next_menu_item ? next_menu_item->handleMouseUp(x, 0, mask) : FALSE; -// } } BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask) @@ -1767,35 +1751,50 @@ void LLMenuGL::setCanTearOff(BOOL tear_off) bool LLMenuGL::addChild(LLView* view, S32 tab_group) { - if (LLMenuGL* menup = dynamic_cast(view)) + LLMenuGL* menup = dynamic_cast(view); + if (menup) { - appendMenu(menup); - return true; + return appendMenu(menup); } - else if (LLMenuItemGL* itemp = dynamic_cast(view)) + + LLMenuItemGL* itemp = dynamic_cast(view); + if (itemp) { - append(itemp); - return true; + return append(itemp); } + return false; } // Used in LLContextMenu and in LLTogleableMenu -// to add an item of context menu branch + +// Add an item to the context menu branch bool LLMenuGL::addContextChild(LLView* view, S32 tab_group) { LLContextMenu* context = dynamic_cast(view); if (context) + { return appendContextSubMenu(context); + } LLMenuItemSeparatorGL* separator = dynamic_cast(view); if (separator) + { return append(separator); + } LLMenuItemGL* item = dynamic_cast(view); if (item) + { return append(item); - + } + + LLMenuGL* menup = dynamic_cast(view); + if (menup) + { + return appendMenu(menup); + } + return false; } @@ -2465,6 +2464,56 @@ void LLMenuGL::empty( void ) deleteAllChildren(); } +// erase group of items from menu +void LLMenuGL::erase( S32 begin, S32 end, bool arrange/* = true*/) +{ + S32 items = mItems.size(); + + if ( items == 0 || begin >= end || begin < 0 || end > items ) + { + return; + } + + item_list_t::iterator start_position = mItems.begin(); + std::advance(start_position, begin); + + item_list_t::iterator end_position = mItems.begin(); + std::advance(end_position, end); + + for (item_list_t::iterator position_iter = start_position; position_iter != end_position; position_iter++) + { + LLUICtrl::removeChild(*position_iter); + } + + mItems.erase(start_position, end_position); + + if (arrange) + { + needsArrange(); + } +} + +// add new item at position +void LLMenuGL::insert( S32 position, LLView * ctrl, bool arrange /*= true*/ ) +{ + LLMenuItemGL * item = dynamic_cast(ctrl); + + if (NULL == item || position < 0 || position >= mItems.size()) + { + return; + } + + item_list_t::iterator position_iter = mItems.begin(); + std::advance(position_iter, position); + mItems.insert(position_iter, item); + LLUICtrl::addChild(item); + + if (arrange) + { + needsArrange(); + } +} + // Adjust rectangle of the menu void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom) { @@ -2506,7 +2555,8 @@ BOOL LLMenuGL::append( LLMenuItemGL* item ) // add a separator to this menu BOOL LLMenuGL::addSeparator() { - LLMenuItemGL* separator = new LLMenuItemSeparatorGL(); + LLMenuItemSeparatorGL::Params p; + LLMenuItemGL* separator = LLUICtrlFactory::create(p); return addChild(separator); } @@ -2731,35 +2781,21 @@ LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disa while(1) { // skip separators and disabled/invisible items -// [SL:KB] - Patch: UI-Misc | Checked: 2012-08-03 (Catznip-3.3) - bool fCanHighlight = ((*next_item_iter)->getVisible()) && (!dynamic_cast(*next_item_iter)); - if ( (fCanHighlight) && ((*next_item_iter)->getEnabled()) ) + if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast(*next_item_iter)) { + if (cur_item) + { + cur_item->setHighlight(FALSE); + } (*next_item_iter)->setHighlight(TRUE); return (*next_item_iter); } - // We always want to skip invisible items and separators so only break if it was actually disabled - if ( ((!skip_disabled) && (fCanHighlight)) || (next_item_iter == cur_item_iter) ) + + if (!skip_disabled || next_item_iter == cur_item_iter) { break; } -// [/SL:KB] -// if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getVisible() && !dynamic_cast(*next_item_iter)) -// { -// if (cur_item) -// { -// cur_item->setHighlight(FALSE); -// } -// (*next_item_iter)->setHighlight(TRUE); -// return (*next_item_iter); -// } -// -// -// if (!skip_disabled || next_item_iter == cur_item_iter) -// { -// break; -// } next_item_iter++; if (next_item_iter == mItems.end()) @@ -2835,30 +2871,16 @@ LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disa while(1) { // skip separators and disabled/invisible items -// [SL:KB] - Patch: UI-Misc | Checked: 2012-08-03 (Catznip-3.3) - bool fCanHighlight = ((*prev_item_iter)->getVisible()) && (!dynamic_cast(*prev_item_iter)); - if ( (fCanHighlight) && ((*prev_item_iter)->getEnabled()) ) + if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) { (*prev_item_iter)->setHighlight(TRUE); return (*prev_item_iter); } - // We always want to skip invisible items and separators (unless the iterator round-tripped) - if ( ((!skip_disabled) && (fCanHighlight)) || (prev_item_iter == cur_item_iter) ) + if (!skip_disabled || prev_item_iter == cur_item_iter) { break; } -// [/SL:KB] -// if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getVisible() && (*prev_item_iter)->getName() != SEPARATOR_NAME) -// { -// (*prev_item_iter)->setHighlight(TRUE); -// return (*prev_item_iter); -// } -// -// if (!skip_disabled || prev_item_iter == cur_item_iter) -// { -// break; -// } prev_item_iter++; if (prev_item_iter == mItems.rend()) @@ -3127,7 +3149,17 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) const S32 CURSOR_HEIGHT = 22; // Approximate "normal" cursor size const S32 CURSOR_WIDTH = 12; - if(menu->getChildList()->empty()) + //Do not show menu if all menu items are disabled + BOOL item_enabled = false; + for (LLView::child_list_t::const_iterator itor = menu->getChildList()->begin(); + itor != menu->getChildList()->end(); + ++itor) + { + LLView *menu_item = (*itor); + item_enabled = item_enabled || menu_item->getEnabled(); + } + + if(menu->getChildList()->empty() || !item_enabled) { return; } @@ -4126,11 +4158,6 @@ BOOL LLContextMenu::handleRightMouseUp( S32 x, S32 y, MASK mask ) return result; } -void LLContextMenu::draw() -{ - LLMenuGL::draw(); -} - bool LLContextMenu::addChild(LLView* view, S32 tab_group) { return addContextChild(view, tab_group); diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 32a70830ca..fa78e4164f 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -478,6 +478,12 @@ public: // remove all items on the menu void empty( void ); + // erase group of items from menu + void erase( S32 begin, S32 end, bool arrange = true ); + + // add new item at position + void insert( S32 begin, LLView * ctrl, bool arrange = true ); + void setItemLastSelected(LLMenuItemGL* item); // must be in menu U32 getItemCount(); // number of menu items LLMenuItemGL* getItem(S32 number); // 0 = first item @@ -675,8 +681,6 @@ public: // can't set visibility directly, must call show or hide virtual void setVisible (BOOL visible); - virtual void draw (); - virtual void show (S32 x, S32 y, LLView* spawning_view = NULL); virtual void hide (); @@ -702,7 +706,6 @@ protected: // }; - //----------------------------------------------------------------------------- // class LLContextMenuBranch // A branch to another context menu diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index a5c7011eec..b81492e69a 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -31,7 +31,7 @@ #include "llmultifloater.h" #include "llresizehandle.h" -#include "lldraghandle.h" +//#include "lldraghandle.h" CHUI Merge Check // // LLMultiFloater @@ -42,8 +42,8 @@ LLMultiFloater::LLMultiFloater(const LLSD& key, const LLFloater::Params& params) mTabContainer(NULL), mTabPos(LLTabContainer::TOP), mAutoResize(TRUE), - mOrigMinWidth(0), - mOrigMinHeight(0) + mOrigMinWidth(params.min_width), + mOrigMinHeight(params.min_height) { } @@ -55,20 +55,12 @@ void LLMultiFloater::buildTabContainer() LLTabContainer::Params p; p.name(std::string("Preview Tabs")); p.rect(LLRect(LLPANEL_BORDER_WIDTH, getRect().getHeight() - floater_header_size, getRect().getWidth() - LLPANEL_BORDER_WIDTH, 0)); - p.follows.flags(FOLLOWS_ALL); - // Update torn off status and add title bar - //p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this)); p.tab_position(mTabPos); - - // remove existing tab container - if (mTabContainer) - { - removeChild(mTabContainer); - delete mTabContainer; - mTabContainer = NULL; - } - - addChild(LLUICtrlFactory::create(p)); + p.follows.flags(FOLLOWS_ALL); + p.commit_callback.function(boost::bind(&LLMultiFloater::onTabSelected, this)); + + mTabContainer = LLUICtrlFactory::create(p); + addChild(mTabContainer); if (isResizable()) { @@ -129,15 +121,8 @@ void LLMultiFloater::growToFit(S32 content_width, S32 content_height) static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); const LLFloater::Params& default_params = LLFloater::getDefaultParams(); S32 floater_header_size = default_params.header_height; - S32 tabcntr_header_height = - ((mTabContainer->getTabPosition() == LLTabContainer::LEFT) - ?0 - :(LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size)); - S32 tabcntr_header_width = - ((mTabContainer->getTabPosition() == LLTabContainer::LEFT) - ?mTabContainer->getMinTabWidth() - :0); - S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2 + tabcntr_header_width); + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + S32 new_width = llmax(getRect().getWidth(), content_width + LLPANEL_BORDER_WIDTH * 2); S32 new_height = llmax(getRect().getHeight(), content_height + floater_header_size + tabcntr_header_height); if (isMinimized()) @@ -189,7 +174,7 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, else if (floaterp->getHost()) { // floaterp is hosted by somebody else and - // this is adding it, so remove it from it's old host + // this is adding it, so remove it from its old host floaterp->getHost()->removeFloater(floaterp); } else if (floaterp->getParent() == gFloaterView) @@ -204,20 +189,14 @@ void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, floater_data.mHeight = floaterp->getRect().getHeight(); floater_data.mCanMinimize = floaterp->isMinimizeable(); floater_data.mCanResize = floaterp->isResizable(); - - // Update torn off status and add title bar - floaterp->getDragHandle()->setTitleVisible(FALSE); - LLRect rect = floaterp->getRect(); - rect.mTop -= floaterp->getHeaderHeight(); - floaterp->setRect(rect); - // + floater_data.mSaveRect = floaterp->mSaveRect; // remove minimize and close buttons floaterp->setCanMinimize(FALSE); floaterp->setCanResize(FALSE); floaterp->setCanDrag(FALSE); - // Fixed edge case where hosted floates changed size on tear-off if user logged out inbetween - //floaterp->storeRectControl(); + floaterp->mSaveRect = FALSE; + floaterp->storeRectControl(); // avoid double rendering of floater background (makes it more opaque) floaterp->setBackgroundVisible(FALSE); @@ -262,13 +241,6 @@ void LLMultiFloater::updateFloaterTitle(LLFloater* floaterp) if (index != -1) { mTabContainer->setPanelTitle(index, floaterp->getShortTitle()); - // If the tab we're updating is the current tab, then - // update the overall title too, since we're showing it - // exclusively now. -- TS - if (floaterp == mTabContainer->getCurrentPanel()) - { - mDragHandle->setTitle(mTitle.getString() + " - " + floaterp->getTitle()); - } } } @@ -317,18 +289,12 @@ void LLMultiFloater::removeFloater(LLFloater* floaterp) if (!floaterp || floaterp->getHost() != this ) return; - // Update torn off status and add title bar - floaterp->getDragHandle()->setTitleVisible(TRUE); - LLRect rect = floaterp->getRect(); - rect.mTop += floaterp->getHeaderHeight(); - floaterp->setRect(rect); - // - floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle()); if (found_data_it != mFloaterDataMap.end()) { LLFloaterData& floater_data = found_data_it->second; floaterp->setCanMinimize(floater_data.mCanMinimize); + floaterp->mSaveRect = floater_data.mSaveRect; if (!floater_data.mCanResize) { // restore original size @@ -472,22 +438,9 @@ void LLMultiFloater::onTabSelected() if (floaterp) { tabOpen(floaterp, true); - // Update torn off status and add title bar - mDragHandle->setTitle(mTitle.getString() + " - " + floaterp->getTitle()); } } -// Update torn off status and add title bar -void LLMultiFloater::setTabContainer(LLTabContainer* tab_container) -{ - if (!mTabContainer) - { - mTabContainer = tab_container; - mTabContainer->setCommitCallback(boost::bind(&LLMultiFloater::onTabSelected, this)); - } -} -// - void LLMultiFloater::setCanResize(BOOL can_resize) { LLFloater::setCanResize(can_resize); @@ -514,9 +467,7 @@ BOOL LLMultiFloater::postBuild() return TRUE; } - // Update torn off status and add title bar - //mTabContainer = getChild("Preview Tabs"); - setTabContainer(getChild("Preview Tabs")); + mTabContainer = getChild("Preview Tabs"); setCanResize(mResizable); return TRUE; @@ -524,30 +475,12 @@ BOOL LLMultiFloater::postBuild() void LLMultiFloater::updateResizeLimits() { - static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); - const LLFloater::Params& default_params = LLFloater::getDefaultParams(); - S32 floater_header_size = default_params.header_height; - S32 tabcntr_header_height = - ((mTabContainer->getTabPosition() == LLTabContainer::LEFT) - ?0 - :(LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size)); - S32 tabcntr_header_width = - ((mTabContainer->getTabPosition() == LLTabContainer::LEFT) - ?mTabContainer->getMinTabWidth() - :0); // initialize minimum size constraint to the original xml values. S32 new_min_width = mOrigMinWidth; S32 new_min_height = mOrigMinHeight; - // possibly increase minimum size constraint due to children's minimums. - for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) - { - LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); - if (floaterp) - { - new_min_width = llmax(new_min_width, floaterp->getMinWidth() + tabcntr_header_width + LLPANEL_BORDER_WIDTH * 2); - new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); - } - } + + computeResizeLimits(new_min_width, new_min_height); + setResizeLimits(new_min_width, new_min_height); S32 cur_height = getRect().getHeight(); @@ -573,3 +506,22 @@ void LLMultiFloater::updateResizeLimits() gFloaterView->adjustToFitScreen(this, TRUE); } } + +void LLMultiFloater::computeResizeLimits(S32& new_min_width, S32& new_min_height) +{ + static LLUICachedControl tabcntr_close_btn_size ("UITabCntrCloseBtnSize", 0); + const LLFloater::Params& default_params = LLFloater::getDefaultParams(); + S32 floater_header_size = default_params.header_height; + S32 tabcntr_header_height = LLPANEL_BORDER_WIDTH + tabcntr_close_btn_size; + + // possibly increase minimum size constraint due to children's minimums. + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(tab_idx); + if (floaterp) + { + new_min_width = llmax(new_min_width, floaterp->getMinWidth() + LLPANEL_BORDER_WIDTH * 2); + new_min_height = llmax(new_min_height, floaterp->getMinHeight() + floater_header_size + tabcntr_header_height); + } + } +} diff --git a/indra/llui/llmultifloater.h b/indra/llui/llmultifloater.h index 465b9fff22..92edf21b58 100644 --- a/indra/llui/llmultifloater.h +++ b/indra/llui/llmultifloater.h @@ -45,8 +45,8 @@ public: virtual BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void draw(); - /*virtual*/ void setVisible(BOOL visible); + virtual void draw(); + virtual void setVisible(BOOL visible); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); @@ -82,10 +82,11 @@ public: protected: struct LLFloaterData { - S32 mWidth; - S32 mHeight; - BOOL mCanMinimize; - BOOL mCanResize; + S32 mWidth; + S32 mHeight; + BOOL mCanMinimize; + BOOL mCanResize; + BOOL mSaveRect; }; LLTabContainer* mTabContainer; @@ -96,6 +97,9 @@ protected: LLTabContainer::TabPosition mTabPos; BOOL mAutoResize; S32 mOrigMinWidth, mOrigMinHeight; // logically const but initialized late + +private: + virtual void computeResizeLimits(S32& new_min_width, S32& new_min_height); }; #endif // LL_MULTI_FLOATER_H diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index c82157fc4d..c7cae837e9 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -39,7 +39,6 @@ #include "lldir.h" #include "llsdserialize.h" #include "lltrans.h" -#include "llnotificationslistener.h" #include "llstring.h" #include "llsdparam.h" #include "llsdutil.h" @@ -60,7 +59,8 @@ void NotificationPriorityValues::declareValues() } LLNotificationForm::FormElementBase::FormElementBase() -: name("name") +: name("name"), + enabled("enabled", true) {} LLNotificationForm::FormIgnore::FormIgnore() @@ -105,39 +105,7 @@ LLNotificationForm::Params::Params() form_elements("") {} -// Local channel for persistent notifications -// Stores only persistent notifications. -// Class users can use connectChanged() to process persistent notifications -// (see LLNotificationStorage for example). -class LLPersistentNotificationChannel : public LLNotificationChannel -{ - LOG_CLASS(LLPersistentNotificationChannel); -public: - LLPersistentNotificationChannel() : - LLNotificationChannel("Persistent", "Visible", ¬ificationFilter, LLNotificationComparators::orderByUUID()) - { - } -private: - - // The channel gets all persistent notifications except those that have been canceled - static bool notificationFilter(LLNotificationPtr pNotification) - { - bool handle_notification = false; - - handle_notification = pNotification->isPersistent() - && !pNotification->isCancelled(); - - return handle_notification; - } - - void onDelete(LLNotificationPtr pNotification) - { - // we want to keep deleted notifications in our log, otherwise some - // notifications will be lost on exit. - mItems.insert(pNotification); - } -}; bool filterIgnoredNotifications(LLNotificationPtr notification) { @@ -211,6 +179,14 @@ LLNotificationForm::LLNotificationForm() { } +LLNotificationForm::LLNotificationForm( const LLNotificationForm& other ) +{ + mFormData = other.mFormData; + mIgnore = other.mIgnore; + mIgnoreMsg = other.mIgnoreMsg; + mIgnoreSetting = other.mIgnoreSetting; + mInvertSetting = other.mInvertSetting; +} LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotificationForm::Params& p) : mIgnore(IGNORE_NO), @@ -247,14 +223,6 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica LLParamSDParser parser; parser.writeSD(mFormData, p.form_elements); - if (!mFormData.isArray()) - { - // change existing contents to a one element array - LLSD new_llsd_array = LLSD::emptyArray(); - new_llsd_array.append(mFormData); - mFormData = new_llsd_array; - } - for (LLSD::array_iterator it = mFormData.beginArray(), end_it = mFormData.endArray(); it != end_it; ++it) @@ -301,7 +269,7 @@ LLSD LLNotificationForm::getElement(const std::string& element_name) } -bool LLNotificationForm::hasElement(const std::string& element_name) +bool LLNotificationForm::hasElement(const std::string& element_name) const { for (LLSD::array_const_iterator it = mFormData.beginArray(); it != mFormData.endArray(); @@ -312,7 +280,48 @@ bool LLNotificationForm::hasElement(const std::string& element_name) return false; } -void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value) +void LLNotificationForm::getElements(LLSD& elements, S32 offset) +{ + //Finds elements that the template did not add + LLSD::array_const_iterator it = mFormData.beginArray() + offset; + + //Keeps track of only the dynamic elements + for(; it != mFormData.endArray(); ++it) + { + elements.append(*it); + } +} + +bool LLNotificationForm::getElementEnabled(const std::string& element_name) const +{ + for (LLSD::array_const_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + return (*it)["enabled"].asBoolean(); + } + } + + return false; +} + +void LLNotificationForm::setElementEnabled(const std::string& element_name, bool enabled) +{ + for (LLSD::array_iterator it = mFormData.beginArray(); + it != mFormData.endArray(); + ++it) + { + if ((*it)["name"].asString() == element_name) + { + (*it)["enabled"] = enabled; + } + } +} + + +void LLNotificationForm::addElement(const std::string& type, const std::string& name, const LLSD& value, bool enabled) { LLSD element; element["type"] = type; @@ -320,6 +329,7 @@ void LLNotificationForm::addElement(const std::string& type, const std::string& element["text"] = name; element["value"] = value; element["index"] = mFormData.size(); + element["enabled"] = enabled; mFormData.append(element); } @@ -409,14 +419,19 @@ LLNotificationTemplate::LLNotificationTemplate(const LLNotificationTemplate::Par mURLOption(p.url.option), mURLTarget(p.url.target), mUnique(p.unique.isProvided()), + mCombineBehavior(p.unique.combine), mPriority(p.priority), mPersist(p.persist), - mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()) + mDefaultFunctor(p.functor.isProvided() ? p.functor() : p.name()), + mLogToChat(p.log_to_chat), + mLogToIM(p.log_to_im), + mShowToast(p.show_toast), + mSoundName("") { if (p.sound.isProvided() && LLUI::sSettingGroups["config"]->controlExists(p.sound)) { - mSoundEffect = LLUUID(LLUI::sSettingGroups["config"]->getString(p.sound)); + mSoundName = p.sound; } BOOST_FOREACH(const LLNotificationTemplate::UniquenessContext& context, p.unique.contexts) @@ -461,18 +476,20 @@ LLNotificationVisibilityRule::LLNotificationVisibilityRule(const LLNotificationV } } -LLNotification::LLNotification(const LLNotification::Params& p) : +LLNotification::LLNotification(const LLSDParamAdapter& p) : mTimestamp(p.time_stamp), mSubstitutions(p.substitutions), mPayload(p.payload), - mExpiresAt(0), + mExpiresAt(p.expiry), mTemporaryResponder(false), mRespondedTo(false), mPriority(p.priority), mCancelled(false), mIgnored(false), mResponderObj(NULL), - mIsReusable(false) + mId(p.id.isProvided() ? p.id : LLUUID::generateNewID()), + mOfferFromAgent(p.offer_from_agent), + mIsDND(p.is_dnd) { if (p.functor.name.isChosen()) { @@ -495,52 +512,52 @@ LLNotification::LLNotification(const LLNotification::Params& p) : mResponderObj = p.responder; } - mId.generate(); init(p.name, p.form_elements); } -LLNotification::LLNotification(const LLSD& sd) : - mTemporaryResponder(false), - mRespondedTo(false), - mCancelled(false), - mIgnored(false), - mResponderObj(NULL), - mIsReusable(false) -{ - mId.generate(); - mSubstitutions = sd["substitutions"]; - mPayload = sd["payload"]; - mTimestamp = sd["time"]; - mExpiresAt = sd["expiry"]; - mPriority = (ENotificationPriority)sd["priority"].asInteger(); - mResponseFunctorName = sd["responseFunctor"].asString(); - std::string templatename = sd["name"].asString(); - init(templatename, LLSD()); - // replace form with serialized version - mForm = LLNotificationFormPtr(new LLNotificationForm(sd["form"])); -} - - -LLSD LLNotification::asLLSD() +LLSD LLNotification::asLLSD(bool excludeTemplateElements) { - LLSD output; - output["id"] = mId; - output["name"] = mTemplatep->mName; - output["form"] = getForm()->asLLSD(); - output["substitutions"] = mSubstitutions; - output["payload"] = mPayload; - output["time"] = mTimestamp; - output["expiry"] = mExpiresAt; - output["priority"] = (S32)mPriority; - output["responseFunctor"] = mResponseFunctorName; - output["reusable"] = mIsReusable; + LLParamSDParser parser; - if(mResponder) + Params p; + p.id = mId; + p.name = mTemplatep->mName; + p.substitutions = mSubstitutions; + p.payload = mPayload; + p.time_stamp = mTimestamp; + p.expiry = mExpiresAt; + p.priority = mPriority; + + LLNotificationFormPtr templateForm = mTemplatep->mForm; + LLSD formElements = mForm->asLLSD(); + + //All form elements (dynamic or not) + if(!excludeTemplateElements) + { + p.form_elements = formElements; + } + //Only dynamic form elements (exclude template elements) + else if(templateForm->getNumElements() < formElements.size()) + { + LLSD dynamicElements; + //Offset to dynamic elements and store them + mForm->getElements(dynamicElements, templateForm->getNumElements()); + p.form_elements = dynamicElements; + } + + if(mResponder) + { + p.functor.responder_sd = mResponder->asLLSD(); + } + + if(!mResponseFunctorName.empty()) { - output["responder"] = mResponder->asLLSD(); + p.functor.name = mResponseFunctorName; } + LLSD output; + parser.writeSD(output, p); return output; } @@ -570,7 +587,6 @@ void LLNotification::updateFrom(LLNotificationPtr other) mRespondedTo = other->mRespondedTo; mResponse = other->mResponse; mTemporaryResponder = other->mTemporaryResponder; - mIsReusable = other->isReusable(); update(); } @@ -669,7 +685,7 @@ void LLNotification::respond(const LLSD& response) return; } - if (mTemporaryResponder && !isReusable()) + if (mTemporaryResponder) { LLNotificationFunctorRegistry::instance().unregisterFunctor(mResponseFunctorName); mResponseFunctorName = ""; @@ -830,7 +846,7 @@ void LLNotification::init(const std::string& template_name, const LLSD& form_ele //mSubstitutions["_ARGS"] = get_all_arguments_as_text(mSubstitutions); mForm = LLNotificationFormPtr(new LLNotificationForm(*mTemplatep->mForm)); - mForm->append(form_elements); + mForm->append(form_elements); // apply substitution to form labels mForm->formatElements(mSubstitutions); @@ -905,6 +921,49 @@ std::string LLNotification::getURL() const return (mTemplatep ? url : ""); } +bool LLNotification::canLogToChat() const +{ + return mTemplatep->mLogToChat; +} + +bool LLNotification::canLogToIM() const +{ + return mTemplatep->mLogToIM; +} + +bool LLNotification::canShowToast() const +{ + return mTemplatep->mShowToast; +} + +bool LLNotification::hasFormElements() const +{ + return mTemplatep->mForm->getNumElements() != 0; +} + +void LLNotification::playSound() +{ + make_ui_sound(mTemplatep->mSoundName.c_str()); +} + +LLNotification::ECombineBehavior LLNotification::getCombineBehavior() const +{ + return mTemplatep->mCombineBehavior; +} + +void LLNotification::updateForm( const LLNotificationFormPtr& form ) +{ + mForm = form; +} + +void LLNotification::repost() +{ + mRespondedTo = false; + LLNotifications::instance().update(shared_from_this()); +} + + + // ========================================================= // LLNotificationChannel implementation // --- @@ -965,7 +1024,7 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt std::string cmd = payload["sigtype"]; LLNotificationSet::iterator foundItem = mItems.find(pNotification); bool wasFound = (foundItem != mItems.end()); - bool passesFilter = mFilter(pNotification); + bool passesFilter = mFilter ? mFilter(pNotification) : true; // first, we offer the result of the filter test to the simple // signals for pass/fail. One of these is guaranteed to be called. @@ -974,10 +1033,12 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt bool abortProcessing = false; if (passesFilter) { + onFilterPass(pNotification); abortProcessing = mPassedFilter(payload); } else { + onFilterFail(pNotification); abortProcessing = mFailedFilter(payload); } @@ -995,8 +1056,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // not in our list, add it and say so mItems.insert(pNotification); - abortProcessing = mChanged(payload); onLoad(pNotification); + abortProcessing = mChanged(payload); } } else if (cmd == "change") @@ -1011,18 +1072,18 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // it already existed, so this is a change // since it changed in place, all we have to do is resend the signal - abortProcessing = mChanged(payload); onChange(pNotification); + abortProcessing = mChanged(payload); } else { // not in our list, add it and say so mItems.insert(pNotification); + onChange(pNotification); // our payload is const, so make a copy before changing it LLSD newpayload = payload; newpayload["sigtype"] = "add"; abortProcessing = mChanged(newpayload); - onChange(pNotification); } } else @@ -1031,11 +1092,11 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // it already existed, so this is a delete mItems.erase(pNotification); + onChange(pNotification); // our payload is const, so make a copy before changing it LLSD newpayload = payload; newpayload["sigtype"] = "delete"; abortProcessing = mChanged(newpayload); - onChange(pNotification); } // didn't pass, not on our list, do nothing } @@ -1049,8 +1110,8 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt { // not in our list, add it and say so mItems.insert(pNotification); - abortProcessing = mChanged(payload); onAdd(pNotification); + abortProcessing = mChanged(payload); } } else if (cmd == "delete") @@ -1058,65 +1119,35 @@ bool LLNotificationChannelBase::updateItem(const LLSD& payload, LLNotificationPt // if we have it in our list, pass on the delete, then delete it, else do nothing if (wasFound) { + onDelete(pNotification); abortProcessing = mChanged(payload); - // do not delete the notification to make LLChatHistory::appendMessage add notification panel to IM window - if( ! pNotification->isReusable() ) - { - mItems.erase(pNotification); - onDelete(pNotification); - } + mItems.erase(pNotification); } } return abortProcessing; } -/* static */ -LLNotificationChannelPtr LLNotificationChannel::buildChannel(const std::string& name, - const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) +LLNotificationChannel::LLNotificationChannel(const Params& p) +: LLNotificationChannelBase(p.filter()), + LLInstanceTracker(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()), + mName(p.name.isProvided() ? p.name : LLUUID::generateNewID().asString()) { - // note: this is not a leak; notifications are self-registering. - // This factory helps to prevent excess deletions by making sure all smart - // pointers to notification channels come from the same source - new LLNotificationChannel(name, parent, filter, comparator); - return LLNotifications::instance().getChannel(name); + BOOST_FOREACH(const std::string& source, p.sources) +{ + connectToChannel(source); + } } LLNotificationChannel::LLNotificationChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter, - LLNotificationComparator comparator) : -LLNotificationChannelBase(filter, comparator), -mName(name), -mParent(parent) + LLNotificationFilter filter) +: LLNotificationChannelBase(filter), + LLInstanceTracker(name), + mName(name) { - // store myself in the channel map - LLNotifications::instance().addChannel(LLNotificationChannelPtr(this)); // bind to notification broadcast - if (parent.empty()) - { - LLNotifications::instance().connectChanged( - boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } - else - { - LLNotificationChannelPtr p = LLNotifications::instance().getChannel(parent); - p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); - } -} - - -void LLNotificationChannel::setComparator(LLNotificationComparator comparator) -{ - mComparator = comparator; - LLNotificationSet s2(mComparator); - s2.insert(mItems.begin(), mItems.end()); - mItems.swap(s2); - - // notify clients that we've been resorted - mChanged(LLSD().with("sigtype", "sort")); + connectToChannel(parent); } bool LLNotificationChannel::isEmpty() const @@ -1139,6 +1170,11 @@ LLNotificationChannel::Iterator LLNotificationChannel::end() return mItems.end(); } +size_t LLNotificationChannel::size() +{ + return mItems.size(); +} + std::string LLNotificationChannel::summarize() { std::string s("Channel '"); @@ -1152,22 +1188,33 @@ std::string LLNotificationChannel::summarize() return s; } +void LLNotificationChannel::connectToChannel( const std::string& channel_name ) +{ + if (channel_name.empty()) + { + LLNotifications::instance().connectChanged( + boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); + } + else + { + LLNotificationChannelPtr p = LLNotifications::instance().getChannel(channel_name); + p->connectChanged(boost::bind(&LLNotificationChannelBase::updateItem, this, _1)); + } +} // --- // END OF LLNotificationChannel implementation // ========================================================= -// ========================================================= +// ============================================== =========== // LLNotifications implementation // --- -LLNotifications::LLNotifications() : LLNotificationChannelBase(LLNotificationFilters::includeEverything, - LLNotificationComparators::orderByUUID()), - mIgnoreAllNotifications(false) +LLNotifications::LLNotifications() +: LLNotificationChannelBase(LLNotificationFilters::includeEverything), + mIgnoreAllNotifications(false) { LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Notification.Show", boost::bind(&LLNotifications::addFromCallback, this, _2)); - - mListener.reset(new LLNotificationsListener(*this)); } @@ -1204,7 +1251,15 @@ bool LLNotifications::uniqueFilter(LLNotificationPtr pNotif) if (pNotif != existing_notification && pNotif->isEquivalentTo(existing_notification)) { - return false; + if (pNotif->getCombineBehavior() == LLNotification::CANCEL_OLD) + { + cancel(existing_notification); + return true; + } + else + { + return false; + } } } @@ -1244,43 +1299,43 @@ bool LLNotifications::failedUniquenessTest(const LLSD& payload) return false; } - // Update the existing unique notification with the data from this particular instance... - // This guarantees that duplicate notifications will be collapsed to the one - // most recently triggered - for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); - existing_it != mUniqueNotifications.end(); - ++existing_it) + switch(pNotif->getCombineBehavior()) { - LLNotificationPtr existing_notification = existing_it->second; - if (pNotif != existing_notification - && pNotif->isEquivalentTo(existing_notification)) + case LLNotification::REPLACE_WITH_NEW: + // Update the existing unique notification with the data from this particular instance... + // This guarantees that duplicate notifications will be collapsed to the one + // most recently triggered + for (LLNotificationMap::iterator existing_it = mUniqueNotifications.find(pNotif->getName()); + existing_it != mUniqueNotifications.end(); + ++existing_it) { - // copy notification instance data over to oldest instance - // of this unique notification and update it - existing_notification->updateFrom(pNotif); - // then delete the new one - cancel(pNotif); + LLNotificationPtr existing_notification = existing_it->second; + if (pNotif != existing_notification + && pNotif->isEquivalentTo(existing_notification)) + { + // copy notification instance data over to oldest instance + // of this unique notification and update it + existing_notification->updateFrom(pNotif); + // then delete the new one + cancel(pNotif); + } } + break; + case LLNotification::KEEP_OLD: + break; + case LLNotification::CANCEL_OLD: + // already handled by filter logic + break; + default: + break; } return false; } - -void LLNotifications::addChannel(LLNotificationChannelPtr pChan) -{ - mChannels[pChan->getName()] = pChan; -} - LLNotificationChannelPtr LLNotifications::getChannel(const std::string& channelName) { - ChannelMap::iterator p = mChannels.find(channelName); - if(p == mChannels.end()) - { - llerrs << "Did not find channel named " << channelName << llendl; - return LLNotificationChannelPtr(); - } - return p->second; + return LLNotificationChannelPtr(LLNotificationChannel::getInstance(channelName)); } @@ -1296,24 +1351,21 @@ void LLNotifications::createDefaultChannels() { // now construct the various channels AFTER loading the notifications, // because the history channel is going to rewrite the stored notifications file - LLNotificationChannel::buildChannel("Enabled", "", - !boost::bind(&LLNotifications::getIgnoreAllNotifications, this)); - LLNotificationChannel::buildChannel("Expiration", "Enabled", - boost::bind(&LLNotifications::expirationFilter, this, _1)); - LLNotificationChannel::buildChannel("Unexpired", "Enabled", - !boost::bind(&LLNotifications::expirationFilter, this, _1)); // use negated bind - LLNotificationChannel::buildChannel("Unique", "Unexpired", - boost::bind(&LLNotifications::uniqueFilter, this, _1)); - LLNotificationChannel::buildChannel("Ignore", "Unique", - filterIgnoredNotifications); - LLNotificationChannel::buildChannel("VisibilityRules", "Ignore", - boost::bind(&LLNotifications::isVisibleByRules, this, _1)); - LLNotificationChannel::buildChannel("Visible", "VisibilityRules", - &LLNotificationFilters::includeEverything); - - // create special persistent notification channel - // this isn't a leak, don't worry about the empty "new" - new LLPersistentNotificationChannel(); + mDefaultChannels.push_back(new LLNotificationChannel("Enabled", "", + !boost::bind(&LLNotifications::getIgnoreAllNotifications, this))); + mDefaultChannels.push_back(new LLNotificationChannel("Expiration", "Enabled", + boost::bind(&LLNotifications::expirationFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Unexpired", "Enabled", + !boost::bind(&LLNotifications::expirationFilter, this, _1))); // use negated bind + mDefaultChannels.push_back(new LLNotificationChannel("Unique", "Unexpired", + boost::bind(&LLNotifications::uniqueFilter, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Ignore", "Unique", + filterIgnoredNotifications)); + mDefaultChannels.push_back(new LLNotificationChannel("VisibilityRules", "Ignore", + boost::bind(&LLNotifications::isVisibleByRules, this, _1))); + mDefaultChannels.push_back(new LLNotificationChannel("Visible", "VisibilityRules", + &LLNotificationFilters::includeEverything)); + mDefaultChannels.push_back(new LLPersistentNotificationChannel()); // connect action methods to these channels LLNotifications::instance().getChannel("Enabled")-> @@ -1545,34 +1597,32 @@ void LLNotifications::addFromCallback(const LLSD& name) add(name.asString(), LLSD(), LLSD()); } -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload) { LLNotification::Params::Functor functor_p; functor_p.name = name; return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); } -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - const std::string& functor_name) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, const std::string& functor_name) { LLNotification::Params::Functor functor_p; functor_p.name = functor_name; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); } //virtual -LLNotificationPtr LLNotifications::add(const std::string& name, - const LLSD& substitutions, - const LLSD& payload, - LLNotificationFunctorRegistry::ResponseFunctor functor) +LLNotificationPtr LLNotifications::add(const std::string& name, const LLSD& substitutions, const LLSD& payload, LLNotificationFunctorRegistry::ResponseFunctor functor) { LLNotification::Params::Functor functor_p; functor_p.function = functor; - return add(LLNotification::Params().name(name).substitutions(substitutions).payload(payload).functor(functor_p)); + return add(LLNotification::Params().name(name) + .substitutions(substitutions) + .payload(payload) + .functor(functor_p)); } // generalized add function that takes a parameter block object for more complex instantiations @@ -1603,12 +1653,11 @@ void LLNotifications::cancel(LLNotificationPtr pNotif) if (pNotif == NULL || pNotif->isCancelled()) return; LLNotificationSet::iterator it=mItems.find(pNotif); - if (it == mItems.end()) + if (it != mItems.end()) { - llerrs << "Attempted to delete nonexistent notification " << pNotif->getName() << llendl; + pNotif->cancel(); + updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); } - pNotif->cancel(); - updateItem(LLSD().with("sigtype", "delete").with("id", pNotif->id()), pNotif); } void LLNotifications::cancelByName(const std::string& name) @@ -1647,7 +1696,7 @@ void LLNotifications::update(const LLNotificationPtr pNotif) LLNotificationPtr LLNotifications::find(LLUUID uuid) { - LLNotificationPtr target = LLNotificationPtr(new LLNotification(uuid)); + LLNotificationPtr target = LLNotificationPtr(new LLNotification(LLNotification::Params().id(uuid))); LLNotificationSet::iterator it=mItems.find(target); if (it == mItems.end()) { @@ -1786,22 +1835,18 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification) return s; } -//static -void LLPostponedNotification::lookupName(LLPostponedNotification* thiz, - const LLUUID& id, +void LLPostponedNotification::lookupName(const LLUUID& id, bool is_group) { if (is_group) { gCacheName->getGroup(id, boost::bind(&LLPostponedNotification::onGroupNameCache, - thiz, _1, _2, _3)); + this, _1, _2, _3)); } else { - LLAvatarNameCache::get(id, - boost::bind(&LLPostponedNotification::onAvatarNameCache, - thiz, _1, _2)); + fetchAvatarName(id); } } @@ -1812,9 +1857,24 @@ void LLPostponedNotification::onGroupNameCache(const LLUUID& id, finalizeName(full_name); } +void LLPostponedNotification::fetchAvatarName(const LLUUID& id) +{ + if (id.notNull()) + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + + mAvatarNameCacheConnection = LLAvatarNameCache::get(id, boost::bind(&LLPostponedNotification::onAvatarNameCache, this, _1, _2)); + } +} + void LLPostponedNotification::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { + mAvatarNameCacheConnection.disconnect(); + std::string name = av_name.getCompleteName(); // from PE merge - we should figure out if this is the right thing to do diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 94c9f544e7..abf8a1b58f 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -87,17 +87,16 @@ #include #include #include +#include -// we want to minimize external dependencies, but this one is important -#include "llsd.h" - -// and we need this to manage the notification callbacks #include "llevents.h" #include "llfunctorregistry.h" -#include "llpointer.h" #include "llinitparam.h" -#include "llnotificationslistener.h" +#include "llmortician.h" #include "llnotificationptr.h" +#include "llpointer.h" +#include "llrefcount.h" +#include "llsdparam.h" class LLAvatarName; typedef enum e_notification_priority @@ -164,6 +163,7 @@ public: struct FormElementBase : public LLInitParam::Block { Optional name; + Optional enabled; FormElementBase(); }; @@ -234,16 +234,21 @@ public: } EIgnoreType; LLNotificationForm(); + LLNotificationForm(const LLNotificationForm&); LLNotificationForm(const LLSD& sd); LLNotificationForm(const std::string& name, const Params& p); + void fromLLSD(const LLSD& sd); LLSD asLLSD() const; S32 getNumElements() { return mFormData.size(); } LLSD getElement(S32 index) { return mFormData.get(index); } LLSD getElement(const std::string& element_name); - bool hasElement(const std::string& element_name); - void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD()); + void getElements(LLSD& elements, S32 offset = 0); + bool hasElement(const std::string& element_name) const; + bool getElementEnabled(const std::string& element_name) const; + void setElementEnabled(const std::string& element_name, bool enabled); + void addElement(const std::string& type, const std::string& name, const LLSD& value = LLSD(), bool enabled = true); void formatElements(const LLSD& substitutions); // appends form elements from another form serialized as LLSD void append(const LLSD& sub_form); @@ -297,42 +302,52 @@ LOG_CLASS(LLNotification); friend class LLNotifications; public: + // parameter object used to instantiate a new notification struct Params : public LLInitParam::Block { friend class LLNotification; Mandatory name; - - // optional - Optional substitutions; - Optional payload; + Optional id; + Optional substitutions, + form_elements, + payload; Optional priority; - Optional form_elements; - Optional time_stamp; + Optional time_stamp, + expiry; Optional context; Optional responder; + Optional offer_from_agent; + Optional is_dnd; struct Functor : public LLInitParam::ChoiceBlock { Alternative name; Alternative function; Alternative responder; + Alternative responder_sd; Functor() - : name("functor_name"), + : name("responseFunctor"), function("functor"), - responder("responder") + responder("responder"), + responder_sd("responder_sd") {} }; Optional functor; Params() : name("name"), + id("id"), priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time_stamp"), + time_stamp("time"), payload("payload"), - form_elements("form_elements") + form_elements("form"), + substitutions("substitutions"), + expiry("expiry"), + offer_from_agent("offer_from_agent", false), + is_dnd("is_dnd", false) { time_stamp = LLDate::now(); responder = NULL; @@ -341,9 +356,13 @@ public: Params(const std::string& _name) : name("name"), priority("priority", NOTIFICATION_PRIORITY_UNSPECIFIED), - time_stamp("time_stamp"), + time_stamp("time"), payload("payload"), - form_elements("form_elements") + form_elements("form"), + substitutions("substitutions"), + expiry("expiry"), + offer_from_agent("offer_from_agent", false), + is_dnd("is_dnd", false) { functor.name = _name; name = _name; @@ -356,7 +375,7 @@ public: private: - LLUUID mId; + const LLUUID mId; LLSD mPayload; LLSD mSubstitutions; LLDate mTimestamp; @@ -368,8 +387,9 @@ private: ENotificationPriority mPriority; LLNotificationFormPtr mForm; void* mResponderObj; // TODO - refactor/remove this field - bool mIsReusable; LLNotificationResponderPtr mResponder; + bool mOfferFromAgent; + bool mIsDND; // a reference to the template LLNotificationTemplatePtr mTemplatep; @@ -393,18 +413,10 @@ private: void init(const std::string& template_name, const LLSD& form_elements); - LLNotification(const Params& p); - - // this is just for making it easy to look things up in a set organized by UUID -- DON'T USE IT - // for anything real! - LLNotification(LLUUID uuid) : mId(uuid), mCancelled(false), mRespondedTo(false), mIgnored(false), mPriority(NOTIFICATION_PRIORITY_UNSPECIFIED), mTemporaryResponder(false) {} - void cancel(); public: - - // constructor from a saved notification - LLNotification(const LLSD& sd); + LLNotification(const LLSDParamAdapter& p); void setResponseFunctor(std::string const &responseFunctorName); @@ -447,7 +459,12 @@ public: // ["time"] = time at which notification was generated; // ["expiry"] = time at which notification expires; // ["responseFunctor"] = name of registered functor that handles responses to notification; - LLSD asLLSD(); + LLSD asLLSD(bool excludeTemplateElements = false); + + const LLNotificationFormPtr getForm(); + void updateForm(const LLNotificationFormPtr& form); + + void repost(); void respond(const LLSD& sd); void respondWithDefault(); @@ -512,6 +529,21 @@ public: bool hasLabel() const; // [/SL:KB] + bool getOfferFromAgent() const + { + return mOfferFromAgent; + } + + bool isDND() const + { + return mIsDND; + } + + void setDND(const bool flag) + { + mIsDND = flag; + } + std::string getType() const; std::string getMessage() const; std::string getFooter() const; @@ -519,8 +551,21 @@ public: std::string getURL() const; S32 getURLOption() const; S32 getURLOpenExternally() const; + bool canLogToChat() const; + bool canLogToIM() const; + bool canShowToast() const; + bool hasFormElements() const; + void playSound(); + + typedef enum e_combine_behavior + { + REPLACE_WITH_NEW, + KEEP_OLD, + CANCEL_OLD + + } ECombineBehavior; - const LLNotificationFormPtr getForm(); + ECombineBehavior getCombineBehavior() const; const LLDate getExpiration() const { @@ -537,10 +582,6 @@ public: return mId; } - bool isReusable() { return mIsReusable; } - - void setReusable(bool reusable) { mIsReusable = reusable; } - // comparing two notifications normally means comparing them by UUID (so we can look them // up quickly this way) bool operator<(const LLNotification& rhs) const @@ -652,44 +693,17 @@ namespace LLNotificationFilters namespace LLNotificationComparators { - typedef enum e_direction { ORDER_DECREASING, ORDER_INCREASING } EDirection; - - // generic order functor that takes method or member variable reference - template - struct orderBy + struct orderByUUID { - typedef boost::function field_t; - orderBy(field_t field, EDirection direction = ORDER_INCREASING) : mField(field), mDirection(direction) {} bool operator()(LLNotificationPtr lhs, LLNotificationPtr rhs) { - if (mDirection == ORDER_DECREASING) - { - return mField(lhs) > mField(rhs); - } - else - { - return mField(lhs) < mField(rhs); - } + return lhs->id() < rhs->id(); } - - field_t mField; - EDirection mDirection; - }; - - struct orderByUUID : public orderBy - { - orderByUUID(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::id, direction) {} - }; - - struct orderByDate : public orderBy - { - orderByDate(EDirection direction = ORDER_INCREASING) : orderBy(&LLNotification::getDate, direction) {} }; }; typedef boost::function LLNotificationFilter; -typedef boost::function LLNotificationComparator; -typedef std::set LLNotificationSet; +typedef std::set LLNotificationSet; typedef std::multimap LLNotificationMap; // ======================================================== @@ -710,12 +724,14 @@ typedef std::multimap LLNotificationMap; // all of the built-in tests should attach to the "Visible" channel // class LLNotificationChannelBase : - public LLEventTrackable + public LLEventTrackable, + public LLRefCount { LOG_CLASS(LLNotificationChannelBase); public: - LLNotificationChannelBase(LLNotificationFilter filter, LLNotificationComparator comp) : - mFilter(filter), mItems(comp) + LLNotificationChannelBase(LLNotificationFilter filter) + : mFilter(filter), + mItems() {} virtual ~LLNotificationChannelBase() {} // you can also connect to a Channel, so you can be notified of @@ -781,6 +797,9 @@ protected: virtual void onDelete(LLNotificationPtr p) {} virtual void onChange(LLNotificationPtr p) {} + virtual void onFilterPass(LLNotificationPtr p) {} + virtual void onFilterFail(LLNotificationPtr p) {} + bool updateItem(const LLSD& payload, LLNotificationPtr pNotification); LLNotificationFilter mFilter; }; @@ -790,64 +809,53 @@ protected: // destroy it, but if it becomes necessary to do so, the shared_ptr model // will ensure that we don't leak resources. class LLNotificationChannel; -typedef boost::shared_ptr LLNotificationChannelPtr; +typedef boost::intrusive_ptr LLNotificationChannelPtr; // manages a list of notifications // Note that if this is ever copied around, we might find ourselves with multiple copies // of a queue with notifications being added to different nonequivalent copies. So we -// make it inherit from boost::noncopyable, and then create a map of shared_ptr to manage it. -// -// NOTE: LLNotificationChannel is self-registering. The *correct* way to create one is to -// do something like: -// LLNotificationChannel::buildChannel("name", "parent"...); -// This returns an LLNotificationChannelPtr, which you can store, or -// you can then retrieve the channel by using the registry: -// LLNotifications::instance().getChannel("name")... +// make it inherit from boost::noncopyable, and then create a map of LLPointer to manage it. // class LLNotificationChannel : boost::noncopyable, - public LLNotificationChannelBase + public LLNotificationChannelBase, + public LLInstanceTracker { LOG_CLASS(LLNotificationChannel); public: + // Notification Channels have a filter, which determines which notifications + // will be added to this channel. + // Channel filters cannot change. + struct Params : public LLInitParam::Block + { + Mandatory name; + Optional filter; + Multiple sources; + }; + + LLNotificationChannel(const Params& p = Params()); + LLNotificationChannel(const std::string& name, const std::string& parent, LLNotificationFilter filter); + virtual ~LLNotificationChannel() {} typedef LLNotificationSet::iterator Iterator; std::string getName() const { return mName; } - std::string getParentChannelName() { return mParent; } + + void connectToChannel(const std::string& channel_name); bool isEmpty() const; S32 size() const; Iterator begin(); Iterator end(); + size_t size(); - // Channels have a comparator to control sort order; - // the default sorts by arrival date - void setComparator(LLNotificationComparator comparator); - std::string summarize(); - // factory method for constructing these channels; since they're self-registering, - // we want to make sure that you can't use new to make them - static LLNotificationChannelPtr buildChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter=LLNotificationFilters::includeEverything, - LLNotificationComparator comparator=LLNotificationComparators::orderByUUID()); - -protected: - // Notification Channels have a filter, which determines which notifications - // will be added to this channel. - // Channel filters cannot change. - // Channels have a protected constructor so you can't make smart pointers that don't - // come from our internal reference; call NotificationChannel::build(args) - LLNotificationChannel(const std::string& name, const std::string& parent, - LLNotificationFilter filter, LLNotificationComparator comparator); - private: std::string mName; std::string mParent; - LLNotificationComparator mComparator; }; // An interface class to provide a clean linker seam to the LLNotifications class. @@ -930,10 +938,6 @@ public: void createDefaultChannels(); - typedef std::map ChannelMap; - ChannelMap mChannels; - - void addChannel(LLNotificationChannelPtr pChan); LLNotificationChannelPtr getChannel(const std::string& channelName); std::string getGlobalString(const std::string& key) const; @@ -971,7 +975,7 @@ private: bool mIgnoreAllNotifications; - boost::scoped_ptr mListener; + std::vector mDefaultChannels; }; /** @@ -984,7 +988,7 @@ private: * 1 create class derived from LLPostponedNotification; * 2 call LLPostponedNotification::add method; */ -class LLPostponedNotification +class LLPostponedNotification : public LLMortician { public: /** @@ -1002,26 +1006,38 @@ public: thiz->mParams = params; // Avoid header file dependency on llcachename.h - lookupName(thiz, id, is_group); + thiz->lookupName(id, is_group); } private: - static void lookupName(LLPostponedNotification* thiz, const LLUUID& id, bool is_group); + void lookupName(const LLUUID& id, bool is_group); // only used for groups void onGroupNameCache(const LLUUID& id, const std::string& full_name, bool is_group); // only used for avatars + void fetchAvatarName(const LLUUID& id); void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); // used for both group and avatar names void finalizeName(const std::string& name); void cleanup() { - delete this; + die(); } protected: - LLPostponedNotification() {} - virtual ~LLPostponedNotification() {} + LLPostponedNotification() + : mParams(), + mName(), + mAvatarNameCacheConnection() + {} + + virtual ~LLPostponedNotification() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + } /** * Abstract method provides possibility to modify notification parameters and @@ -1032,6 +1048,58 @@ protected: LLNotification::Params mParams; std::string mName; + boost::signals2::connection mAvatarNameCacheConnection; +}; + +// Stores only persistent notifications. +// Class users can use connectChanged() to process persistent notifications +// (see LLPersistentNotificationStorage for example). +class LLPersistentNotificationChannel : public LLNotificationChannel +{ + LOG_CLASS(LLPersistentNotificationChannel); +public: + LLPersistentNotificationChannel() + : LLNotificationChannel("Persistent", "Visible", ¬ificationFilter) + { + } + + typedef std::vector history_list_t; + history_list_t::iterator beginHistory() { sortHistory(); return mHistory.begin(); } + history_list_t::iterator endHistory() { return mHistory.end(); } + +private: + + struct sortByTime + { + S32 operator ()(const LLNotificationPtr& a, const LLNotificationPtr& b) + { + return a->getDate() < b->getDate(); + } + }; + + void sortHistory() + { + std::sort(mHistory.begin(), mHistory.end(), sortByTime()); + } + + + // The channel gets all persistent notifications except those that have been canceled + static bool notificationFilter(LLNotificationPtr pNotification) + { + bool handle_notification = false; + + handle_notification = pNotification->isPersistent() + && !pNotification->isCancelled(); + + return handle_notification; + } + + void onAdd(LLNotificationPtr p) + { + mHistory.push_back(p); + } + + std::vector mHistory; }; #endif//LL_LLNOTIFICATIONS_H diff --git a/indra/llui/llnotificationslistener.cpp b/indra/llui/llnotificationslistener.cpp deleted file mode 100644 index 3bbeb3a778..0000000000 --- a/indra/llui/llnotificationslistener.cpp +++ /dev/null @@ -1,354 +0,0 @@ -/** - * @file llnotificationslistener.cpp - * @author Brad Kittenbrink - * @date 2009-07-08 - * @brief Implementation for llnotificationslistener. - * - * $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 "linden_common.h" -#include "llnotificationslistener.h" -#include "llnotifications.h" -#include "llnotificationtemplate.h" -#include "llsd.h" -#include "llui.h" - -LLNotificationsListener::LLNotificationsListener(LLNotifications & notifications) : - LLEventAPI("LLNotifications", - "LLNotifications listener to (e.g.) pop up a notification"), - mNotifications(notifications) -{ - add("requestAdd", - "Add a notification with specified [\"name\"], [\"substitutions\"] and [\"payload\"].\n" - "If optional [\"reply\"] specified, arrange to send user response on that LLEventPump.", - &LLNotificationsListener::requestAdd); - add("listChannels", - "Post to [\"reply\"] a map of info on existing channels", - &LLNotificationsListener::listChannels, - LLSD().with("reply", LLSD())); - add("listChannelNotifications", - "Post to [\"reply\"] an array of info on notifications in channel [\"channel\"]", - &LLNotificationsListener::listChannelNotifications, - LLSD().with("reply", LLSD()).with("channel", LLSD())); - add("respond", - "Respond to notification [\"uuid\"] with data in [\"response\"]", - &LLNotificationsListener::respond, - LLSD().with("uuid", LLSD())); - add("cancel", - "Cancel notification [\"uuid\"]", - &LLNotificationsListener::cancel, - LLSD().with("uuid", LLSD())); - add("ignore", - "Ignore future notification [\"name\"]\n" - "(from in notifications.xml)\n" - "according to boolean [\"ignore\"].\n" - "If [\"name\"] is omitted or undefined, [un]ignore all future notifications.\n" - "Note that ignored notifications are not forwarded unless intercepted before\n" - "the \"Ignore\" channel.", - &LLNotificationsListener::ignore); - add("forward", - "Forward to [\"pump\"] future notifications on channel [\"channel\"]\n" - "according to boolean [\"forward\"]. When enabled, only types matching\n" - "[\"types\"] are forwarded, as follows:\n" - "omitted or undefined: forward all notifications\n" - "string: forward only the specific named [sig]type\n" - "array of string: forward any notification matching any named [sig]type.\n" - "When boolean [\"respond\"] is true, we auto-respond to each forwarded\n" - "notification.", - &LLNotificationsListener::forward, - LLSD().with("channel", LLSD())); -} - -// This is here in the .cpp file so we don't need the definition of class -// Forwarder in the header file. -LLNotificationsListener::~LLNotificationsListener() -{ -} - -void LLNotificationsListener::requestAdd(const LLSD& event_data) const -{ - if(event_data.has("reply")) - { - mNotifications.add(event_data["name"], - event_data["substitutions"], - event_data["payload"], - boost::bind(&LLNotificationsListener::NotificationResponder, - this, - event_data["reply"].asString(), - _1, _2 - ) - ); - } - else - { - mNotifications.add(event_data["name"], - event_data["substitutions"], - event_data["payload"]); - } -} - -void LLNotificationsListener::NotificationResponder(const std::string& reply_pump, - const LLSD& notification, - const LLSD& response) const -{ - LLSD reponse_event; - reponse_event["notification"] = notification; - reponse_event["response"] = response; - LLEventPumps::getInstance()->obtain(reply_pump).post(reponse_event); -} - -void LLNotificationsListener::listChannels(const LLSD& params) const -{ - LLReqID reqID(params); - LLSD response(reqID.makeResponse()); - for (LLNotifications::ChannelMap::const_iterator cmi(mNotifications.mChannels.begin()), - cmend(mNotifications.mChannels.end()); - cmi != cmend; ++cmi) - { - LLSD channelInfo; - channelInfo["parent"] = cmi->second->getParentChannelName(); - response[cmi->first] = channelInfo; - } - LLEventPumps::instance().obtain(params["reply"]).post(response); -} - -void LLNotificationsListener::listChannelNotifications(const LLSD& params) const -{ - LLReqID reqID(params); - LLSD response(reqID.makeResponse()); - LLNotificationChannelPtr channel(mNotifications.getChannel(params["channel"])); - if (channel) - { - LLSD notifications(LLSD::emptyArray()); - for (LLNotificationChannel::Iterator ni(channel->begin()), nend(channel->end()); - ni != nend; ++ni) - { - notifications.append(asLLSD(*ni)); - } - response["notifications"] = notifications; - } - LLEventPumps::instance().obtain(params["reply"]).post(response); -} - -void LLNotificationsListener::respond(const LLSD& params) const -{ - LLNotificationPtr notification(mNotifications.find(params["uuid"])); - if (notification) - { - notification->respond(params["response"]); - } -} - -void LLNotificationsListener::cancel(const LLSD& params) const -{ - LLNotificationPtr notification(mNotifications.find(params["uuid"])); - if (notification) - { - mNotifications.cancel(notification); - } -} - -void LLNotificationsListener::ignore(const LLSD& params) const -{ - // Calling a method named "ignore", but omitting its "ignore" Boolean - // argument, should by default cause something to be ignored. Explicitly - // pass ["ignore"] = false to cancel ignore. - bool ignore = true; - if (params.has("ignore")) - { - ignore = params["ignore"].asBoolean(); - } - // This method can be used to affect either a single notification name or - // all future notifications. The two use substantially different mechanisms. - if (params["name"].isDefined()) - { - // ["name"] was passed: ignore just that notification - LLNotificationTemplatePtr templatep = mNotifications.getTemplate(params["name"]); - if (templatep) - { - templatep->mForm->setIgnored(ignore); - } - } - else - { - // no ["name"]: ignore all future notifications - mNotifications.setIgnoreAllNotifications(ignore); - } -} - -class LLNotificationsListener::Forwarder: public LLEventTrackable -{ - LOG_CLASS(LLNotificationsListener::Forwarder); -public: - Forwarder(LLNotifications& llnotifications, const std::string& channel): - mNotifications(llnotifications), - mRespond(false) - { - // Connect to the specified channel on construction. Because - // LLEventTrackable is a base, we should automatically disconnect when - // destroyed. - LLNotificationChannelPtr channelptr(llnotifications.getChannel(channel)); - if (channelptr) - { - // Insert our processing as a "passed filter" listener. This way - // we get to run before all the "changed" listeners, and we get to - // swipe it (hide it from the other listeners) if desired. - channelptr->connectPassedFilter(boost::bind(&Forwarder::handle, this, _1)); - } - } - - void setPumpName(const std::string& name) { mPumpName = name; } - void setTypes(const LLSD& types) { mTypes = types; } - void setRespond(bool respond) { mRespond = respond; } - -private: - bool handle(const LLSD& notification) const; - bool matchType(const LLSD& filter, const std::string& type) const; - - LLNotifications& mNotifications; - std::string mPumpName; - LLSD mTypes; - bool mRespond; -}; - -void LLNotificationsListener::forward(const LLSD& params) -{ - std::string channel(params["channel"]); - // First decide whether we're supposed to start forwarding or stop it. - // Default to true. - bool forward = true; - if (params.has("forward")) - { - forward = params["forward"].asBoolean(); - } - if (! forward) - { - // This is a request to stop forwarding notifications on the specified - // channel. The rest of the params don't matter. - // Because mForwarders contains scoped_ptrs, erasing the map entry - // DOES delete the heap Forwarder object. Because Forwarder derives - // from LLEventTrackable, destroying it disconnects it from the - // channel. - mForwarders.erase(channel); - return; - } - // From here on, we know we're being asked to start (or modify) forwarding - // on the specified channel. Find or create an appropriate Forwarder. - ForwarderMap::iterator - entry(mForwarders.insert(ForwarderMap::value_type(channel, ForwarderMap::mapped_type())).first); - if (! entry->second) - { - entry->second.reset(new Forwarder(mNotifications, channel)); - } - // Now, whether this Forwarder is brand-new or not, update it with the new - // request info. - Forwarder& fwd(*entry->second); - fwd.setPumpName(params["pump"]); - fwd.setTypes(params["types"]); - fwd.setRespond(params["respond"]); -} - -bool LLNotificationsListener::Forwarder::handle(const LLSD& notification) const -{ - LL_INFOS("LLNotificationsListener") << "handle(" << notification << ")" << LL_ENDL; - if (notification["sigtype"].asString() == "delete") - { - LL_INFOS("LLNotificationsListener") << "ignoring delete" << LL_ENDL; - // let other listeners see the "delete" operation - return false; - } - LLNotificationPtr note(mNotifications.find(notification["id"])); - if (! note) - { - LL_INFOS("LLNotificationsListener") << notification["id"] << " not found" << LL_ENDL; - return false; - } - if (! matchType(mTypes, note->getType())) - { - LL_INFOS("LLNotificationsListener") << "didn't match types " << mTypes << LL_ENDL; - // We're not supposed to intercept this particular notification. Let - // other listeners process it. - return false; - } - LL_INFOS("LLNotificationsListener") << "sending via '" << mPumpName << "'" << LL_ENDL; - // This is a notification we care about. Forward it through specified - // LLEventPump. - LLEventPumps::instance().obtain(mPumpName).post(asLLSD(note)); - // Are we also being asked to auto-respond? - if (mRespond) - { - LL_INFOS("LLNotificationsListener") << "should respond" << LL_ENDL; - note->respond(LLSD::emptyMap()); - // Did that succeed in removing the notification? Only cancel() if - // it's still around -- otherwise we get an LL_ERRS crash! - note = mNotifications.find(notification["id"]); - if (note) - { - LL_INFOS("LLNotificationsListener") << "respond() didn't clear, canceling" << LL_ENDL; - mNotifications.cancel(note); - } - } - // If we've auto-responded to this notification, then it's going to be - // deleted. Other listeners would get the change operation, try to look it - // up and be baffled by lookup failure. So when we auto-respond, suppress - // this notification: don't pass it to other listeners. - return mRespond; -} - -bool LLNotificationsListener::Forwarder::matchType(const LLSD& filter, const std::string& type) const -{ - // Decide whether this notification matches filter: - // undefined: forward all notifications - if (filter.isUndefined()) - { - return true; - } - // array of string: forward any notification matching any named type - if (filter.isArray()) - { - for (LLSD::array_const_iterator ti(filter.beginArray()), tend(filter.endArray()); - ti != tend; ++ti) - { - if (ti->asString() == type) - { - return true; - } - } - // Didn't match any entry in the array - return false; - } - // string: forward only the specific named type - return (filter.asString() == type); -} - -LLSD LLNotificationsListener::asLLSD(LLNotificationPtr note) -{ - LLSD notificationInfo(note->asLLSD()); - // For some reason the following aren't included in LLNotification::asLLSD(). - notificationInfo["summary"] = note->summarize(); - notificationInfo["id"] = note->id(); - notificationInfo["type"] = note->getType(); - notificationInfo["message"] = note->getMessage(); - notificationInfo["label"] = note->getLabel(); - return notificationInfo; -} diff --git a/indra/llui/llnotificationslistener.h b/indra/llui/llnotificationslistener.h deleted file mode 100644 index f9f7641de6..0000000000 --- a/indra/llui/llnotificationslistener.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * @file llnotificationslistener.h - * @author Brad Kittenbrink - * @date 2009-07-08 - * @brief Wrap subset of LLNotifications API in event API for test scripts. - * - * $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_LLNOTIFICATIONSLISTENER_H -#define LL_LLNOTIFICATIONSLISTENER_H - -#include "lleventapi.h" -#include "llnotificationptr.h" -#include -#include -#include - -class LLNotifications; -class LLSD; - -class LLNotificationsListener : public LLEventAPI -{ -public: - LLNotificationsListener(LLNotifications & notifications); - ~LLNotificationsListener(); - -private: - void requestAdd(LLSD const & event_data) const; - - void NotificationResponder(const std::string& replypump, - const LLSD& notification, - const LLSD& response) const; - - void listChannels(const LLSD& params) const; - void listChannelNotifications(const LLSD& params) const; - void respond(const LLSD& params) const; - void cancel(const LLSD& params) const; - void ignore(const LLSD& params) const; - void forward(const LLSD& params); - - static LLSD asLLSD(LLNotificationPtr); - - class Forwarder; - typedef std::map > ForwarderMap; - ForwarderMap mForwarders; - LLNotifications & mNotifications; -}; - -#endif // LL_LLNOTIFICATIONSLISTENER_H diff --git a/indra/llui/llnotificationtemplate.h b/indra/llui/llnotificationtemplate.h index b3b0bae862..18a82190b5 100644 --- a/indra/llui/llnotificationtemplate.h +++ b/indra/llui/llnotificationtemplate.h @@ -49,7 +49,6 @@ //#include "llfunctorregistry.h" //#include "llpointer.h" #include "llinitparam.h" -//#include "llnotificationslistener.h" //#include "llnotificationptr.h" //#include "llcachename.h" #include "llnotifications.h" @@ -61,6 +60,18 @@ typedef boost::shared_ptr LLNotificationFormPtr; // from the appropriate local language directory). struct LLNotificationTemplate { + struct CombineBehaviorNames + : public LLInitParam::TypeValuesHelper + { + static void declareValues() + { + declare("replace_with_new", LLNotification::REPLACE_WITH_NEW); + declare("keep_old", LLNotification::KEEP_OLD); + declare("cancel_old", LLNotification::CANCEL_OLD); + } + }; + + struct GlobalString : public LLInitParam::Block { Mandatory name, @@ -94,9 +105,11 @@ struct LLNotificationTemplate Optional dummy_val; public: Multiple contexts; + Optional combine; UniquenessConstraint() : contexts("context"), + combine("combine", LLNotification::REPLACE_WITH_NEW), dummy_val("") {} }; @@ -183,7 +196,10 @@ struct LLNotificationTemplate struct Params : public LLInitParam::Block { Mandatory name; - Optional persist; + Optional persist, + log_to_im, + show_toast, + log_to_chat; Optional functor, icon, label, @@ -204,6 +220,9 @@ struct LLNotificationTemplate Params() : name("name"), persist("persist", false), + log_to_im("log_to_im", false), + show_toast("show_toast", true), + log_to_chat("log_to_chat", true), functor("functor"), icon("icon"), label("label"), @@ -262,6 +281,7 @@ struct LLNotificationTemplate // (used for things like progress indications, or repeating warnings // like "the grid is going down in N minutes") bool mUnique; + LLNotification::ECombineBehavior mCombineBehavior; // if we want to be unique only if a certain part of the payload or substitutions args // are constant specify the field names for the payload. The notification will only be // combined if all of the fields named in the context are identical in the @@ -302,12 +322,15 @@ struct LLNotificationTemplate LLNotificationFormPtr mForm; // default priority for notifications of this type ENotificationPriority mPriority; - // UUID of the audio file to be played when this notification arrives - // this is loaded as a name, but looked up to get the UUID upon template load. - // If null, it wasn't specified. - LLUUID mSoundEffect; + // Stores the sound name which can then be used to play the sound using make_ui_sound + std::string mSoundName; // List of tags that rules can match against. std::list mTags; + + // inject these notifications into chat/IM streams + bool mLogToChat; + bool mLogToIM; + bool mShowToast; }; #endif //LL_LLNOTIFICATION_TEMPLATE_H diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp index ba90fa5e0c..15e56cbfe5 100644 --- a/indra/llui/llresizebar.cpp +++ b/indra/llui/llresizebar.cpp @@ -45,7 +45,8 @@ LLResizeBar::LLResizeBar(const LLResizeBar::Params& p) mSide( p.side ), mSnappingEnabled(p.snapping_enabled), mAllowDoubleClickSnapping(p.allow_double_click_snapping), - mResizingView(p.resizing_view) + mResizingView(p.resizing_view), + mResizeListener(NULL) { setFollowsNone(); // set up some generically good follow code. @@ -300,6 +301,11 @@ BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask) } } + if (mResizeListener) + { + mResizeListener(NULL); + } + return handled; } // end LLResizeBar::handleHover diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h index 6daf191918..8190a95a71 100644 --- a/indra/llui/llresizebar.h +++ b/indra/llui/llresizebar.h @@ -71,6 +71,7 @@ public: void setEnableSnapping(BOOL enable) { mSnappingEnabled = enable; } void setAllowDoubleClickSnapping(BOOL allow) { mAllowDoubleClickSnapping = allow; } bool canResize() { return getEnabled() && mMaxSize > mMinSize; } + void setResizeListener(boost::function listener) {mResizeListener = listener;} private: S32 mDragLastScreenX; @@ -84,6 +85,7 @@ private: BOOL mSnappingEnabled; BOOL mAllowDoubleClickSnapping; LLView* mResizingView; + boost::function mResizeListener; }; #endif // LL_RESIZEBAR_H diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 5d3bf7a670..13887cbe73 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -642,3 +642,8 @@ void LLScrollbar::onLineDownBtnPressed( const LLSD& data ) { changeLine( mStepSize, TRUE ); } + +void LLScrollbar::setThickness(S32 thickness) +{ + mThickness = thickness < 0 ? LLUI::sSettingGroups["config"]->getS32("UIScrollbarSize") : thickness; +} diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index ff74f753b9..21fd2d631e 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -124,6 +124,9 @@ public: void onLineUpBtnPressed(const LLSD& data); void onLineDownBtnPressed(const LLSD& data); + + S32 getThickness() const { return mThickness; } + void setThickness(S32 thickness); private: void updateThumbRect(); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 2fd187a526..cbcce0ece5 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -73,7 +73,8 @@ LLScrollContainer::Params::Params() hide_scrollbar("hide_scrollbar"), min_auto_scroll_rate("min_auto_scroll_rate", 100), max_auto_scroll_rate("max_auto_scroll_rate", 1000), - reserve_scroll_corner("reserve_scroll_corner", false) + reserve_scroll_corner("reserve_scroll_corner", false), + size("size", -1) {} @@ -88,9 +89,12 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mReserveScrollCorner(p.reserve_scroll_corner), mMinAutoScrollRate(p.min_auto_scroll_rate), mMaxAutoScrollRate(p.max_auto_scroll_rate), - mScrolledView(NULL) + mScrolledView(NULL), + mSize(p.size) { - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + LLRect border_rect( 0, getRect().getHeight(), getRect().getWidth(), 0 ); LLViewBorder::Params params; params.name("scroll border"); @@ -276,7 +280,6 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, EAcceptance* accept, std::string& tooltip_msg) { - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; BOOL handled = autoScroll(x, y); @@ -292,7 +295,8 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, bool LLScrollContainer::autoScroll(S32 x, S32 y) { - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); bool scrolling = false; if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() ) @@ -365,7 +369,9 @@ bool LLScrollContainer::autoScroll(S32 x, S32 y) void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const { const LLRect& doc_rect = getScrolledViewRect(); - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); @@ -406,7 +412,9 @@ void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height void LLScrollContainer::draw() { - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + if (mAutoScrolling) { // add acceleration to autoscroll @@ -515,7 +523,9 @@ void LLScrollContainer::updateScroll() { return; } - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size_control ("UIScrollbarSize", 0); + S32 scrollbar_size = (mSize == -1 ? scrollbar_size_control : mSize); + LLRect doc_rect = mScrolledView->getRect(); S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); @@ -716,3 +726,9 @@ S32 LLScrollContainer::getBorderWidth() const return 0; } +void LLScrollContainer::setSize(S32 size) +{ + mSize = size; + mScrollbar[VERTICAL]->setThickness(size); + mScrollbar[HORIZONTAL]->setThickness(size); +} diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index d87c95b3d7..4eb43539b8 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -68,6 +68,7 @@ public: max_auto_scroll_rate; Optional bg_color; Optional scroll_callback; + Optional size; Params(); }; @@ -116,6 +117,9 @@ public: bool autoScroll(S32 x, S32 y); + S32 getSize() const { return mSize; } + void setSize(S32 thickness); + protected: LLView* mScrolledView; diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index a07b0297ef..3478909860 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1906,6 +1906,9 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) // (N.B. callbacks don't take const refs as id is local scope) bool is_group = (mContextMenuType == MENU_GROUP); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Url.ShowProfile", boost::bind(&LLScrollListCtrl::showProfile, id, is_group)); + registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id)); + registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id)); registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group)); registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group)); registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group)); @@ -1937,11 +1940,33 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) return FALSE; } -void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) +void LLScrollListCtrl::showProfile(std::string id, bool is_group) { // show the resident's profile or the group profile std::string sltype = is_group ? "group" : "agent"; std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; + LLUrlAction::showProfile(slurl); +} + +void LLScrollListCtrl::sendIM(std::string id) +{ + // send im to the resident + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::sendIM(slurl); +} + +void LLScrollListCtrl::addFriend(std::string id) +{ + // add resident to friends list + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::addFriend(slurl); +} + +void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) +{ + // open the resident's details or the group details + std::string sltype = is_group ? "group" : "agent"; + std::string slurl = "secondlife:///app/" + sltype + "/" + id + "/about"; LLUrlAction::clickAction(slurl); } @@ -1957,7 +1982,7 @@ void LLScrollListCtrl::copyNameToClipboard(std::string id, bool is_group) { LLAvatarName av_name; LLAvatarNameCache::get(LLUUID(id), &av_name); - name = av_name.getLegacyName(); + name = av_name.getAccountName(); } LLUrlAction::copyURLToClipboard(name); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 1a909768ae..c533674bca 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -443,6 +443,9 @@ private: BOOL setSort(S32 column, BOOL ascending); S32 getLinesPerPage(); + static void showProfile(std::string id, bool is_group); + static void sendIM(std::string id); + static void addFriend(std::string id); static void showNameDetails(std::string id, bool is_group); static void copyNameToClipboard(std::string id, bool is_group); static void copySLURLToClipboard(std::string id, bool is_group); diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 2e887906c0..09e2db8f27 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -52,6 +52,7 @@ LLSpinCtrl::Params::Params() : label_width("label_width"), decimal_digits("decimal_digits"), allow_text_entry("allow_text_entry", true), + allow_digits_only("allow_digits_only", false), label_wrap("label_wrap", false), text_enabled_color("text_enabled_color"), text_disabled_color("text_disabled_color"), @@ -129,6 +130,10 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) params.follows.flags(FOLLOWS_LEFT | FOLLOWS_BOTTOM); mEditor = LLUICtrlFactory::create (params); mEditor->setFocusReceivedCallback( boost::bind(&LLSpinCtrl::onEditorGainFocus, _1, this )); + if (p.allow_digits_only) + { + mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); + } //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus // than when it doesn't. Instead, if you always have to double click to select all the text, // it's easier to understand diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h index 5d88439ab0..56d37421ea 100644 --- a/indra/llui/llspinctrl.h +++ b/indra/llui/llspinctrl.h @@ -44,6 +44,7 @@ public: Optional label_width; Optional decimal_digits; Optional allow_text_entry; + Optional allow_digits_only; Optional label_wrap; Optional text_enabled_color; diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index c23855cfea..5984c907db 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -532,8 +532,8 @@ void LLTabContainer::draw() } } - mPrevArrowBtn->setFlashing(FALSE); - mNextArrowBtn->setFlashing(FALSE); + mPrevArrowBtn->setFlashing(false); + mNextArrowBtn->setFlashing(false); } @@ -891,86 +891,62 @@ BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDrag { BOOL has_scroll_arrows = (getMaxScrollPos() > 0); -// [SL:KB] - Checked: UI-TabDndButtonCommit | Checked: 2011-06-16 (Catznip-2.6.0c) | Added: Catznip-2.6.0c - S32 curHoverIdx = -1; - if (mOpenTabsOnDragAndDrop && !getTabsHidden()) + if(mOpenTabsOnDragAndDrop && !getTabsHidden()) { - for(tuple_list_t::const_iterator itTab = mTabList.begin(); itTab != mTabList.end(); ++itTab) + // In that case, we'll open the hovered tab while dragging and dropping items. + // This allows for drilling through tabs. + if (mDragAndDropDelayTimer.getStarted()) { - const LLTabTuple* pTuple = *itTab; - S32 local_x = x - pTuple->mButton->getRect().mLeft; - S32 local_y = y - pTuple->mButton->getRect().mBottom; - if ( (pTuple->mButton->getEnabled()) && (pTuple->mButton->pointInView(local_x, local_y)) ) - curHoverIdx = itTab - mTabList.begin(); - } - } + if (mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME) + { + if (has_scroll_arrows) + { + if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; + mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); + } + if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; + S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; + mJumpNextArrowBtn->handleHover(local_x, local_y, mask); + } + if (mPrevArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mPrevArrowBtn->getRect().mLeft; + S32 local_y = y - mPrevArrowBtn->getRect().mBottom; + mPrevArrowBtn->handleHover(local_x, local_y, mask); + } + else if (mNextArrowBtn->getRect().pointInRect(x, y)) + { + S32 local_x = x - mNextArrowBtn->getRect().mLeft; + S32 local_y = y - mNextArrowBtn->getRect().mBottom; + mNextArrowBtn->handleHover(local_x, local_y, mask); + } + } - if (-1 == curHoverIdx) - mDragAndDropDelayTimer.stop(); // Mouse not hovering over a tab button - else if ( (mCurrentTabIdx != curHoverIdx) && (!mDragAndDropDelayTimer.getStarted()) ) - mDragAndDropDelayTimer.start(); // Mouse hovering over a tab button - else if ( (mDragAndDropHoverIdx != curHoverIdx) && (mDragAndDropDelayTimer.getStarted()) ) - mDragAndDropDelayTimer.reset(); // Mouse hovering over a different tab button - - mDragAndDropHoverIdx = curHoverIdx; -// [/SL:KB] - -// if( mDragAndDropDelayTimer.getStarted() && mDragAndDropDelayTimer.getElapsedTimeF32() > SCROLL_DELAY_TIME ) -// [SL:KB] - Checked: UI-TabDndButtonCommit | Checked: 2011-06-16 (Catznip-2.6.0c) | Added: Catznip-2.6.0c - if ( (mDragAndDropHoverCommit) && (mDragAndDropDelayTimer.getStarted()) && (mDragAndDropDelayTimer.getElapsedTimeF32() > DELAY_DRAG_HOVER_COMMIT) ) -// [/SL:KB] - { - if (has_scroll_arrows) - { - if (mJumpPrevArrowBtn && mJumpPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpPrevArrowBtn->getRect().mBottom; - mJumpPrevArrowBtn->handleHover(local_x, local_y, mask); - } - if (mJumpNextArrowBtn && mJumpNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mJumpNextArrowBtn->getRect().mLeft; - S32 local_y = y - mJumpNextArrowBtn->getRect().mBottom; - mJumpNextArrowBtn->handleHover(local_x, local_y, mask); - } - if (mPrevArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mPrevArrowBtn->getRect().mLeft; - S32 local_y = y - mPrevArrowBtn->getRect().mBottom; - mPrevArrowBtn->handleHover(local_x, local_y, mask); - } - else if (mNextArrowBtn->getRect().pointInRect(x, y)) - { - S32 local_x = x - mNextArrowBtn->getRect().mLeft; - S32 local_y = y - mNextArrowBtn->getRect().mBottom; - mNextArrowBtn->handleHover(local_x, local_y, mask); - } - } - -// [SL:KB] - Checked: UI-TabDndButtonCommit | Checked: 2011-06-16 (Catznip-2.6.0c) | Added: Catznip-2.6.0c - if (-1 != mDragAndDropHoverIdx) - { - const LLTabTuple* pTuple = mTabList[mDragAndDropHoverIdx]; - if (!pTuple->mTabPanel->getVisible()) - { - pTuple->mButton->onCommit(); + for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) + { + LLTabTuple* tuple = *iter; + tuple->mButton->setVisible( TRUE ); + S32 local_x = x - tuple->mButton->getRect().mLeft; + S32 local_y = y - tuple->mButton->getRect().mBottom; + if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible()) + { + tuple->mButton->onCommit(); + } + } + // Stop the timer whether successful or not. Don't let it run forever. mDragAndDropDelayTimer.stop(); } } -// [/SL:KB] -// for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) -// { -// LLTabTuple* tuple = *iter; -// tuple->mButton->setVisible( TRUE ); -// S32 local_x = x - tuple->mButton->getRect().mLeft; -// S32 local_y = y - tuple->mButton->getRect().mBottom; -// if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible()) -// { -// tuple->mButton->onCommit(); -// mDragAndDropDelayTimer.stop(); -// } -// } + else + { + // Start a timer so we don't open tabs as soon as we hover on them + mDragAndDropDelayTimer.start(); + } } return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip); @@ -1314,11 +1290,17 @@ void LLTabContainer::removeTabPanel(LLPanel* child) update_images(mTabList[mTabList.size()-2], mLastTabParams, getTabPosition()); } - removeChild( tuple->mButton ); + if (!getTabsHidden()) + { + // We need to remove tab buttons only if the tabs are not hidden. + removeChild( tuple->mButton ); + } delete tuple->mButton; + tuple->mButton = NULL; removeChild( tuple->mTabPanel ); // delete tuple->mTabPanel; + tuple->mTabPanel = NULL; mTabList.erase( iter ); delete tuple; @@ -1380,9 +1362,11 @@ void LLTabContainer::deleteAllTabs() removeChild( tuple->mButton ); delete tuple->mButton; + tuple->mButton = NULL; removeChild( tuple->mTabPanel ); // delete tuple->mTabPanel; + tuple->mTabPanel = NULL; } // Actually delete the tuples themselves @@ -1585,13 +1569,20 @@ BOOL LLTabContainer::setTab(S32 which) { LLTabTuple* tuple = *iter; BOOL is_selected = ( tuple == selected_tuple ); - tuple->mButton->setUseEllipses(mUseTabEllipses); - tuple->mButton->setHAlign(mFontHalign); - tuple->mTabPanel->setVisible( is_selected ); -// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. - tuple->mButton->setToggleState( is_selected ); - // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs - tuple->mButton->setTabStop( is_selected ); + // Although the selected tab must be complete, we may have hollow LLTabTuple tucked in the list + if (tuple && tuple->mButton) + { + tuple->mButton->setUseEllipses(mUseTabEllipses); + tuple->mButton->setHAlign(mFontHalign); + tuple->mButton->setToggleState( is_selected ); + // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs + tuple->mButton->setTabStop( is_selected ); + } + if (tuple && tuple->mTabPanel) + { + tuple->mTabPanel->setVisible( is_selected ); + //tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here. + } if (is_selected) { @@ -1618,7 +1609,7 @@ BOOL LLTabContainer::setTab(S32 which) else { S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); - S32 running_tab_width = tuple->mButton->getRect().getWidth(); + S32 running_tab_width = (tuple && tuple->mButton ? tuple->mButton->getRect().getWidth() : 0); S32 j = i - 1; S32 min_scroll_pos = i; if (running_tab_width < available_width_with_arrows) @@ -1626,7 +1617,7 @@ BOOL LLTabContainer::setTab(S32 which) while (j >= 0) { LLTabTuple* other_tuple = getTab(j); - running_tab_width += other_tuple->mButton->getRect().getWidth(); + running_tab_width += (other_tuple && other_tuple->mButton ? other_tuple->mButton->getRect().getWidth() : 0); if (running_tab_width > available_width_with_arrows) { break; @@ -1662,8 +1653,7 @@ BOOL LLTabContainer::selectTabByName(const std::string& name) LLPanel* panel = getPanelByName(name); if (!panel) { - llwarns << "LLTabContainer::selectTabByName(" - << name << ") failed" << llendl; + llwarns << "LLTabContainer::selectTabByName(" << name << ") failed" << llendl; return FALSE; } diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index e1a11a0f8a..51f987fb2e 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -200,10 +200,11 @@ public: void selectFirstTab(); void selectLastTab(); void selectNextTab(); - void selectPrevTab(); + void selectPrevTab(); BOOL selectTabPanel( LLPanel* child ); BOOL selectTab(S32 which); BOOL selectTabByName(const std::string& title); + void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; } BOOL getTabPanelFlashing(LLPanel* child); void setTabPanelFlashing(LLPanel* child, BOOL state); @@ -254,8 +255,6 @@ private: void setTabsHidden(BOOL hidden) { mTabsHidden = hidden; } BOOL getTabsHidden() const { return mTabsHidden; } - - void setCurrentPanelIndex(S32 index) { mCurrentTabIdx = index; } void scrollPrev() { mScrollPos = llmax(0, mScrollPos-1); } // No wrap void scrollNext() { mScrollPos = llmin(mScrollPos+1, mMaxScrollPos); } // No wrap diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 1a5f7c786f..edfbf824d1 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -46,6 +46,7 @@ const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds const S32 CURSOR_THICKNESS = 2; +const F32 TRIPLE_CLICK_INTERVAL = 0.3f; // delay between double and triple click. LLTextBase::line_info::line_info(S32 index_start, S32 index_end, LLRect rect, S32 line_num) : mDocIndexStart(index_start), @@ -156,9 +157,7 @@ LLTextBase::Params::Params() : cursor_color("cursor_color"), text_color("text_color"), text_readonly_color("text_readonly_color"), -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-09-05 (Catznip-3.2.0a) | Added: Catznip-2.8.0b - text_label_color("text_label_color"), -// [/SL:KB] + text_tentative_color("text_tentative_color"), bg_visible("bg_visible", false), border_visible("border_visible", false), bg_readonly_color("bg_readonly_color"), @@ -196,7 +195,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) : LLUICtrl(p, LLTextViewModelPtr(new LLTextViewModel)), mURLClickSignal(NULL), mMaxTextByteLength( p.max_text_length ), - mDefaultFont(p.font), + mFont(p.font), mFontShadow(p.font_shadow), mPopupMenu(NULL), mReadOnly(p.read_only), @@ -207,6 +206,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mFgColor(p.text_color), mBorderVisible( p.border_visible ), mReadOnlyFgColor(p.text_readonly_color), + mTentativeFgColor(p.text_tentative_color()), mWriteableBgColor(p.bg_writeable_color), mReadOnlyBgColor(p.bg_readonly_color), mFocusBgColor(p.bg_focus_color), @@ -231,10 +231,6 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mIsSelecting( FALSE ), mPlainText ( p.plain_text ), mWordWrap(p.wrap), -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-09-05 (Catznip-3.2.0a) | Added: Catznip-2.8.0b - mLabel(p.label), - mLabelColor(p.text_label_color), -// [/SL:KB] mUseEllipses( p.use_ellipses ), mParseHTML(p.parse_urls), mParseHighlights(p.parse_highlights), @@ -345,21 +341,26 @@ bool LLTextBase::truncate() return did_truncate; } -const LLStyle::Params& LLTextBase::getDefaultStyleParams() +const LLStyle::Params& LLTextBase::getStyleParams() { //FIXME: convert mDefaultStyle to a flyweight http://www.boost.org/doc/libs/1_40_0/libs/flyweight/doc/index.html //and eliminate color member values if (mStyleDirty) { - mDefaultStyle + mStyle .color(LLUIColor(&mFgColor)) // pass linked color instead of copy of mFGColor .readonly_color(LLUIColor(&mReadOnlyFgColor)) .selected_color(LLUIColor(&mTextSelectedColor)) - .font(mDefaultFont) + .font(mFont) .drop_shadow(mFontShadow); mStyleDirty = false; } - return mDefaultStyle; + return mStyle; +} + +void LLTextBase::beforeValueChange() +{ + } void LLTextBase::onValueChange(S32 start, S32 end) @@ -461,6 +462,7 @@ void LLTextBase::drawSelectionBackground() ++rect_it) { LLRect selection_rect = *rect_it; + selection_rect = *rect_it; selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); gl_rect_2d(selection_rect, selection_color); } @@ -547,19 +549,17 @@ void LLTextBase::drawCursor() void LLTextBase::drawText() { - const S32 text_len = getLength(); - if( text_len <= 0 ) + S32 text_len = getLength(); + + if (text_len <= 0 && mLabel.empty()) { -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-09-05 (Catznip-3.2.0a) | Added: Catznip-2.8.0b - if ( (!hasFocus()) && (mLabel.length()) ) - { - mDefaultFont->render( - mLabel.getWString(), 0, mLineInfoList.begin()->mRect.mLeft, mDocumentView->getRect().mBottom, mLabelColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, S32_MAX, mDocumentView->getRect().getWidth()); - } -// [/SL:KB] return; } + else if (useLabel()) + { + text_len = mLabel.getWString().length(); + } + S32 selection_left = -1; S32 selection_right = -1; // Draw selection even if we don't have keyboard focus for search/replace @@ -584,7 +584,7 @@ void LLTextBase::drawText() { return; } - + // Perform spell check if needed if ( (getSpellCheck()) && (getWText().length() > 2) ) { @@ -625,20 +625,18 @@ void LLTextBase::drawText() // Find the start of the first word U32 word_start = seg_start, word_end = -1; - while ( (word_start < wstrText.length()) && (!LLStringOps::isAlpha(wstrText[word_start])) ) + U32 text_length = wstrText.length(); + while ( (word_start < text_length) && (!LLStringOps::isAlpha(wstrText[word_start])) ) { word_start++; } // Iterate over all words in the text block and check them one by one - // while (word_start < seg_end) - while ( word_start < wstrText.length() && word_start < seg_end ) // Do not run past end of wstrTest + while (word_start < seg_end) { // Find the end of the current word (special case handling for "'" when it's used as a contraction) word_end = word_start + 1; - - // while ( (word_end < seg_end) && - while ( (word_end < seg_end) && word_end < wstrText.length() && // Don't run past end of wstrText + while ( (word_end < seg_end) && ((LLWStringUtil::isPartOfWord(wstrText[word_end])) || ((L'\'' == wstrText[word_end]) && (LLStringOps::isAlnum(wstrText[word_end - 1])) && (LLStringOps::isAlnum(wstrText[word_end + 1])))) ) @@ -650,27 +648,20 @@ void LLTextBase::drawText() break; } - // Exit if we ran past end of string - if( word_start >= wstrText.length() || word_end >= wstrText.length() ) - break; - // - - // Don't process words shorter than 3 characters - - std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start)); - if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) + if (word_start < text_length && word_end <= text_length && word_end > word_start) { - mMisspellRanges.push_back(std::pair(word_start, word_end)); + std::string word = wstring_to_utf8str(wstrText.substr(word_start, word_end - word_start)); + + // Don't process words shorter than 3 characters + if ( (word.length() >= 3) && (!LLSpellChecker::instance().checkSpelling(word)) ) + { + mMisspellRanges.push_back(std::pair(word_start, word_end)); + } } - + // Find the start of the next word word_start = word_end + 1; - - // Do not run past end of string. - - // while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) ) - while ( word_start < wstrText.length() && (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) ) - // + while ( (word_start < seg_end) && (!LLWStringUtil::isPartOfWord(wstrText[word_start])) ) { word_start++; } @@ -786,6 +777,8 @@ void LLTextBase::drawText() S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::segment_vec_t* segments ) { + beforeValueChange(); + S32 old_len = getLength(); // length() returns character length S32 insert_len = wstr.length(); @@ -817,7 +810,7 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s else { // create default editable segment to hold new text - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); default_segment = new LLNormalTextSegment( sp, pos, pos + insert_len, *this); } @@ -861,6 +854,8 @@ S32 LLTextBase::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextBase::s S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) { + + beforeValueChange(); segment_set_t::iterator seg_iter = getSegIterContaining(pos); while(seg_iter != mSegments.end()) { @@ -919,6 +914,8 @@ S32 LLTextBase::removeStringNoUndo(S32 pos, S32 length) S32 LLTextBase::overwriteCharNoUndo(S32 pos, llwchar wc) { + beforeValueChange(); + if (pos > (S32)getLength()) { return 0; @@ -937,7 +934,7 @@ void LLTextBase::createDefaultSegment() // ensures that there is always at least one segment if (mSegments.empty()) { - LLStyleConstSP sp(new LLStyle(getDefaultStyleParams())); + LLStyleConstSP sp(new LLStyle(getStyleParams())); LLTextSegmentPtr default_segment = new LLNormalTextSegment( sp, 0, getLength() + 1, *this); mSegments.insert(default_segment); default_segment->linkToDocument(this); @@ -1027,6 +1024,13 @@ void LLTextBase::insertSegment(LLTextSegmentPtr segment_to_insert) BOOL LLTextBase::handleMouseDown(S32 x, S32 y, MASK mask) { + // handle triple click + if (!mTripleClickTimer.hasExpired()) + { + selectAll(); + return TRUE; + } + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleMouseDown(x, y, mask)) { @@ -1101,6 +1105,14 @@ BOOL LLTextBase::handleRightMouseUp(S32 x, S32 y, MASK mask) BOOL LLTextBase::handleDoubleClick(S32 x, S32 y, MASK mask) { + //Don't start triple click timer if user have clicked on scrollbar + mVisibleTextRect = mScroller ? mScroller->getContentWindowRect() : getLocalRect(); + if (x >= mVisibleTextRect.mLeft && x <= mVisibleTextRect.mRight + && y >= mVisibleTextRect.mBottom && y <= mVisibleTextRect.mTop) + { + mTripleClickTimer.setTimerExpirySec(TRIPLE_CLICK_INTERVAL); + } + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos(x, y); if (cur_segment && cur_segment->handleDoubleClick(x, y, mask)) { @@ -1384,6 +1396,26 @@ void LLTextBase::onSpellCheckSettingsChange() mMisspellRanges.clear(); mSpellCheckStart = mSpellCheckEnd = -1; } + +void LLTextBase::onFocusReceived() +{ + LLUICtrl::onFocusReceived(); + if (!getLength() && !mLabel.empty()) + { + // delete label which is LLLabelTextSegment + clearSegments(); + } +} + +void LLTextBase::onFocusLost() +{ + LLUICtrl::onFocusLost(); + if (!getLength() && !mLabel.empty()) + { + resetLabel(); + } +} + // Sets the scrollbar from the cursor position void LLTextBase::updateScrollFromCursor() { @@ -1971,6 +2003,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) registrar.add("Url.Execute", boost::bind(&LLUrlAction::executeSLURL, url)); registrar.add("Url.Teleport", boost::bind(&LLUrlAction::teleportToLocation, url)); registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url)); + registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url)); registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); registrar.add("Url.CopyUrl", boost::bind(&LLUrlAction::copyURLToClipboard, url)); @@ -2044,7 +2077,7 @@ static LLFastTimer::DeclareTimer FTM_PARSE_HTML("Parse HTML"); void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Params& input_params) { LLStyle::Params style_params(input_params); - style_params.fillFrom(getDefaultStyleParams()); + style_params.fillFrom(getStyleParams()); S32 part = (S32)LLTextParser::WHOLE; if (mParseHTML && !style_params.is_link) // Don't search for URLs inside a link segment (STORM-358). @@ -2143,31 +2176,69 @@ void LLTextBase::appendText(const std::string &new_text, bool prepend_newline, c appendTextImpl(new_text,input_params); } +void LLTextBase::setLabel(const LLStringExplicit& label) +{ + mLabel = label; + resetLabel(); +} + +BOOL LLTextBase::setLabelArg(const std::string& key, const LLStringExplicit& text ) +{ + mLabel.setArg(key, text); + return TRUE; +} + +void LLTextBase::resetLabel() +{ + if (useLabel()) + { + clearSegments(); + + LLStyle* style = new LLStyle(getStyleParams()); + style->setColor(mTentativeFgColor); + LLStyleConstSP sp(style); + + LLTextSegmentPtr label = new LLLabelTextSegment(sp, 0, mLabel.getWString().length() + 1, *this); + insertSegment(label); + } +} + +bool LLTextBase::useLabel() +{ + return !getLength() && !mLabel.empty() && !hasFocus(); +} + +void LLTextBase::setFont(const LLFontGL* font) +{ + mFont = font; + mStyleDirty = true; +} + void LLTextBase::needsReflow(S32 index) { lldebugs << "reflow on object " << (void*)this << " index = " << mReflowIndex << ", new index = " << index << llendl; mReflowIndex = llmin(mReflowIndex, index); } -// [SL:KB] - Patch: Control-TextEditorFont | Checked: 2012-01-10 (Catznip-3.2.1) | Added: Catznip-3.2.1 -void LLTextBase::setFont(const LLFontGL* pFont) -{ - for(segment_set_t::iterator itSegment = mSegments.begin(); itSegment != mSegments.end(); ++itSegment) - { - LLTextSegmentPtr segment = *itSegment; - - LLStyleConstSP curStyle = segment->getStyle(); - if (curStyle->getFont() == mDefaultFont) - { - LLStyleSP newStyle(new LLStyle(*curStyle)); - newStyle->setFont(pFont); - LLStyleConstSP newStyleConst(newStyle); - segment->setStyle(newStyleConst); - } - } - mDefaultFont = pFont; -} -// [/SL:KB] +//// [SL:KB] - Patch: Control-TextEditorFont | Checked: 2012-01-10 (Catznip-3.2.1) | Added: Catznip-3.2.1 +//void LLTextBase::setFont(const LLFontGL* pFont) +//{ +// for(segment_set_t::iterator itSegment = mSegments.begin(); itSegment != mSegments.end(); ++itSegment) +// { +// LLTextSegmentPtr segment = *itSegment; +// +// LLStyleConstSP curStyle = segment->getStyle(); +// if (curStyle->getFont() == mDefaultFont) +// { +// LLStyleSP newStyle(new LLStyle(*curStyle)); +// newStyle->setFont(pFont); +// LLStyleConstSP newStyleConst(newStyle); +// segment->setStyle(newStyleConst); +// } +// } +// mDefaultFont = pFont; +//} +//// [/SL:KB] void LLTextBase::appendLineBreakSegment(const LLStyle::Params& style_params) { @@ -2554,7 +2625,7 @@ LLRect LLTextBase::getLocalRectFromDocIndex(S32 pos) const { // return default height rect in upper left local_rect = content_window_rect; - local_rect.mBottom = local_rect.mTop - mDefaultFont->getLineHeight(); + local_rect.mBottom = local_rect.mTop - mFont->getLineHeight(); return local_rect; } @@ -2915,19 +2986,6 @@ void LLTextBase::endSelection() } } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a -void LLTextBase::setSelection(S32 start, S32 end) -{ - S32 len = getLength(); - - mIsSelecting = TRUE; - mSelectionStart = llclamp(start, 0, len); - mSelectionEnd = llclamp(end, 0, len); - - setCursorPos(mSelectionEnd); -} -// [/SL:KB] - // get portion of document that is visible in text editor LLRect LLTextBase::getVisibleDocumentRect() const { @@ -3049,7 +3107,7 @@ LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 mToken(NULL), mEditor(editor) { - mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color).font(editor.getDefaultFont())); + mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color).font(editor.getDefaultFont())); // [SL:KB] - Patch: Control-TextEditorFont mFontHeight = mStyle->getFont()->getLineHeight(); } @@ -3074,7 +3132,7 @@ F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 sele { F32 alpha = LLViewDrawContext::getCurrentContext().mAlpha; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); F32 right_x = rect.mLeft; if (!mStyle->isVisible()) @@ -3237,7 +3295,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt if (num_chars > 0) { height = mFontHeight; - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); // if last character is a newline, then return true, forcing line break width = mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); } @@ -3246,7 +3304,7 @@ bool LLNormalTextSegment::getDimensions(S32 first_char, S32 num_chars, S32& widt S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, (F32)segment_local_x_coord, F32_MAX, @@ -3256,7 +3314,7 @@ S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { - const LLWString &text = mEditor.getWText(); + const LLWString &text = getWText(); LLUIImagePtr image = mStyle->getImage(); if( image.notNull()) @@ -3274,7 +3332,23 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin LLFontGL::EWordWrapStyle word_wrap_style = (line_offset == 0) ? LLFontGL::WORD_BOUNDARY_IF_POSSIBLE : LLFontGL::ONLY_WORD_BOUNDARIES; - S32 num_chars = mStyle->getFont()->maxDrawableChars(text.c_str() + segment_offset + mStart, + + + LLWString offsetString(text.c_str() + segment_offset + mStart); + + if(getLength() < segment_offset + mStart) + { + llerrs << "getLength() < segment_offset + mStart\t getLength()\t" << getLength() << "\tsegment_offset:\t" + << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << "\tmax_chars\t" << max_chars << llendl; + } + + if(offsetString.length() + 1 < max_chars) + { + llerrs << "offsetString.length() + 1 < max_chars\t max_chars:\t" << max_chars << "\toffsetString.length():\t" << offsetString.length() + << getLength() << "\tsegment_offset:\t" << segment_offset << "\tmStart:\t" << mStart << "\tsegments\t" << mEditor.mSegments.size() << llendl; + } + + S32 num_chars = mStyle->getFont()->maxDrawableChars(offsetString.c_str(), (F32)num_pixels, max_chars, word_wrap_style); @@ -3292,7 +3366,7 @@ S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 lin S32 last_char_in_run = mStart + segment_offset + num_chars; // check length first to avoid indexing off end of string if (last_char_in_run < mEnd - && (last_char_in_run >= mEditor.getLength() )) + && (last_char_in_run >= getLength())) { num_chars++; } @@ -3310,6 +3384,39 @@ void LLNormalTextSegment::dump() const llendl; } +/*virtual*/ +const LLWString& LLNormalTextSegment::getWText() const +{ + return mEditor.getWText(); +} + +/*virtual*/ +const S32 LLNormalTextSegment::getLength() const +{ + return mEditor.getLength(); +} + +LLLabelTextSegment::LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ) +: LLNormalTextSegment(style, start, end, editor) +{ +} + +LLLabelTextSegment::LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible) +: LLNormalTextSegment(color, start, end, editor, is_visible) +{ +} + +/*virtual*/ +const LLWString& LLLabelTextSegment::getWText() const +{ + return mEditor.getWlabel(); +} +/*virtual*/ +const S32 LLLabelTextSegment::getLength() const +{ + return mEditor.getWlabel().length(); +} + // // LLOnHoverChangeableTextSegment // @@ -3520,3 +3627,7 @@ F32 LLImageTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 select return 0.0; } +void LLTextBase::setWordWrap(bool wrap) +{ + mWordWrap = wrap; +} diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index a004ac562b..4e40f1c091 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -41,6 +41,7 @@ #include +class LLScrollContainer; class LLContextMenu; class LLUrlMatch; @@ -106,7 +107,7 @@ class LLNormalTextSegment : public LLTextSegment public: LLNormalTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); - ~LLNormalTextSegment(); + virtual ~LLNormalTextSegment(); /*virtual*/ bool getDimensions(S32 first_char, S32 num_chars, S32& width, S32& height) const; /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; @@ -134,6 +135,9 @@ public: protected: F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, LLRect rect); + virtual const LLWString& getWText() const; + virtual const S32 getLength() const; + protected: class LLTextBase& mEditor; LLStyleConstSP mStyle; @@ -143,6 +147,21 @@ protected: boost::signals2::connection mImageLoadedConnection; }; +// This text segment is the same as LLNormalTextSegment, the only difference +// is that LLNormalTextSegment draws value of LLTextBase (LLTextBase::getWText()), +// but LLLabelTextSegment draws label of the LLTextBase (LLTextBase::mLabel) +class LLLabelTextSegment : public LLNormalTextSegment +{ +public: + LLLabelTextSegment( LLStyleConstSP style, S32 start, S32 end, LLTextBase& editor ); + LLLabelTextSegment( const LLColor4& color, S32 start, S32 end, LLTextBase& editor, BOOL is_visible = TRUE); + +protected: + + /*virtual*/ const LLWString& getWText() const; + /*virtual*/ const S32 getLength() const; +}; + // Text segment that changes it's style depending of mouse pointer position ( is it inside or outside segment) class LLOnHoverChangeableTextSegment : public LLNormalTextSegment { @@ -278,9 +297,7 @@ public: Optional cursor_color, text_color, text_readonly_color, -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-09-05 (Catznip-3.2.0a) | Added: Catznip-2.8.0b - text_label_color, -// [/SL:KB] + text_tentative_color, bg_readonly_color, bg_writeable_color, bg_focus_color, @@ -347,6 +364,9 @@ public: /*virtual*/ BOOL canDeselect() const; /*virtual*/ void deselect(); + virtual void onFocusReceived(); + virtual void onFocusLost(); + // void setParseHTML(bool parse_html) { mParseHTML = parse_html; } // @@ -388,6 +408,21 @@ public: const LLWString& getWText() const; void appendText(const std::string &new_text, bool prepend_newline, const LLStyle::Params& input_params = LLStyle::Params()); + + void setLabel(const LLStringExplicit& label); + virtual BOOL setLabelArg(const std::string& key, const LLStringExplicit& text ); + + const std::string& getLabel() { return mLabel.getString(); } + const LLWString& getWlabel() { return mLabel.getWString();} + + /** + * If label is set, draws text label (which is LLLabelTextSegment) + * that is visible when no user text provided + */ + void resetLabel(); + + void setFont(const LLFontGL* font); + // force reflow of text void needsReflow(S32 index = 0); @@ -427,23 +462,19 @@ public: void changePage( S32 delta ); void changeLine( S32 delta ); -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - void setSelection(S32 start, S32 end); -// [/SL:KB] - bool scrolledToStart(); bool scrolledToEnd(); - const LLFontGL* getDefaultFont() const { return mDefaultFont; } -// [SL:KB] - Patch: Control-TextEditorFont | Checked: 2012-01-10 (Catznip-3.2.1) | Added: Catznip-3.2.1 - void setFont(const LLFontGL* pFont); -// [/SL:KB] + const LLFontGL* getFont() const { return mFont; } virtual void appendLineBreakSegment(const LLStyle::Params& style_params); virtual void appendImageSegment(const LLStyle::Params& style_params); virtual void appendWidget(const LLInlineViewSegment::Params& params, const std::string& text, bool allow_undo); boost::signals2::connection setURLClickedCallback(const commit_signal_t::slot_type& cb); + void setWordWrap(bool wrap); + LLScrollContainer* getScrollContainer() const { return mScroller; } + protected: // helper structs struct compare_bottom; @@ -511,7 +542,9 @@ protected: LLTextBase(const Params &p); virtual ~LLTextBase(); void initFromParams(const Params& p); + virtual void beforeValueChange(); virtual void onValueChange(S32 start, S32 end); + virtual bool useLabel(); // draw methods void drawSelectionBackground(); // draws the black box behind the selected text @@ -541,7 +574,7 @@ protected: void createDefaultSegment(); virtual void updateSegments(); void insertSegment(LLTextSegmentPtr segment_to_insert); - const LLStyle::Params& getDefaultStyleParams(); + const LLStyle::Params& getStyleParams(); // manage lines S32 getLineStart( S32 line ) const; @@ -586,18 +619,16 @@ protected: LLRect mTextBoundingRect; // default text style - LLStyle::Params mDefaultStyle; + LLStyle::Params mStyle; bool mStyleDirty; -// const LLFontGL* const mDefaultFont; // font that is used when none specified, can only be set by constructor -// [SL:KB] - Patch: Control-TextEditorFont | Checked: 2012-01-10 (Catznip-3.2.1) | Added: Catznip-3.2.1 - const LLFontGL* mDefaultFont; // font that is used when none specified -// [/SL:KB] - const LLFontGL::ShadowType mFontShadow; // shadow style, can only be set by constructor + const LLFontGL* mFont; + const LLFontGL::ShadowType mFontShadow; // colors LLUIColor mCursorColor; LLUIColor mFgColor; LLUIColor mReadOnlyFgColor; + LLUIColor mTentativeFgColor; LLUIColor mWriteableBgColor; LLUIColor mReadOnlyBgColor; LLUIColor mFocusBgColor; @@ -612,7 +643,8 @@ protected: // selection S32 mSelectionStart; S32 mSelectionEnd; - + LLTimer mTripleClickTimer; + BOOL mIsSelecting; // Are we in the middle of a drag-select? // spell checking @@ -622,7 +654,7 @@ protected: LLTimer mSpellCheckTimer; std::list > mMisspellRanges; std::vector mSuggestionList; - + // configuration S32 mHPad; // padding on left of text S32 mVPad; // padding above text @@ -641,26 +673,23 @@ protected: bool mClip; // clip text to widget rect bool mClipPartial; // false if we show lines that are partially inside bounding rect bool mPlainText; // didn't use Image or Icon segments + bool mAutoIndent; S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes // support widgets LLContextMenu* mPopupMenu; LLView* mDocumentView; - class LLScrollContainer* mScroller; + LLScrollContainer* mScroller; // transient state S32 mReflowIndex; // index at which to start reflow. S32_MAX indicates no reflow needed. bool mScrollNeeded; // need to change scroll region because of change to cursor position S32 mScrollIndex; // index of first character to keep visible in scroll region -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-09-05 (Catznip-3.2.0a) | Added: Catznip-2.8.0b - LLUIString mLabel; - LLUIColor mLabelColor; -// [/SL:KB] - // Fired when a URL link is clicked commit_signal_t* mURLClickSignal; + LLUIString mLabel; // text label that is visible when no user text provided // Optional icon position LLTextBaseEnums::EIconPositioning mIconPositioning; }; diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 89203b6bcf..6ad890e378 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -237,11 +237,9 @@ LLTextEditor::Params::Params() embedded_items("embedded_items", false), ignore_tab("ignore_tab", true), show_line_numbers("show_line_numbers", false), + auto_indent("auto_indent", true), default_color("default_color"), commit_on_focus_lost("commit_on_focus_lost", false), -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - commit_on_return("commit_on_return", false), -// [/SL:KB] show_context_menu("show_context_menu"), enable_tooltip_paste("enable_tooltip_paste") { @@ -250,15 +248,14 @@ LLTextEditor::Params::Params() LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : LLTextBase(p), + mAutoreplaceCallback(), mBaseDocIsPristine(TRUE), mPristineCmd( NULL ), mLastCmd( NULL ), mDefaultColor( p.default_color() ), mShowLineNumbers ( p.show_line_numbers ), + mAutoIndent(p.auto_indent), mCommitOnFocusLost( p.commit_on_focus_lost), -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - mCommitOnReturn(p.commit_on_return), -// [/SL:KB] mAllowEmbeddedItems( p.embedded_items ), mMouseDownX(0), mMouseDownY(0), @@ -266,7 +263,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) : mPrevalidateFunc(p.prevalidate_callback()), mContextMenu(NULL), mShowContextMenu(p.show_context_menu), - mEnableTooltipPaste(p.enable_tooltip_paste) + mEnableTooltipPaste(p.enable_tooltip_paste), + mPassDelete(FALSE) { mSourceID.generate(); @@ -1152,6 +1150,23 @@ void LLTextEditor::addChar(llwchar wc) } setCursorPos(mCursorPos + addChar( mCursorPos, wc )); + + if (!mReadOnly && mAutoreplaceCallback != NULL) + { + // autoreplace the text, if necessary + S32 replacement_start; + S32 replacement_length; + LLWString replacement_string; + S32 new_cursor_pos = mCursorPos; + mAutoreplaceCallback(replacement_start, replacement_length, replacement_string, new_cursor_pos, getWText()); + + if (replacement_length > 0 || !replacement_string.empty()) + { + remove(replacement_start, replacement_length, true); + insert(replacement_start, replacement_string, false, LLTextSegmentPtr()); + setCursorPos(new_cursor_pos); + } + } } void LLTextEditor::addLineBreakChar(BOOL group_with_next) { @@ -1688,22 +1703,14 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask) case KEY_RETURN: if (mask == MASK_NONE) { -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - if (!mCommitOnReturn) + if( hasSelection() ) { -// [/SL:KB] - if( hasSelection() ) - { - deleteSelection(FALSE); - } - autoIndent(); // TODO: make this optional -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a + deleteSelection(FALSE); } - else + if (mAutoIndent) { - onCommit(); + autoIndent(); } -// [/SL:KB] } else { @@ -1874,7 +1881,7 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) // virtual BOOL LLTextEditor::canDoDelete() const { - return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) ); + return !mReadOnly && ( !mPassDelete || ( hasSelection() || (mCursorPos < getLength())) ); } void LLTextEditor::doDelete() @@ -2143,7 +2150,7 @@ void LLTextEditor::drawPreeditMarker() return; } - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); S32 line_start = getLineStart(cur_line); S32 line_y = mVisibleTextRect.mTop - line_height; @@ -2182,16 +2189,16 @@ void LLTextEditor::drawPreeditMarker() S32 preedit_left = mVisibleTextRect.mLeft; if (left > line_start) { - preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start); + preedit_left += mFont->getWidth(text, line_start, left - line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (right < line_end) { - preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start); + preedit_right += mFont->getWidth(text, line_start, right - line_start); } else { - preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start); + preedit_right += mFont->getWidth(text, line_start, line_end - line_start); } if (mPreeditStandouts[i]) @@ -2868,11 +2875,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect const LLWString textString(getWText()); const llwchar * const text = textString.c_str(); - const S32 line_height = mDefaultFont->getLineHeight(); + const S32 line_height = mFont->getLineHeight(); if (coord) { - const S32 query_x = mVisibleTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); + const S32 query_x = mVisibleTextRect.mLeft + mFont->getWidth(text, current_line_start, query - current_line_start); const S32 query_y = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; S32 query_screen_x, query_screen_y; localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); @@ -2884,17 +2891,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect S32 preedit_left = mVisibleTextRect.mLeft; if (preedit_left_position > current_line_start) { - preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); + preedit_left += mFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); } S32 preedit_right = mVisibleTextRect.mLeft; if (preedit_right_position < current_line_end) { - preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); } else { - preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start); + preedit_right += mFont->getWidth(text, current_line_start, current_line_end - current_line_start); } const S32 preedit_top = mVisibleTextRect.mTop - (current_line - first_visible_line) * line_height; @@ -2971,7 +2978,7 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) S32 LLTextEditor::getPreeditFontSize() const { - return llround((F32)mDefaultFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); + return llround((F32)mFont->getLineHeight() * LLUI::getScaleFactor().mV[VY]); } BOOL LLTextEditor::isDirty() const diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index ca7a457718..b896287619 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -64,11 +64,9 @@ public: ignore_tab, show_line_numbers, commit_on_focus_lost, -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - commit_on_return, -// [/SL:KB] show_context_menu, - enable_tooltip_paste; + enable_tooltip_paste, + auto_indent; //colors Optional default_color; @@ -165,6 +163,11 @@ public: BOOL isPristine() const; BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; } + // Autoreplace (formerly part of LLLineEditor) + typedef boost::function autoreplace_callback_t; + autoreplace_callback_t mAutoreplaceCallback; + void setAutoreplaceCallback(autoreplace_callback_t cb) { mAutoreplaceCallback = cb; } + // // Text manipulation // @@ -212,6 +215,8 @@ public: void setShowContextMenu(bool show) { mShowContextMenu = show; } bool getShowContextMenu() const { return mShowContextMenu; } + void setPassDelete(BOOL b) { mPassDelete = b; } + protected: void showContextMenu(S32 x, S32 y); void drawPreeditMarker(); @@ -224,8 +229,8 @@ protected: S32 indentLine( S32 pos, S32 spaces ); void unindentLineBeforeCloseBrace(); + virtual BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleNavigationKey(const KEY key, const MASK mask); - BOOL handleSpecialKey(const KEY key, const MASK mask); BOOL handleSelectionKey(const KEY key, const MASK mask); BOOL handleControlKey(const KEY key, const MASK mask); @@ -289,6 +294,7 @@ protected: LLUIColor mDefaultColor; BOOL mShowLineNumbers; + bool mAutoIndent; /*virtual*/ void updateSegments(); void updateLinkSegments(); @@ -328,15 +334,13 @@ private: BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces BOOL mCommitOnFocusLost; -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-08-20 (Catznip-3.2.0a) | Added: Catznip-2.8.0a - BOOL mCommitOnReturn; -// [/SL:KB] BOOL mTakesFocus; BOOL mAllowEmbeddedItems; bool mShowContextMenu; bool mParseOnTheFly; bool mEnableTooltipPaste; + bool mPassDelete; LLUUID mSourceID; diff --git a/indra/llui/lltoggleablemenu.h b/indra/llui/lltoggleablemenu.h index 4717b0d0ba..dfe70cbf54 100644 --- a/indra/llui/lltoggleablemenu.h +++ b/indra/llui/lltoggleablemenu.h @@ -60,6 +60,8 @@ public: // its visibility off. bool toggleVisibility(); + LLHandle getHandle() { return getDerivedHandle(); } + protected: bool mClosedByButtonClick; LLRect mButtonRect; diff --git a/indra/llui/lltoolbar.cpp b/indra/llui/lltoolbar.cpp index 8c7dc35db7..116f0ef3e7 100644 --- a/indra/llui/lltoolbar.cpp +++ b/indra/llui/lltoolbar.cpp @@ -1013,8 +1013,15 @@ void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent) void LLToolBar::createButtons() { + std::set set_flashing; + BOOST_FOREACH(LLToolBarButton* button, mButtons) { + if (button->getFlashTimer() && button->getFlashTimer()->isFlashingInProgress()) + { + set_flashing.insert(button->getCommandId().uuid()); + } + if (mButtonRemoveSignal) { (*mButtonRemoveSignal)(button); @@ -1037,6 +1044,11 @@ void LLToolBar::createButtons() { (*mButtonAddSignal)(button); } + + if (set_flashing.find(button->getCommandId().uuid()) != set_flashing.end()) + { + button->setFlashing(true); + } } mNeedsLayout = true; } @@ -1085,6 +1097,7 @@ LLToolBarButton* LLToolBar::createButton(const LLCommandId& id) button_p.image_overlay = LLUI::getUIImage(commandp->icon()); } // + button_p.button_flash_enable = commandp->isFlashingAllowed(); button_p.overwriteFrom(mButtonParams[mButtonType]); LLToolBarButton* button = LLUICtrlFactory::create(button_p); @@ -1211,10 +1224,9 @@ BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, // Convert drag position into insert position and rank if (!isReadOnly() && handled && !drop) { - LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; - LLAssetType::EType type = inv_item->getType(); - if (type == LLAssetType::AT_WIDGET) + if (cargo_type == DAD_WIDGET) { + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; LLCommandId dragged_command(inv_item->getUUID()); int orig_rank = getRankFromPosition(dragged_command); mDragRank = getRankFromPosition(x, y); diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 7f1566d64a..f52a3b3323 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -288,7 +288,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p) mTextBox->setText(p.message()); } - S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth()); + S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1); S32 text_height = mTextBox->getTextPixelHeight(); mTextBox->reshape(text_width, text_height); if (mInfoButton) diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index df075725ad..3ba837763a 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -76,6 +76,7 @@ std::map gTranslation; std::list gUntranslated; /*static*/ LLUI::settings_map_t LLUI::sSettingGroups; /*static*/ LLUIAudioCallback LLUI::sAudioCallback = NULL; +/*static*/ LLUIAudioCallback LLUI::sDeferredAudioCallback = NULL; /*static*/ LLWindow* LLUI::sWindow = NULL; /*static*/ LLView* LLUI::sRootView = NULL; /*static*/ BOOL LLUI::sDirty = FALSE; @@ -101,19 +102,23 @@ static LLDefaultChildRegistry::Register register_toolbar_vert // Functions // +// CHUI merge check +LLUUID find_ui_sound(const char * namep, BOOL forceSound) //LL post CHUI // UI Sounds preview -// void make_ui_sound(const char* namep) -void make_ui_sound(const char* namep, BOOL forceSound) +// void make_ui_sound(const char* namep) //LL pre CHUI, LL moved make_ui_sound below and renamed this segment +// void make_ui_sound(const char* namep, BOOL forceSound) //Old +// LLUUID find_ui_sound(const char * namep, BOOL forceSound) //proposed // UI Sounds preview { std::string name = ll_safe_string(namep); + LLUUID uuid = LLUUID(NULL); if (!LLUI::sSettingGroups["config"]->controlExists(name)) { llwarns << "tried to make UI sound for unknown sound name: " << name << llendl; } else { - LLUUID uuid(LLUI::sSettingGroups["config"]->getString(name)); + uuid = LLUUID(LLUI::sSettingGroups["config"]->getString(name)); if (uuid.isNull()) { if (LLUI::sSettingGroups["config"]->getString(name) == LLUUID::null.asString()) @@ -127,7 +132,6 @@ void make_ui_sound(const char* namep, BOOL forceSound) { llwarns << "UI sound named: " << name << " does not translate to a valid uuid" << llendl; } - } else if (LLUI::sAudioCallback != NULL) { @@ -143,18 +147,38 @@ void make_ui_sound(const char* namep, BOOL forceSound) { llinfos << "UI sound name: " << name << llendl; } - LLUI::sAudioCallback(uuid); } } + + return uuid; +} + +void make_ui_sound(const char* namep) +{ + LLUUID soundUUID = find_ui_sound(namep); + if(soundUUID.notNull()) + { + LLUI::sAudioCallback(soundUUID); + } +} + +void make_ui_sound_deferred(const char* namep) +{ + LLUUID soundUUID = find_ui_sound(namep); + if(soundUUID.notNull()) + { + LLUI::sDeferredAudioCallback(soundUUID); + } } void LLUI::initClass(const settings_map_t& settings, LLImageProviderInterface* image_provider, LLUIAudioCallback audio_callback, + LLUIAudioCallback deferred_audio_callback, const LLVector2* scale_factor, const std::string& language) { - LLRender2D::initClass(image_provider, scale_factor); + LLRender2D::initClass(image_provider,scale_factor); sSettingGroups = settings; if ((get_ptr_in_map(sSettingGroups, std::string("config")) == NULL) || @@ -165,6 +189,7 @@ void LLUI::initClass(const settings_map_t& settings, } sAudioCallback = audio_callback; + sDeferredAudioCallback = deferred_audio_callback; sWindow = NULL; // set later in startup LLFontGL::sShadowColor = LLUIColorTable::instance().getColor("ColorDropShadow"); @@ -528,7 +553,7 @@ const LLView* LLUI::resolvePath(const LLView* context, const std::string& path) namespace LLInitParam { - ParamValue >::ParamValue(const LLUIColor& color) + ParamValue::ParamValue(const LLUIColor& color) : super_t(color), red("red"), green("green"), @@ -539,7 +564,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() { if (control.isProvided() && !control().empty()) { @@ -551,7 +576,7 @@ namespace LLInitParam } } - void ParamValue >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue::updateBlockFromValue(bool make_block_authoritative) { LLColor4 color = getValue(); red.set(color.mV[VRED], make_block_authoritative); @@ -567,7 +592,7 @@ namespace LLInitParam && !(b->getFontDesc() < a->getFontDesc()); } - ParamValue >::ParamValue(const LLFontGL* fontp) + ParamValue::ParamValue(const LLFontGL* fontp) : super_t(fontp), name("name"), size("size"), @@ -581,7 +606,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() { const LLFontGL* res_fontp = LLFontGL::getFontByName(name); if (res_fontp) @@ -604,7 +629,7 @@ namespace LLInitParam } } - void ParamValue >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue::updateBlockFromValue(bool make_block_authoritative) { if (getValue()) { @@ -614,7 +639,7 @@ namespace LLInitParam } } - ParamValue >::ParamValue(const LLRect& rect) + ParamValue::ParamValue(const LLRect& rect) : super_t(rect), left("left"), top("top"), @@ -626,7 +651,7 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() { LLRect rect; @@ -690,7 +715,7 @@ namespace LLInitParam updateValue(rect); } - void ParamValue >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue::updateBlockFromValue(bool make_block_authoritative) { // because of the ambiguity in specifying a rect by position and/or dimensions // we use the lowest priority pairing so that any valid pairing in xui @@ -707,7 +732,7 @@ namespace LLInitParam height.set(value.getHeight(), make_block_authoritative); } - ParamValue >::ParamValue(const LLCoordGL& coord) + ParamValue::ParamValue(const LLCoordGL& coord) : super_t(coord), x("x"), y("y") @@ -715,12 +740,12 @@ namespace LLInitParam updateBlockFromValue(false); } - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() { updateValue(LLCoordGL(x, y)); } - void ParamValue >::updateBlockFromValue(bool make_block_authoritative) + void ParamValue::updateBlockFromValue(bool make_block_authoritative) { x.set(getValue().mX, make_block_authoritative); y.set(getValue().mY, make_block_authoritative); diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 7c3a917d85..39c6c77fb3 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -53,11 +53,11 @@ class LLWindow; class LLView; class LLHelp; - // UI Sounds preview // void make_ui_sound(const char* name); -void make_ui_sound(const char* name, BOOL forceSound = FALSE); +void make_ui_sound(const char* name, bool forcesound = false); // UI Sounds preview +void make_ui_sound_deferred(const char * name); // CHUI Merge check class LLImageProviderInterface; @@ -194,6 +194,7 @@ public: static void initClass(const settings_map_t& settings, LLImageProviderInterface* image_provider, LLUIAudioCallback audio_callback = NULL, + LLUIAudioCallback deferred_audio_callback = NULL, const LLVector2 *scale_factor = NULL, const std::string& language = LLStringUtil::null); static void cleanupClass(); @@ -282,6 +283,7 @@ public: // static settings_map_t sSettingGroups; static LLUIAudioCallback sAudioCallback; + static LLUIAudioCallback sDeferredAudioCallback; static LLWindow* sWindow; static LLView* sRootView; static LLHelp* sHelpImpl; @@ -344,12 +346,11 @@ public: // this avoids a MSVC bug where non-referenced static members are "optimized" away // even if their constructors have side effects - void reference() + S32 reference() { -#ifdef LL_WINDOWS S32 dummy; dummy = 0; -#endif /*LL_WINDOWS*/ + return dummy; } }; @@ -417,7 +418,7 @@ public: namespace LLInitParam { template<> - class ParamValue > + class ParamValue : public CustomParamValue { typedef CustomParamValue super_t; @@ -436,7 +437,7 @@ namespace LLInitParam }; template<> - class ParamValue > + class ParamValue : public CustomParamValue { typedef CustomParamValue super_t; @@ -454,7 +455,7 @@ namespace LLInitParam }; template<> - class ParamValue > + class ParamValue : public CustomParamValue { typedef CustomParamValue super_t; @@ -494,7 +495,7 @@ namespace LLInitParam template<> - class ParamValue > + class ParamValue : public CustomParamValue { typedef CustomParamValue super_t; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 9bbac86f1e..a0db227e46 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -257,13 +257,13 @@ const LLInitParam::BaseBlock& get_empty_param_block() // adds a widget and its param block to various registries //static -void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& tag) +void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const std::type_info* param_block_type, const std::string& name) { // associate parameter block type with template .xml file - std::string* existing_tag = LLWidgetNameRegistry::instance().getValue(param_block_type); - if (existing_tag != NULL) + std::string* existing_name = LLWidgetNameRegistry::instance().getValue(param_block_type); + if (existing_name != NULL) { - if(*existing_tag != tag) + if(*existing_name != name) { std::cerr << "Duplicate entry for T::Params, try creating empty param block in derived classes that inherit T::Params" << std::endl; // forcing crash here @@ -272,19 +272,15 @@ void LLUICtrlFactory::registerWidget(const std::type_info* widget_type, const st } else { - // widget already registered + // widget already registered this name return; } } - LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, tag); + + LLWidgetNameRegistry::instance().defaultRegistrar().add(param_block_type, name); //FIXME: comment this in when working on schema generation //LLWidgetTypeRegistry::instance().defaultRegistrar().add(tag, widget_type); //LLDefaultParamBlockRegistry::instance().defaultRegistrar().add(widget_type, &get_empty_param_block); } -//static -const std::string* LLUICtrlFactory::getWidgetTag(const std::type_info* widget_type) -{ - return LLWidgetNameRegistry::instance().getValue(widget_type); -} diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index f6971261d7..876bb5ef46 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -98,7 +98,7 @@ private: ParamDefaults() { // look up template file for this param block... - const std::string* param_block_tag = getWidgetTag(&typeid(PARAM_BLOCK)); + const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK)); if (param_block_tag) { // ...and if it exists, back fill values using the most specific template first PARAM_BLOCK params; @@ -132,7 +132,6 @@ public: template static const typename T::Params& getDefaultParams() { - //#pragma message("Generating ParamDefaults") return ParamDefaults::instance().get(); } @@ -285,8 +284,6 @@ private: } - static const std::string* getWidgetTag(const std::type_info* widget_type); - // this exists to get around dependency on llview static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group); diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp index 680c42607f..c9004eb7ad 100644 --- a/indra/llui/llurlaction.cpp +++ b/indra/llui/llurlaction.cpp @@ -24,7 +24,6 @@ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - #include "linden_common.h" #include "llurlaction.h" @@ -32,6 +31,7 @@ #include "llwindow.h" #include "llurlregistry.h" + // global state for the callback functions LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback; LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback; @@ -158,6 +158,35 @@ void LLUrlAction::showProfile(std::string url) } } +std::string LLUrlAction::getUserID(std::string url) +{ + LLURI uri(url); + LLSD path_array = uri.pathArray(); + std::string id_str; + if (path_array.size() == 4) + { + id_str = path_array.get(2).asString(); + } + return id_str; +} + +void LLUrlAction::sendIM(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/im"); + } +} + +void LLUrlAction::addFriend(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/requestfriend"); + } +} // FSSlurlCommand support LLUUID LLUrlAction::extractUuidFromSlurl(const std::string& url) @@ -176,4 +205,4 @@ LLUUID LLUrlAction::extractUuidFromSlurl(const std::string& url) return LLUUID::null; } -// FSSlurlCommand support \ No newline at end of file +// FSSlurlCommand support diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h index 7b9f3efecc..79348fe0ed 100644 --- a/indra/llui/llurlaction.h +++ b/indra/llui/llurlaction.h @@ -76,6 +76,9 @@ public: /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile static void showProfile(std::string url); + static std::string getUserID(std::string url); + static void sendIM(std::string url); + static void addFriend(std::string url); // FSSlurlCommand support static LLUUID extractUuidFromSlurl(const std::string& url); diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 106992d1bc..c29f90218f 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -350,7 +350,8 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // -LLUrlEntryAgent::LLUrlEntryAgent() +LLUrlEntryAgent::LLUrlEntryAgent() : + mAvatarNameCacheConnection() { mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); @@ -381,7 +382,9 @@ void LLUrlEntryAgent::callObservers(const std::string &id, void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { - std::string label = av_name.getCompleteName(); + mAvatarNameCacheConnection.disconnect(); + + std::string label = av_name.getCompleteName(); // received the agent name from the server - tell our observers callObservers(id.asString(), label, mIcon); @@ -466,9 +469,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa } else { - LLAvatarNameCache::get(agent_id, - boost::bind(&LLUrlEntryAgent::onAvatarNameCache, - this, _1, _2)); + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgent::onAvatarNameCache, this, _1, _2)); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -525,12 +530,15 @@ std::string LLUrlEntryAgent::getIcon(const std::string &url) // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) // -LLUrlEntryAgentName::LLUrlEntryAgentName() +LLUrlEntryAgentName::LLUrlEntryAgentName() : + mAvatarNameCacheConnection() {} void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name) { + mAvatarNameCacheConnection.disconnect(); + std::string label = getName(av_name); // received the agent name from the server - tell our observers callObservers(id.asString(), label, mIcon); @@ -564,9 +572,11 @@ std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLab } else { - LLAvatarNameCache::get(agent_id, - boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache, - this, _1, _2)); + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + mAvatarNameCacheConnection = LLAvatarNameCache::get(agent_id, boost::bind(&LLUrlEntryAgentName::onAvatarNameCache, this, _1, _2)); addObserver(agent_id_string, url, cb); return LLTrans::getString("LoadingData"); } @@ -607,7 +617,7 @@ LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) { - return avatar_name.mDisplayName; + return avatar_name.getDisplayName(); } // @@ -623,7 +633,7 @@ LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) { - return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername; + return avatar_name.getAccountName(); } // [RLVa:KB] - Checked: 2010-11-01 (RLVa-1.2.2a) | Added: RLVa-1.2.2a diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h index 80e744fd07..1c4e8410f4 100644 --- a/indra/llui/llurlentry.h +++ b/indra/llui/llurlentry.h @@ -171,6 +171,13 @@ class LLUrlEntryAgent : public LLUrlEntryBase { public: LLUrlEntryAgent(); + ~LLUrlEntryAgent() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + } /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ std::string getIcon(const std::string &url); /*virtual*/ std::string getTooltip(const std::string &string) const; @@ -181,6 +188,7 @@ protected: /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + boost::signals2::connection mAvatarNameCacheConnection; }; /// @@ -192,6 +200,13 @@ class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::track { public: LLUrlEntryAgentName(); + ~LLUrlEntryAgentName() + { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + } /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); /*virtual*/ LLStyle::Params getStyle() const; protected: @@ -199,6 +214,7 @@ protected: virtual std::string getName(const LLAvatarName& avatar_name) = 0; private: void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + boost::signals2::connection mAvatarNameCacheConnection; }; diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 185f115220..810f2ac02a 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -55,6 +55,8 @@ #include "lltexteditor.h" #include "lltextbox.h" +static const S32 LINE_HEIGHT = 15; + S32 LLView::sDepth = 0; bool LLView::sDebugRects = false; bool LLView::sDebugRectsShowNames = true; @@ -394,7 +396,7 @@ void LLView::removeChild(LLView* child) } else { - llwarns << child->getName() << "is not a child of " << getName() << llendl; + llwarns << "\"" << child->getName() << "\" is not a child of " << getName() << llendl; } updateBoundingRect(); } @@ -924,13 +926,12 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) // allow "scrubbing" over ui by showing next tooltip immediately // if previous one was still visible F32 timeout = LLToolTipMgr::instance().toolTipVisible() - ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" ) - : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); + ? LLUI::sSettingGroups["config"]->getF32( "ToolTipFastDelay" ) + : LLUI::sSettingGroups["config"]->getF32( "ToolTipDelay" ); LLToolTipMgr::instance().show(LLToolTip::Params() - .message(tooltip) - .sticky_rect(calcScreenRect()) - .delay_time(timeout)); - + .message(tooltip) + .sticky_rect(calcScreenRect()) + .delay_time(timeout)); handled = TRUE; } @@ -1255,11 +1256,24 @@ void LLView::drawDebugRect() && preview_iter == sPreviewHighlightedElements.end() && sDebugRectsShowNames) { - //char temp[256]; S32 x, y; gGL.color4fv( border_color.mV ); - x = debug_rect.getWidth()/2; - y = debug_rect.getHeight()/2; + + x = debug_rect.getWidth() / 2; + + S32 rect_height = debug_rect.getHeight(); + S32 lines = rect_height / LINE_HEIGHT + 1; + + S32 depth = 0; + LLView * viewp = this; + while (NULL != viewp) + { + viewp = viewp->getParent(); + depth++; + } + + y = rect_height - LINE_HEIGHT * (depth % lines + 1); + std::string debug_text = llformat("%s (%d x %d)", getName().c_str(), debug_rect.getWidth(), debug_rect.getHeight()); LLFontGL::getFontSansSerifSmall()->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color, diff --git a/indra/llui/llview.h b/indra/llui/llview.h index a69867854f..2a49584c4d 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -67,7 +67,6 @@ const BOOL NOT_MOUSE_OPAQUE = FALSE; const U32 GL_NAME_UI_RESERVED = 2; - // maintains render state during traversal of UI tree class LLViewDrawContext { diff --git a/indra/llui/llxuiparser.cpp b/indra/llui/llxuiparser.cpp index afc76024d1..3ad5ad7d42 100644 --- a/indra/llui/llxuiparser.cpp +++ b/indra/llui/llxuiparser.cpp @@ -42,7 +42,7 @@ #include #include "lluicolor.h" - +#include "v3math.h" using namespace BOOST_SPIRIT_CLASSIC_NS; const S32 MAX_STRING_ATTRIBUTE_SIZE = 40; @@ -79,7 +79,6 @@ struct Occurs : public LLInitParam::Block {} }; - typedef enum { USE_REQUIRED, @@ -101,14 +100,23 @@ namespace LLInitParam struct Element; struct Group; -struct Choice; struct Sequence; -struct Any; + +struct All : public LLInitParam::Block +{ + Multiple< Lazy > elements; + + All() + : elements("element") + { + maxOccurs = 1; + } +}; struct Attribute : public LLInitParam::Block { - Mandatory name; - Mandatory type; + Mandatory name, + type; Mandatory use; Attribute() @@ -127,24 +135,13 @@ struct Any : public LLInitParam::Block {} }; -struct All : public LLInitParam::Block -{ - Multiple< Lazy > elements; - - All() - : elements("element") - { - maxOccurs = 1; - } -}; - struct Choice : public LLInitParam::ChoiceBlock { - Alternative< Lazy > element; - Alternative< Lazy > group; - Alternative< Lazy > choice; - Alternative< Lazy > sequence; - Alternative< Lazy > any; + Alternative< Lazy > element; + Alternative< Lazy > group; + Alternative< Lazy > choice; + Alternative< Lazy > sequence; + Alternative< Lazy > any; Choice() : element("element"), @@ -158,11 +155,11 @@ struct Choice : public LLInitParam::ChoiceBlock struct Sequence : public LLInitParam::ChoiceBlock { - Alternative< Lazy > element; - Alternative< Lazy > group; - Alternative< Lazy > choice; - Alternative< Lazy > sequence; - Alternative< Lazy > any; + Alternative< Lazy > element; + Alternative< Lazy > group; + Alternative< Lazy > choice; + Alternative< Lazy > sequence; + Alternative< Lazy > any; }; struct GroupContents : public LLInitParam::ChoiceBlock @@ -247,7 +244,7 @@ struct ComplexType : public LLInitParam::Block Optional mixed; Multiple attribute; - Multiple< Lazy > elements; + Multiple< Lazy > elements; ComplexType() : name("name"), @@ -313,7 +310,6 @@ public: setNameSpace(ns); }; } - }; // @@ -625,7 +621,7 @@ void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& p nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd"); // add to front of schema - mSchemaNode->addChild(nodep, mSchemaNode); + mSchemaNode->addChild(nodep); } for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems(); @@ -670,6 +666,7 @@ LLXUIParser::LLXUIParser() registerParserFuncs(readS32Value, writeS32Value); registerParserFuncs(readF32Value, writeF32Value); registerParserFuncs(readF64Value, writeF64Value); + registerParserFuncs(readVector3Value, writeVector3Value); registerParserFuncs(readColor4Value, writeColor4Value); registerParserFuncs(readUIColorValue, writeUIColorValue); registerParserFuncs(readUUIDValue, writeUUIDValue); @@ -880,16 +877,24 @@ LLXMLNodePtr LLXUIParser::getNode(name_stack_t& stack) it = next_it) { ++next_it; + bool force_new_node = false; + if (it->first.empty()) { it->second = false; continue; } + if (next_it != stack.end() && next_it->first.empty() && next_it->second) + { + force_new_node = true; + } + + out_nodes_t::iterator found_it = mOutNodes.find(it->first); // node with this name not yet written - if (found_it == mOutNodes.end() || it->second) + if (found_it == mOutNodes.end() || it->second || force_new_node) { // make an attribute if we are the last element on the name stack bool is_attribute = next_it == stack.end(); @@ -1144,6 +1149,31 @@ bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, name_stack_ return false; } +bool LLXUIParser::readVector3Value(Parser& parser, void* val_ptr) +{ + LLXUIParser& self = static_cast(parser); + LLVector3* vecp = (LLVector3*)val_ptr; + if(self.mCurReadNode->getFloatValue(3, vecp->mV) >= 3) + { + return true; + } + + return false; +} + +bool LLXUIParser::writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t& stack) +{ + LLXUIParser& self = static_cast(parser); + LLXMLNodePtr node = self.getNode(stack); + if (node.notNull()) + { + LLVector3 vector = *((LLVector3*)val_ptr); + node->setFloatValue(3, vector.mV); + return true; + } + return false; +} + bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr) { LLXUIParser& self = static_cast(parser); diff --git a/indra/llui/llxuiparser.h b/indra/llui/llxuiparser.h index d7cd256967..e48663e5cc 100644 --- a/indra/llui/llxuiparser.h +++ b/indra/llui/llxuiparser.h @@ -127,6 +127,7 @@ private: static bool readS32Value(Parser& parser, void* val_ptr); static bool readF32Value(Parser& parser, void* val_ptr); static bool readF64Value(Parser& parser, void* val_ptr); + static bool readVector3Value(Parser& parser, void* val_ptr); static bool readColor4Value(Parser& parser, void* val_ptr); static bool readUIColorValue(Parser& parser, void* val_ptr); static bool readUUIDValue(Parser& parser, void* val_ptr); @@ -144,6 +145,7 @@ private: static bool writeS32Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeF32Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeF64Value(Parser& parser, const void* val_ptr, name_stack_t&); + static bool writeVector3Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeColor4Value(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeUIColorValue(Parser& parser, const void* val_ptr, name_stack_t&); static bool writeUUIDValue(Parser& parser, const void* val_ptr, name_stack_t&); diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index 8749ea90bd..7151139c77 100644 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -55,11 +55,6 @@ LLAvatarNameCache::callback_connection_t LLAvatarNameCache::get(const LLUUID& ag return connection; } -bool LLAvatarNameCache::useDisplayNames() -{ - return false; -} - // // Stub implementation for LLCacheName // @@ -115,14 +110,14 @@ LLStyle::Params::Params() namespace LLInitParam { - ParamValue >::ParamValue(const LLUIColor& color) + ParamValue::ParamValue(const LLUIColor& color) : super_t(color) {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} bool ParamCompare::equals(const LLFontGL* a, const LLFontGL* b) @@ -130,14 +125,14 @@ namespace LLInitParam return false; } - ParamValue >::ParamValue(const LLFontGL* fontp) + ParamValue::ParamValue(const LLFontGL* fontp) : super_t(fontp) {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} void TypeValues::declareValues() @@ -149,10 +144,10 @@ namespace LLInitParam void TypeValues::declareValues() {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 6c51024d2c..c3f0e92cb0 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -31,7 +31,7 @@ #include "llurlentry_stub.cpp" #include "lltut.h" #include "../lluicolortable.h" -#include "lluiimage.h" +#include "../llrender/lluiimage.h" #include diff --git a/indra/llui/tests/llurlmatch_test.cpp b/indra/llui/tests/llurlmatch_test.cpp index 88a2cfb1e0..55c1efefef 100644 --- a/indra/llui/tests/llurlmatch_test.cpp +++ b/indra/llui/tests/llurlmatch_test.cpp @@ -28,7 +28,7 @@ #include "linden_common.h" #include "../llurlmatch.h" -#include "lluiimage.h" +#include "../llrender/lluiimage.h" #include "lltut.h" // link seams @@ -63,14 +63,14 @@ S32 LLUIImage::getHeight() const namespace LLInitParam { - ParamValue >::ParamValue(const LLUIColor& color) + ParamValue::ParamValue(const LLUIColor& color) : super_t(color) {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} bool ParamCompare::equals(const LLFontGL* a, const LLFontGL* b) @@ -79,14 +79,14 @@ namespace LLInitParam } - ParamValue >::ParamValue(const LLFontGL* fontp) + ParamValue::ParamValue(const LLFontGL* fontp) : super_t(fontp) {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} void TypeValues::declareValues() @@ -98,10 +98,10 @@ namespace LLInitParam void TypeValues::declareValues() {} - void ParamValue >::updateValueFromBlock() + void ParamValue::updateValueFromBlock() {} - void ParamValue >::updateBlockFromValue(bool) + void ParamValue::updateBlockFromValue(bool) {} bool ParamCompare::equals( diff --git a/indra/llvfs/CMakeLists.txt b/indra/llvfs/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 85b3da0bb1..2725c28198 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -90,7 +90,8 @@ LLDir::LLDir() mCAFile(""), mTempDir(""), mDirDelimiter("/"), // fallback to forward slash if not overridden - mLanguage("en") + mLanguage("en"), + mUserName("undefined") { } @@ -355,6 +356,11 @@ const std::string &LLDir::getLLPluginDir() const return mLLPluginDir; } +const std::string &LLDir::getUserName() const +{ + return mUserName; +} + static std::string ELLPathToString(ELLPath location) { typedef std::map ELLPathMap; @@ -862,6 +868,12 @@ void LLDir::setChatLogsDir(const std::string &path) #ifdef OPENSIM void LLDir::setPerAccountChatLogsDir(const std::string &username, const std::string &gridname) #else + +void LLDir::updatePerAccountChatLogsDir() +{ + mPerAccountChatLogsDir = add(getChatLogsDir(), mUserName); +} + void LLDir::setPerAccountChatLogsDir(const std::string &username) #endif // OPENSIM // @@ -881,7 +893,8 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) LLStringUtil::replaceChar(gridlower, ' ', '_'); #endif // OPENSIM // - mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); + mUserName = userlower; + updatePerAccountChatLogsDir(); // Seperate user directories per grid on OS build #ifdef OPENSIM if (!gridname.empty() && gridlower != "second_life") @@ -896,7 +909,6 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username) { llerrs << "NULL name for LLDir::setPerAccountChatLogsDir" << llendl; } - } //void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) @@ -930,7 +942,8 @@ void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& the mSkinDir = getSkinBaseDir(); append(mSkinDir, skin_folder); // Next level of generality is a skin installed with the viewer. - addSearchSkinDir(mSkinDir); + addSearchSkinDir(mSkinDir); mUserName = userlower; + updatePerAccountChatLogsDir(); // [SL:KB] - Patch: Viewer-Skins | Checked: 2012-12-26 (Catznip-3.4) if (!theme_folder.empty()) diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index ab37b25341..4bda944473 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -140,6 +140,7 @@ class LLDir const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin const std::string getSkinBaseDir() const; // folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins const std::string &getLLPluginDir() const; // Directory containing plugins and plugin shell + const std::string &getUserName() const; // Expanded filename std::string getExpandedFilename(ELLPath location, const std::string &filename) const; @@ -235,6 +236,7 @@ class LLDir // [/SL:KB] virtual std::string getLanguage() const; virtual bool setCacheDir(const std::string &path); + virtual void updatePerAccountChatLogsDir(); virtual void dumpCurrentDirectories(); @@ -298,6 +300,7 @@ protected: std::vector mSearchSkinDirs; std::string mLanguage; // Current viewer language std::string mLLPluginDir; // Location for plugins and plugin shell + std::string mUserName; // Current user name }; void dir_exists_or_crash(const std::string &dir_name); diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 300245ebc3..93b9d36939 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -257,8 +257,6 @@ std::vector LLWindow::getDynamicFallbackFontList() return LLWindowWin32::getDynamicFallbackFontList(); #elif LL_DARWIN return LLWindowMacOSX::getDynamicFallbackFontList(); -#elif LL_MESA_HEADLESS - return std::vector(); #elif LL_SDL return LLWindowSDL::getDynamicFallbackFontList(); #else @@ -389,28 +387,38 @@ LLWindow* LLWindowManager::createWindow( BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, + BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples) { LLWindow* new_window; + if (use_gl) + { #if LL_MESA_HEADLESS - new_window = new LLWindowMesaHeadless(callbacks, - title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, ignore_pixel_depth); + new_window = new LLWindowMesaHeadless(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); #elif LL_SDL - new_window = new LLWindowSDL(callbacks, - title, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); + new_window = new LLWindowSDL(callbacks, + title, x, y, width, height, flags, + fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); #elif LL_WINDOWS - new_window = new LLWindowWin32(callbacks, - title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); + new_window = new LLWindowWin32(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); #elif LL_DARWIN - new_window = new LLWindowMacOSX(callbacks, - title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); + new_window = new LLWindowMacOSX(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); #endif + } + else + { + new_window = new LLWindowHeadless(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); + } if (FALSE == new_window->isValid()) { diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 888f4a9a6d..c5f59b216e 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -276,6 +276,7 @@ public: BOOL fullscreen = FALSE, BOOL clearBg = FALSE, BOOL disable_vsync = TRUE, + BOOL use_gl = TRUE, BOOL ignore_pixel_depth = FALSE, U32 fsaa_samples = 0); static BOOL destroyWindow(LLWindow* window); diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp index dbdb40f5b9..e6e6bc67ff 100644 --- a/indra/llwindow/llwindowheadless.cpp +++ b/indra/llwindow/llwindowheadless.cpp @@ -35,7 +35,7 @@ // LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { // Initialize a headless keyboard. diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 72f9684ca3..1f767f4c97 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -96,7 +96,7 @@ public: S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); virtual ~LLWindowHeadless(); private: diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index f49c1e010c..ca73e8b246 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -212,7 +212,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(NULL, fullscreen, flags) @@ -230,6 +230,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, gKeyboard = new LLKeyboardMacOSX(); gKeyboard->setCallbacks(callbacks); + // Ignore use_gl for now, only used for drones on PC mWindow = NULL; mContext = NULL; mPixelFormat = NULL; diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 73b0323ed7..dbfd03132e 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -124,7 +124,7 @@ public: protected: LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowMacOSX(); diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp index 2b668d3fc4..11c22ac94e 100644 --- a/indra/llwindow/llwindowmesaheadless.cpp +++ b/indra/llwindow/llwindowmesaheadless.cpp @@ -41,25 +41,28 @@ U16 *gMesaBuffer = NULL; LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { - llinfos << "MESA Init" << llendl; - mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); - - /* Allocate the image buffer */ - mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; - llassert(mMesaBuffer); - - gMesaBuffer = (U16*)mMesaBuffer; - - /* Bind the buffer to the context and make it current */ - if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) + if (use_gl) { - llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; - } + llinfos << "MESA Init" << llendl; + mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); - llverify(gGLManager.initGL()); + /* Allocate the image buffer */ + mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; + llassert(mMesaBuffer); + + gMesaBuffer = (U16*)mMesaBuffer; + + /* Bind the buffer to the context and make it current */ + if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) + { + llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; + } + + llverify(gGLManager.initGL()); + } } diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index c8d2bf2824..8f70aee4f6 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -51,7 +51,6 @@ public: /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;}; /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;}; /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;}; - /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;}; /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;}; /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;}; /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;}; @@ -98,7 +97,7 @@ public: LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); ~LLWindowMesaHeadless(); private: diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 310c9c0c74..ef57371d8f 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -188,7 +188,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), Lock_Display(NULL), @@ -199,6 +199,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, gKeyboard->setCallbacks(callbacks); // Note that we can't set up key-repeat until after SDL has init'd video + // Ignore use_gl for now, only used for drones on PC mWindow = NULL; mNeedsResize = FALSE; mOverrideAspectRatio = 0.f; @@ -1651,35 +1652,53 @@ void check_vm_bloat() { #if LL_LINUX // watch our own VM and RSS sizes, warn if we bloated rapidly - FILE *fp = fopen("/proc/self/stat", "r"); + static const std::string STATS_FILE = "/proc/self/stat"; + FILE *fp = fopen(STATS_FILE.c_str(), "r"); if (fp) { static long long last_vm_size = 0; static long long last_rss_size = 0; const long long significant_vm_difference = 250 * 1024*1024; const long long significant_rss_difference = 50 * 1024*1024; + long long this_vm_size = 0; + long long this_rss_size = 0; + ssize_t res; size_t dummy; - char *ptr; - size_t delim_result = 0; - for (int i=0; i<22 && delim_result > -1; ++i) // parse past the values we don't want + char *ptr = NULL; + for (int i=0; i<22; ++i) // parse past the values we don't want { - ptr = NULL; - delim_result = getdelim(&ptr, &dummy, ' ', fp); + res = getdelim(&ptr, &dummy, ' ', fp); + if (-1 == res) + { + llwarns << "Unable to parse " << STATS_FILE << llendl; + goto finally; + } free(ptr); + ptr = NULL; } // 23rd space-delimited entry is vsize - ptr = NULL; - delim_result = getdelim(&ptr, &dummy, ' ', fp); + res = getdelim(&ptr, &dummy, ' ', fp); llassert(ptr); - long long this_vm_size = atoll(ptr); + if (-1 == res) + { + llwarns << "Unable to parse " << STATS_FILE << llendl; + goto finally; + } + this_vm_size = atoll(ptr); free(ptr); + ptr = NULL; // 24th space-delimited entry is RSS - ptr = NULL; - delim_result = getdelim(&ptr, &dummy, ' ', fp); + res = getdelim(&ptr, &dummy, ' ', fp); llassert(ptr); - long long this_rss_size = getpagesize() * atoll(ptr); + if (-1 == res) + { + llwarns << "Unable to parse " << STATS_FILE << llendl; + goto finally; + } + this_rss_size = getpagesize() * atoll(ptr); free(ptr); + ptr = NULL; llinfos << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << llendl; @@ -1712,6 +1731,12 @@ void check_vm_bloat() last_rss_size = this_rss_size; last_vm_size = this_vm_size; +finally: + if (NULL != ptr) + { + free(ptr); + ptr = NULL; + } fclose(fp); } #endif // LL_LINUX diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index e29498ec09..2ce19036ed 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -150,7 +150,7 @@ public: protected: LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowSDL(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 20f542f707..dae8315dd9 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -362,7 +362,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, + BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags) diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 16c1919f04..234f2a247b 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -122,7 +122,7 @@ public: protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowWin32(); diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 38eb6a8a30..0c720fffbb 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -147,13 +147,15 @@ LLXMLNodePtr LLXMLNode::deepCopy() for (LLXMLChildList::iterator iter = mChildren->map.begin(); iter != mChildren->map.end(); ++iter) { - newnode->addChild(iter->second->deepCopy()); + LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); + newnode->addChild(temp_ptr_for_gcc); } } for (LLXMLAttribList::iterator iter = mAttributes.begin(); iter != mAttributes.end(); ++iter) { - newnode->addChild(iter->second->deepCopy()); + LLXMLNodePtr temp_ptr_for_gcc(iter->second->deepCopy()); + newnode->addChild(temp_ptr_for_gcc); } return newnode; @@ -259,7 +261,7 @@ BOOL LLXMLNode::removeChild(LLXMLNode *target_child) return FALSE; } -void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) +void LLXMLNode::addChild(LLXMLNodePtr& new_child) { if (new_child->mParent != NULL) { @@ -273,6 +275,11 @@ void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) new_child->mParent = this; if (new_child->mIsAttribute) { + LLXMLAttribList::iterator found_it = mAttributes.find(new_child->mName); + if (found_it != mAttributes.end()) + { + removeChild(found_it->second); + } mAttributes.insert(std::make_pair(new_child->mName, new_child)); } else @@ -285,49 +292,11 @@ void LLXMLNode::addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child) } mChildren->map.insert(std::make_pair(new_child->mName, new_child)); - // if after_child is specified, it damn well better be in the list of children - // for this node. I'm not going to assert that, because it would be expensive, - // but don't specify that parameter if you didn't get the value for it from the - // list of children of this node! - if (after_child.isNull()) + if (mChildren->tail != new_child) { - if (mChildren->tail != new_child) - { - mChildren->tail->mNext = new_child; - new_child->mPrev = mChildren->tail; - mChildren->tail = new_child; - } - } - // if after_child == parent, then put new_child at beginning - else if (after_child == this) - { - // add to front of list - new_child->mNext = mChildren->head; - if (mChildren->head) - { - mChildren->head->mPrev = new_child; - mChildren->head = new_child; - } - else // no children - { - mChildren->head = new_child; - mChildren->tail = new_child; - } - } - else - { - if (after_child->mNext.notNull()) - { - // if after_child was not the last item, fix up some pointers - after_child->mNext->mPrev = new_child; - new_child->mNext = after_child->mNext; - } - new_child->mPrev = after_child; - after_child->mNext = new_child; - if (mChildren->tail == after_child) - { - mChildren->tail = new_child; - } + mChildren->tail->mNext = new_child; + new_child->mPrev = mChildren->tail; + mChildren->tail = new_child; } } @@ -343,8 +312,9 @@ LLXMLNodePtr LLXMLNode::createChild(const char* name, BOOL is_attribute) // virtual LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, BOOL is_attribute) { - LLXMLNode* ret = new LLXMLNode(name, is_attribute); + LLXMLNodePtr ret(new LLXMLNode(name, is_attribute)); ret->mID.clear(); + addChild(ret); return ret; } @@ -358,11 +328,12 @@ BOOL LLXMLNode::deleteChild(LLXMLNode *child) return FALSE; } -void LLXMLNode::setParent(LLXMLNodePtr new_parent) +void LLXMLNode::setParent(LLXMLNodePtr& new_parent) { if (new_parent.notNull()) { - new_parent->addChild(this); + LLXMLNodePtr this_ptr(this); + new_parent->addChild(this_ptr); } else { @@ -681,27 +652,6 @@ bool LLXMLNode::updateNode( return TRUE; } - -// static -LLXMLNodePtr LLXMLNode::replaceNode(LLXMLNodePtr node, LLXMLNodePtr update_node) -{ - if (!node || !update_node) - { - llwarns << "Node invalid" << llendl; - return node; - } - - LLXMLNodePtr cloned_node = update_node->deepCopy(); - node->mParent->addChild(cloned_node, node); // add after node - LLXMLNodePtr parent = node->mParent; - parent->removeChild(node); - parent->updateDefault(); - - return cloned_node; -} - - - // static bool LLXMLNode::parseFile(const std::string& filename, LLXMLNodePtr& node, LLXMLNode* defaults_tree) { @@ -1221,7 +1171,8 @@ void LLXMLNode::scrubToTree(LLXMLNode *tree) std::vector::iterator itor3; for (itor3=to_delete_list.begin(); itor3!=to_delete_list.end(); ++itor3) { - (*itor3)->setParent(NULL); + LLXMLNodePtr ptr; + (*itor3)->setParent(ptr); } } } @@ -2756,7 +2707,8 @@ void LLXMLNode::setName(LLStringTableEntry* name) mName = name; if (old_parent) { - old_parent->addChild(this); + LLXMLNodePtr this_ptr(this); + old_parent->addChild(this_ptr); } } diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h index e3da7169e7..ec486d7957 100644 --- a/indra/llxml/llxmlnode.h +++ b/indra/llxml/llxmlnode.h @@ -127,8 +127,8 @@ public: BOOL isNull(); BOOL deleteChild(LLXMLNode* child); - void addChild(LLXMLNodePtr new_child, LLXMLNodePtr after_child = LLXMLNodePtr(NULL)); - void setParent(LLXMLNodePtr new_parent); // reparent if necessary + void addChild(LLXMLNodePtr& new_child); + void setParent(LLXMLNodePtr& new_parent); // reparent if necessary // Serialization static bool parseFile( @@ -147,7 +147,6 @@ public: static bool updateNode( LLXMLNodePtr& node, LLXMLNodePtr& update_node); - static LLXMLNodePtr replaceNode(LLXMLNodePtr node, LLXMLNodePtr replacement_node); static bool getLayeredXMLNode(LLXMLNodePtr& root, const std::vector& paths); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0e7b09953f..2ee23c6b29 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -204,12 +204,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 @@ -226,16 +227,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 @@ -282,7 +291,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 @@ -352,13 +364,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 @@ -384,8 +396,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 @@ -436,10 +449,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 @@ -469,7 +481,6 @@ set(viewer_SOURCE_FILES llpanelgroupnotices.cpp llpanelgrouproles.cpp llpanelhome.cpp - llpanelimcontrolpanel.cpp llpanelland.cpp llpanellandaudio.cpp llpanellandmarkinfo.cpp @@ -480,7 +491,6 @@ set(viewer_SOURCE_FILES llpanelmaininventory.cpp llpanelmarketplaceinbox.cpp llpanelmarketplaceinboxinventory.cpp - llpanelmarketplaceoutboxinventory.cpp llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp @@ -530,11 +540,13 @@ set(viewer_SOURCE_FILES llpathfindingobject.cpp llpathfindingobjectlist.cpp llpathfindingpathtool.cpp + llpersistentnotificationstorage.cpp llphysicsmotion.cpp llphysicsshapebuilderutil.cpp llpipelinelistener.cpp llplacesinventorybridge.cpp llplacesinventorypanel.cpp + llplacesfolderview.cpp llpopupview.cpp llpostcard.cpp llpreview.cpp @@ -742,6 +754,7 @@ set(viewer_SOURCE_FILES pipeline.cpp qtoolalign.cpp quickprefs.cpp + rlvactions.cpp rlvhandler.cpp rlvhelper.cpp rlvcommon.cpp @@ -874,11 +887,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 @@ -896,16 +910,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 @@ -952,7 +974,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 @@ -1022,14 +1047,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 @@ -1054,8 +1078,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 @@ -1106,10 +1131,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 @@ -1133,7 +1157,6 @@ set(viewer_HEADER_FILES llpanelgroupnotices.h llpanelgrouproles.h llpanelhome.h - llpanelimcontrolpanel.h llpanelland.h llpanellandaudio.h llpanellandmarkinfo.h @@ -1144,7 +1167,6 @@ set(viewer_HEADER_FILES llpanelmaininventory.h llpanelmarketplaceinbox.h llpanelmarketplaceinboxinventory.h - llpanelmarketplaceoutboxinventory.h llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h @@ -1189,11 +1211,13 @@ set(viewer_HEADER_FILES llpathfindingobject.h llpathfindingobjectlist.h llpathfindingpathtool.h + llpersistentnotificationstorage.h llphysicsmotion.h llphysicsshapebuilderutil.h llpipelinelistener.h llplacesinventorybridge.h llplacesinventorypanel.h + llplacesfolderview.h llpopupview.h llpostcard.h llpreview.h @@ -1405,6 +1429,7 @@ set(viewer_HEADER_FILES pipeline.h qtoolalign.h quickprefs.h + rlvactions.h rlvdefines.h rlvhandler.h rlvhelper.h diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index ffb2fb4d19..3a81e10bcc 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -46,9 +46,10 @@ /> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 896d96ac3f..1ee942ffed 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3220,6 +3220,28 @@ Value 0 + ChatLoadGroupMaxMembers + + Comment + Max number of active members we'll show up for an unresponsive group + Persist + 1 + Type + S32 + Value + 100 + + ChatLoadGroupTimeout + + Comment + Time we give the server to send group participants before we hit the server for group info (seconds) + Persist + 1 + Type + F32 + Value + 10.0 + ChatOnlineNotification Comment @@ -3254,17 +3276,6 @@ Value 1 - ChatWindow - - Comment - Show chat in multiple windows(by default) or in one multi-tabbed window(requires restart) - Persist - 1 - Type - S32 - Value - 1 - CheesyBeacon Comment @@ -3287,6 +3298,61 @@ Value + ContextConeInAlpha + + Comment + Cone In Alpha + Persist + 0 + Type + F32 + Value + 0.0 + + ContextConeOutAlpha + + Comment + Cone Out Alpha + Persist + 0 + Type + F32 + Value + 1.0 + + ContextConeFadeTime + + Comment + Cone Fade Time + Persist + 0 + Type + F32 + Value + .08 + + ConversationHistoryPageSize + + Comment + Chat history of conversation opened from call log is displayed by pages. So this is number of entries per page. + Persist + 1 + Type + S32 + Value + 100 + + ConversationSortOrder + + Comment + Specifies sort key for conversations + Persist + 1 + Type + U32 + Value + 131073 + CloseChatOnReturn Comment @@ -5845,6 +5911,17 @@ Value 0.0 + ForceInitialCOFDelay + + Comment + Number of seconds to delay initial processing of COF contents + Persist + 1 + Type + F32 + Value + 0.0 + ForceShowGrid Comment @@ -5882,6 +5959,17 @@ Backup 0 + ForceMissingType + + Comment + Force this wearable type to be missing from COF + Persist + 1 + Type + U32 + Value + 255 + FreezeTime Comment @@ -6160,6 +6248,17 @@ Boolean Value 1 + + IMShowContentPanel + + Comment + Show Toolbar and Body Panels + Persist + 1 + Type + Boolean + Value + 1 IgnoreAllNotifications @@ -6205,7 +6304,7 @@ Type F32 Value - 0.65 + 0.95 InBandwidth @@ -8592,6 +8691,61 @@ Value 0 + NotificationConferenceIMOptions + + Comment + Specifies how the UI responds to Conference IM Notifications. + Persist + 1 + Type + String + Value + toast + + NotificationFriendIMOptions + + Comment + Specifies how the UI responds to Friend IM Notifications. + Persist + 1 + Type + String + Value + toast + + NotificationGroupChatOptions + + Comment + Specifies how the UI responds to Group Chat Notifications. + Persist + 1 + Type + String + Value + toast + + NotificationNearbyChatOptions + + Comment + Specifies how the UI responds to Nearby Chat Notifications. + Persist + 1 + Type + String + Value + toast + + NotificationNonFriendIMOptions + + Comment + Specifies how the UI responds to Non Friend IM Notifications. + Persist + 1 + Type + String + Value + toast + NotificationToastLifeTime Comment @@ -9142,6 +9296,50 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + PlaySoundIncomingVoiceCall + + Comment + Plays a sound when have an incoming voice call. + Persist + 1 + Type + Boolean + Value + 1 + + PlaySoundInventoryOffer + + Comment + Plays a sound when have an inventory offer. + Persist + 1 + Type + Boolean + Value + 0 + + PlaySoundNewConversation + + Comment + Plays a sound when have a new conversation. + Persist + 1 + Type + Boolean + Value + 1 + + PlaySoundTeleportOffer + + Comment + Plays a sound when have a teleport offer. + Persist + 1 + Type + Boolean + Value + 0 + PluginAttachDebuggerToPlugins Comment @@ -12497,7 +12695,7 @@ Change of this parameter will affect the layout of buttons in notification toast ShowScriptErrorsLocation Comment - Show script error in chat or window + Show script error in chat (0) or window (1). Persist 1 Type @@ -12637,6 +12835,39 @@ Change of this parameter will affect the layout of buttons in notification toast Value 2 + BlockPeopleSortOrder + + Comment + Specifies sort order for recent people (0 = by name, 1 = by type) + Persist + 1 + Type + U32 + Value + 0 + + CallLogSortOrder + + Comment + Specifies sort order for Call Log (0 = by name, 1 = by date) + Persist + 1 + Type + U32 + Value + 1 + + SortFriendsFirst + + Comment + Specifies whether friends will be sorted first in Call Log + Persist + 1 + Type + Boolean + Value + 1 + ShowPGSearchAll Comment @@ -15418,17 +15649,6 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 - SpeakerParticipantDefaultOrder - - Comment - Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent. - Persist - 1 - Type - U32 - Value - 0 - SpeakerParticipantRemoveDelay Comment @@ -15473,6 +15693,17 @@ Change of this parameter will affect the layout of buttons in notification toast Value 1 + UsePeopleAPI + + Comment + Use the people API cap for avatar name fetching, use old legacy protocol if false. Requires restart. + Persist + 1 + Type + Boolean + Value + 1 + UseStartScreen Comment @@ -15650,6 +15881,17 @@ Change of this parameter will affect the layout of buttons in notification toast Backup 0 + VerifyInitialWearables + + Comment + Compares the initial wearables to the COF contents to determine which one to use for the intial outfit + Persist + 1 + Type + Boolean + Value + 0 + VertexShaderEnable Comment @@ -16080,10 +16322,10 @@ Change of this parameter will affect the layout of buttons in notification toast Value 50.0 - WellIconFlashCount + FlashCount Comment - Number of flashes of IM Well and Notification Well icons after which flashing buttons stay lit up. Requires restart. + Number of flashes of item. Requires restart. Persist 1 Type @@ -16091,16 +16333,16 @@ Change of this parameter will affect the layout of buttons in notification toast Value 3 - WellIconFlashPeriod + FlashPeriod Comment - Period at which IM Well and Notification Well icons flash (seconds). Requires restart. + Period at which item flash (seconds). Requires restart. Persist 1 Type F32 Value - 0.25 + 0.5 WindLightUseAtmosShaders diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index f2e18731de..a842694219 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -1,33 +1,20 @@ - RLVaLoginLastLocation - - Comment - Determines whether the next login will be forced to the last logoff location (set by the viewer) - Persist - 1 - Type - Boolean - Value - 1 - - - BusyResponseChanged + DoNotDisturbResponseChanged Comment - Does user's busy mode message differ from default? + Does user's do not disturb mode message differ from default? Persist 1 Type @@ -35,17 +22,71 @@ Value 0 - BusyModeResponse + DoNotDisturbModeResponse Comment - Auto response to instant messages while in unavailable mode. + Auto response to instant messages while in do not disturb mode. Persist 1 Type String Value - The Resident you messaged has activated Firestorm viewer's 'unavailable mode' which means they have requested not to be disturbed. Your message will still be shown in their IM panel for later viewing. + This resident has turned on 'Do Not Disturb' and will see your message later. + ConversationsExpandMessagePaneFirst + + Comment + Expand either messages or conversations list pane from Conversations compact mode. + Persist + 1 + Type + Boolean + Value + 1 + + ConversationsListPaneCollapsed + + Comment + Stores the expanded/collapsed state of the conversations list pane in Conversations floater. + Persist + 1 + Type + Boolean + Value + 0 + + ConversationsListPaneWidth + + Comment + Conversations floater list pane width. + Persist + 1 + Type + S32 + Value + 205 + + ConversationsMessagePaneCollapsed + + Comment + Stores the expanded/collapsed state of Conversations floater message pane. + Persist + 1 + Type + Boolean + Value + 0 + + ConversationsMessagePaneWidth + + Comment + Conversations floater message pane width. + Persist + 1 + Type + S32 + Value + 412 FSAutorespondMode Comment @@ -137,7 +178,7 @@ Boolean Value 1 - + InstantMessageLogPath Comment @@ -239,17 +280,6 @@ Value 1 - LogInstantMessages - - Comment - Log Instant Messages - Persist - 1 - Type - Boolean - Value - 1 - LogShowHistory Comment @@ -333,7 +363,51 @@ Value 0 - ShowFavoritesOnLogin + TranslatingEnabled + + Comment + Translation prefs are set + Persist + 1 + Type + Boolean + Value + 0 + + KeepConversationLogTranscripts + + Comment + Keep a conversation log and transcripts + Persist + 1 + Type + S32 + Value + 2 + + NearbyChatIsNotTornOff + + Comment + saving torn-off state of the nearby chat between sessions + Persist + 1 + Type + Boolean + Value + 1 + + NearbyChatIsNotCollapsed + + Comment + Saving expanded/collapsed state of the nearby chat between sessions + Persist + 1 + Type + Boolean + Value + 0 + + ShowFavoritesOnLogin Comment Determines whether favorites of last logged in user will be saved on exit from viewer and shown on login screen diff --git a/indra/newview/character/avatar_lad.xml b/indra/newview/character/avatar_lad.xml old mode 100755 new mode 100644 diff --git a/indra/newview/fschathistory.cpp b/indra/newview/fschathistory.cpp index 1ce46aa0ac..ea1c379f27 100644 --- a/indra/newview/fschathistory.cpp +++ b/indra/newview/fschathistory.cpp @@ -542,8 +542,8 @@ public: //void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, EChatType chat_type) void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name, EChatType chat_type, std::string& group) { - //mFrom = av_name.mDisplayName; - //mFrom = av_name.mDisplayName; + //mFrom = av_name.getDisplayName(); + //mFrom = av_name.getDisplayName(); //if (chat_type == CHAT_TYPE_IM) mFrom = LLTrans::getString("IMPrefix") + " " + mFrom; mFrom = ""; if (chat_type == CHAT_TYPE_IM || chat_type == CHAT_TYPE_IM_GROUP) @@ -554,15 +554,15 @@ public: mFrom += group; } } - mFrom += av_name.mDisplayName; + mFrom += av_name.getUserName(); // FS:LO FIRE-5230 - Chat Console Improvement: Replacing the "IM" in front of group chat messages with the actual group name LLTextBox* user_name = getChild("user_name"); user_name->setValue( LLSD(mFrom) ); - user_name->setToolTip( av_name.mUsername ); + user_name->setToolTip( av_name.getUserName() ); if (gSavedSettings.getBOOL("NameTagShowUsernames") && - LLAvatarNameCache::useDisplayNames() ) //&& + LLAvatarName::useDisplayNames() ) //&& // !av_name.mIsDisplayNameDefault) { LLStyle::Params style_params_name; @@ -571,9 +571,9 @@ public: 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); + user_name->appendText(" - " + av_name.getUserName(), FALSE, style_params_name); } - setToolTip( av_name.mUsername ); + setToolTip( av_name.getUserName() ); // name might have changed, update width updateMinUserNameWidth(); } @@ -883,7 +883,7 @@ static LLFastTimer::DeclareTimer FTM_APPEND_MESSAGE("Append Chat Message"); void FSChatHistory::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { - mDisplayName = av_name.mDisplayName; + mDisplayName = av_name.getDisplayName(); mDisplayName_Username = av_name.getCompleteName(); } diff --git a/indra/newview/fsconsoleutils.cpp b/indra/newview/fsconsoleutils.cpp index 60dd9845c7..6d98b4d7fd 100644 --- a/indra/newview/fsconsoleutils.cpp +++ b/indra/newview/fsconsoleutils.cpp @@ -175,7 +175,7 @@ void FSConsoleUtils::onProcessChatAvatarNameLookup(const LLUUID& agent_id, const } else if (useDisplayNames) { - senderName = av_name.mDisplayName; + senderName = av_name.getDisplayName(); } } @@ -253,7 +253,7 @@ void FSConsoleUtils::onProccessInstantMessageNameLookup(const LLUUID& agent_id, } else if (useDisplayNames) { - senderName = av_name.mDisplayName; + senderName = av_name.getDisplayName(); } else { diff --git a/indra/newview/fscontactsfloater.cpp b/indra/newview/fscontactsfloater.cpp index ace55b34b9..94340329e9 100644 --- a/indra/newview/fscontactsfloater.cpp +++ b/indra/newview/fscontactsfloater.cpp @@ -586,7 +586,7 @@ void FSFloaterContacts::addFriend(const LLUUID& agent_id) element["id"] = agent_id; LLSD& username_column = element["columns"][LIST_FRIEND_USER_NAME]; username_column["column"] = "user_name"; - username_column["value"] = av_name.mUsername; + username_column["value"] = av_name.getUserName(); LLSD& friend_column = element["columns"][LIST_FRIEND_NAME]; friend_column["column"] = "full_name"; @@ -674,7 +674,7 @@ void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelatio itemp->getColumn(LIST_ONLINE_STATUS)->setValue(statusIcon); - itemp->getColumn(LIST_FRIEND_USER_NAME)->setValue( av_name.mUsername ); + itemp->getColumn(LIST_FRIEND_USER_NAME)->setValue( av_name.getUserName() ); itemp->getColumn(LIST_FRIEND_NAME)->setValue( av_name.getCompleteName() ); // render name of online friends in bold text diff --git a/indra/newview/fsfloaterim.cpp b/indra/newview/fsfloaterim.cpp index 4f456ded58..5c05228adf 100644 --- a/indra/newview/fsfloaterim.cpp +++ b/indra/newview/fsfloaterim.cpp @@ -370,13 +370,6 @@ void FSFloaterIM::sendMsg() updateMessages(); } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-02 (Catznip-3.2.0d) | Added: Catznip-3.2.0d - else if (gSavedSettings.getBOOL("CloseIMOnEmptyReturn")) - { - // Close if we're the child of a floater - closeFloater(); - } -// [/SL:KB] } } @@ -861,21 +854,21 @@ void FSFloaterIM::onAvatarNameCache(const LLUUID& agent_id, // 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); + //updateSessionName(ui_title, av_name.getDisplayName()); //mTypingStart.setArg("[NAME]", ui_title); std::string name = av_name.getCompleteName(); - if (LLAvatarNameCache::useDisplayNames()) + if (LLAvatarName::useDisplayNames()) { switch (gSavedSettings.getS32("FSIMTabNameFormat")) { // Display name case 0: - name = av_name.mDisplayName; + name = av_name.getDisplayName(); break; // Username case 1: - name = av_name.mUsername; + name = av_name.getUserName(); break; // Display name (username) case 2: @@ -885,11 +878,11 @@ void FSFloaterIM::onAvatarNameCache(const LLUUID& agent_id, case 3: if (av_name.mIsDisplayNameDefault) { - name = av_name.mUsername; + name = av_name.getUserName(); } else { - name = av_name.mUsername + " (" + av_name.mDisplayName + ")"; + name = av_name.getUserName() + " (" + av_name.getDisplayName() + ")"; } break; default: diff --git a/indra/newview/fsfloaterimcontainer.cpp b/indra/newview/fsfloaterimcontainer.cpp index dcba10f449..70684c66a6 100644 --- a/indra/newview/fsfloaterimcontainer.cpp +++ b/indra/newview/fsfloaterimcontainer.cpp @@ -25,96 +25,459 @@ */ // Original file: llimfloatercontainer.cpp +// CHUI Merge below was from anserial's refactoring, seems based on old code +//#if 0 +//#include "llviewerprecompiledheaders.h" +// +//#include "fsfloaterimcontainer.h" +//#include "llfloaterreg.h" +//#include "llimview.h" +//#include "llavatariconctrl.h" +//#include "llgroupiconctrl.h" +//#include "llagent.h" +//#include "lltransientfloatermgr.h" +//#include "fsfloaternearbychat.h" +//#include "fscontactsfloater.h" +//#include "llfloater.h" +//#include "llviewercontrol.h" +// +//// +//// FSFloaterIMContainer +//// +//FSFloaterIMContainer::FSFloaterIMContainer(const LLSD& seed) +//: LLMultiFloater(seed) +//{ +// mAutoResize = FALSE; +// LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); +//} +// +//FSFloaterIMContainer::~FSFloaterIMContainer() +//{ +// mNewMessageConnection.disconnect(); +// LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); +//} +// +//BOOL FSFloaterIMContainer::postBuild() +//{ +// +// if (!gSavedSettings.getBOOL("ContactsTornOff")) +// { +// addFloater(FSFloaterContacts::getInstance(), TRUE); +// } +// +// mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&FSFloaterIMContainer::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 FSFloaterIMContainer::onOpen(const LLSD& key) +//{ +// +// LLMultiFloater::onOpen(key); +// +// +// // If we're using multitabs, and we open up for the first time +// // Add localchat by default if it's not already on the screen somewhere else. -AO +// // But only if it hasnt been already so we can reopen it to the same tab -KC +// // Improved handling to leave most of the work to the LL tear-off code -Zi +// +// LLFloater* floater = FSFloaterNearbyChat::getInstance(); +// if (! LLFloater::isVisible(floater) && (floater->getHost() != this)) +// { +// if (gSavedSettings.getBOOL("ChatHistoryTornOff")) +// { +// // first set the tear-off host to this floater +// floater->setHost(this); +// // clear the tear-off host right after, the "last host used" will still stick +// floater->setHost(NULL); +// // reparent to floater view +// gFloaterView->addChild(floater); +// } +// else +// { +// LLMultiFloater::showFloater(floater); +// } +// } +// +///* +// if (key.isDefined()) +// { +// LLIMFloater* im_floater = LLIMFloater::findInstance(key.asUUID()); +// if (im_floater) +// { +// im_floater->openFloater(); +// } +// } +//*/ +//} +// +//void FSFloaterIMContainer::addFloater(LLFloater* floaterp, +// BOOL select_added_floater, +// LLTabContainer::eInsertionPoint insertion_point) +//{ +// if(!floaterp) return; +// +// // already here +// if (floaterp->getHost() == this) +// { +// openFloater(floaterp->getKey()); +// return; +// } +// +// if (floaterp->getName() == "imcontacts" || floaterp->getName() == "nearby_chat") +// { +// S32 num_locked_tabs = mTabContainer->getNumLockedTabs(); +// mTabContainer->unlockTabs(); +// // add contacts window as first tab +// if (floaterp->getName() == "imcontacts") +// { +// LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); +// gSavedSettings.setBOOL("ContactsTornOff", FALSE); +// } +// else +// { +// // add chat history as second tab if contact window is present, first tab otherwise +// if (getChildView("imcontacts")) +// { +// // assuming contacts window is first tab, select it +// mTabContainer->selectFirstTab(); +// // and add ourselves after +// LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT); +// } +// else +// { +// LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); +// } +// gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE); +// } +// // make sure first two tabs are now locked +// mTabContainer->lockTabs(num_locked_tabs + 1); +// +// floaterp->setCanClose(FALSE); +// 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(icon_params); +// +// mSessions[session_id] = floaterp; +// floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::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(icon_params); +// +// mSessions[session_id] = floaterp; +// floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::onCloseFloater, this, session_id)); +// } +// mTabContainer->setTabImage(floaterp, icon); +//} +// +//void FSFloaterIMContainer::onCloseFloater(LLUUID& id) +//{ +// mSessions.erase(id); +// setFocus(TRUE); +//} +// +//void FSFloaterIMContainer::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(); +// +// // KC: Don't flash tab on friend status changes per setting +// if (floaterp && current_floater && floaterp != current_floater +// && (gSavedSettings.getBOOL("FSIMChatFlashOnFriendStatusChange") || data["from_id"].asUUID() != LLUUID::null)) +// { +// if(LLMultiFloater::isFloaterFlashing(floaterp)) +// LLMultiFloater::setFloaterFlashing(floaterp, FALSE); +// LLMultiFloater::setFloaterFlashing(floaterp, TRUE); +// } +//} +// +//FSFloaterIMContainer* FSFloaterIMContainer::findInstance() +//{ +// return LLFloaterReg::findTypedInstance("fs_im_container"); +//} +// +//FSFloaterIMContainer* FSFloaterIMContainer::getInstance() +//{ +// return LLFloaterReg::getTypedInstance("fs_im_container"); +//} +// +//void FSFloaterIMContainer::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 +//#endif - -#include "llviewerprecompiledheaders.h" - +#include "llfloaterimsession.h" #include "fsfloaterimcontainer.h" + #include "llfloaterreg.h" -#include "llimview.h" -#include "llavatariconctrl.h" -#include "llgroupiconctrl.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 "fsfloaternearbychat.h" -#include "fscontactsfloater.h" -#include "llfloater.h" #include "llviewercontrol.h" +#include "llconversationview.h" +#include "llcallbacklist.h" +#include "llworld.h" +#include "llsdserialize.h" +#include "llviewerobjectlist.h" // // FSFloaterIMContainer // -FSFloaterIMContainer::FSFloaterIMContainer(const LLSD& seed) -: LLMultiFloater(seed) +FSFloaterIMContainer::FSFloaterIMContainer(const LLSD& seed, const Params& params /*= getDefaultParams()*/) +: LLMultiFloater(seed, params), + mExpandCollapseBtn(NULL), + mConversationsRoot(NULL), + mConversationsEventStream("ConversationsEvents"), + mInitialized(false) { + mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&FSFloaterIMContainer::isActionChecked, this, _2)); + mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&FSFloaterIMContainer::onCustomAction, this, _2)); + + mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&FSFloaterIMContainer::checkContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&FSFloaterIMContainer::enableContextMenuItem, this, _2)); + mEnableCallbackRegistrar.add("Avatar.VisibleItem", boost::bind(&FSFloaterIMContainer::visibleContextMenuItem, this, _2)); + mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&FSFloaterIMContainer::doToSelected, this, _2)); + + mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&FSFloaterIMContainer::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); } FSFloaterIMContainer::~FSFloaterIMContainer() { + 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::destroyed()) + { + LLIMMgr::getInstance()->removeSessionObserver(this); + } +} + +void FSFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg) +{ + addConversationListItem(session_id); + FSFloaterIMSessionTab::addToHost(session_id); +} + +void FSFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) +{ + selectConversationPair(session_id, true); + collapseMessagesPane(false); +} + +void FSFloaterIMContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) +{ + addConversationListItem(session_id); + LLFloaterIMSessionTab::addToHost(session_id); +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::sessionRemoved(const LLUUID& session_id) +{ + removeConversationListItem(session_id); +} + +// static +void FSFloaterIMContainer::onCurrentChannelChanged(const LLUUID& session_id) +{ + if (session_id != LLUUID::null) + { + FSFloaterIMContainer::getInstance()->showConversation(session_id); + } } BOOL FSFloaterIMContainer::postBuild() { - - if (!gSavedSettings.getBOOL("ContactsTornOff")) - { - addFloater(FSFloaterContacts::getInstance(), TRUE); - } + mOrigMinWidth = getMinWidth(); + mOrigMinHeight = getMinHeight(); mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&FSFloaterIMContainer::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("im_box_tab_container")); + mStubPanel = getChild("stub_panel"); + mStubTextBox = getChild("stub_textbox"); + mStubTextBox->setURLClickedCallback(boost::bind(&FSFloaterIMContainer::returnFloaterToHost, this)); + + mConversationsStack = getChild("conversations_stack"); + mConversationsPane = getChild("conversations_layout_panel"); + mMessagesPane = getChild("messages_layout_panel"); + + mConversationsListPanel = getChild("conversations_list_panel"); + + // Open IM session with selected participant on double click event + mConversationsListPanel->setDoubleClickCallback(boost::bind(&FSFloaterIMContainer::doToSelected, this, LLSD("im"))); + + // The resize limits for FSFloaterIMContainer should be updated, based on current values of width of conversation and message panels + mConversationsPane->getResizeBar()->setResizeListener(boost::bind(&FSFloaterIMContainer::assignResizeLimits, this)); + + // Create the root model and view for all conversation sessions + LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); + + LLFolderView::Params p(LLUICtrlFactory::getDefaultParams()); + 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(p); + mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); + + // Add listener to conversation model events + mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&FSFloaterIMContainer::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()); + scroller_params.rect(scroller_view_rect); + + LLScrollContainer* scroller = LLUICtrlFactory::create(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("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&FSFloaterIMContainer::onExpandCollapseButtonClicked, this)); + mStubCollapseBtn = getChild("stub_collapse_btn"); + mStubCollapseBtn->setClickedCallback(boost::bind(&FSFloaterIMContainer::onStubCollapseButtonClicked, this)); + getChild("speak_btn")->setClickedCallback(boost::bind(&FSFloaterIMContainer::onSpeakButtonClicked, this)); + + childSetAction("add_btn", boost::bind(&FSFloaterIMContainer::onAddButtonClicked, this)); + + collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); + collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed"), false); + LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false)); + mMicroChangedSignal = LLVoiceClient::getInstance()->MicroChangedCallback(boost::bind(&FSFloaterIMContainer::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(&FSFloaterIMContainer::processParticipantsStyleUpdate, this)); + return TRUE; } void FSFloaterIMContainer::onOpen(const LLSD& key) { - LLMultiFloater::onOpen(key); - - - // If we're using multitabs, and we open up for the first time - // Add localchat by default if it's not already on the screen somewhere else. -AO - // But only if it hasnt been already so we can reopen it to the same tab -KC - // Improved handling to leave most of the work to the LL tear-off code -Zi - - LLFloater* floater = FSFloaterNearbyChat::getInstance(); - if (! LLFloater::isVisible(floater) && (floater->getHost() != this)) - { - if (gSavedSettings.getBOOL("ChatHistoryTornOff")) - { - // first set the tear-off host to this floater - floater->setHost(this); - // clear the tear-off host right after, the "last host used" will still stick - floater->setHost(NULL); - // reparent to floater view - gFloaterView->addChild(floater); - } - else - { - LLMultiFloater::showFloater(floater); - } - } - -/* - if (key.isDefined()) - { - LLIMFloater* im_floater = LLIMFloater::findInstance(key.asUUID()); - if (im_floater) - { - im_floater->openFloater(); - } - } -*/ + openNearbyChat(); + reSelectConversation(); + assignResizeLimits(); } -void FSFloaterIMContainer::addFloater(LLFloater* floaterp, - BOOL select_added_floater, - LLTabContainer::eInsertionPoint insertion_point) +// virtual +void FSFloaterIMContainer::addFloater(LLFloater* floaterp, + BOOL select_added_floater, + LLTabContainer::eInsertionPoint insertion_point) { if(!floaterp) return; @@ -124,69 +487,17 @@ void FSFloaterIMContainer::addFloater(LLFloater* floaterp, openFloater(floaterp->getKey()); return; } - - if (floaterp->getName() == "imcontacts" || floaterp->getName() == "nearby_chat") - { - S32 num_locked_tabs = mTabContainer->getNumLockedTabs(); - mTabContainer->unlockTabs(); - // add contacts window as first tab - if (floaterp->getName() == "imcontacts") - { - LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); - gSavedSettings.setBOOL("ContactsTornOff", FALSE); - } - else - { - // add chat history as second tab if contact window is present, first tab otherwise - if (getChildView("imcontacts")) - { - // assuming contacts window is first tab, select it - mTabContainer->selectFirstTab(); - // and add ourselves after - LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT); - } - else - { - LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START); - } - gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE); - } - // make sure first two tabs are now locked - mTabContainer->lockTabs(num_locked_tabs + 1); - - floaterp->setCanClose(FALSE); - return; - } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-11-17 (Catznip-3.2.0a) | Added: Catznip-3.2.0a LLUUID session_id = floaterp->getKey(); - if (session_id.isNull()) - { - // Re-insert the nearby chat floater at the start - insertion_point = LLTabContainer::START; - } -// [/SL:KB] - + + // Add the floater LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); -// LLUUID session_id = floaterp->getKey(); + LLIconCtrl* icon = 0; -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-11-17 (Catznip-3.2.0a) | Added: Catznip-3.2.0a - if (session_id.isNull()) - { - // Don't allow the nearby chat tab to be drag-rearranged - mTabContainer->lockTabs(1); - - // Add an icon for the nearby chat floater - LLIconCtrl::Params icon_params; - icon_params.image = LLUI::getUIImage("Command_Chat_Icon"); - icon = LLUICtrlFactory::instance().create(icon_params); - } - else if (gAgent.isInGroup(session_id, TRUE)) -// [/SL:KB] -// if(gAgent.isInGroup(session_id, TRUE)) + if(gAgent.isInGroup(session_id, TRUE)) { LLGroupIconCtrl::Params icon_params; icon_params.group_id = session_id; @@ -196,8 +507,8 @@ void FSFloaterIMContainer::addFloater(LLFloater* floaterp, floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::onCloseFloater, this, session_id)); } else - { - LLUUID avatar_id = LLIMModel::getInstance()->getOtherParticipantID(session_id); + { LLUUID avatar_id = session_id.notNull()? + LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); LLAvatarIconCtrl::Params icon_params; icon_params.avatar_id = avatar_id; @@ -206,38 +517,14 @@ void FSFloaterIMContainer::addFloater(LLFloater* floaterp, mSessions[session_id] = floaterp; floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::onCloseFloater, this, session_id)); } + + // forced resize of the floater + LLRect wrapper_rect = this->mTabContainer->getLocalRect(); + floaterp->setRect(wrapper_rect); + mTabContainer->setTabImage(floaterp, icon); } -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-11 (Catznip-3.2.0d) | Added: Catznip-3.2.0d -void FSFloaterIMContainer::removeFloater(LLFloater* floaterp) -{ - // old code from FS - if (floaterp->getName() == "nearby_chat") - { - // only my friends floater now locked - mTabContainer->lockTabs(mTabContainer->getNumLockedTabs() - 1); - gSavedSettings.setBOOL("ChatHistoryTornOff", TRUE); - floaterp->setCanClose(TRUE); - } - else if (floaterp->getName() == "imcontacts") - { - // only chat floater now locked - mTabContainer->lockTabs(mTabContainer->getNumLockedTabs() - 1); - gSavedSettings.setBOOL("ContactsTornOff", TRUE); - floaterp->setCanClose(TRUE); - } - // - - - LLUUID idSession = floaterp->getKey(); - if (idSession.isNull()) - { - mTabContainer->unlockTabs(); - } - LLMultiFloater::removeFloater(floaterp); -} -// [/SL:KB] void FSFloaterIMContainer::onCloseFloater(LLUUID& id) { @@ -251,9 +538,7 @@ void FSFloaterIMContainer::onNewMessageReceived(const LLSD& data) LLFloater* floaterp = get_ptr_in_map(mSessions, session_id); LLFloater* current_floater = LLMultiFloater::getActiveFloater(); - // KC: Don't flash tab on friend status changes per setting - if (floaterp && current_floater && floaterp != current_floater - && (gSavedSettings.getBOOL("FSIMChatFlashOnFriendStatusChange") || data["from_id"].asUUID() != LLUUID::null)) + if(floaterp && current_floater && floaterp != current_floater) { if(LLMultiFloater::isFloaterFlashing(floaterp)) LLMultiFloater::setFloaterFlashing(floaterp, FALSE); @@ -261,30 +546,1694 @@ void FSFloaterIMContainer::onNewMessageReceived(const LLSD& data) } } +void FSFloaterIMContainer::onStubCollapseButtonClicked() +{ + collapseMessagesPane(true); +} + +void FSFloaterIMContainer::onSpeakButtonClicked() +{ + LLAgent::toggleMicrophone("speak"); + updateSpeakBtnState(); +} +void FSFloaterIMContainer::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(); +} + FSFloaterIMContainer* FSFloaterIMContainer::findInstance() { - return LLFloaterReg::findTypedInstance("fs_im_container"); + return LLFloaterReg::findTypedInstance("im_container"); } FSFloaterIMContainer* FSFloaterIMContainer::getInstance() { - return LLFloaterReg::getTypedInstance("fs_im_container"); + return LLFloaterReg::getTypedInstance("im_container"); +} + +// Update all participants in the conversation lists +void FSFloaterIMContainer::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(*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 FSFloaterIMContainer::idle(void* user_data) +{ + FSFloaterIMContainer* self = static_cast(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 FSFloaterIMContainer::onConversationModelEvent(const LLSD& event) +{ + // For debug only + //std::ostringstream llsd_value; + //llsd_value << LLSDOStreamer(event) << std::endl; + //llinfos << "FSFloaterIMContainer::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(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("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(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 FSFloaterIMContainer::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(*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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::returnFloaterToHost() +{ + LLUUID session_id = this->getSelectedSession(); + LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id); + floater->onTearOffClicked(); } void FSFloaterIMContainer::setMinimized(BOOL b) { - if (isMinimized() == b) return; - + bool was_minimized = isMinimized(); LLMultiFloater::setMinimized(b); - // Hide minimized floater (see EXT-5315) - setVisible(!b); - if (isMinimized()) return; - - if (getActiveFloater()) + //Switching from minimized to un-minimized + if(was_minimized && !b) { - getActiveFloater()->setVisible(TRUE); + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); + + if(session_floater && !session_floater->isTornOff()) + { + //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() && mSelectedSession.notNull()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); + } + } } } -// EOF +void FSFloaterIMContainer::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("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); + selectConversationPair(LLUUID(NULL), false, false); + } + openNearbyChat(); + flashConversationItemWidget(mSelectedSession,false); + + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); + if(session_floater && !session_floater->isMinimized()) + { + //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() && mSelectedSession.notNull()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); + } + } + } + + nearby_chat = LLFloaterReg::findTypedInstance("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(widget_it->second); + if (widget) + { + widget->setVisibleIfDetached(visible); + } + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); +} + +void FSFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key) +{ + LLMultiFloater::setVisibleAndFrontmost(take_focus, key); + selectConversationPair(getSelectedSession(), false, take_focus); +} + +void FSFloaterIMContainer::updateResizeLimits() +{ + LLMultiFloater::updateResizeLimits(); + assignResizeLimits(); +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::collapseConversationsPane(bool collapse, bool save_is_allowed /*=true*/) +{ + if (mConversationsPane->isCollapsed() == collapse) + { + return; + } + + LLView* button_panel = getChild("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(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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::onAddButtonClicked() +{ + LLView * button = findChild("conversations_pane_buttons_expanded")->findChild("add_btn"); + LLFloater* root_floater = gFloaterView->getParentFloater(this); + LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&FSFloaterIMContainer::onAvatarPicked, this, _1), TRUE, TRUE, TRUE, root_floater->getName(), button); + + if (picker && root_floater) + { + root_floater->addDependentFloater(picker); + } +} + +void FSFloaterIMContainer::onAvatarPicked(const uuid_vec_t& ids) +{ + if (ids.size() == 1) + { + LLAvatarActions::startIM(ids.back()); + } + else + { + LLAvatarActions::startConference(ids); + } +} + +void FSFloaterIMContainer::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("preferences"); + if (floater_prefp) + { + floater_prefp->selectChatPanel(); + } + } + if ("privacy_preferences" == command) + { + LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance("preferences"); + if (floater_prefp) + { + floater_prefp->selectPrivacyPanel(); + } + } + if ("Translating.Toggle" == command) + { + gSavedSettings.setBOOL("TranslateChat", !gSavedSettings.getBOOL("TranslateChat")); + } +} + +BOOL FSFloaterIMContainer::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 FSFloaterIMContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderSessions()) + { + old_order.setSortOrderSessions(order); + setSortOrder(old_order); + } +} + +void FSFloaterIMContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order) +{ + LLConversationSort old_order = mConversationViewModel.getSorter(); + if (order != old_order.getSortOrderParticipants()) + { + old_order.setSortOrderParticipants(order); + setSortOrder(old_order); + } +} + +void FSFloaterIMContainer::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("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id))); + if (conversation_floater) + { + conversation_floater->setSortOrder(order); + } + } + + gSavedSettings.setU32("ConversationSortOrder", (U32)order); +} + +void FSFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids) +{ + const std::set selectedItems = mConversationsRoot->getSelectionList(); + + std::set::const_iterator it = selectedItems.begin(); + const std::set::const_iterator it_end = selectedItems.end(); + LLConversationItem * conversationItem; + + for (; it != it_end; ++it) + { + conversationItem = static_cast((*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 * FSFloaterIMContainer::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(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); + } + } + + return conversation_item; +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::checkContextMenuItem(const LLSD& userdata) +{ + std::string item = userdata.asString(); + uuid_vec_t uuids; + getParticipantUUIDs(uuids); + + return checkContextMenuItem(item, uuids); +} + +bool FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::showConversation(const LLUUID& session_id) +{ + setVisibleAndFrontmost(false); + selectConversationPair(session_id, true); + + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); + if (session_floater) + { + session_floater->restoreFloater(); + } +} + +void FSFloaterIMContainer::clearAllFlashStates() +{ + conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); + for (;widget_it != mConversationsWidgets.end(); ++widget_it) + { + LLConversationViewSession* widget = dynamic_cast(widget_it->second); + if (widget) + { + widget->setFlashState(false); + } + } +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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(); + } + } + + /* 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); + } + + //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); + } + } + + // Set the focus on the selected floater + if (!session_floater->hasFocus() && !session_floater->isMinimized()) + { + session_floater->setFocus(focus_floater); + } + } + flashConversationItemWidget(session_id,false); + return handled; +} + +void FSFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,session_id)); + if (item) + { + item->setTimeNow(participant_id); + mConversationViewModel.requestSortAll(); + mConversationsRoot->arrangeAll(); + } +} + +void FSFloaterIMContainer::setNearbyDistances() +{ + // Get the nearby chat session: that's the one with uuid nul + LLConversationItemSession* item = dynamic_cast(get_ptr_in_map(mConversationsItems,LLUUID())); + if (item) + { + // Get the positions of the nearby avatars and their ids + std::vector 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* FSFloaterIMContainer::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(*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("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 FSFloaterIMContainer::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(new_selection->getViewModelItem()); + if (vmi) + { + selectConversationPair(vmi->getUUID(), true); + } + } + } + } + return is_widget_selected; +} + +LLConversationViewSession* FSFloaterIMContainer::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(params); +} + +LLConversationViewParticipant* FSFloaterIMContainer::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(params); +} + +bool FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::isMuted(const LLUUID& avatar_id) +{ + const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); + return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED; +} + +void FSFloaterIMContainer::moderateVoiceAllParticipants(bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(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 FSFloaterIMContainer::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 ( + LLIMModel::getInstance()->getSpeakerManager(session_id)); + if (speaker_manager) + { + speaker_manager->moderateVoiceAllParticipants(false); + } + + return; +} + +void FSFloaterIMContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); + + if (NULL != speaker_managerp) + { + speaker_managerp->moderateVoiceParticipant(avatar_id, unmute); + } +} + +LLSpeakerMgr * FSFloaterIMContainer::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 * FSFloaterIMContainer::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 FSFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid) +{ + LLIMSpeakerMgr * speaker_managerp = dynamic_cast(getSpeakerMgrForSelectedParticipant()); + if (NULL != speaker_managerp) + { + speaker_managerp->toggleAllowTextChat(participant_uuid); + } +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::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(get_ptr_in_map(mConversationsWidgets,LLUUID())); + if (nearby_chat) + { + reSelectConversation(); + nearby_chat->setOpen(TRUE); + } + } +} + +void FSFloaterIMContainer::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 FSFloaterIMContainer::reSelectConversation() +{ + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); + if (session_floater->getHost()) + { + selectFloater(session_floater); + } +} + +void FSFloaterIMContainer::updateSpeakBtnState() +{ + LLButton* mSpeakBtn = getChild("speak_btn"); + mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState()); + mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak")); +} + +bool FSFloaterIMContainer::isConversationLoggingAllowed() +{ + return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; +} + +void FSFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, bool is_flashes) +{ + //Finds the conversation line item to flash using the session_id + LLConversationViewSession * widget = dynamic_cast(get_ptr_in_map(mConversationsWidgets,session_id)); + + if (widget) + { + widget->setFlashState(is_flashes); + } +} + +bool FSFloaterIMContainer::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 FSFloaterIMContainer::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 FSFloaterIMContainer::selectAdjacentConversation(bool focus_selected) +{ + bool selectedAdjacentConversation = selectNextorPreviousConversation(true, focus_selected); + + if(!selectedAdjacentConversation) + { + selectedAdjacentConversation = selectNextorPreviousConversation(false, focus_selected); + } + + return selectedAdjacentConversation; +} + +bool FSFloaterIMContainer::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(new_selection->getViewModelItem()); + if (vmi) + { + selectConversationPair(vmi->getUUID(), true, focus_selected); + return true; + } + } + } + } + return false; +} + +void FSFloaterIMContainer::expandConversation() +{ + if(!mConversationsPane->isCollapsed()) + { + LLConversationViewSession* widget = dynamic_cast(get_ptr_in_map(mConversationsWidgets,getSelectedSession())); + if (widget) + { + widget->setOpen(!widget->isOpen()); + } + } +} + +void FSFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) +{ + // Always unminimize before trying to close. + // Most of the time the user will never see this state. + if(isMinimized()) + { + LLMultiFloater::setMinimized(FALSE); + } + + LLFloater::closeFloater(app_quitting); +} + +// EOF \ No newline at end of file diff --git a/indra/newview/fsfloaterimcontainer.h b/indra/newview/fsfloaterimcontainer.h index b3b8f8a106..ef0fdfd0c7 100644 --- a/indra/newview/fsfloaterimcontainer.h +++ b/indra/newview/fsfloaterimcontainer.h @@ -25,7 +25,8 @@ */ // Original file: llimfloatercontainer.h - +// CHUI Merge below was from anserial's refactoring, seems based on old code +#if 0 #ifndef FS_FLOATERIMCONTAINER_H #define FS_FLOATERIMCONTAINER_H @@ -53,9 +54,6 @@ public: /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainer::END); -// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-11 (Catznip-3.2.0d) | Added: Catznip-3.2.0d - /*virtual*/ void removeFloater(LLFloater* floaterp); -// [/SL:KB] static LLFloater* getCurrentVoiceFloater(); @@ -74,3 +72,192 @@ private: }; #endif // FS_FLOATERIMCONTAINER_H +#endif + +// CHUI Merge below is modified from LL's llfloaterimcontainer.h + +#ifndef FS_FLOATERIMCONTAINER_H +#define FS_FLOATERIMCONTAINER_H + +#include +#include + +#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 FSFloaterIMContainer; +class LLSpeaker; +class LLSpeakerMgr; + +class FSFloaterIMContainer + : public LLMultiFloater + , public LLIMSessionObserver +{ +public: + FSFloaterIMContainer(const LLSD& seed, const Params& params = getDefaultParams()); + virtual ~FSFloaterIMContainer(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void draw(); + /*virtual*/ void setMinimized(BOOL b); + /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); + /*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 FSFloaterIMContainer* findInstance(); + static FSFloaterIMContainer* 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 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 // FS_FLOATERIMCONTAINER_H \ No newline at end of file diff --git a/indra/newview/fsfloaternearbychat.cpp b/indra/newview/fsfloaternearbychat.cpp index ff4a56f929..337406c328 100644 --- a/indra/newview/fsfloaternearbychat.cpp +++ b/indra/newview/fsfloaternearbychat.cpp @@ -45,7 +45,6 @@ #include "llmenugl.h" #include "llviewermenu.h"//for gMenuHolder -#include "llnearbychathandler.h" // #include "llnearbychatbar.h" // Remove floating chat bar #include "fsnearbychathub.h" #include "llchannelmanager.h" @@ -535,12 +534,12 @@ void FSFloaterNearbyChat::loadHistory() EIMType im_type = IM_TYPE_NONE; // FS:LO FIRE-5230 - Chat Console Improvement: Replacing the "IM" in front of group chat messages with the actual group name const LLSD& msg = *it; - std::string from = msg[IM_FROM]; + std::string from = msg[LL_IM_FROM]; std::string fromGroup = ""; // FS:LO FIRE-5230 - Chat Console Improvement: Replacing the "IM" in front of group chat messages with the actual group name 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 { @@ -576,8 +575,8 @@ void FSFloaterNearbyChat::loadHistory() LLChat chat; chat.mFromName = from; chat.mFromID = from_id; - chat.mText = msg[IM_TEXT].asString(); - chat.mTimeStr = msg[IM_TIME].asString(); + chat.mText = msg[LL_IM_TEXT].asString(); + chat.mTimeStr = msg[LL_IM_TIME].asString(); chat.mChatStyle = CHAT_STYLE_HISTORY; // FS:LO FIRE-5230 - Chat Console Improvement: Replacing the "IM" in front of group chat messages with the actual group name diff --git a/indra/newview/fsfloatervoicecontrols.cpp b/indra/newview/fsfloatervoicecontrols.cpp index 8278a80bd8..1771d42ccc 100644 --- a/indra/newview/fsfloatervoicecontrols.cpp +++ b/indra/newview/fsfloatervoicecontrols.cpp @@ -526,7 +526,7 @@ void FSFloaterVoiceControls::initAgentData() // Just use display name, because it's you LLAvatarName av_name; LLAvatarNameCache::get( gAgentID, &av_name ); - mAgentPanel->getChild("user_text")->setValue(av_name.mDisplayName); + mAgentPanel->getChild("user_text")->setValue(av_name.getDisplayName()); mSpeakingIndicator = mAgentPanel->getChild("speaking_indicator"); mSpeakingIndicator->setSpeakerId(gAgentID); @@ -547,7 +547,7 @@ void FSFloaterVoiceControls::setModeratorMutedVoice(bool moderator_muted) void FSFloaterVoiceControls::onModeratorNameCache(const LLAvatarName& av_name) { std::string name; - name = av_name.mDisplayName; + name = av_name.getDisplayName(); if(mSpeakerManager && gAgent.isInGroup(mSpeakerManager->getSessionID())) { diff --git a/indra/newview/fsnearbychatcontrol.cpp b/indra/newview/fsnearbychatcontrol.cpp index 3006351aac..9d2c5be26e 100644 --- a/indra/newview/fsnearbychatcontrol.cpp +++ b/indra/newview/fsnearbychatcontrol.cpp @@ -47,7 +47,7 @@ #include "llmenugl.h" #include "llviewermenu.h"//for gMenuHolder -// #include "llnearbychathandler.h" +#include "llfloaterimnearbychathandler.h" // #include "llnearbychatbar.h" // Remove floating chat bar // #include "llchannelmanager.h" diff --git a/indra/newview/fsnearbychathub.cpp b/indra/newview/fsnearbychathub.cpp index dc09271183..163a45f830 100644 --- a/indra/newview/fsnearbychathub.cpp +++ b/indra/newview/fsnearbychathub.cpp @@ -43,7 +43,7 @@ #include "llmenugl.h" #include "llviewermenu.h"//for gMenuHolder -#include "llnearbychathandler.h" +#include "llfloaterimnearbychathandler.h" #include "llchannelmanager.h" #include "llagent.h" // gAgent diff --git a/indra/newview/fspanelimcontrolpanel.h b/indra/newview/fspanelimcontrolpanel.h index 33bb13e650..a32dbcadb4 100644 --- a/indra/newview/fspanelimcontrolpanel.h +++ b/indra/newview/fspanelimcontrolpanel.h @@ -24,7 +24,7 @@ * $/LicenseInfo$ */ -// Original file: // Original file: llpanelimcontrolpanel.h +// Original file: llpanelimcontrolpanel.h #ifndef FS_PANELIMCONTROLPANEL_H #define FS_PANELIMCONTROLPANEL_H diff --git a/indra/newview/fspanelprofile.cpp b/indra/newview/fspanelprofile.cpp index bb55f703bc..f5ee35278e 100644 --- a/indra/newview/fspanelprofile.cpp +++ b/indra/newview/fspanelprofile.cpp @@ -333,7 +333,7 @@ void FSPanelProfile::onOpen(const LLSD& key) mGroupList->setShowNone(false); mGroupList->enableForAgent(false); - if (LLAvatarNameCache::useDisplayNames()) + if (LLAvatarName::useDisplayNames()) { mDisplayNameButton->setVisible( true ); mDisplayNameButton->setEnabled( true ); @@ -771,7 +771,7 @@ void FSPanelProfile::onClickSetName() void FSPanelProfile::onAvatarNameCacheSetName(const LLUUID& agent_id, const LLAvatarName& av_name) { - if (av_name.mDisplayName.empty()) + if (av_name.getDisplayName().empty()) { // something is wrong, tell user to try again later LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); @@ -858,10 +858,10 @@ void FSPanelProfileWeb::apply(LLAvatarData* data) void FSPanelProfileWeb::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) { //[ADD: FIRE-2266: SJ] make sure username is always filled even when Displaynames are not enabled - std::string username = av_name.mUsername; + std::string username = av_name.getUserName(); if (username.empty()) { - username = LLCacheName::buildUsername(av_name.mDisplayName); + username = LLCacheName::buildUsername(av_name.getDisplayName()); } //[ADD: FIRE-2266: SJ] Adding link to webprofiles on profile which opens Web Profiles in browser diff --git a/indra/newview/fsradarentry.cpp b/indra/newview/fsradarentry.cpp index 43633e48e6..996ee441e9 100644 --- a/indra/newview/fsradarentry.cpp +++ b/indra/newview/fsradarentry.cpp @@ -85,8 +85,8 @@ void FSRadarEntry::onAvatarNameCache(const LLUUID& av_id, const LLAvatarName& av { if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) { - mUserName = av_name.mUsername; - mDisplayName = av_name.mDisplayName; + mUserName = av_name.getUserName(); + mDisplayName = av_name.getDisplayName(); mName = getRadarName(av_name); mIsLinden = FSCommon::isLinden(av_id); } @@ -126,44 +126,44 @@ std::string FSRadarEntry::getRadarName(const LLAvatarName& av_name) { if (fmt == FSRADAR_NAMEFORMAT_DISPLAYNAME) { - return av_name.mDisplayName; + return av_name.getDisplayName(); } else if (fmt == FSRADAR_NAMEFORMAT_USERNAME) { - return av_name.mUsername; + return av_name.getUserName(); } else if (fmt == FSRADAR_NAMEFORMAT_DISPLAYNAME_USERNAME) { - std::string s1 = av_name.mDisplayName; + std::string s1 = av_name.getDisplayName(); to_lower(s1); - std::string s2 = av_name.mUsername; + std::string s2 = av_name.getUserName(); replace_all(s2, ".", " "); if (s1.compare(s2) == 0) { - return av_name.mDisplayName; + return av_name.getDisplayName(); } else { - return llformat("%s (%s)", av_name.mDisplayName.c_str(), av_name.mUsername.c_str()); + return llformat("%s (%s)", av_name.getDisplayName().c_str(), av_name.getUserName().c_str()); } } else if (fmt == FSRADAR_NAMEFORMAT_USERNAME_DISPLAYNAME) { - std::string s1 = av_name.mDisplayName; + std::string s1 = av_name.getDisplayName(); to_lower(s1); - std::string s2 = av_name.mUsername; + std::string s2 = av_name.getUserName(); replace_all(s2, ".", " "); if (s1.compare(s2) == 0) { - return av_name.mDisplayName; + return av_name.getDisplayName(); } else { - return llformat("%s (%s)", av_name.mUsername.c_str(), av_name.mDisplayName.c_str()); + return llformat("%s (%s)", av_name.getUserName().c_str(), av_name.getDisplayName().c_str()); } } } // else use legacy name lookups - return av_name.mDisplayName; // will be mapped to legacyname automatically by the name cache + return av_name.getDisplayName(); // will be mapped to legacyname automatically by the name cache } diff --git a/indra/newview/lggcontactsetsfloater.cpp b/indra/newview/lggcontactsetsfloater.cpp index 42838226b1..4a05afc6e5 100644 --- a/indra/newview/lggcontactsetsfloater.cpp +++ b/indra/newview/lggcontactsetsfloater.cpp @@ -1865,8 +1865,8 @@ void lggContactSetsFloater::draw() switch (sPhoenixNameSystem()) { case 0 : fullname = avatar_name.getLegacyName(); break; - case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.mDisplayName : avatar_name.getCompleteName()); break; - case 2 : fullname = avatar_name.mDisplayName; break; + case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.getDisplayName() : avatar_name.getCompleteName()); break; + case 2 : fullname = avatar_name.getDisplayName(); break; default : fullname = avatar_name.getCompleteName(); break; } @@ -2234,8 +2234,8 @@ BOOL lggContactSetsFloater::compareAv(LLUUID av1, LLUUID av2) switch (sPhoenixNameSystem()) { case 0 : fullname = avatar_name.getLegacyName(); break; - case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.mDisplayName : avatar_name.getCompleteName()); break; - case 2 : fullname = avatar_name.mDisplayName; break; + case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.getDisplayName() : avatar_name.getCompleteName()); break; + case 2 : fullname = avatar_name.getDisplayName(); break; default : fullname = avatar_name.getCompleteName(); break; } @@ -2247,8 +2247,8 @@ BOOL lggContactSetsFloater::compareAv(LLUUID av1, LLUUID av2) switch (sPhoenixNameSystem()) { case 0 : fullname = avatar_name.getLegacyName(); break; - case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.mDisplayName : avatar_name.getCompleteName()); break; - case 2 : fullname = avatar_name.mDisplayName; break; + case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.getDisplayName() : avatar_name.getCompleteName()); break; + case 2 : fullname = avatar_name.getDisplayName(); break; default : fullname = avatar_name.getCompleteName(); break; } @@ -2329,8 +2329,8 @@ BOOL lggContactSetsFloater::generateCurrentList() switch (sPhoenixNameSystem()) { case 0 : fullname = avatar_name.getLegacyName(); break; - case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.mDisplayName : avatar_name.getCompleteName()); break; - case 2 : fullname = avatar_name.mDisplayName; break; + case 1 : fullname = (avatar_name.mIsDisplayNameDefault ? avatar_name.getDisplayName() : avatar_name.getCompleteName()); break; + case 2 : fullname = avatar_name.getDisplayName(); break; default : fullname = avatar_name.getCompleteName(); break; } avN = fullname; diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 7ef619b97e..3a8212a602 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -42,6 +42,7 @@ #include "llchannelmanager.h" #include "llchicletbar.h" #include "llconsole.h" +#include "lldonotdisturbnotificationstorage.h" #include "llenvmanager.h" #include "llfirstuse.h" #include "llfloatercamera.h" @@ -55,10 +56,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" // to show/hide navigation bar when changing mouse look state -// Remove floating chat bar -//#include "llnearbychatbar.h" -#include "fsnearbychathub.h" -// +#include "llfloaterimnearbychat.h" #include "llspeakers.h" // [/SL:KB] #include "llnotificationsutil.h" @@ -408,7 +406,7 @@ LLAgent::LLAgent() : mShowAvatar(TRUE), mFrameAgent(), - mIsBusy(FALSE), + mIsDoNotDisturb(false), mIsAutorespond(FALSE), mIsAutorespondNonFriends(FALSE), @@ -1531,24 +1529,9 @@ void LLAgent::setAFK() if (!(mControlFlags & AGENT_CONTROL_AWAY)) { - llinfos << "Setting AFK" << llendl; sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START); setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP); - LL_INFOS("AFK") << "Setting Away" << LL_ENDL; gAwayTimer.start(); - // [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity] - //if (gAFKMenu) - //{ - // gAFKMenu->setLabel(LLTrans::getString("AvatarSetNotAway")); - //} - - // Gsit on away, antigrief protection - if (gSavedSettings.getBOOL("AvatarSitOnAway") == TRUE) - { - if (!gAgentAvatarp->isSitting() && !gRlvHandler.hasBehaviour(RLV_BHVR_SIT)) - gAgent.sitDown(); - } - // } } @@ -1567,21 +1550,6 @@ void LLAgent::clearAFK() { sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP); clearControlFlags(AGENT_CONTROL_AWAY); - LL_INFOS("AFK") << "Clearing Away" << LL_ENDL; - // [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity] - //if (gAFKMenu) - //{ - // gAFKMenu->setLabel(LLTrans::getString("AvatarSetAway")); - //} - - // if we sat while away, stand back up on clear - if (gSavedSettings.getBOOL("AvatarSitOnAway") == TRUE) - { - if (gAgentAvatarp->isSitting() && !gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) - gAgent.standUp(); - } - // - } } @@ -1594,41 +1562,26 @@ BOOL LLAgent::getAFK() const } //----------------------------------------------------------------------------- -// setBusy() +// setDoNotDisturb() //----------------------------------------------------------------------------- -void LLAgent::setBusy() +void LLAgent::setDoNotDisturb(bool pIsDoNotDisturb) { - sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START); - mIsBusy = TRUE; - // [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity] - //if (gBusyMenu) - //{ - // gBusyMenu->setLabel(LLTrans::getString("AvatarSetNotBusy")); - //} - LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(true); + 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) + { + LLDoNotDisturbNotificationStorage::getInstance()->updateNotifications(); + } } //----------------------------------------------------------------------------- -// clearBusy() +// isDoNotDisturb() //----------------------------------------------------------------------------- -void LLAgent::clearBusy() +bool LLAgent::isDoNotDisturb() const { - mIsBusy = FALSE; - sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP); - // [SJ - FIRE-2177 - Making Autorespons a simple Check in the menu again for clarity] - //if (gBusyMenu) - //{ - // gBusyMenu->setLabel(LLTrans::getString("AvatarSetBusy")); - //} - LLNotificationsUI::LLChannelManager::getInstance()->muteAllChannels(false); -} - -//----------------------------------------------------------------------------- -// getBusy() -//----------------------------------------------------------------------------- -BOOL LLAgent::getBusy() const -{ - return mIsBusy; + return mIsDoNotDisturb; } //----------------------------------------------------------------------------- @@ -2200,10 +2153,8 @@ void LLAgent::startTyping() { sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START); } - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_START, FALSE); - FSNearbyChat::instance().sendChatFromViewer("", CHAT_TYPE_START, FALSE); - // + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_START, FALSE); } //----------------------------------------------------------------------------- @@ -2215,10 +2166,8 @@ void LLAgent::stopTyping() { clearRenderState(AGENT_STATE_TYPING); sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP); - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); - FSNearbyChat::instance().sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); - // + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer("", CHAT_TYPE_STOP, FALSE); } } @@ -2945,51 +2894,21 @@ void LLMaturityPreferencesResponder::errorWithContent(U32 pStatus, const std::st 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()) + + 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()) { - 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); - } - } - } + LLSD::String actualPreference = pContent.get("access_prefs").get("max").asString(); + LLStringUtil::trim(actualPreference); + maturity = LLViewerRegion::shortStringToAccess(actualPreference); } return maturity; @@ -4903,7 +4822,7 @@ void LLAgent::sendAgentSetAppearance() } - LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL; + LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "TAT: Sent AgentSetAppearance: " << gAgentAvatarp->getBakedStatusForPrintout() << LL_ENDL; //dumpAvatarTEs( "sendAgentSetAppearance()" ); LLMessageSystem* msg = gMessageSystem; @@ -4961,7 +4880,7 @@ void LLAgent::sendAgentSetAppearance() { dumpSentAppearance(dump_prefix); } - LL_INFOS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL; + LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "TAT: Sending cached texture data" << LL_ENDL; for (U8 baked_index = 0; baked_index < BAKED_NUM_INDICES; baked_index++) { BOOL generate_valid_hash = TRUE; diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h old mode 100755 new mode 100644 index efc6d6ed85..8100bbb68d --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -401,14 +401,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; //-------------------------------------------------------------------- // Autorespond //-------------------------------------------------------------------- @@ -621,7 +620,6 @@ public: protected: bool teleportCore(bool is_local = false); // Stuff for all teleports; returns true if the teleport can proceed - //-------------------------------------------------------------------- // Teleport State //-------------------------------------------------------------------- diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 6bfa311cb5..7f08fa80f0 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -2325,6 +2325,13 @@ void LLAgentCamera::changeCameraToCustomizeAvatar() return; } +// [RLVa:KB] - Checked: 2010-03-07 (RLVa-1.2.0c) | Modified: RLVa-1.0.0g + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStand()) ) + { + return; + } +// [/RLVa:KB] + // [RLVa:KB] - Checked: 2010-03-07 (RLVa-1.2.0c) | Modified: RLVa-1.0.0g if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStand()) ) { diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp old mode 100755 new mode 100644 index c7ab6600c9..1e0e5bac69 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1532,7 +1532,7 @@ void LLAgentWearables::queryWearableCache() gAgentAvatarp->outputRezTiming("Fetching textures from cache"); } - LL_INFOS("Avatar") << gAgentAvatarp->avString() << "Requesting texture cache entry for " << num_queries << " baked textures" << LL_ENDL; + LL_DEBUGS("Avatar") << gAgentAvatarp->avString() << "Requesting texture cache entry for " << num_queries << " baked textures" << LL_ENDL; gMessageSystem->sendReliable(gAgent.getRegion()->getHost()); gAgentQueryManager.mNumPendingQueries++; gAgentQueryManager.mWearablesCacheQueryID++; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h old mode 100755 new mode 100644 diff --git a/indra/newview/llagentwearablesfetch.cpp b/indra/newview/llagentwearablesfetch.cpp index 8f1a4799e5..14c9d3e294 100644 --- a/indra/newview/llagentwearablesfetch.cpp +++ b/indra/newview/llagentwearablesfetch.cpp @@ -406,7 +406,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 old mode 100755 new mode 100644 index d87ea78ee5..0e6a798877 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1253,21 +1253,6 @@ S32 LLAppearanceMgr::getCOFVersion() const } } -S32 LLAppearanceMgr::getLastUpdateRequestCOFVersion() const -{ - return mLastUpdateRequestCOFVersion; -} - -S32 LLAppearanceMgr::getLastAppearanceUpdateCOFVersion() const -{ - return mLastAppearanceUpdateCOFVersion; -} - -void LLAppearanceMgr::setLastAppearanceUpdateCOFVersion(S32 new_val) -{ - mLastAppearanceUpdateCOFVersion = new_val; -} - const LLViewerInventoryItem* LLAppearanceMgr::getBaseOutfitLink() { const LLUUID& current_outfit_cat = getCOF(); @@ -2229,8 +2214,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) @@ -2343,7 +2326,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) BoolSetter setIsInUpdateAppearanceFromCOF(mIsInUpdateAppearanceFromCOF); selfStartPhase("update_appearance_from_cof"); - LL_INFOS("Avatar") << self_av_string() << "starting" << LL_ENDL; + LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL; //checking integrity of the COF in terms of ordering of wearables, //checking and updating links' descriptions of wearables in the COF (before analyzed for "dirty" state) @@ -2368,7 +2351,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool update_base_outfit_ordering) //dumpCat(getCOF(),"COF, start"); - bool follow_folder_links = true; + bool follow_folder_links = false; LLUUID current_outfit_id = getCOF(); // Find all the wearables that are in the COF's subtree. @@ -2736,7 +2719,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; @@ -2776,8 +2758,6 @@ void LLAppearanceMgr::wearOutfitByName(const std::string& name) llwarns << "Couldn't find outfit " <= mLastUpdateRequestCOFVersion); - mLastUpdateRequestCOFVersion = cof_version; + llassert(cof_version >= gAgentAvatarp->mLastUpdateRequestCOFVersion); + gAgentAvatarp->mLastUpdateRequestCOFVersion = cof_version; } class LLIncrementCofVersionResponder : public LLHTTPClient::Responder @@ -3769,12 +3749,10 @@ public: llinfos << "Successfully incremented agent's COF." << llendl; S32 new_version = pContent["category"]["version"].asInteger(); - LLAppearanceMgr* app_mgr = LLAppearanceMgr::getInstance(); - // cof_version should have increased - llassert(new_version > app_mgr->mLastUpdateRequestCOFVersion); + llassert(new_version > gAgentAvatarp->mLastUpdateRequestCOFVersion); - app_mgr->mLastUpdateRequestCOFVersion = new_version; + gAgentAvatarp->mLastUpdateRequestCOFVersion = new_version; } virtual void errorWithContent(U32 pStatus, const std::string& pReason, const LLSD& content) { @@ -3909,14 +3887,14 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove) bool fUpdateAppearance = false; for (uuid_vec_t::const_iterator it = ids_to_remove.begin(); it != ids_to_remove.end(); ++it) { - const LLInventoryItem* linked_item = gInventory.getLinkedItem(*it); - if (linked_item && (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(linked_item)) ) + const LLUUID& linked_item_id = gInventory.getLinkedItemID(*it); + + if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(gInventory.getItem(linked_item_id))) ) { continue; } fUpdateAppearance = true; - const LLUUID& linked_item_id = gInventory.getLinkedItemID(*it); removeCOFItemLinks(linked_item_id); } @@ -3936,15 +3914,15 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove) void LLAppearanceMgr::removeItemFromAvatar(const LLUUID& id_to_remove) { -// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) - const LLInventoryItem* linked_item = gInventory.getLinkedItem(id_to_remove); + LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove); - if (linked_item && (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(linked_item)) ) +// [RLVa:KB] - Checked: 2013-02-12 (RLVa-1.4.8) + if ( (rlv_handler_t::isEnabled()) && (!rlvPredCanRemoveItem(gInventory.getItem(linked_item_id))) ) { return; } // [/RLVA:KB] - LLUUID linked_item_id = gInventory.getLinkedItemID(id_to_remove); + removeCOFItemLinks(linked_item_id); updateAppearanceFromCOF(); } @@ -4055,9 +4033,7 @@ LLAppearanceMgr::LLAppearanceMgr(): mAttachmentInvLinkEnabled(false), mOutfitIsDirty(false), mOutfitLocked(false), - mIsInUpdateAppearanceFromCOF(false), - mLastUpdateRequestCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), - mLastAppearanceUpdateCOFVersion(LLViewerInventoryCategory::VERSION_UNKNOWN) + mIsInUpdateAppearanceFromCOF(false) { LLOutfitObserver& outfit_observer = LLOutfitObserver::instance(); @@ -4077,7 +4053,7 @@ LLAppearanceMgr::~LLAppearanceMgr() void LLAppearanceMgr::setAttachmentInvLinkEnable(bool val) { - llinfos << "setAttachmentInvLinkEnable => " << (int) val << llendl; + LL_DEBUGS("Avatar") << "setAttachmentInvLinkEnable => " << (int) val << llendl; mAttachmentInvLinkEnabled = val; // [SL:KB] - Patch: Appearance-SyncAttach | Checked: 2010-10-05 (Catznip-2.2) if (mAttachmentInvLinkEnabled) @@ -4291,7 +4267,6 @@ public: { llwarns << "Nothing fetched in category " << mComplete.front() << llendl; - //dec_busy_count(); gInventory.removeObserver(this); doOnIdleOneTime(mCallable); diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h old mode 100755 new mode 100644 index 4b025c0fca..fa50656257 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -114,15 +114,6 @@ public: // Debugging - get truncated LLSD summary of COF contents. LLSD dumpCOF() const; - // COF version of last viewer-initiated appearance update request. - S32 mLastUpdateRequestCOFVersion; - S32 getLastUpdateRequestCOFVersion() const; - - // COF version of last appearance message received for self av. - S32 mLastAppearanceUpdateCOFVersion; - S32 getLastAppearanceUpdateCOFVersion() const; - void setLastAppearanceUpdateCOFVersion(S32 new_val); - // Finds the folder link to the currently worn outfit const LLViewerInventoryItem *getBaseOutfitLink(); bool getBaseOutfitName(std::string &name); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 1e42422179..f779363070 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" @@ -101,7 +103,6 @@ #include "llsecondlifeurls.h" #include "llupdaterservice.h" // [FS communication UI] -//#include "llcallfloater.h" #include "fsfloatervoicecontrols.h" // [FS communication UI] #include "llfloatertexturefetchdebugger.h" @@ -218,6 +219,7 @@ #include "llviewercontrol.h" #include "lleventnotifier.h" #include "llcallbacklist.h" +#include "lldeferredsounds.h" #include "pipeline.h" #include "llgesturemgr.h" #include "llsky.h" @@ -237,7 +239,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 @@ -314,8 +315,10 @@ LLTimer gLogoutTimer; static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; + S32 gPendingMetricsUploads = 0; + BOOL gDisconnected = FALSE; // used to restore texture state after a mode switch @@ -512,7 +515,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); } } @@ -583,9 +597,6 @@ static void settings_to_globals() LLBUTTON_H_PAD = gSavedSettings.getS32("ButtonHPad"); BTN_HEIGHT_SMALL = gSavedSettings.getS32("ButtonHeightSmall"); BTN_HEIGHT = gSavedSettings.getS32("ButtonHeight"); -// [SL:KB] - Patch: UI-DndButtonCommit | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c - DELAY_DRAG_HOVER_COMMIT = gSavedSettings.getF32("DragAndDropCommitDelay"); -// [/SL:KB] MENU_BAR_HEIGHT = gSavedSettings.getS32("MenuBarHeight"); MENU_BAR_WIDTH = gSavedSettings.getS32("MenuBarWidth"); @@ -693,6 +704,7 @@ LLAppViewer::LLAppViewer() : mForceGraphicsDetail(false), mQuitRequested(false), mLogoutRequestSent(false), + mYieldTime(-1), mMainloopTimeout(NULL), mAgentRegionLastAlive(false), mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), @@ -926,6 +938,7 @@ bool LLAppViewer::init() LLUI::initClass(settings_map, LLUIImageList::getInstance(), ui_audio_callback, + deferred_ui_audio_callback, &LLUI::getScaleFactor()); LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; @@ -1392,10 +1405,11 @@ bool LLAppViewer::mainLoop() LLVoiceChannel::initClass(); LLVoiceClient::getInstance()->init(gServicePump); // [FS communication UI] - //LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true); - LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&FSFloaterVoiceControls::sOnCurrentChannelChanged, _1), true); + //LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLCallFloater::sOnCurrentChannelChanged, _1), true); // CHUI merge old + //LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true); // CHUI merge new + LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&FSFloaterVoiceControls::sOnCurrentChannelChanged, _1), true); // FS origonal // [FS communication UI] - LLTimer frameTimer,idleTimer,periodicRenderingTimer; + LLTimer frameTimer,idleTimer; LLTimer debugTime; LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); joystick->setNeedsReset(true); @@ -1407,8 +1421,6 @@ bool LLAppViewer::mainLoop() // point of posting. LLSD newFrame; - BOOL restore_rendering_masks = FALSE; - //LLPrivateMemoryPoolTester::getInstance()->run(false) ; //LLPrivateMemoryPoolTester::getInstance()->run(true) ; //LLPrivateMemoryPoolTester::destroy() ; @@ -1427,28 +1439,6 @@ bool LLAppViewer::mainLoop() try { - // Check if we need to restore rendering masks. - if (restore_rendering_masks) - { - gPipeline.popRenderDebugFeatureMask(); - gPipeline.popRenderTypeMask(); - } - // Check if we need to temporarily enable rendering. - F32 periodic_rendering = gSavedSettings.getF32("ForcePeriodicRenderingTime"); - if (periodic_rendering > F_APPROXIMATELY_ZERO && periodicRenderingTimer.getElapsedTimeF64() > periodic_rendering) - { - periodicRenderingTimer.reset(); - restore_rendering_masks = TRUE; - gPipeline.pushRenderTypeMask(); - gPipeline.pushRenderDebugFeatureMask(); - gPipeline.setAllRenderTypes(); - gPipeline.setAllRenderDebugFeatures(); - } - else - { - restore_rendering_masks = FALSE; - } - pingMainloopTimeout("Main:MiscNativeWindowEvents"); if (gViewerWindow) @@ -1496,11 +1486,11 @@ bool LLAppViewer::mainLoop() // Scan keyboard for movement keys. Command keys and typing // are handled by windows callbacks. Don't do this until we're // done initializing. JC - if (gViewerWindow->getWindow()->getVisible() + if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible()) && gViewerWindow->getActive() && !gViewerWindow->getWindow()->getMinimized() && LLStartUp::getStartupState() == STATE_STARTED - && !gViewerWindow->getShowProgress() + && (gHeadlessClient || !gViewerWindow->getShowProgress()) && !gFocusMgr.focusLocked()) { joystick->scanJoystick(); @@ -1554,7 +1544,8 @@ bool LLAppViewer::mainLoop() } // Render scene. - if (!LLApp::isExiting()) + // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18 + if (!LLApp::isExiting() && !gHeadlessClient) { pingMainloopTimeout("Main:Display"); gGLActive = TRUE; @@ -1575,11 +1566,10 @@ bool LLAppViewer::mainLoop() LLFastTimer t2(FTM_SLEEP); // yield some time to the os based on command line option - S32 yield_time = gSavedSettings.getS32("YieldTime"); - if(yield_time >= 0) + if(mYieldTime >= 0) { LLFastTimer t(FTM_YIELD); - ms_sleep(yield_time); + ms_sleep(mYieldTime); } // yield cooperatively when not running as foreground window @@ -1691,26 +1681,6 @@ bool LLAppViewer::mainLoop() { gFrameStalls++; } - - // Limit FPS - F32 max_fps = gSavedSettings.getF32("MaxFPS"); - // Only limit FPS when we are actually rendering something. Otherwise - // logins, logouts and teleports take much longer to complete. - if (max_fps > F_APPROXIMATELY_ZERO && - LLStartUp::getStartupState() == STATE_STARTED && - !gTeleportDisplay && - !logoutRequestSent()) - { - // Sleep a while to limit frame rate. - F32 min_frame_time = 1.f / max_fps; - S32 milliseconds_to_sleep = llclamp((S32)((min_frame_time - frameTimer.getElapsedTimeF64()) * 1000.f), 0, 1000); - if (milliseconds_to_sleep > 0) - { - LLFastTimer t(FTM_YIELD); - ms_sleep(milliseconds_to_sleep); - } - } - frameTimer.reset(); resumeMainloopTimeout(); @@ -2156,6 +2126,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; @@ -3010,6 +2983,8 @@ bool LLAppViewer::initConfiguration() } } + mYieldTime = gSavedSettings.getS32("YieldTime"); + // Read skin/branding settings if specified. //if (! gDirUtilp->getSkinDir().empty() ) //{ @@ -3442,6 +3417,9 @@ bool LLAppViewer::initWindow() { LL_INFOS("AppInit") << "Initializing window..." << LL_ENDL; + // store setting in a global for easy access and modification + gHeadlessClient = gSavedSettings.getBOOL("HeadlessClient"); + // always start windowed BOOL ignorePixelDepth = gSavedSettings.getBOOL("IgnorePixelDepth"); @@ -5520,6 +5498,11 @@ void LLAppViewer::disconnectViewer() // close inventory interface, close all windows LLFloaterInventory::cleanup(); +// [SL:KB] - Patch: Appearance-Misc | Checked: 2013-02-12 (Catznip-3.4) + // Destroying all objects below will trigger attachment detaching code and attempt to remove the COF links for them + LLAppearanceMgr::instance().setAttachmentInvLinkEnable(false); +// [/SL:KB] + // [SL:KB] - Patch: Appearance-Misc | Checked: 2013-02-12 (Catznip-3.4) // Destroying all objects below will trigger attachment detaching code and attempt to remove the COF links for them LLAppearanceMgr::instance().setAttachmentInvLinkEnable(false); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 2ac23e6ea6..baa8826748 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -283,6 +283,7 @@ private: bool mQuitRequested; // User wants to quit, may have modified documents open. bool mLogoutRequestSent; // Disconnect message sent to simulator, no longer safe to send messages to the sim. + S32 mYieldTime; struct SettingsFiles* mSettingsLocationList; LLWatchdogTimeout* mMainloopTimeout; 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() +void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text) { -} + // make sure these returned values are cleared in case there is no replacement + replacement_start = 0; + replacement_length = 0; + replacement_string.clear(); -LLAutoReplace::~LLAutoReplace() -{ - sInstance = NULL; -} - -void LLAutoReplace::autoreplaceCallback(LLUIString& inputText, S32& cursorPos) -{ static LLCachedControl 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: "<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 { - public: - LLAutoReplace(); - ~LLAutoReplace(); +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); - /// @return a pointer to the active instance - static LLAutoReplace* getInstance(); + /// Get a copy of the current settings + LLAutoReplaceSettings getSettings(); - /// Callback that provides the hook for use in text entry methods - void autoreplaceCallback(LLUIString& inputText, S32& cursorPos); + /// Commit new settings after making changes + void setSettings(const LLAutoReplaceSettings& settings); - /// Get a copy of the current settings - LLAutoReplaceSettings getSettings(); +private: + friend class LLSingleton; + LLAutoReplace(); + /*virtual*/ void initSingleton(); - /// Commit new settings after making changes - void setSettings(const LLAutoReplaceSettings& settings); - - private: - friend class LLSingleton; - static LLAutoReplace* sInstance; ///< the active settings instance - - 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 a6705d3448..7462f1e439 100644 --- 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 @@ -62,32 +65,29 @@ #include "llpanelprofile.h" #include "llrecentpeople.h" #include "lltrans.h" -// [SL:KB] - Patch : UI-ProfileGroupFloater | Checked: 2010-09-08 (Catznip-2.1.2c) | Added: Catznip-2.1.2c #include "llviewercontrol.h" -// [/SL:KB] #include "llviewerobjectlist.h" #include "llviewermessage.h" // for handle_lure #include "llviewerregion.h" // [FS communication UI] -//#include "llimfloater.h" #include "fsfloaterim.h" +#include "fsfloaterimcontainer.h" // [FS communication UI] #include "lltrans.h" #include "llcallingcard.h" #include "llslurl.h" // IDEVO -#include "llviewercontrol.h" -#include "llfloaterreporter.h" -#include "llparcel.h" -#include "llviewerparcelmgr.h" -#include "llvoavatar.h" -#include "llworld.h" -#include "llviewermenu.h" -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h -#include "rlvhandler.h" -// [/RLVa:KB] #include "llsidepanelinventory.h" +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0) +#include "rlvactions.h" +#include "rlvcommon.h" +#include "rlvhandler.h" // FIRE-8804: Prevent opening inventory from using share in radar context menu +// [/RLVa:KB] #include "fslslbridge.h" // +#include "llworld.h" //Added: Catznip-2.4.0g +#include "llfloaterreporter.h" // [SL:KB] - Patch: UI-SidepanelPeople +#include "llparcel.h" //Added: Catznip-2.4.0g +#include "llviewerparcelmgr.h" //Added: Catznip-2.4.0g #include "fsfloaterprofile.h" #include "llfloaterregioninfo.h" #include "lltrans.h" @@ -114,7 +114,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()); } @@ -155,7 +155,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"; @@ -200,13 +200,14 @@ 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) { // [FS communication UI] - //LLIMFloater::show(session_id); - FSFloaterIM::show(session_id); + //LLFloaterIMContainer::getInstance()->showConversation(session_id); CHUI merge LL New + //LLIMFloater::show(session_id); CHUI merge LL old + FSFloaterIM::show(session_id); // FS orig // [FS communication UI] } make_ui_sound("UISndStartIM"); @@ -215,24 +216,19 @@ 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; -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(id)) ) +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + if ( (!RlvActions::canStartIM(id)) && (!RlvActions::hasOpenP2PSession(id)) ) { - LLUUID idSession = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); - if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) - { - make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); - return; - } + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); + return; } // [/RLVa:KB] - 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 @@ -251,7 +247,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) { @@ -268,25 +264,20 @@ void LLAvatarActions::startCall(const LLUUID& id) return; } -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(id)) ) +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + if ( (!RlvActions::canStartIM(id)) && (!RlvActions::hasOpenP2PSession(id)) ) { - LLUUID idSession = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); - if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) - { - make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); - return; - } + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); + return; } // [/RLVa:KB] - 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) { @@ -297,12 +288,12 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) LLDynamicArray id_array; for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) { -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0) const LLUUID& idAgent = *it; - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) + if (!RlvActions::canStartIM(idAgent)) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF); return; } id_array.push_back(idAgent); @@ -313,7 +304,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; @@ -346,18 +337,18 @@ 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 id_array; for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) { -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0) const LLUUID& idAgent = *it; - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) + if (!RlvActions::canStartIM(idAgent)) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF); return; } id_array.push_back(idAgent); @@ -365,14 +356,19 @@ 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) { // [FS communication UI] - //LLIMFloater::show(session_id); - FSFloaterIM::show(session_id); + // return; CHUI merge LL new + //LLIMFloater::show(session_id); CHUI merge LL old + FSFloaterIM::show(session_id); // CHUI merge FS orig // [FS communication UI] } + + FSFloaterIMContainer::getInstance()->showConversation(session_id); + make_ui_sound("UISndStartIM"); } @@ -384,19 +380,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); } @@ -486,19 +474,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 @@ -567,6 +555,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); @@ -661,23 +650,6 @@ namespace action_give_inventory } - static void build_residents_string(const std::vector avatar_names, std::string& residents_string) - { - llassert(avatar_names.size() > 0); - - const std::string& separator = LLTrans::getString("words_separator"); - for (std::vector::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& inventory_selected_uuids , std::string& items_string) { llassert(inventory_selected_uuids.size() > 0); @@ -842,7 +814,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); @@ -881,38 +853,85 @@ namespace action_give_inventory } } +// static +void LLAvatarActions::buildResidentsString(std::vector 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::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 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 LLAvatarActions::getInventorySelectedUUIDs() { - std::set inventory_selected_uuids; + std::set 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("inventory"); if (sidepanel_inventory) { - inventory_selected_uuids = sidepanel_inventory->getInboxSelectionList(); + inventory_selected= sidepanel_inventory->getInboxSelectionList(); } } + std::set inventory_selected_uuids; + for (std::set::iterator it = inventory_selected.begin(), end_it = inventory_selected.end(); + it != end_it; + ++it) + { + inventory_selected_uuids.insert(static_cast((*it)->getViewModelItem())->getUUID()); + } return inventory_selected_uuids; } //static -void LLAvatarActions::shareWithAvatars() +void LLAvatarActions::shareWithAvatars(LLView * panel) { using namespace action_give_inventory; -// LLFloaterAvatarPicker* picker = -// LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE); + LLFloater* root_floater = gFloaterView->getParentFloater(panel); // CHUI merge LL new +// LLFloaterAvatarPicker* picker = +// LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE); // CHUI merge old +// LLFloaterAvatarPicker::show(boost::bind(give_inventory, _1, _2), TRUE, FALSE, FALSE, root_floater->getName()); // CHUI merge LL new // if (!picker) // { // return; @@ -925,6 +944,11 @@ void LLAvatarActions::shareWithAvatars() picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable, idItems)); // [/SL:KB] picker->openFriendsTab(); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } LLNotificationsUtil::add("ShareNotification"); } @@ -943,15 +967,15 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL // check selection in the panel LLFolderView* root_folder = inv_panel->getRootFolder(); - const std::set inventory_selected_uuids = root_folder->getSelectionList(); - if (inventory_selected_uuids.empty()) return false; // nothing selected + const std::set inventory_selected = root_folder->getSelectionList(); + if (inventory_selected.empty()) return false; // nothing selected bool can_share = true; - std::set::const_iterator it = inventory_selected_uuids.begin(); - const std::set::const_iterator it_end = inventory_selected_uuids.end(); + std::set::const_iterator it = inventory_selected.begin(); + const std::set::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((*it)->getViewModelItem())->getUUID()); // any category can be offered. if (inv_cat) { @@ -959,9 +983,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(item->getListener()); + LLInvFVBridge* bridge = dynamic_cast(item->getViewModelItem()); if (bridge && bridge->canShare()) { continue; @@ -993,6 +1017,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) { @@ -1036,6 +1080,33 @@ void LLAvatarActions::inviteToGroup(const LLUUID& id) } } +// static +void LLAvatarActions::viewChatHistory(const LLUUID& id) +{ + const std::vector& conversations = LLConversationLog::instance().getConversations(); + std::vector::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 @@ -1078,7 +1149,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); @@ -1186,7 +1257,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); } @@ -1204,6 +1274,12 @@ bool LLAvatarActions::isBlocked(const LLUUID& id) return LLMuteList::getInstance()->isMuted(id, name); } +// static +bool LLAvatarActions::isVoiceMuted(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); +} + // static bool LLAvatarActions::canBlock(const LLUUID& id) { diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 30f011537c..61b87287e9 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -34,9 +34,10 @@ #include #include +class LLAvatarName; class LLInventoryPanel; class LLFloater; -class LLAvatarName; +class LLView; /** * Friend-related actions (add, remove, offer teleport, etc) @@ -82,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. @@ -121,13 +122,18 @@ public: /** * Share items with the picked avatars. */ - static void shareWithAvatars(); + static void shareWithAvatars(LLView * panel); /** * Block/unblock the avatar. */ 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 */ @@ -138,6 +144,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 */ @@ -202,6 +213,27 @@ public: * @return false if the selected items cannot be shared or the active inventory panel cannot be obtained */ 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 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); // [SL:KB] - Patch: UI-SidepanelPeople | Checked: 2010-12-02 (Catznip-2.4.0g) | Modified: Catznip-2.4.0g static void report(const LLUUID& idAgent); diff --git a/indra/newview/llavatariconctrl.cpp b/indra/newview/llavatariconctrl.cpp index eea9bec10b..eaf738481c 100644 --- a/indra/newview/llavatariconctrl.cpp +++ b/indra/newview/llavatariconctrl.cpp @@ -28,6 +28,8 @@ #include "llavatariconctrl.h" +#include + // 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" #include "llviewertexture.h" #include "llavatarappearancedefines.h" @@ -150,9 +152,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; @@ -205,6 +211,11 @@ LLAvatarIconCtrl::~LLAvatarIconCtrl() LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); // Name callbacks will be automatically disconnected since LLUICtrl is trackable } + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } //virtual @@ -247,9 +258,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() @@ -294,11 +315,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 abb0f545dc..aca925dd10 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 + +#include "../llui/lliconctrl.h" #include "llavatarpropertiesprocessor.h" #include "llviewermenu.h" @@ -90,20 +92,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 9c624bee25..41dca4371f 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" // [RLVa:KB] - Checked: 2010-06-04 (RLVa-1.2.2a) #include "rlvhandler.h" // [/RLVa:KB] @@ -476,7 +477,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) { @@ -487,9 +488,22 @@ void LLAvatarList::refresh() { // *NOTE: If you change the UI to show a different string, // be sure to change the filter code below. + + // CHUI merge LL old begin v + //if (LLRecentPeople::instance().isAvalineCaller(buddy_id)) + //{ + // const LLSD& call_data = LLRecentPeople::instance().getData(buddy_id); + // addAvalineItem(buddy_id, call_data["session_id"].asUUID(), call_data["call_number"].asString()); + //} + //else + //{ + // CHUI merge new addNewItem(buddy_id, - //av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName, - av_name.getCompleteName(), + //display_name.empty() ? waiting_str : display_name, CHUI merge new + //av_name.mDisplayName.empty() ? waiting_str : av_name.mDisplayName, CHUI merge old + av_name.getCompleteName(), // FS orig LLAvatarTracker::instance().isBuddyOnline(buddy_id)); modified = true; nadded++; @@ -515,7 +529,9 @@ void LLAvatarList::refresh() const LLUUID& buddy_id = it->asUUID(); LLAvatarName av_name; have_names &= LLAvatarNameCache::get(buddy_id, &av_name); - if (!findInsensitive(av_name.getCompleteName(), mNameFilter)) + //if (!findInsensitive(av_name.mDisplayName, mNameFilter)) CHUI merge old + //if (!findInsensitive(av_name.getDisplayName(), mNameFilter)) CHUI merge new + if (!findInsensitive(av_name.getCompleteName(), mNameFilter)) // FS orig { removeItemByUUID(buddy_id); modified = true; @@ -588,7 +604,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; } @@ -616,6 +632,7 @@ S32 LLAvatarList::notifyParent(const LLSD& info) sort(); return 1; } +// CHUI Merge check // [SL:KB] - Patch: UI-AvatarListDndShare | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c else if ( (info.has("select")) && (info["select"].isUUID()) ) { @@ -699,6 +716,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 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 selected_items; diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 86700fdd46..db8acf640b 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -97,6 +97,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); // [Ansariel: Colorful radar] void setUseRangeColors(bool UseRangeColors); diff --git a/indra/newview/llavatarlistitem.cpp b/indra/newview/llavatarlistitem.cpp index e4c19ba407..5f3a6aee0b 100644 --- a/indra/newview/llavatarlistitem.cpp +++ b/indra/newview/llavatarlistitem.cpp @@ -27,6 +27,8 @@ #include "llviewerprecompiledheaders.h" +#include + #include "llavataractions.h" #include "llavatarlistitem.h" @@ -39,12 +41,10 @@ #include "llavatarnamecache.h" #include "llavatariconctrl.h" #include "lloutputmonitorctrl.h" +#include "lltooldraganddrop.h" // [RLVa:KB] - Checked: 2010-04-05 (RLVa-1.2.2a) #include "rlvhandler.h" // [/RLVa:KB] -// [SL:KB] - Patch: UI-AvatarListDndShare | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c -#include "lltooldraganddrop.h" -// [/SL:KB] #include #include "llavatarpropertiesprocessor.h" #include "lldateutil.h" @@ -75,7 +75,8 @@ LLAvatarListItem::Params::Params() LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) -: LLPanel(), + : LLPanel(), + LLFriendObserver(), mAvatarIcon(NULL), mAvatarName(NULL), mLastInteractionTime(NULL), @@ -94,6 +95,7 @@ LLAvatarListItem::LLAvatarListItem(bool not_from_ui_factory/* = true*/) // [/RLVa:KB] mShowPermissions(false), mHovered(false), + mAvatarNameCacheConnection(), mShowVoiceVolume(false), mNearbyRange(0), mShowDisplayName(true), @@ -127,8 +129,13 @@ LLAvatarListItem::~LLAvatarListItem() if (mAvatarId.notNull()) { LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarId, this); - LLAvatarTracker::instance().removeFriendPermissionObserver(mAvatarId, this); - LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); // may try to remove null observer + //LLAvatarTracker::instance().removeFriendPermissionObserver(mAvatarId, this); // CHUI Merge are these 2 lines needed? if so, comment + //LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId, this); // may try to remove null observer + } + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); } } @@ -207,6 +214,29 @@ void LLAvatarListItem::onVolumeChange(const LLSD& data) LLVoiceClient::getInstance()->setUserVolume(mAvatarId, volume); } +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")) @@ -220,12 +250,11 @@ S32 LLAvatarListItem::notifyParent(const LLSD& info) void LLAvatarListItem::onMouseEnter(S32 x, S32 y, MASK mask) { getChildView("hovered_icon")->setVisible( true); - // AO, removed on-hover visibility. Don't do this. instead flip info buttons on full-time in postbuild. // mInfoBtn->setVisible(mShowInfoBtn); // mProfileBtn->setVisible(mShowProfileBtn); // [RLVa:KB] - Checked: 2010-04-05 (RLVa-1.2.2a) | Added: RLVa-1.2.0d - //mInfoBtn->setVisible( (mShowInfoBtn) && ((!mRlvCheckShowNames) || (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))) ); - //mProfileBtn->setVisible( (mShowProfileBtn) && ((!mRlvCheckShowNames) || (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))) ); + mInfoBtn->setVisible( (mShowInfoBtn) && ((!mRlvCheckShowNames) || (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))) ); + mProfileBtn->setVisible( (mShowProfileBtn) && ((!mRlvCheckShowNames) || (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))) ); // [/RLVa:KB] mHovered = true; @@ -275,7 +304,7 @@ void LLAvatarListItem::setOnline(bool online) } void LLAvatarListItem::setAvatarName(const std::string& name) -{ +{ setNameInternal(name, mHighlihtSubstring); } @@ -351,8 +380,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(); } // AO: Always show permissions icons, like in V1. @@ -638,8 +666,7 @@ bool LLAvatarListItem::getShowingBothNames() const void LLAvatarListItem::updateAvatarName() { - LLAvatarNameCache::get(getAvatarId(), - boost::bind(&LLAvatarListItem::onAvatarNameCache, this, _2)); + fetchAvatarName(); } void LLAvatarListItem::showAvatarAge(bool display) @@ -694,26 +721,26 @@ 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); +// setAvatarName(av_name.getDisplayName()); +// setAvatarToolTip(av_name.getUserName()); // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a bool fRlvFilter = (mRlvCheckShowNames) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)); if (mShowDisplayName && !mShowUsername) - setAvatarName( (!fRlvFilter) ? av_name.mDisplayName : RlvStrings::getAnonym(av_name) ); + setAvatarName( (!fRlvFilter) ? av_name.getDisplayName() : RlvStrings::getAnonym(av_name) ); else if (!mShowDisplayName && mShowUsername) - setAvatarName( (!fRlvFilter) ? av_name.mUsername : RlvStrings::getAnonym(av_name) ); + setAvatarName( (!fRlvFilter) ? av_name.getUserName() : RlvStrings::getAnonym(av_name) ); else setAvatarName( (!fRlvFilter) ? av_name.getCompleteName() : RlvStrings::getAnonym(av_name) ); // NOTE: If you change this, you will break sorting the contacts list // by username unless you go change the comparator too. -- TS - setAvatarToolTip( (!fRlvFilter) ? av_name.mUsername : RlvStrings::getAnonym(av_name) ); + setAvatarToolTip( (!fRlvFilter) ? av_name.getUserName() : RlvStrings::getAnonym(av_name) ); // TODO-RLVa: bit of a hack putting this here. Maybe find a better way? mAvatarIcon->setDrawTooltip(!fRlvFilter); // [/RLVa:KB] //KC - store the username for use in sorting - mUserName = av_name.mUsername; + mUserName = 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 09b25855eb..3f27a8386c 100644 --- a/indra/newview/llavatarlistitem.h +++ b/indra/newview/llavatarlistitem.h @@ -27,6 +27,8 @@ #ifndef LL_LLAVATARLISTITEM_H #define LL_LLAVATARLISTITEM_H +#include + #include "llpanel.h" #include "lloutputmonitorctrl.h" #include "llbutton.h" @@ -87,6 +89,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); @@ -302,6 +305,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("item_name"); + mTitleCtrl->setValue(mItemName); + + switch (mMuteType) + { + case LLMute::AGENT: + case LLMute::EXTERNAL: + { + LLAvatarIconCtrl* avatar_icon = getChild("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mItemID); + } + break; + case LLMute::GROUP: + { + LLGroupIconCtrl* group_icon = getChild("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mItemID); + } + break; + case LLMute::OBJECT: + case LLMute::BY_NAME: + getChild("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 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( + "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 mutes = LLMuteList::instance().getMutes(); + std::vector::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(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(item1); + const LLBlockedListItem* blocked_item2 = dynamic_cast(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(){}; + }; + + 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 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/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp deleted file mode 100644 index 3e9268a1d5..0000000000 --- a/indra/newview/llcallfloater.cpp +++ /dev/null @@ -1,825 +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" - -#if 0 - -#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 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("speakers_list"); - mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this)); - - childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this)); - - mNonAvatarCaller = findChild("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("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 ("my_panel"); - - if ( mAgentPanel ) - { - mAgentPanel->getChild("user_icon")->setValue(gAgentID); - - // Just use display name, because it's you - LLAvatarName av_name; - LLAvatarNameCache::get( gAgentID, &av_name ); - mAgentPanel->getChild("user_text")->setValue(av_name.mDisplayName); - - mSpeakingIndicator = mAgentPanel->getChild("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("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 participants; - LLVoiceClient::getInstance()->getParticipantList(participants); - - for (std::set::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 items; - mAvatarList->getItems(items); - std::vector::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(*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 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 items; - mAvatarList->getItems(items); - std::vector::const_iterator - it = items.begin(), - it_end = items.end(); - - for(; it != it_end; ++it) - { - LLAvatarListItem *item = dynamic_cast(*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 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 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: 00912Audio - - 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 - -#endif diff --git a/indra/newview/llcallfloater.h b/indra/newview/llcallfloater.h deleted file mode 100644 index 2273aa3f2d..0000000000 --- a/indra/newview/llcallfloater.h +++ /dev/null @@ -1,276 +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$ - */ - -#if 0 - -#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 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 - -#endif diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 8de177442f..c6f477e304 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" @@ -62,15 +63,16 @@ #include "llavataractions.h" #include "lggcontactsets.h" +// CHUI Merge varify if any of the below are still needed // Remove floating chat bar // #include "llnearbychat.h" // [FS communication UI] //#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" +#include "fsfloaternearbychat.h" // CHUI Merge can we get rid if this? line 903 // [FS communication UI] // -#include "llfloaterreg.h" -#include "llnotificationmanager.h" +//#include "llfloaterreg.h" +//#include "llnotificationmanager.h" // [FS communication UI] //#include "llimfloater.h" #include "fsfloaterim.h" @@ -803,9 +805,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online) // { // 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; @@ -838,12 +838,14 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, else if (UseDisplayNames) // { - args["NAME"] = av_name.mDisplayName; + args["NAME"] = av_name.getDisplayName(); } else { args["NAME"] = av_name.getLegacyName(); } + + args["STATUS"] = online ? LLTrans::getString("OnlineStatus") : LLTrans::getString("OfflineStatus"); // CHUI merge check location, is right under 'args["NAME"] = av_name.getDisplayName();' in LL args["AGENT-ID"] = agent_id; @@ -853,7 +855,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, if (online) { notification = - LLNotificationsUtil::add("FriendOnline", + LLNotificationsUtil::add("FriendOnlineOffline", args, payload.with("respond_on_mousedown", TRUE), boost::bind(&LLAvatarActions::startIM, agent_id)); @@ -861,7 +863,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. @@ -893,7 +895,7 @@ static void on_avatar_name_cache_notify(const LLUUID& agent_id, LLChat chat; chat.mText = notify_msg; chat.mSourceType = CHAT_SOURCE_SYSTEM; - args["type"] = LLNotificationsUI::NT_NEARBYCHAT; + args["type"] = LLNotificationsUI::NT_NEARBYCHAT; // CHUI Merge error- has no member NT_NEARBYCHAT if (history_only) { // [FS communication UI] @@ -1056,28 +1058,28 @@ bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship LLAvatarName av_name; LLAvatarNameCache::get( buddy_id, &av_name); // Friend names on worldmap should respect display name settings - //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)) - { - // Friend names on worldmap should respect display name settings - //mMappable.insert(value); - - // Attempt to speed up things a little - // if (LLAvatarNameCache::useDisplayNames() && gSavedSettings.getBOOL("NameTagShowUsernames")) + { + // // Friend names on worldmap should respect display name settings + // //mMappable.insert(value); + // + // // Attempt to speed up things a little + // // if (LLAvatarNameCache::useDisplayNames() && gSavedSettings.getBOOL("NameTagShowUsernames")) static LLCachedControl NameTagShowUsernames(gSavedSettings, "NameTagShowUsernames"); - if (LLAvatarNameCache::useDisplayNames() && NameTagShowUsernames) - // + if (LLAvatarName::useDisplayNames() && NameTagShowUsernames) + // // { buddy_map_t::value_type value(av_name.getCompleteName(), buddy_id); mMappable.insert(value); } else { - buddy_map_t::value_type value(av_name.mDisplayName, buddy_id); + buddy_map_t::value_type value(av_name.getDisplayName(), buddy_id); mMappable.insert(value); } - // - } + // // + //} return true; } @@ -1096,7 +1098,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 98ad0836ea..2e9d33fd4f 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" @@ -149,6 +150,9 @@ void LLChannelManager::onLoginCompleted() } LLPersistentNotificationStorage::getInstance()->loadNotifications(); + + LLDoNotDisturbNotificationStorage::getInstance()->initialize(); + LLDoNotDisturbNotificationStorage::getInstance()->loadNotifications(); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 2d2f4754d1..345a502eb7 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -30,6 +30,8 @@ #include "llchathistory.h" +#include + #include "llavatarnamecache.h" #include "llinstantmessage.h" @@ -58,8 +60,11 @@ #include "llworld.h" #include "lluiconstants.h" #include "llstring.h" - +#include "llurlaction.h" #include "llviewercontrol.h" +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.0f) +#include "rlvcommon.h" +// [/RLVa:KB] static LLDefaultChildRegistry::Register r("chat_history"); @@ -91,6 +96,10 @@ public: LLSD payload; payload["object_id"] = object_id; payload["owner_id"] = query_map["owner"]; +// [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a + if (query_map.has("rlv_shownames")) + payload["rlv_shownames"] = query_map["rlv_shownames"]; +// [/RLVa:KB] payload["name"] = query_map["name"]; payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]); payload["group_owned"] = query_map["groupowned"]; @@ -105,6 +114,10 @@ class LLChatHistoryHeader: public LLPanel public: LLChatHistoryHeader() : LLPanel(), +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + mShowContextMenu(true), + mShowInfoCtrl(true), +// [/RLVa:KB] mPopupMenuHandleAvatar(), mPopupMenuHandleObject(), mAvatarID(), @@ -114,7 +127,8 @@ public: mMinUserNameWidth(0), mUserNameFont(NULL), mUserNameTextBox(NULL), - mTimeBoxTextBox(NULL) + mTimeBoxTextBox(NULL), + mAvatarNameCacheConnection() {} static LLChatHistoryHeader* createInstance(const std::string& file_name) @@ -128,6 +142,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) @@ -147,8 +166,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) @@ -228,7 +259,11 @@ public: void showInspector() { - if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType) return; +// if (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType) return; +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + // Don't double-click show the inspector if we're not showing the info control + if ( (!mShowInfoCtrl) || (mAvatarID.isNull() && CHAT_SOURCE_SYSTEM != mSourceType) ) return; +// [/RLVa:KB] if (mSourceType == CHAT_SOURCE_OBJECT) { @@ -288,9 +323,24 @@ 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)); +// user_name->setValue( LLSD() ); +// fetchAvatarName(); +// [RLVa:KB] - Checked: 2010-11-01 (RLVa-1.2.2a) | Added: RLVa-1.2.2a + if (!chat.mRlvNamesFiltered) + { + user_name->setValue( LLSD() ); + fetchAvatarName(); + } + else + { + // If the agent's chat was subject to @shownames=n we should display their anonimized name + mFrom = chat.mFromName; + user_name->setValue(mFrom); + user_name->setToolTip(mFrom); + setToolTip(mFrom); + updateMinUserNameWidth(); + } +// [/RLVa:KB] } else if (chat.mChatStyle == CHAT_STYLE_HISTORY || mSourceType == CHAT_SOURCE_AGENT) @@ -342,6 +392,15 @@ public: if(mSourceType != CHAT_SOURCE_AGENT || mAvatarID.isNull()) icon->setDrawTooltip(false); +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + // Don't show the context menu, info control or avatar icon tooltip if this chat was subject to @shownames=n + if ( (chat.mRlvNamesFiltered) && ((CHAT_SOURCE_AGENT == mSourceType) || (CHAT_SOURCE_OBJECT == mSourceType)) ) + { + mShowInfoCtrl = mShowContextMenu = false; + icon->setDrawTooltip(false); + } +// [/RLVa:KB] + switch (mSourceType) { case CHAT_SOURCE_AGENT: @@ -391,16 +450,7 @@ public: S32 user_name_width = user_name_rect.getWidth(); S32 time_box_width = time_box->getRect().getWidth(); - if (time_box->getVisible() && user_name_width <= mMinUserNameWidth) - { - time_box->setVisible(FALSE); - - user_name_rect.mRight += time_box_width; - user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight()); - user_name->setRect(user_name_rect); - } - - if (!time_box->getVisible() && user_name_width > mMinUserNameWidth + time_box_width) + if (!time_box->getVisible() && user_name_width > mMinUserNameWidth) { user_name_rect.mRight -= time_box_width; user_name->reshape(user_name_rect.getWidth(), user_name_rect.getHeight()); @@ -422,36 +472,15 @@ public: } } - void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) - { - mFrom = av_name.mDisplayName; - - LLTextBox* user_name = getChild("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; void showContextMenu(S32 x,S32 y) { +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + if (!mShowContextMenu) + return; +// [/RLVa:KB] if(mSourceType == CHAT_SOURCE_SYSTEM) showSystemContextMenu(x,y); if(mAvatarID.notNull() && mSourceType == CHAT_SOURCE_AGENT) @@ -503,6 +532,9 @@ protected: void showInfoCtrl() { if (mAvatarID.isNull() || mFrom.empty() || CHAT_SOURCE_SYSTEM == mSourceType) return; +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + if ( (!mShowInfoCtrl) || (mAvatarID.isNull() || mFrom.empty() || CHAT_SOURCE_SYSTEM == mSourceType) ) return; +// [/RLVa:KB] if (!sInfoCtrl) { @@ -561,6 +593,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("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 mPopupMenuHandleAvatar; LLHandle mPopupMenuHandleObject; @@ -572,11 +644,18 @@ protected: EChatSourceType mSourceType; std::string mFrom; LLUUID mSessionID; +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.2a) | Added: RLVa-1.2.0f + bool mShowContextMenu; + bool mShowInfoCtrl; +// [/RLVa:KB] S32 mMinUserNameWidth; const LLFontGL* mUserNameFont; LLTextBox* mUserNameTextBox; LLTextBox* mTimeBoxTextBox; + +private: + boost::signals2::connection mAvatarNameCacheConnection; }; LLUICtrl* LLChatHistoryHeader::sInfoCtrl = NULL; @@ -593,7 +672,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(); @@ -603,6 +683,14 @@ LLChatHistory::LLChatHistory(const LLChatHistory::Params& p) mEditor = LLUICtrlFactory::create(editor_params, this); } +LLSD LLChatHistory::getValue() const +{ + LLSD* text=new LLSD(); + text->assign(mEditor->getText()); + return *text; + +} + LLChatHistory::~LLChatHistory() { this->clear(); @@ -704,6 +792,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) @@ -711,9 +800,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); @@ -743,16 +833,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); @@ -775,64 +872,123 @@ 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()) { - // for object IMs, create a secondlife:///app/objectim SLapp - std::string url = LLViewerChat::getSenderSLURL(chat, args); +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.0f) | Added: RLVa-1.2.0f + // NOTE-RLVa: we don't need to do any @shownames or @showloc filtering here because we'll already have an existing URL + std::string url = chat.mURL; + RLV_ASSERT( (url.empty()) || (std::string::npos != url.find("objectim")) ); + if ( (url.empty()) || (std::string::npos == url.find("objectim")) ) + { +// [/RLVa:KB] + // for object IMs, create a secondlife:///app/objectim SLapp + /*std::string*/ url = LLViewerChat::getSenderSLURL(chat, args); +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.0f) | Added: RLVa-1.2.0f + } +// [/RLVa:KB] // 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) +// else if (chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log) +// [RLVa:KB] - Checked: 2010-04-22 (RLVa-1.2.0f) | Added: RLVa-1.2.0f + else if (chat.mFromName != SYSTEM_FROM && chat.mFromID.notNull() && !message_from_log && !chat.mRlvNamesFiltered) +// [/RLVa:KB] { - 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("" + chat.mFromName + "" + delimiter, false, style_params); + mEditor->appendText("" + chat.mFromName + "" + 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; @@ -853,7 +1009,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 @@ -882,41 +1038,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 (*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(); @@ -933,6 +1064,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; @@ -940,7 +1073,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() && @@ -955,13 +1088,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 e396c7036d..99ce64bcea 100644 --- a/indra/newview/llchathistory.h +++ b/indra/newview/llchathistory.h @@ -62,6 +62,8 @@ class LLChatHistory : public LLUICtrl Optional more_chat_text; + Optional notify_unread_msg; + Params() : message_header("message_header"), message_separator("message_separator"), @@ -73,7 +75,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) {} }; @@ -102,7 +105,7 @@ class LLChatHistory : public LLUICtrl public: ~LLChatHistory(); - + LLSD getValue() const; void initFromParams(const Params&); /** @@ -124,6 +127,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 6b6876df50..6ed1939429 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" // Remove floating chat bar +#include "llfloaterimnearbychat.h" #include "llviewercontrol.h" #include "llagentdata.h" @@ -46,11 +46,6 @@ #include "rlvhandler.h" // [/RLVa:KB] -// [FS communication UI] -//#include "llfloaternearbychat.h" // Remove floating chat bar -#include "fsfloaternearbychat.h" -// [FS communication UI] - static const S32 msg_left_offset = 10; static const S32 msg_right_offset = 10; static const S32 msg_height_pad = 5; @@ -90,23 +85,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("msg_text", false); - LLUICtrl* icon = getChild("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("msg_text", false); + LLUICtrl* icon = findChild("avatar_icon", false); + + if (!msg_text || !icon) + { + return; + } LLRect msg_text_rect = msg_text->getRect(); LLRect avatar_rect = icon->getRect(); @@ -123,12 +125,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 @@ -183,7 +185,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 @@ -290,7 +292,7 @@ void LLNearbyChatToastPanel::init(LLSD& notification) mIsDirty = true;//will set Avatar Icon in draw } -void LLNearbyChatToastPanel::snapToMessageHeight () +void LLFloaterIMNearbyChatToastPanel::snapToMessageHeight () { LLChatMsgBox* text_box = getChild("msg_text", false); S32 new_height = llmax (text_box->getTextPixelHeight() + 2*text_box->getVPad() + 2*msg_height_pad, 25); @@ -305,22 +307,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 @@ -340,50 +342,16 @@ BOOL LLNearbyChatToastPanel::handleMouseUp (S32 x, S32 y, MASK mask) return TRUE; else { - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->showHistory(); - // [FS communication UI] - //LLFloaterNearbyChat::getInstance()->setVisible(TRUE); - FSFloaterNearbyChat::getInstance()->setVisible(TRUE); - // [FS communication UI] - // - - // If nearby chat history is docked, we also need - // to open the container floater (FIRE-6265) - if (!gSavedSettings.getBOOL("ChatHistoryTornOff")) - { - // [FS communication UI] - //LLFloaterReg::showInstance("im_container"); - LLFloaterReg::showInstance("fs_im_container"); - // [FS communication UI] - } - // + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return FALSE; } } - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->showHistory(); - // [FS communication UI] - //LLFloaterNearbyChat::getInstance()->setVisible(TRUE); - FSFloaterNearbyChat::getInstance()->setVisible(TRUE); - // [FS communication UI] - // - - // If nearby chat history is docked, we also need - // to open the container floater (FIRE-6265) - if (!gSavedSettings.getBOOL("ChatHistoryTornOff")) - { - // [FS communication UI] - //LLFloaterReg::showInstance("im_container"); - LLFloaterReg::showInstance("fs_im_container"); - // [FS communication UI] - } - // + (LLFloaterReg::getTypedInstance("nearby_chat"))->showHistory(); return LLPanel::handleMouseUp(x,y,mask); } -void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) +void LLFloaterIMNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) { LLUICtrl* icon = getChild("avatar_icon", false); if(icon) @@ -391,7 +359,7 @@ void LLNearbyChatToastPanel::setHeaderVisibility(EShowItemHeader e) } -bool LLNearbyChatToastPanel::canAddText () +bool LLFloaterIMNearbyChatToastPanel::canAddText () { LLChatMsgBox* msg_text = findChild("msg_text"); if(!msg_text) @@ -399,7 +367,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("avatar_icon", false); @@ -411,8 +379,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("avatar_icon", false); @@ -433,7 +403,6 @@ void LLNearbyChatToastPanel::draw() } mIsDirty = false; } - LLToastPanelBase::draw(); } diff --git a/indra/newview/llchatitemscontainerctrl.h b/indra/newview/llchatitemscontainerctrl.h index f1ae1fe283..f239098024 100644 --- a/indra/newview/llchatitemscontainerctrl.h +++ b/indra/newview/llchatitemscontainerctrl.h @@ -40,10 +40,10 @@ typedef enum e_show_item_header CHATITEMHEADER_SHOW_BOTH } EShowItemHeader; -class LLNearbyChatToastPanel: public LLToastPanelBase +class LLFloaterIMNearbyChatToastPanel : public LLPanel { protected: - LLNearbyChatToastPanel() + LLFloaterIMNearbyChatToastPanel() : mIsDirty(false), // [RLVa:KB] - Checked: 2010-04-200 (RLVa-1.2.0f) | Added: RLVa-1.2.0f @@ -52,9 +52,9 @@ protected: 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 57218f7624..960995205e 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -27,38 +27,24 @@ #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 "llfloaterimsession.h" +#include "llfloaterimcontainer.h" // [FS communication UI] -//#include "llimfloater.h" +//#include "llimfloater.h" CHUI Merge LL removed this #include "fsfloaterim.h" // [FS communication UI] -#include "llimview.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" +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) +#include "llchannelmanager.h" +// [/SL:KB] static LLDefaultChildRegistry::Register t1("chiclet_panel"); -static LLDefaultChildRegistry::Register t2_0("chiclet_im_well"); static LLDefaultChildRegistry::Register t2("chiclet_notification"); -static LLDefaultChildRegistry::Register t3("chiclet_im_p2p"); -static LLDefaultChildRegistry::Register t4("chiclet_im_group"); -static LLDefaultChildRegistry::Register t5("chiclet_im_adhoc"); static LLDefaultChildRegistry::Register t6("chiclet_script"); static LLDefaultChildRegistry::Register t7("chiclet_offer"); @@ -69,65 +55,10 @@ boost::signals2::signal 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; @@ -135,30 +66,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(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) @@ -193,7 +118,7 @@ void LLSysWellChiclet::setToggleState(BOOL toggled) { mButton->setToggleState(toggled); } -void LLSysWellChiclet::changeLitState() +void LLSysWellChiclet::changeLitState(bool blink) { setNewMessagesState(!mIsNewMessagesState); } @@ -237,141 +162,26 @@ BOOL LLSysWellChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask) return TRUE; } -/************************************************************************/ -/* 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 - ("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(); - // [FS communication UI] - //const bool im_not_visible = !LLFloaterReg::instanceVisible("im_container") - // && !LLFloaterReg::instanceVisible("impanel", session_id); - const bool im_not_visible = !LLFloaterReg::instanceVisible("fs_im_container") - && !LLFloaterReg::instanceVisible("fs_impanel", session_id); - // [FS communication UI] - - 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(); if("close all" == action) { LLNotificationWellWindow::getInstance()->closeAll(); + LLIMWellWindow::getInstance()->closeAll(); } } @@ -414,6 +224,34 @@ 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; + } + +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + if ("offer" == notification->getType()) + { + // Assume that any offer notification with "getCanBeStored() == true" is the result of RLVa routing it to the notifcation syswell + /*const*/ LLNotificationsUI::LLScreenChannel* pChannel = LLNotificationsUI::LLChannelManager::instance().getNotificationScreenChannel(); + /*const*/ LLNotificationsUI::LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification->getID()) : NULL; + return (pToast) && (pToast->getCanBeStored()); + } +// [/SL:KB] + + return false; +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -429,12 +267,6 @@ LLChiclet::LLChiclet(const Params& p) , mSessionId(LLUUID::null) , mShowCounter(p.show_counter) { - -} - -LLChiclet::~LLChiclet() -{ - } boost::signals2::connection LLChiclet::setLeftButtonClickCallback( @@ -469,7 +301,9 @@ LLSD LLChiclet::getValue() const void LLChiclet::setValue(const LLSD& value) { if(value.isUUID()) + { setSessionId(value.asUUID()); + } } ////////////////////////////////////////////////////////////////////////// @@ -481,12 +315,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*/ @@ -497,16 +328,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) { @@ -517,87 +338,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) @@ -615,10 +362,10 @@ bool LLIMChiclet::getShowNewMessagesIcon() void LLIMChiclet::onMouseDown() { // [FS communication UI] - //LLIMFloater::toggle(getSessionId()); + //LLFloaterIMSession::toggle(getSessionId()); CHUI merge new + //LLIMFloater::toggle(getSessionId()); CHUI Merge old FSFloaterIM::toggle(getSessionId()); // [FS communication UI] - setCounter(0); } void LLIMChiclet::setToggleState(bool toggle) @@ -626,52 +373,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) @@ -703,388 +404,6 @@ bool LLIMChiclet::canCreateMenu() return true; } -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -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(button_params); - addChild(mChicletButton); - - LLIconCtrl::Params new_msg_params = p.new_message_icon; - mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); - addChild(mNewMessagesIcon); - - LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon; - mChicletIconCtrl = LLUICtrlFactory::create(avatar_params); - addChild(mChicletIconCtrl); - - LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; - mCounterCtrl = LLUICtrlFactory::create(unread_params); - addChild(mCounterCtrl); - - setCounter(getCounter()); - setShowCounter(getShowCounter()); - - LLChicletSpeakerCtrl::Params speaker_params = p.speaker; - mSpeakerCtrl = LLUICtrlFactory::create(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; - - // [FS communication UI] - //LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId()); - FSFloaterIM* open_im_floater = FSFloaterIM::findInstance(getSessionId()); - // [FS communication UI] - bool open_window_exists = open_im_floater && open_im_floater->getVisible(); - mPopupMenu->getChild("Send IM")->setEnabled(!open_window_exists); - - bool is_friend = LLAvatarActions::isFriend(getOtherParticipantId()); - mPopupMenu->getChild("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 - ("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(button_params); - addChild(mChicletButton); - - LLIconCtrl::Params new_msg_params = p.new_message_icon; - mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); - addChild(mNewMessagesIcon); - - LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon; - mChicletIconCtrl = LLUICtrlFactory::create(avatar_params); - //Make the avatar modified - mChicletIconCtrl->setColor(p.avatar_icon_color); - addChild(mChicletIconCtrl); - - LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; - mCounterCtrl = LLUICtrlFactory::create(unread_params); - addChild(mCounterCtrl); - - setCounter(getCounter()); - setShowCounter(getShowCounter()); - - LLChicletSpeakerCtrl::Params speaker_params = p.speaker; - mSpeakerCtrl = LLUICtrlFactory::create(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 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 - ("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(button_params); - addChild(mChicletButton); - - LLIconCtrl::Params new_msg_params = p.new_message_icon; - mNewMessagesIcon = LLUICtrlFactory::create(new_msg_params); - addChild(mNewMessagesIcon); - - LLChicletGroupIconCtrl::Params avatar_params = p.group_icon; - mChicletIconCtrl = LLUICtrlFactory::create(avatar_params); - addChild(mChicletIconCtrl); - - LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; - mCounterCtrl = LLUICtrlFactory::create(unread_params); - addChild(mCounterCtrl); - - setCounter(getCounter()); - setShowCounter(getShowCounter()); - - LLChicletSpeakerCtrl::Params speaker_params = p.speaker; - mSpeakerCtrl = LLUICtrlFactory::create(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 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; - - // [FS communication UI] - //LLIMFloater* open_im_floater = LLIMFloater::findInstance(getSessionId()); - FSFloaterIM* open_im_floater = FSFloaterIM::findInstance(getSessionId()); - // [FS communication UI] - bool open_window_exists = open_im_floater && open_im_floater->getVisible(); - mPopupMenu->getChild("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 - ("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); - } -} - - ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -1131,31 +450,15 @@ LLChicletPanel::~LLChicletPanel() void LLChicletPanel::onMessageCountChanged(const LLSD& data) { - LLUUID session_id = data["session_id"].asUUID(); - S32 unread = data["participant_unread"].asInteger(); + // *TODO : we either suppress this method or return a value. Right now, it servers no purpose. + /* - // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); - FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); - // [FS communication UI] - if (im_floater && im_floater->getVisible() && im_floater->hasFocus()) - { - unread = 0; - } - - std::list chiclets = LLIMChiclet::sFindChicletsSignal(session_id); - std::list::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; - } - } + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); + //if (im_floater && im_floater->getVisible() && im_floater->hasFocus()) + //{ + // unread = 0; + //} + */ } void LLChicletPanel::objectChicletCallback(const LLSD& data) @@ -1170,10 +473,6 @@ void LLChicletPanel::objectChicletCallback(const LLSD& data) LLIMChiclet* chiclet = dynamic_cast(*iter); if (chiclet != NULL) { - if(data.has("unread")) - { - chiclet->setCounter(data["unread"]); - } chiclet->setShowNewMessagesIcon(new_message); } } @@ -1215,31 +514,17 @@ void LLChicletPanel::onCurrentVoiceChannelChanged(const LLUUID& session_id) LLIMChiclet* chiclet = dynamic_cast(*it); if(chiclet) { - chiclet->setShowSpeaker(true); if (gSavedSettings.getBOOL("OpenIMOnVoice")) { // [FS communication UI] - //LLIMFloater::show(chiclet->getSessionId()); + //LLFloaterIMContainer::getInstance()->showConversation(session_id); CHUI Merge new + //LLIMFloater::show(chiclet->getSessionId()); CHUI Merge old FSFloaterIM::show(chiclet->getSessionId()); // [FS communication UI] } } } - 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::iterator it = chiclets.begin(); it != chiclets.end(); ++it) - { - LLIMChiclet* chiclet = dynamic_cast(*it); - if(chiclet) - { - chiclet->setShowSpeaker(false); - } - } - } - s_previous_active_voice_session_id = session_id; } @@ -1713,7 +998,8 @@ bool LLChicletPanel::isAnyIMFloaterDoked() != mChicletList.end(); it++) { // [FS communication UI] - //LLIMFloater* im_floater = LLFloaterReg::findTypedInstance( + //LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance( CHUI Merge new + //LLIMFloater* im_floater = LLFloaterReg::findTypedInstance( CHUI Merge old // "impanel", (*it)->getSessionId()); FSFloaterIM* im_floater = LLFloaterReg::findTypedInstance( "fs_impanel", (*it)->getSessionId()); @@ -1729,89 +1015,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(*it); - if(chiclet) - { - count += chiclet->getCounter(); - } - } - return count; -} - ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// LLChicletNotificationCounterCtrl::Params::Params() -: max_displayed_count("max_displayed_count", 99) + : 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) -{ - if(value.isInteger()) - setCounter(value.asInteger()); -} - -LLSD LLChicletNotificationCounterCtrl::getValue() const -{ - return LLSD(getCounter()); -} - ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// - LLChicletAvatarIconCtrl::LLChicletAvatarIconCtrl(const Params& p) : LLAvatarIconCtrl(p) { @@ -1821,29 +1035,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) @@ -1866,15 +1057,6 @@ void LLChicletInvOfferIconCtrl::setValue(const LLSD& value ) ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -LLChicletSpeakerCtrl::LLChicletSpeakerCtrl(const Params&p) - : LLOutputMonitorCtrl(p) -{ -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - LLScriptChiclet::Params::Params() : icon("icon") , chiclet_button("chiclet_button") @@ -1910,11 +1092,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()); @@ -1993,11 +1170,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 { /** - * 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 max_displayed_count; Params(); @@ -119,35 +116,6 @@ protected: friend class LLUICtrlFactory; }; -/** - * Class for displaying group's icon in Group chiclet. - */ -class LLChicletGroupIconCtrl : public LLIconCtrl -{ -public: - - struct Params : public LLInitParam::Block - { - Optional 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. */ @@ -181,23 +149,6 @@ private: std::string mDefaultIcon; }; -/** - * Class for displaying of speaker's voice indicator - */ -class LLChicletSpeakerCtrl : public LLOutputMonitorCtrl -{ -public: - - struct Params : public LLInitParam::Block - { - Params(){}; - }; -protected: - - LLChicletSpeakerCtrl(const Params&p); - friend class LLUICtrlFactory; -}; - /** * Base class for all chiclets. */ @@ -213,7 +164,7 @@ public: Params(); }; - /*virtual*/ ~LLChiclet(); + virtual ~LLChiclet() {} /** * Associates chat session id with chiclet. @@ -225,26 +176,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. */ @@ -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,51 +271,10 @@ 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. */ @@ -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 - { - Optional chiclet_button; - - Optional avatar_icon; - - Optional unread_notifications; - - Optional speaker; - - Optional new_message_icon; - - Optional 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 - { - Optional chiclet_button; - - Optional avatar_icon; - - Optional unread_notifications; - - Optional speaker; - - Optional new_message_icon; - - Optional show_speaker; - - Optional 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 */ @@ -707,89 +447,6 @@ private: LLChicletInvOfferIconCtrl* mChicletIconCtrl; }; -/** - * Implements Group chat chiclet. - */ -class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver -{ -public: - - struct Params : public LLInitParam::Block - { - Optional chiclet_button; - - Optional group_icon; - - Optional unread_notifications; - - Optional speaker; - - Optional new_message_icon; - - Optional 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 @@ -797,7 +454,7 @@ private: class LLSysWellChiclet : public LLChiclet { public: - + struct Params : public LLInitParam::Block { Optional 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{}; - + 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 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 6ad89a1852..2ea50d0dbf 100644 --- a/indra/newview/llchicletbar.cpp +++ b/indra/newview/llchicletbar.cpp @@ -25,17 +25,12 @@ */ #include "llviewerprecompiledheaders.h" // must be first include - #include "llchicletbar.h" -// library includes -#include "llfloaterreg.h" -#include "lllayoutstack.h" - -// newview includes #include "llchiclet.h" +#include "lllayoutstack.h" // [FS communication UI] -//#include "llimfloater.h" // for LLIMFloater +//#include "llimfloater.h" // for LLIMFloater CHUI Merge LL removed this line #include "fsfloaterim.h" // [FS communication UI] #include "llpaneltopinfobar.h" @@ -60,113 +55,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::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(session_id); - case LLIMChiclet::TYPE_GROUP: - return getChicletPanel()->createChiclet(session_id); - case LLIMChiclet::TYPE_AD_HOC: - return getChicletPanel()->createChiclet(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(session_id)) return; - - LLIMChiclet* chiclet = createIMChiclet(session_id); - if(chiclet) - { - chiclet->setIMSessionName(name); - chiclet->setOtherParticipantId(other_participant_id); - - // [FS communication UI] - //LLIMFloater::onIMChicletCreated(session_id); - FSFloaterIM::onIMChicletCreated(session_id); - // [FS communication UI] - - } - 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 - // [FS communication UI] - //LLIMFloater* iMfloater = LLFloaterReg::findTypedInstance("impanel", session_id); - FSFloaterIM* iMfloater = LLFloaterReg::findTypedInstance("fs_impanel", session_id); - // [FS communication UI] - 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(old_session_id); - if (chiclet) chiclet->setSessionId(new_session_id); - } -} - -S32 LLChicletBar::getTotalUnreadIMCount() -{ - return getChicletPanel()->getTotalUnreadIMCount(); -} - BOOL LLChicletBar::postBuild() { mToolbarStack = getChild("toolbar_stack"); mChicletPanel = getChild("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 6123929a67..f30a305cca 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 , public LLPanel - , public LLIMSessionObserver { LOG_CLASS(LLChicletBar); friend class LLSingleton; 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/llcofwearables.cpp b/indra/newview/llcofwearables.cpp old mode 100755 new mode 100644 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 +#include + +#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(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 +#include + +#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 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 +#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& 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 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(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::const_iterator iter = mObservers.begin(); + for (; iter != mObservers.end(); ++iter) + { + (*iter)->changed(); + } +} + +void LLConversationLog::notifyParticularConversationObservers(const LLUUID& session_id, U32 mask) +{ + std::set::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, LLIMSessionObserver +{ + friend class LLSingleton; +public: + + void removeConversation(const LLConversation& conversation); + + /** + * Returns first conversation with matched session_id + */ + const LLConversation* getConversation(const LLUUID& session_id); + const std::vector& 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& 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 conversations_vec_t; + std::vector mConversations; + std::set 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 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( + "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& conversations = log_instance.getConversations(); + std::vector::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(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 panels; + LLFlatListViewEx::getItems(panels); + std::vector::iterator iter = panels.begin(); + + for (; iter != panels.end(); ++iter) + { + LLConversationLogListItem* item = dynamic_cast(*iter); + if (item && session_id == item->getConversation()->getSessionID()) + { + return item; + } + } + + return NULL; +} + +LLConversationLogList::ESortOrder LLConversationLogList::getSortOrder() +{ + return static_cast(gSavedSettings.getU32("CallLogSortOrder")); +} + +bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const +{ + const LLConversationLogListItem* conversation_item1 = dynamic_cast(item1); + const LLConversationLogListItem* conversation_item2 = dynamic_cast(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(){}; + }; + + 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 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("conversation_name"); + mConversationName->setValue(mConversation->getConversationName()); + + // set conversation date and time + mConversationDate = getChild("date_time"); + mConversationDate->setValue(mConversation->getTimestamp()); + + getChild("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("avatar_icon"); + avatar_icon->setVisible(TRUE); + avatar_icon->setValue(mConversation->getParticipantID()); + break; + } + case LLIMModel::LLIMSession::GROUP_SESSION: + { + LLGroupIconCtrl* group_icon = getChild("group_icon"); + group_icon->setVisible(TRUE); + group_icon->setValue(mConversation->getSessionID()); + break; + } + default: + break; + } + + if (mConversation->hasOfflineMessages()) + { + getChild("unread_ims_icon")->setVisible(TRUE); + } +} + +void LLConversationLogListItem::updateTimestamp() +{ + mConversationDate->setValue(mConversation->getTimestamp()); +} + +void LLConversationLogListItem::updateName() +{ + mConversationName->setValue(mConversation->getConversationName()); +} + +void LLConversationLogListItem::updateOfflineIMs() +{ + getChild("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("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 + +// +// 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(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(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(*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(*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(*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(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(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 100644 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 + +#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 conversations_items_map; +typedef std::map conversations_widgets_map; + +typedef std::vector 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 getIcon() const { return NULL; } + virtual LLPointer 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& 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 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 +{ +public: + typedef LLFolderViewModel 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& 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 100644 index 0000000000..b6c53e5e30 --- /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 +#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 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 (isInVisibleChain() && 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("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); + addChild(mItemPanel); + + mCallIconLayoutPanel = mItemPanel->getChild("call_icon_panel"); + mSessionTitle = mItemPanel->getChild("conversation_title"); + + mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1)); + mSpeakingIndicator = getChild("speaking_indicator"); + + LLConversationItem* vmi = dynamic_cast(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("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("group_icon"); + icon->setVisible(true); + mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); + break; + } + case LLConversationItem::CONV_SESSION_GROUP: + { + LLGroupIconCtrl* icon = mItemPanel->getChild("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("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(); + 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(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(getViewModelItem()); + LLUUID session_id = item? item->getUUID() : LLUUID(); + + LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance("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("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(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(*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(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(*iter); + if (participant) + { + participant->hideSpeakingIndicator(); + } + } + } + requestArrange(); + // Do the regular upstream refresh + LLFolderViewFolder::refresh(); +} + +void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) +{ + LLConversationItem* vmi = dynamic_cast(getViewModelItem()); + + if (vmi) + { + mIsInActiveVoiceChannel = vmi->getUUID() == session_id; + mCallIconLayoutPanel->setVisible(mIsInActiveVoiceChannel); + } +} + +// +// Implementation of conversations list participant (avatar) widgets +// + +static LLDefaultChildRegistry::Register 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(avatar_icon_params); + addChild(avatarIcon); + + LLButton::Params info_button_params(params.info_button()); + applyXUILayout(info_button_params, this); + LLButton * button = LLUICtrlFactory::create(info_button_params); + addChild(button); + + LLOutputMonitorCtrl::Params output_monitor_params(params.output_monitor()); + applyXUILayout(output_monitor_params, this); + LLOutputMonitorCtrl * outputMonitor = LLUICtrlFactory::create(output_monitor_params); + addChild(outputMonitor); +} + +BOOL LLConversationViewParticipant::postBuild() +{ + mAvatarIcon = getChild("avatar_icon"); + + mInfoBtn = getChild("info_btn"); + mInfoBtn->setClickedCallback(boost::bind(&LLConversationViewParticipant::onInfoBtnClick, this)); + mInfoBtn->setVisible(false); + + mSpeakingIndicator = getChild("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(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(getParentFolder()->getViewModelItem()) : NULL; + LLUUID session_id = vmi? vmi->getUUID() : LLUUID(); + + LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance("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 100644 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 + { + Optional 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 + { + Optional container; + Optional participant_id; + Optional avatar_icon; + Optional info_button; + Optional 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 +{ +private: + std::vector 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..82affcf068 --- /dev/null +++ b/indra/newview/lldonotdisturbnotificationstorage.cpp @@ -0,0 +1,350 @@ +/** +* @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() + , 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(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); + + LL_INFOS("LLDoNotDisturbNotificationStorage") << "start loading notifications" << LL_ENDL; + + 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(); + + LL_INFOS("LLDoNotDisturbNotificationStorage") << "finished loading notifications" << LL_ENDL; +} + +void LLDoNotDisturbNotificationStorage::updateNotifications() +{ + + LLNotificationChannelPtr channelPtr = getCommunicationChannel(); + LLCommunicationChannel *commChannel = dynamic_cast(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(channelPtr.get()); + LLNotificationPtr notification; + LLSD payload; + LLUUID notificationObjectID; + std::string notificationName; + std::string payloadVariable = nameToPayloadParameterMap[name]; + LLCommunicationChannel::history_list_t::iterator it; + std::vector 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, 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 nameToPayloadParameterMap; +}; + +#endif // LL_LLDONOTDISTURBNOTIFICATIONSTORAGE_H + diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index cff3e128bf..e6429e11d3 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++) @@ -576,6 +576,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(); @@ -641,20 +647,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/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llenvmanager.cpp b/indra/newview/llenvmanager.cpp index 3c18917ed4..37a3f6f98f 100644 --- a/indra/newview/llenvmanager.cpp +++ b/indra/newview/llenvmanager.cpp @@ -505,9 +505,9 @@ void LLEnvManagerNew::onRegionSettingsResponse(const LLSD& content) if (KCWindlightInterface::instance().haveParcelOverride(new_settings)) { // If using server settings, update managers. -// if (getUseRegionSettings()) +// if (getUseRegionSettings()) // [RLVa:KB] - Checked: 2011-08-29 (RLVa-1.4.1a) | Added: RLVa-1.4.1a - if ( (getUseRegionSettings()) && (LLWLParamManager::getInstance()->mAnimator.getIsRunning()) ) + if ( (getUseRegionSettings()) && (LLWLParamManager::getInstance()->mAnimator.getIsRunning()) ) // [/RLVa:KB] { updateManagersFromPrefs(mInterpNextChangeMessage); 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 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 a0f986e4db..9adc2372b2 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 r("favorites_bar"); @@ -302,26 +305,36 @@ protected: }; /** - * This callback is needed to update an item being copied to the favorites folder + * This class is needed to update an item being copied to the favorites folder * with a sort field value (required to save favorites bar's tabs order). * See method handleNewFavoriteDragAndDrop for more details on how this class is used. */ -void item_copied_cb(const LLUUID& inv_item, S32 sort_field) +class LLItemCopiedCallback : public LLInventoryCallback { - LLViewerInventoryItem* item = gInventory.getItem(inv_item); - - if (item) +public: + LLItemCopiedCallback(S32 sortField): mSortField(sortField) {} + + virtual void fire(const LLUUID& inv_item) { - item->setSortField(sort_field); - item->setComplete(TRUE); - item->updateServer(FALSE); - - gInventory.updateItem(item); - gInventory.notifyObservers(); + LLViewerInventoryItem* item = gInventory.getItem(inv_item); + + if (item) + { + LLFavoritesOrderStorage::instance().setSortIndex(item, mSortField); + + item->setComplete(TRUE); + item->updateServer(FALSE); + + gInventory.updateItem(item); + gInventory.notifyObservers(); + } + + LLView::getWindow()->setCursor(UI_CURSOR_ARROW); } - - LLView::getWindow()->setCursor(UI_CURSOR_ARROW); -} + +private: + S32 mSortField; +}; // updateButtons's helper struct LLFavoritesSort @@ -330,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)) { @@ -538,7 +551,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(); @@ -584,7 +597,7 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con } int sortField = 0; - LLPointer cb; + LLPointer cb; // current order is saved by setting incremental values (1, 2, 3, ...) for the sort field for (LLInventoryModel::item_array_t::iterator i = mItems.begin(); i != mItems.end(); ++i) @@ -593,11 +606,12 @@ void LLFavoritesBarCtrl::handleNewFavoriteDragAndDrop(LLInventoryItem *item, con if (currItem->getUUID() == item->getUUID()) { - cb = new LLBoostFuncInventoryCallback(boost::bind(item_copied_cb, _1, ++sortField)); + cb = new LLItemCopiedCallback(++sortField); } else { - currItem->setSortField(++sortField); + LLFavoritesOrderStorage::instance().setSortIndex(currItem, ++sortField); + currItem->setComplete(TRUE); currItem->updateServer(FALSE); @@ -650,7 +664,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(); } @@ -940,7 +954,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); } } @@ -1399,7 +1413,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; @@ -1434,4 +1448,294 @@ 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& a, const LLPointer& 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 19793c4e57..3f29dd0fc2 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; @@ -167,6 +168,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 "@" prefix. + * Data are stored in user home directory. + */ +class LLFavoritesOrderStorage : public LLSingleton + , public LLDestroyClass +{ + 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() : 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 sort_index_map_t; + sort_index_map_t mSortIndexes; + + typedef std::map 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 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 dd70e446f4..b18381ceec 100644 --- a/indra/newview/llfirstuse.cpp +++ b/indra/newview/llfirstuse.cpp @@ -73,12 +73,13 @@ void LLFirstUse::resetFirstUse() // static void LLFirstUse::otherAvatarChatFirst(bool enable) -{ +{ // CHUI Merge check // Remove floating chat bar - // firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "chat_bar").with("direction", "top_right").with("distance", 24)); + //CHUI: firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "nearby_chat").with("direction", "top_right").with("distance", 24)); + //FUI: firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "chat_bar").with("direction", "top_right").with("distance", 24)); // [FS communication UI] - //firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "nearby_chat").with("direction", "top_right").with("distance", 24)); - firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "fs_nearby_chat").with("direction", "top_right").with("distance", 24)); + firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "nearby_chat").with("direction", "top_right").with("distance", 24)); // CHUI Merge lets try the CHUI line + //firstUseNotification("FirstOtherChatBeforeUser", enable, "HintChat", LLSD(), LLSD().with("target", "fs_nearby_chat").with("direction", "top_right").with("distance", 24)); // [FS communication UI] // } diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index afb0eea7fb..caad0afec0 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -339,10 +339,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(); @@ -378,21 +378,31 @@ void LLVolumeImplFlexible::doIdleUpdate() id = parent->getVolumeInterfaceID(); } - if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) - { - sUpdateDelay[mInstanceIndex] = (S32) update_period-1; - - updateRenderRes(); - - gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); - } - } + if (mVO->isRootEdit()) + { + id = mID; } else { - sUpdateDelay[mInstanceIndex] = (S32) update_period; + LLVOVolume* parent = (LLVOVolume*) mVO->getParent(); + id = parent->getVolumeInterfaceID(); + } + + if ((LLDrawable::getCurrentFrame()+id)%update_period == 0) + { + sUpdateDelay[mInstanceIndex] = (S32) update_period-1; + + updateRenderRes(); + + gPipeline.markRebuild(drawablep, LLDrawable::REBUILD_POSITION, FALSE); } } + } + else + { + sUpdateDelay[mInstanceIndex] = (S32) update_period; + } +} } } @@ -417,7 +427,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)) @@ -432,7 +441,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< 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("avatar_picker"); + LLFloaterReg::showTypedInstance("avatar_picker", LLSD(name)); if (!floater) { llwarns << "Cannot instantiate avatar picker" << llendl; @@ -77,6 +82,7 @@ LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, floater->setAllowMultiple(allow_multiple); floater->mNearMeListComplete = FALSE; floater->mCloseOnSelect = closeOnSelect; + floater->mExcludeAgentFromSearchResults = skip_agent; if (!closeOnSelect) { @@ -87,6 +93,11 @@ LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(select_callback_t callback, floater->getChild("cancel_btn")->setLabel(close_string); } + if(frustumOrigin) + { + floater->mFrustumOrigin = frustumOrigin->getHandle(); + } + return floater; } @@ -95,9 +106,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() @@ -308,9 +327,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; } @@ -358,8 +377,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; @@ -402,8 +480,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) { @@ -416,7 +495,7 @@ public: if (isGoodStatus(status) || status == 400) { LLFloaterAvatarPicker* floater = - LLFloaterReg::findTypedInstance("avatar_picker"); + LLFloaterReg::findTypedInstance("avatar_picker", mName); if (floater) { floater->processResponse(mQueryID, content); @@ -445,9 +524,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 @@ -458,7 +535,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 { @@ -535,8 +612,8 @@ BOOL LLFloaterAvatarPicker::handleDragAndDrop(S32 x, S32 y, MASK mask, if (dest_agent_id.notNull() && dest_agent_id != gAgentID) { // if (drop) -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - if ( (drop) && ( (!rlv_handler_t::isEnabled()) || (gRlvHandler.canStartIM(dest_agent_id)) ) ) +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0) + if ( (drop) && (RlvActions::canStartIM(dest_agent_id)) ) // [/RLVa:KB] { // Start up IM before give the item @@ -603,35 +680,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("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; + if (avatar_id != agent_id || !floater->isExcludeAgentFromSearchResults()) // exclude agent from search results? + { + std::string avatar_name; + if (avatar_id.isNull()) + { + LLStringUtil::format_map_t map; + map["[TEXT]"] = floater->getChild("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.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) @@ -646,52 +724,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; - - LLScrollListCtrl* search_results = getChild("SearchResults"); - - LLSD agents = content["agents"]; - if (agents.size() == 0) + if (query_id == mQueryID) { - LLStringUtil::format_map_t map; - map["[TEXT]"] = childGetText("Edit"); + LLScrollListCtrl* search_results = getChild("SearchResults"); + + LLSD agents = content["agents"]; + + // clear "Searching" label on first results + search_results->deleteAllItems(); + 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; + } + } + + 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); + } } - - // 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); } //static @@ -757,13 +841,14 @@ bool LLFloaterAvatarPicker::isSelectBtnEnabled() uuid_vec_t avatar_ids; std::vector avatar_names; getSelectedAvatarData(list, avatar_ids, avatar_names); - //return mOkButtonValidateSignal(avatar_ids); -// FIRE-9799 - Check that we actually have an avatar selected - if (avatar_ids.size() >= 1) - return mOkButtonValidateSignal(avatar_ids); + if (avatar_ids.size() >= 1) + { + ret_val = mOkButtonValidateSignal(avatar_ids); + } else - return false; -// + { + ret_val = false; + } } } 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 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 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 e1e7fb8121..3c82555817 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -64,10 +64,6 @@ #include #include -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 @@ -107,7 +103,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"); @@ -119,6 +118,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() @@ -498,37 +501,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); } @@ -537,11 +540,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 5004a0bef0..cc2ad3f6cb 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; + // Add float LSL color entry widgets protected: 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("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("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + + getChild("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2)); + + return LLFloater::postBuild(); +} + +void LLFloaterConversationLog::draw() +{ + getChild("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("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("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("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::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..1ea259478c --- /dev/null +++ b/indra/newview/llfloaterconversationpreview.h @@ -0,0 +1,65 @@ +/** + * @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$ + */ +// CHUI Merge this file doesnt exsist in LL's repo + +#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 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/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 97db1e1b68..40c42c732d 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -65,27 +65,40 @@ private: LLFloaterGesture* mFloater; }; //----------------------------- -// gesture callback funcs +// GestureCallback //----------------------------- -void gesture_show_cb(const LLUUID &inv_item) +class GestureShowCallback : public LLInventoryCallback { - LLPreviewGesture::show(inv_item, LLUUID::null); -} - -void gesture_copied_cb(const LLUUID &inv_item, LLFloaterGesture* floater) -{ - if(floater) +public: + void fire(const LLUUID &inv_item) { - floater->addGesture(inv_item,NULL,floater->getChild("gesture_list")); - - // EXP-1909 (Pasted gesture displayed twice) - // The problem is that addGesture is called here for the second time for the same item (which is copied) - // First time addGesture is called from LLFloaterGestureObserver::changed(), which is a callback for inventory - // change. So we need to refresh the gesture list to avoid duplicates. - floater->refreshAll(); + LLPreviewGesture::show(inv_item, LLUUID::null); } -} +}; + +class GestureCopiedCallback : public LLInventoryCallback +{ +private: + LLFloaterGesture* mFloater; + +public: + GestureCopiedCallback(LLFloaterGesture* floater): mFloater(floater) + {} + void fire(const LLUUID &inv_item) + { + if(mFloater) + { + mFloater->addGesture(inv_item,NULL,mFloater->getChild("gesture_list")); + + // EXP-1909 (Pasted gesture displayed twice) + // The problem is that addGesture is called here for the second time for the same item (which is copied) + // First time addGesture is called from LLFloaterGestureObserver::changed(), which is a callback for inventory + // change. So we need to refresh the gesture list to avoid duplicates. + mFloater->refreshAll(); + } + } +}; //--------------------------------------------------------------------------- // LLFloaterGesture @@ -457,7 +470,7 @@ void LLFloaterGesture::onClickPlay() void LLFloaterGesture::onClickNew() { - LLPointer cb = new LLBoostFuncInventoryCallback(gesture_show_cb); + LLPointer cb = new GestureShowCallback(); create_inventory_item(gAgent.getID(), gAgent.getSessionID(), LLUUID::null, LLTransactionID::tnull, "New Gesture", "", LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, NOT_WEARABLE, PERM_MOVE | PERM_TRANSFER, cb); @@ -529,7 +542,7 @@ void LLFloaterGesture::onCopyPasteAction(const LLSD& command) return; LLInventoryCategory* gesture_dir = gInventory.getCategory(mGestureFolderID); llassert(gesture_dir); - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(gesture_copied_cb, _1, this)); + LLPointer cb = new GestureCopiedCallback(this); for(LLDynamicArray::iterator it = ids.begin(); it != ids.end(); it++) { diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp index ab56b8e3d6..fe6223fbf5 100644 --- a/indra/newview/llfloatergodtools.cpp +++ b/indra/newview/llfloatergodtools.cpp @@ -1137,11 +1137,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("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..eb52986085 --- /dev/null +++ b/indra/newview/llfloaterimcontainer.cpp @@ -0,0 +1,2031 @@ +/** + * @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" + +#if 0 + +#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::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("im_box_tab_container")); + mStubPanel = getChild("stub_panel"); + mStubTextBox = getChild("stub_textbox"); + mStubTextBox->setURLClickedCallback(boost::bind(&LLFloaterIMContainer::returnFloaterToHost, this)); + + mConversationsStack = getChild("conversations_stack"); + mConversationsPane = getChild("conversations_layout_panel"); + mMessagesPane = getChild("messages_layout_panel"); + + mConversationsListPanel = getChild("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()); + 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(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()); + scroller_params.rect(scroller_view_rect); + + LLScrollContainer* scroller = LLUICtrlFactory::create(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("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this)); + mStubCollapseBtn = getChild("stub_collapse_btn"); + mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this)); + getChild("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(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(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("im_container"); +} + +LLFloaterIMContainer* LLFloaterIMContainer::getInstance() +{ + return LLFloaterReg::getTypedInstance("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(*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(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(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(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("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(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(*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::setMinimized(BOOL b) +{ + bool was_minimized = isMinimized(); + LLMultiFloater::setMinimized(b); + + //Switching from minimized to un-minimized + if(was_minimized && !b) + { + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); + + if(session_floater && !session_floater->isTornOff()) + { + //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() && mSelectedSession.notNull()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); + } + } + } +} + +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("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); + selectConversationPair(LLUUID(NULL), false, false); + } + openNearbyChat(); + flashConversationItemWidget(mSelectedSession,false); + + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); + if(session_floater && !session_floater->isMinimized()) + { + //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() && mSelectedSession.notNull()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); + } + } + } + + nearby_chat = LLFloaterReg::findTypedInstance("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(widget_it->second); + if (widget) + { + widget->setVisibleIfDetached(visible); + } + } + + // Now, do the normal multifloater show/hide + LLMultiFloater::setVisible(visible); +} + +void LLFloaterIMContainer::setVisibleAndFrontmost(BOOL take_focus, const LLSD& key) +{ + LLMultiFloater::setVisibleAndFrontmost(take_focus, key); + selectConversationPair(getSelectedSession(), false, take_focus); +} + +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("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(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("conversations_pane_buttons_expanded")->findChild("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("preferences"); + if (floater_prefp) + { + floater_prefp->selectChatPanel(); + } + } + if ("privacy_preferences" == command) + { + LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance("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("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 selectedItems = mConversationsRoot->getSelectionList(); + + std::set::const_iterator it = selectedItems.begin(); + const std::set::const_iterator it_end = selectedItems.end(); + LLConversationItem * conversationItem; + + for (; it != it_end; ++it) + { + conversationItem = static_cast((*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(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); + if (session_floater) + { + 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(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(); + } + } + + /* 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); + } + + //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); + } + } + + // Set the focus on the selected floater + if (!session_floater->hasFocus() && !session_floater->isMinimized()) + { + session_floater->setFocus(focus_floater); + } + } + flashConversationItemWidget(session_id,false); + return handled; +} + +void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) +{ + LLConversationItemSession* item = dynamic_cast(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(get_ptr_in_map(mConversationsItems,LLUUID())); + if (item) + { + // Get the positions of the nearby avatars and their ids + std::vector 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(*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("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(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(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(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(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 ( + 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(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(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(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("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(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(new_selection->getViewModelItem()); + if (vmi) + { + selectConversationPair(vmi->getUUID(), true, focus_selected); + return true; + } + } + } + } + return false; +} + +void LLFloaterIMContainer::expandConversation() +{ + if(!mConversationsPane->isCollapsed()) + { + LLConversationViewSession* widget = dynamic_cast(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. + if(isMinimized()) + { + LLMultiFloater::setMinimized(FALSE); + } + + LLFloater::closeFloater(app_quitting); +} + +// EOF + +#endif diff --git a/indra/newview/llfloaterimcontainer.h b/indra/newview/llfloaterimcontainer.h new file mode 100644 index 0000000000..076c76c865 --- /dev/null +++ b/indra/newview/llfloaterimcontainer.h @@ -0,0 +1,215 @@ +/** + * @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$ + */ + +#if 0 + +#ifndef LL_LLFLOATERIMCONTAINER_H +#define LL_LLFLOATERIMCONTAINER_H + +#include +#include + +#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 setMinimized(BOOL b); + /*virtual*/ void setVisible(BOOL visible); + /*virtual*/ void setVisibleAndFrontmost(BOOL take_focus=TRUE, const LLSD& key = LLSD()); + /*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 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 + +#endif diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp new file mode 100644 index 0000000000..6b17ce30df --- /dev/null +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -0,0 +1,942 @@ +/** + * @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" + +//#if 0 CHUI Merge unmask this for now +#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" +// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) +#include "rlvhandler.h" +// [/RLVa:KB] + +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); +// [RLVa:KB] - Checked: 2010-02-27 (RLVa-0.2.2) +void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channel); +// [/RLVa:KB] + +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::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 history; + LLLogChat::loadChatHistory("chat", history); + + std::list::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, take_focus); + } +} + +// 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); + if(!isMessagePaneExpanded()) + { + restoreFloater(); + onCollapseToLine(this); + } + 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() +{ + 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()); + if (!rest_of_match.empty()) + { + 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 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("nearby_chat"); + if (nearby_chat) + { + if(!nearby_chat->isTornOff()) + { + LLFloaterIMContainer::getInstance()->selectConversation(LLUUID(NULL)); + } + if(nearby_chat->isMinimized()) + { + nearby_chat->setMinimized(false); + } + nearby_chat->show(); + nearby_chat->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("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) +// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) | Modified: RLVa-0.2.2a +void send_chat_from_viewer(std::string utf8_out_text, EChatType type, S32 channel) +// [/RLVa:KB] +{ +// [RLVa:KB] - Checked: 2010-02-27 (RLVa-1.2.0b) | Modified: RLVa-1.2.0a + // Only process chat messages (ie not CHAT_TYPE_START, CHAT_TYPE_STOP, etc) + if ( (rlv_handler_t::isEnabled()) && ( (CHAT_TYPE_WHISPER == type) || (CHAT_TYPE_NORMAL == type) || (CHAT_TYPE_SHOUT == type) ) ) + { + if (0 == channel) + { + // (We already did this before, but LLChatHandler::handle() calls this directly) + if ( ((CHAT_TYPE_SHOUT == type) || (CHAT_TYPE_NORMAL == type)) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATNORMAL)) ) + type = CHAT_TYPE_WHISPER; + else if ( (CHAT_TYPE_SHOUT == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATSHOUT)) ) + type = CHAT_TYPE_NORMAL; + else if ( (CHAT_TYPE_WHISPER == type) && (gRlvHandler.hasBehaviour(RLV_BHVR_CHATWHISPER)) ) + type = CHAT_TYPE_NORMAL; + + // Redirect chat if needed + if ( ( (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT) || (gRlvHandler.hasBehaviour(RLV_BHVR_REDIREMOTE)) ) && + (gRlvHandler.redirectChatOrEmote(utf8_out_text)) ) ) + { + return; + } + + // Filter public chat if sendchat restricted + if (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHAT)) + gRlvHandler.filterChat(utf8_out_text, true); + } + else + { + // Don't allow chat on a non-public channel if sendchannel restricted (unless the channel is an exception) + if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHANNEL)) && (!gRlvHandler.isException(RLV_BHVR_SENDCHANNEL, channel)) ) + return; + + // Don't allow chat on debug channel if @sendchat, @redirchat or @rediremote restricted (shows as public chat on viewers) + if (CHAT_CHANNEL_DEBUG == channel) + { + bool fIsEmote = RlvUtil::isEmote(utf8_out_text); + if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDCHAT)) || + ((!fIsEmote) && (gRlvHandler.hasBehaviour(RLV_BHVR_REDIRCHAT))) || + ((fIsEmote) && (gRlvHandler.hasBehaviour(RLV_BHVR_REDIREMOTE))) ) + { + return; + } + } + } + } +// [/RLVa:KB] + + 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; +//#endif diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llfloaterimnearbychat.h similarity index 59% rename from indra/newview/llnearbychatbar.h rename to indra/newview/llfloaterimnearbychat.h index ffff7513bc..8a5d629c81 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,40 +24,56 @@ * $/LicenseInfo$ */ -#if 0 +//#if 0 CHUI Merge unmask this for now -#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() {} - virtual BOOL postBuild(); + static LLFloaterIMNearbyChat* buildFloater(const LLSD& key); + + /*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); @@ -66,24 +82,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); @@ -93,16 +107,17 @@ 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 mListener; +private: + /*virtual*/ void refresh(); + + std::vector mMessageArchive; }; -#endif +#endif // LL_LLFLOATERIMNEARBYCHAT_H -#endif +//#endif diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llfloaterimnearbychathandler.cpp similarity index 70% rename from indra/newview/llnearbychathandler.cpp rename to indra/newview/llfloaterimnearbychathandler.cpp index e8b9035790..5b77e9ea3f 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 @@ -28,26 +28,21 @@ #include "llagent.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" // Remove floating chat bar +#include "llfloaterimnearbychat.h" #include "llrecentpeople.h" #include "llviewercontrol.h" #include "llfloaterreg.h"//for LLFloaterReg::getTypedInstance #include "llviewerwindow.h"//for screen channel position -// Remove floating chat bar -// #include "llnearbychatbar.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" +#include "llfloaterimcontainer.h" #include "llrootview.h" #include "lllayoutstack.h" @@ -57,26 +52,29 @@ #include "fsconsoleutils.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 > toast_vec_t; typedef std::list > toast_list_t; - LLNearbyChatScreenChannel(const Params& p) + LLFloaterIMNearbyChatScreenChannel(const Params& p) : LLScreenChannelBase(p) { mWorldViewRectConnection = gViewerWindow->setOnWorldViewRectUpdated(boost::bind(&LLNearbyChatScreenChannel::updateSize, this, _1, _2)); @@ -85,21 +83,21 @@ 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 (); void showToastsTop (); - typedef boost::function create_toast_panel_callback_t; + typedef boost::function 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); @@ -171,17 +169,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) { @@ -190,40 +190,40 @@ public: /*virtual*/ void onClose(bool app_quitting); private: - LLNearbyChatScreenChannel* mNearbyChatScreenChannelp; + LLFloaterIMNearbyChatScreenChannel* mNearbyChatScreenChannelp; }; //----------------------------------------------------------------------------------------------- -// LLNearbyChatScreenChannel +// LLFloaterIMNearbyChatScreenChannel //----------------------------------------------------------------------------------------------- +// CHUI Merge check if this is still needed +//void LLFloaterIMNearbyChatScreenChannel::reshapePanel(LLToastPanelBase* panel) +//{ +// S32 percentage=gSavedSettings.getS32("NearbyToastWidth"); +// panel->reshape(gViewerWindow->getWindowWidthRaw()*percentage/100,panel->getRect().getHeight(),TRUE); +//} +// CHUI Merge check if this is still needed +//void LLFloaterIMNearbyChatScreenChannel::updateSize(LLRect old_world_rect, LLRect new_world_rect) +//{ +// for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) +// { +// LLToast* toast = it->get(); +// +// if (toast) +// { +// LLNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); +// +// if(panel) +// { +// reshapePanel(panel); +// toast->reshapeToPanel(); +// } +// } +// } +// arrangeToasts(); +//} -void LLNearbyChatScreenChannel::reshapePanel(LLToastPanelBase* panel) -{ - S32 percentage=gSavedSettings.getS32("NearbyToastWidth"); - panel->reshape(gViewerWindow->getWindowWidthRaw()*percentage/100,panel->getRect().getHeight(),TRUE); -} - -void LLNearbyChatScreenChannel::updateSize(LLRect old_world_rect, LLRect new_world_rect) -{ - for(toast_vec_t::iterator it = m_active_toasts.begin(); it != m_active_toasts.end(); ++it) - { - LLToast* toast = it->get(); - - if (toast) - { - LLNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); - - if(panel) - { - reshapePanel(panel); - toast->reshapeToPanel(); - } - } - } - arrangeToasts(); -} - -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()); @@ -237,12 +237,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; @@ -261,7 +261,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; @@ -276,7 +276,7 @@ void LLNearbyChatScreenChannel::onToastFade(LLToast* toast) arrangeToasts(); } -void LLNearbyChatScreenChannel::updateToastsLifetime() +void LLFloaterIMNearbyChatScreenChannel::updateToastsLifetime() { S32 seconds = gSavedSettings.getS32("NearbyToastLifeTime"); toast_list_t::iterator it; @@ -287,7 +287,7 @@ void LLNearbyChatScreenChannel::updateToastsLifetime() } } -void LLNearbyChatScreenChannel::updateToastFadingTime() +void LLFloaterIMNearbyChatScreenChannel::updateToastFadingTime() { S32 seconds = gSavedSettings.getS32("NearbyToastFadingTime"); toast_list_t::iterator it; @@ -298,9 +298,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; @@ -309,20 +309,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); reshapePanel(panel); - 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) @@ -334,16 +334,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(toast->getPanel()); + LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); if(panel && panel->messageID() == fromID && panel->getFromName() == from && panel->canAddText()) { - panel->addMessage(notification); + panel->addMessage(chat); reshapePanel(panel); toast->reshapeToPanel(); toast->startTimer(); @@ -362,11 +362,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)) { @@ -385,10 +385,10 @@ void LLNearbyChatScreenChannel::addNotification(LLSD& notification) m_toast_pool.pop_back(); - LLToastPanelBase* panel = dynamic_cast(toast->getPanel()); + LLFloaterIMNearbyChatToastPanel* panel = dynamic_cast(toast->getPanel()); if(!panel) return; - panel->init(notification); + panel->init(chat); reshapePanel(panel); toast->reshapeToPanel(); @@ -408,7 +408,7 @@ static bool sort_toasts_predicate(LLHandle first, LLHandle sec return v1 > v2; } -void LLNearbyChatScreenChannel::arrangeToasts() +void LLFloaterIMNearbyChatScreenChannel::arrangeToasts() { if(mStopProcessing || isHovering()) return; @@ -490,20 +490,18 @@ void LLNearbyChatScreenChannel::arrangeToasts() //----------------------------------------------------------------------------------------------- -//LLNearbyChatHandler +//LLFloaterIMNearbyChatHandler //----------------------------------------------------------------------------------------------- -boost::scoped_ptr LLNearbyChatHandler::sChatWatcher(new LLEventStream("LLChat")); +boost::scoped_ptr 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); @@ -512,11 +510,11 @@ 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); @@ -524,7 +522,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) @@ -561,27 +559,23 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // [/RLVa:KB] + LLFloaterReg::getInstance("im_container"); + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); -// ## Zi - Post merge fixup ## // LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); -// ## Zi - Post merge fixup ## // LLNearbyChat* nearby_chat = chat_bar->findChild("nearby_chat"); - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat", LLSD()); // ## Zi - Post merge fixup ## - FSFloaterNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("fs_nearby_chat", LLSD()); // ## Zi - Post merge fixup ## - // [FS communication UI] // 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); // [RLVa:KB] - Checked: 2011-12-13 (RLVa-1.4.6) | Added: RLVa-1.4.6 if ((CHAT_SOURCE_AGENT != chat_msg.mSourceType) || (!chat_msg.mRlvNamesFiltered)) - notification["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); + chat["sender_slurl"] = LLViewerChat::getSenderSLURL(chat_msg, args); // [/RLVa:KB] if (chat_msg.mChatType == CHAT_TYPE_DIRECT && @@ -589,7 +583,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, chat_msg.mText[0] == '@') { // Send event on to LLEventStream and exit - sChatWatcher->post(notification); + sChatWatcher->post(chat); return; } @@ -647,8 +641,9 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // Send event on to LLEventStream - sChatWatcher->post(notification); + sChatWatcher->post(chat); + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance("im_container"); // Nearby chat in console if (FSConsoleUtils::ProcessChatMessage(chat_msg, args)) { @@ -656,13 +651,12 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } // - static LLCachedControl useChatBubbles(gSavedSettings, "UseChatBubbles"); -// ## Zi - Post merge fixup ## // if( !chat_bar->isMinimized() && nearby_chat->getVisible() - if( nearby_chat->getVisible() // ## Zi - Post merge fixup ## - || ( chat_msg.mSourceType == CHAT_SOURCE_AGENT + if(( ( chat_msg.mSourceType == CHAT_SOURCE_AGENT && 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 @@ -683,7 +677,7 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, } */ - LLNearbyChatScreenChannel* channel = dynamic_cast(mChannel.get()); + LLFloaterIMNearbyChatScreenChannel* channel = dynamic_cast(mChannel.get()); if(channel) { @@ -696,37 +690,50 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg, tmp_chat.mText = tmp_chat.mText.substr(3); } - // Add a nearby chat toast. - LLUUID id; - id.generate(); - notification["id"] = id; + //Don't show nearby toast, if conversation is visible and selected + if ((nearby_chat->hasFocus()) || + ((im_box->getSelectedSession().isNull() && + ((LLFloater::isVisible(im_box) && !im_box->isMinimized() && im_box->isFrontmost()) + || (LLFloater::isVisible(nearby_chat) && !nearby_chat->isMinimized() && nearby_chat->isFrontmost()))))) + { + if(nearby_chat->isMessagePaneExpanded()) + { + return; + } + } + + //Will show toast when chat preference is set + if((gSavedSettings.getString("NotificationNearbyChatOptions") == "toast") || !nearby_chat->isMessagePaneExpanded()) + { + // Add a nearby chat toast. + LLUUID id; + id.generate(); + chat["id"] = id; // [RLVa:KB] - Checked: 2010-04-20 (RLVa-1.2.0f) | Added: RLVa-1.2.0f - if (rlv_handler_t::isEnabled()) - notification["show_icon_tooltip"] = !chat_msg.mRlvNamesFiltered; + if (rlv_handler_t::isEnabled()) + chat["show_icon_tooltip"] = !chat_msg.mRlvNamesFiltered; // [/RLVa:KB] - 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"] = chat_msg.mText; - channel->addNotification(notification); + 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); + } + } } -void LLNearbyChatHandler::onDeleteToast(LLToast* toast) -{ -} - //----------------------------------------------------------------------------------------------- -// 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 similarity index 77% rename from indra/newview/llnearbychathandler.h rename to indra/newview/llfloaterimnearbychathandler.h index 6d8b0c3e32..d21b55a7c4 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,8 +24,8 @@ * $/LicenseInfo$ */ -#ifndef LL_LLNEARBYCHATHANDLER_H -#define LL_LLNEARBYCHATHANDLER_H +#ifndef LL_LLFLOATERIMNEARBYCHATHANDLER_H +#define LL_LLFLOATERIMNEARBYCHATHANDLER_H #include "llnotificationhandler.h" #include "llavatarname.h" @@ -33,20 +33,19 @@ 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 sChatWatcher; @@ -54,4 +53,4 @@ protected: } -#endif /* LL_LLNEARBYCHATHANDLER_H */ +#endif /* LL_LLFLOATERIMNEARBYCHATHANDLER_H */ diff --git a/indra/newview/llnearbychatbarlistener.cpp b/indra/newview/llfloaterimnearbychatlistener.cpp similarity index 85% rename from indra/newview/llnearbychatbarlistener.cpp rename to indra/newview/llfloaterimnearbychatlistener.cpp index e4c8e1d2bd..549cd4837c 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 @@ -30,15 +30,15 @@ #if 0 -#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) @@ -48,12 +48,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 similarity index 72% rename from indra/newview/llnearbychatbarlistener.h rename to indra/newview/llfloaterimnearbychatlistener.h index f9aa20f979..6a7503768e 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 @@ -28,25 +28,25 @@ #if 0 -#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 #endif diff --git a/indra/newview/llfloaterimsession.cpp b/indra/newview/llfloaterimsession.cpp new file mode 100644 index 0000000000..15aa49bd56 --- /dev/null +++ b/indra/newview/llfloaterimsession.cpp @@ -0,0 +1,1367 @@ +/** + * @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" + +#if 0 +#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 "lldonotdisturbnotificationstorage.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" +// [RLVa:KB] - Checked: 2013-05-10 (RLVa-1.4.9) +#include "rlvactions.h" +#include "rlvcommon.h" +// [/RLVa:KB] + +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("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); +// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) + std::string utf8_text = utf8str_truncate(msg, MAX_MSG_BUF_SIZE - 1); + + if ( (RlvActions::hasBehaviour(RLV_BHVR_SENDIM)) || (RlvActions::hasBehaviour(RLV_BHVR_SENDIMTO)) ) + { + const LLIMModel::LLIMSession* pIMSession = LLIMModel::instance().findIMSession(mSessionID); + RLV_ASSERT(pIMSession); + + bool fRlvFilter = !pIMSession; + if (pIMSession) + { + switch (pIMSession->mSessionType) + { + case LLIMModel::LLIMSession::P2P_SESSION: // One-on-one IM + fRlvFilter = !RlvActions::canSendIM(mOtherParticipantUUID); + break; + case LLIMModel::LLIMSession::GROUP_SESSION: // Group chat + fRlvFilter = !RlvActions::canSendIM(mSessionID); + break; + case LLIMModel::LLIMSession::ADHOC_SESSION: // Conference chat: allow if all participants can be sent an IM + { + if (!pIMSession->mSpeakers) + { + fRlvFilter = true; + break; + } + + LLSpeakerMgr::speaker_list_t speakers; + pIMSession->mSpeakers->getSpeakerList(&speakers, TRUE); + for (LLSpeakerMgr::speaker_list_t::const_iterator itSpeaker = speakers.begin(); + itSpeaker != speakers.end(); ++itSpeaker) + { + const LLSpeaker* pSpeaker = *itSpeaker; + if ( (gAgent.getID() != pSpeaker->mID) && (!RlvActions::canSendIM(pSpeaker->mID)) ) + { + fRlvFilter = true; + break; + } + } + } + break; + default: + fRlvFilter = true; + break; + } + } + + if (fRlvFilter) + { + utf8_text = RlvStrings::getString(RLV_STRING_BLOCKED_SENDIM); + } + } +// [/RLVa:KB] + + 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("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("toolbar_panel")->findChild("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& 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("impanel", session_id); + + return conversation; +} + +LLFloaterIMSession* LLFloaterIMSession::getInstance(const LLUUID& session_id) +{ + LLFloaterIMSession* conversation = + LLFloaterReg::getTypedInstance("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); + // *TODO: Study why we need to restore the floater before we close it. + // Might be because we want to save some state data in some clean open state. + 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::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::setMinimized(BOOL b) +{ + bool wasMinimized = isMinimized(); + LLFloaterIMSessionTab::setMinimized(b); + + //Switching from minimized state to un-minimized state + if(wasMinimized && !b) + { + //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()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSessionID); + } + } +} + +void LLFloaterIMSession::setVisible(BOOL visible) +{ + LLNotificationsUI::LLScreenChannel* channel = static_cast + (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(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; +} + +void LLFloaterIMSession::setFocus(BOOL focus) +{ + LLFloaterIMSessionTab::setFocus(focus); + + //When in DND mode, remove stored IM notifications + //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal + if(focus && gAgent.isDoNotDisturb()) + { + LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSessionID); + } +} + +//static +bool LLFloaterIMSession::toggle(const LLUUID& session_id) +{ + if(!isChatMultiTab()) + { + LLFloaterIMSession* floater = LLFloaterReg::findTypedInstance( + "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 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::const_reverse_iterator iter = messages.rbegin(); + std::list::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::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(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 errorWithContent(U32 statusNum, const std::string& reason, const LLSD& content) + { + llwarns << "Error inviting all agents to session [status:" + << statusNum << "]: " << content << 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); +} + +#endif diff --git a/indra/newview/llimfloater.h b/indra/newview/llfloaterimsession.h similarity index 50% rename from indra/newview/llimfloater.h rename to indra/newview/llfloaterimsession.h index e8cdbc9180..8badc714bf 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 @@ -26,62 +26,81 @@ #if 0 -#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 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 setMinimized(BOOL b); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ BOOL getVisible(); + /*virtual*/ void setFocus(BOOL focus); // 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 @@ -91,62 +110,61 @@ public: void setPositioned(bool b) { mPositioned = b; }; void onVisibilityChange(const LLSD& new_visibility); + bool enableGearMenuItem(const LLSD& userdata); + void GearDoToSelected(const LLSD& userdata); + bool checkGearMenuItem(const LLSD& userdata); + + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + void onChange(EStatusType status, const std::string &channelURI, + bool proximal); + + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } + virtual void onVoiceChannelStateChanged( + const LLVoiceChannel::EState& old_state, + const LLVoiceChannel::EState& new_state); + void processIMTyping(const LLIMInfo* im_info, BOOL typing); void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - void updateChatHistoryStyle(); - static void processChatHistoryStyleUpdate(const LLSD& newvalue); - - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg); - - /** - * Returns true if chat is displayed in multi tabbed floater - * false if chat is displayed in multiple windows - */ - static bool isChatMultiTab(); - - static void initIMFloater(); - //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); + 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); @@ -158,29 +176,31 @@ 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 #endif + diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp new file mode 100644 index 0000000000..ce6e639305 --- /dev/null +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -0,0 +1,1103 @@ +/** + * @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) + , mInputPanels(NULL) + , mChatLayoutPanelHeight(0) +{ + 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("nearby_chat"); + } + else + { + conv = LLFloaterReg::findTypedInstance("impanel", LLSD(uuid)); + } + + return conv; +}; + +//static +LLFloaterIMSessionTab* LLFloaterIMSessionTab::getConversation(const LLUUID& uuid) +{ + LLFloaterIMSessionTab* conv; + + if (uuid.isNull()) + { + conv = LLFloaterReg::getTypedInstance("nearby_chat"); + } + else + { + conv = LLFloaterReg::getTypedInstance("impanel", LLSD(uuid)); + } + + return conv; +}; + +void LLFloaterIMSessionTab::setVisible(BOOL visible) +{ + if(visible && !mHasVisibleBeenInitialized) + { + mHasVisibleBeenInitialized = true; + if(!gAgentCamera.cameraMouselook()) + { + LLFloaterReg::getTypedInstance("im_container")->setVisible(true); + } + LLFloaterIMSessionTab::addToHost(mSessionID); + LLFloaterIMSessionTab* conversp = LLFloaterIMSessionTab::getConversation(mSessionID); + + if (conversp && conversp->isNearbyChat() && gSavedPerAccountSettings.getBOOL("NearbyChatIsNotCollapsed")) + { + onCollapseToLine(this); + } + 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("main_stack"); + mParticipantListAndHistoryStack = getChild("im_panels"); + + mCloseBtn = getChild("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onSlide, this)); + + mExpandCollapseLineBtn = getChild("minz_btn"); + mExpandCollapseLineBtn->setClickedCallback(boost::bind(&LLFloaterIMSessionTab::onCollapseToLine, this)); + + mTearOffBtn = getChild("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLFloaterIMSessionTab::onTearOffClicked, this)); + + mGearBtn = getChild("gear_btn"); + + mParticipantListPanel = getChild("speakers_list_panel"); + mRightPartPanel = getChild("right_part_holder"); + + mToolbarPanel = getChild("toolbar_panel"); + mContentPanel = getChild("body_panel"); + mInputButtonPanel = getChild("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()); + scroller_params.rect(scroller_view_rect); + mScroller = LLUICtrlFactory::create(scroller_params); + mScroller->setFollowsAll(); + + mSpeakingIndicator = getChild("speaking_indicator"); + + // Insert that scroller into the panel widgets hierarchy + mParticipantListPanel->addChild(mScroller); + + mChatHistory = getChild("chat_history"); + + mInputEditor = getChild("chat_editor"); + + mChatLayoutPanel = getChild("chat_layout_panel"); + mInputPanels = getChild("input_panels"); + + mInputEditor->setTextExpandedCallback(boost::bind(&LLFloaterIMSessionTab::reshapeChatLayoutPanel, this)); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setPassDelete(TRUE); + mInputEditor->setFont(LLViewerChat::getChatFont()); + + mChatLayoutPanelHeight = mChatLayoutPanel->getRect().getHeight(); + mInputEditorPad = mChatLayoutPanelHeight - 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()); + 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(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(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(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(*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(*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(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("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); + mParticipantListPanel->setVisible(is_participant_list_visible); + + // Display collapse image (<<) if the floater is hosted + // or if it is torn off but has an open control panel. + bool is_expanded = is_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() +{ + mChatLayoutPanel->reshape(mChatLayoutPanel->getRect().getWidth(), mInputEditor->getRect().getHeight() + mInputEditorPad, FALSE); +} + +void LLFloaterIMSessionTab::showTranslationCheckbox(BOOL show) +{ + getChild("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(*iter); + if (floater) + { + floater->reloadMessages(clean_messages); + } + } + + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("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(*iter); + if (floater && floater->getLastChatMessageIndex() == -1) + { + floater->reloadMessages(true); + } + } + + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat && nearby_chat->getMessageArchiveLength() == 0) + { + nearby_chat->reloadMessages(true); + } +} + +void LLFloaterIMSessionTab::updateCallBtnState(bool callIsActive) +{ + LLButton* voiceButton = getChild("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(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) + { + // The state must toggle the collapsed state of the panel + bool should_be_expanded = self->mParticipantListPanel->isCollapsed(); + + // Update the expand/collapse flag of the participant list panel and save it + gSavedSettings.setBOOL("IMShowControlPanel", should_be_expanded); + self->mIsParticipantListExpanded = should_be_expanded; + + // Refresh for immediate feedback + self->refreshConversation(); + } + } + + self->assignResizeLimits(); +} + +void LLFloaterIMSessionTab::onCollapseToLine(LLFloaterIMSessionTab* self) +{ + LLFloaterIMContainer* host_floater = dynamic_cast(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->mInputEditor->enableSingleLineMode(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() + + mChatLayoutPanel->getRect().getHeight() - mChatLayoutPanelHeight + 2; + floater_rect.mTop -= height; + } + else + { + floater_rect.mTop = floater_rect.mBottom + mFloaterHeight; + } + + enableResizeCtrls(true, true, !collapse); + + saveCollapsedState(); + 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); + saveCollapsedState(); + enableResizeCtrls(true, true, true); + } +} + +/*virtual*/ +void LLFloaterIMSessionTab::onOpen(const LLSD& key) +{ + if (!checkIfTornOff()) + { + LLFloaterIMContainer* host_floater = dynamic_cast(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("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("add_btn")->getRect(); + LLRect call_btn_rect = getChild("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("add_btn")->setRect(add_btn_rect); + getChild("voice_call_btn")->setRect(call_btn_rect); + } +} + +void LLFloaterIMSessionTab::initBtns() +{ + LLRect gear_btn_rect = mGearBtn->getRect(); + LLRect add_btn_rect = getChild("add_btn")->getRect(); + LLRect call_btn_rect = getChild("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("add_btn")->setRect(add_btn_rect); + getChild("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 selected_items = mConversationsRoot->getSelectionList(); + + std::set::const_iterator it = selected_items.begin(); + const std::set::const_iterator it_end = selected_items.end(); + + for (; it != it_end; ++it) + { + LLConversationItem* conversation_item = static_cast((*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(mConversationsRoot->getCurSelectedItem()->getViewModelItem()) ; + } + + return conversationItem; +} + +void LLFloaterIMSessionTab::saveCollapsedState() +{ + LLFloaterIMSessionTab* conversp = LLFloaterIMSessionTab::getConversation(mSessionID); + if(conversp->isNearbyChat()) + { + gSavedPerAccountSettings.setBOOL("NearbyChatIsNotCollapsed", isMessagePaneExpanded()); + } +} +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..302d5a8066 --- /dev/null +++ b/indra/newview/llfloaterimsessiontab.h @@ -0,0 +1,216 @@ +/** + * @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(); + void saveCollapsedState(); + +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; + LLLayoutStack * mInputPanels; + + LLButton* mExpandCollapseLineBtn; + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + LLButton* mGearBtn; + +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. + + S32 mInputEditorPad; + S32 mChatLayoutPanelHeight; + S32 mFloaterHeight; +}; + + +#endif /* LL_FLOATERIMSESSIONTAB_H */ diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 41b7b24f70..57534f421a 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -50,7 +50,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)); @@ -71,6 +73,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()) @@ -84,7 +94,6 @@ LLFloaterInspect::~LLFloaterInspect(void) { LLFloaterReg::showInstance("build", LLSD(), TRUE); } - //sInstance = NULL; } void LLFloaterInspect::onOpen(const LLSD& key) @@ -189,15 +198,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; @@ -256,7 +256,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)) @@ -273,7 +277,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(); @@ -323,6 +331,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 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 be92b5889d..b661517cbb 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -1095,6 +1095,8 @@ void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata) void LLPanelLandGeneral::onClickSellLand(void* data) { LLViewerParcelMgr::getInstance()->startSellLand(); + LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)data; + panelp->refresh(); } // static @@ -2901,11 +2903,13 @@ void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata) void LLPanelLandAccess::onClickAddAccess() { + LLView * button = findChild("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); } } @@ -2950,11 +2954,13 @@ void LLPanelLandAccess::onClickRemoveAccess(void* data) // static void LLPanelLandAccess::onClickAddBanned() { + LLView * button = findChild("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("notifications_list"); scroll->setDoubleClickCallback(onClickNotification, this); scroll->setRect(LLRect( getRect().mLeft, getRect().mTop, getRect().mRight, 0)); - scroll = getChild("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("notifications_list")->setTabStop(!header_button->getToggleState()); self->getChild("notifications_list")->setVisible(!header_button->getToggleState()); - self->getChild("notification_rejects_list")->setTabStop(!header_button->getToggleState()); - self->getChild("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("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("notifications_list")->addElement(row) : - getChild("notification_rejects_list")->addElement(row); + LLScrollListItem* sli = getChild("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("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(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()); + LLNotificationsUI::LLNotificationHandler * notification_handler = dynamic_cast(LLNotifications::instance().getChannel("AlertModal").get()); + llassert(notification_handler); - 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(params); - - addChild(mWindowShade); - mWindowShade->show(notification); - -#else - - LLNotificationsUI::LLEventHandler * handler = - LLNotificationsUI::LLNotificationManager::instance().getHandlerForNotification("alertmodal"); - - LLNotificationsUI::LLSysHandler * sys_handler = dynamic_cast(handler); - llassert(sys_handler); - - sys_handler->processNotification(notify); - -#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 08b019b3d3..048b9876de 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -53,19 +53,14 @@ #include "llfloaterhardwaresettings.h" #include "llfloatersidepanelcontainer.h" // [FS communication UI] -//#include "llimfloater.h" +//#include "llfloaterimsession.h" CHUI Merge new +//#include "llimfloater.h" CHUI Merge old #include "fsfloaterim.h" // [FS communication UI] #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationtemplate.h" @@ -117,6 +112,7 @@ #include "llteleporthistorystorage.h" #include "llproxy.h" // [RLVa:KB] - Checked: 2010-03-18 (RLVa-1.2.0a) +#include "rlvactions.h" #include "rlvhandler.h" // [/RLVa:KB] #include "llsdserialize.h" // KB: SkinsSelector @@ -417,7 +413,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); static bool registered_dialog = false; @@ -446,8 +443,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)); @@ -481,7 +476,7 @@ 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)); // Backup settings mCommitCallbackRegistrar.add("Pref.SetBackupSettingsPath", boost::bind(&LLFloaterPreference::onClickSetBackupSettingsPath, this)); @@ -504,6 +499,9 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) gSavedSettings.getControl("FSPublishRadarTag")->getCommitSignal()->connect(boost::bind(&handlePublishRadarTagOptionChanged, _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 ) @@ -577,10 +575,11 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { // [FS communication UI] - //gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - //gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); - //gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); - //gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); + //gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); CHUI Merge removed by LL + //gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); CHUI Merge removed by LL + //gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate, _2)); CHUI Merge removed by LL + //gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); CHUI Merge removed by LL + //gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false)); CHUI Merge New gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&FSFloaterIM::processChatHistoryStyleUpdate, _2)); gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&FSFloaterNearbyChat::processChatHistoryStyleUpdate, _2)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&FSFloaterIM::processChatHistoryStyleUpdate, _2)); @@ -603,19 +602,33 @@ BOOL LLFloaterPreference::postBuild() std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); setCacheLocation(cache_location); + getChild("log_path_string")->setEnabled(FALSE); // make it read-only but selectable + getChild("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); + getChild("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); + getChild("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); + getChild("ConferenceIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ConferenceIMOptions")); + getChild("GroupChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"GroupChatOptions")); + getChild("NearbyChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NearbyChatOptions")); + // ## Zi: Optional Edit Appearance Lighting gSavedSettings.getControl("AppearanceCameraMovement")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::onAppearanceCameraChanged, this)); onAppearanceCameraChanged(); // ## Zi: Optional Edit Appearance Lighting - // if floater is opened before login set default localized busy message + + // 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)); + // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-06-11 (Catznip-2.6.c) | Added: Catznip-2.6.0c #ifndef LL_SEND_CRASH_REPORTS // Hide the crash report tab if crash reporting isn't enabled @@ -677,17 +690,21 @@ void LLFloaterPreference::onPieColorsOverrideChanged() } // ## Zi: Pie menu -void LLFloaterPreference::onBusyResponseChanged() +void LLFloaterPreference::updateDeleteTranscriptsButton() { - // set "BusyResponseChanged" TRUE if user edited message differs from default, FALSE otherwise - if (LLTrans::getString("BusyModeResponseDefault") != getChild("busy_response")->getValue().asString()) - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", TRUE ); - } - else - { - gSavedPerAccountSettings.setBOOL("BusyResponseChanged", FALSE ); - } + std::vector list_of_transcriptions_file_names; + LLLogChat::getListOfTranscriptFiles(list_of_transcriptions_file_names); + getChild("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("do_not_disturb_response")->getValue().asString(); + + gSavedPerAccountSettings.setBOOL("DoNotDisturbResponseChanged", response_changed_flag ); } // ## Zi: Optional Edit Appearance Lighting @@ -709,6 +726,8 @@ LLFloaterPreference::~LLFloaterPreference() { ctrl_window_size->setCurrentByIndex(i); } + + LLConversationLog::instance().removeObserver(this); CHUI Merge new. so maybe this is used? */ } @@ -776,7 +795,7 @@ void LLFloaterPreference::apply() LLViewerMedia::setCookiesEnabled(getChild("cookies_enabled")->getValue()); - if (hasChild("web_proxy_enabled") &&hasChild("web_proxy_editor") && hasChild("web_proxy_port")) + if (hasChild("web_proxy_enabled", TRUE) &&hasChild("web_proxy_editor", TRUE) && hasChild("web_proxy_port", TRUE)) { bool proxy_enable = getChild("web_proxy_enabled")->getValue(); std::string proxy_address = getChild("web_proxy_editor")->getValue(); @@ -784,14 +803,8 @@ void LLFloaterPreference::apply() LLViewerMedia::setProxyConfig(proxy_enable, proxy_address, proxy_port); } -// LLWString busy_response = utf8str_to_wstring(getChild("busy_response")->getValue().asString()); -// LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); - - gSavedSettings.setBOOL("PlainTextChatHistory", getChild("plain_text_chat_history")->getValue().asBoolean()); - if (mGotPersonalInfo) { -// gSavedSettings.setString("BusyModeResponse2", std::string(wstring_to_utf8str(busy_response))); bool new_im_via_email = getChild("send_im_to_email")->getValue().asBoolean(); bool new_hide_online = getChild("online_visibility")->getValue().asBoolean(); @@ -881,21 +894,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(); @@ -945,6 +958,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(); @@ -982,12 +1003,12 @@ void LLFloaterPreference::onLocalLightsEnable() } //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")); } } @@ -1042,12 +1063,35 @@ 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); // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2011-10-02 (Catznip-2.8.0e) | Added: Catznip-2.8.0e // We need to save all crash settings, even if they're defaults [see LLCrashLogger::loadCrashBehaviorSetting()] gCrashSettings.saveToFile(gSavedSettings.getString("CrashSettingsFile"), FALSE); // [/SL:KB] + + //Only save once logged in and loaded per account settings + if(mGotPersonalInfo) + { + gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); + } } else { @@ -1100,12 +1144,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("preferences"); if (instance) { - instance->setPersonalInfo(visibility, im_via_email, email); + instance->setPersonalInfo(visibility, im_via_email); } } @@ -1147,6 +1191,23 @@ void LLFloaterPreference::onLanguageChange() } } +void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) +{ + mNotificationOptions[OptionName] = getChild(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("notifications_alert")->setVisible(show_notifications_alert); +} + void LLFloaterPreference::onNameTagOpacityChange(const LLSD& newvalue) { LLColorSwatchCtrl* color_swatch = findChild("background"); @@ -1459,6 +1520,13 @@ void LLFloaterPreference::refreshEnabledState() childSetEnabled("busy_response", !gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)); // [/RLVa:KB] +// [RLVa:KB] - Checked: 2013-05-11 (RLVa-1.4.9) + if (rlv_handler_t::isEnabled()) + { + getChild("do_not_disturb_response")->setEnabled(!RlvActions::hasBehaviour(RLV_BHVR_SENDIM)); + } +// [/RLVa:KB] + // Reflections BOOL reflections = gSavedSettings.getBOOL("VertexShaderEnable") && gGLManager.mHasCubeMap @@ -1880,14 +1948,27 @@ 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(); +} //[FIX FIRE-2765 : SJ] Enable Reset button when own Chatlogdirectory is set getChildView("reset_logpath")->setEnabled(TRUE); } @@ -1899,7 +1980,71 @@ void LLFloaterPreference::onClickResetLogPath() gSavedPerAccountSettings.setString("InstantMessageLogPath", gDirUtilp->getChatLogsDir()); } -void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im_via_email, const std::string& email) +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 listOfTranscripts; + std::vector 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) { mGotPersonalInfo = true; mOriginalIMViaEmail = im_via_email; @@ -1921,31 +2066,15 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im } getChild("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("online_visibility")->setValue(mOriginalHideOnlineStatus); getChild("online_visibility")->setLabelArg("[DIR_VIS]", mDirectoryVisibility); getChildView("send_im_to_email")->setEnabled(TRUE); getChild("send_im_to_email")->setValue(im_via_email); - 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("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. || Moved to PostBuild to disable on not logged in state -WoLf +// getChildView("log_path_string")->setEnabled(FALSE);// LineEditor becomes readonly in this case. || Moved to PostBuild to disable on not logged in state -WoLf || CHUI Merge removed by ll, check where this was moved to in FS and see if it needs updating getChildView("log_path_button")->setEnabled(TRUE); + getChildView("chat_font_size")->setEnabled(TRUE); getChildView("open_log_path_button")->setEnabled(TRUE); getChildView("log_path_button-panelsetup")->setEnabled(TRUE);// second set of controls for panel_preferences_setup -WoLf getChildView("open_log_path_button-panelsetup")->setEnabled(TRUE); @@ -1955,25 +2084,22 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im { getChildView("reset_logpath")->setEnabled(TRUE); } - childEnable("logfile_name_datestamp"); - std::string display_email(email); // Show email address in preferences (FIRE-1071) - //getChild("email_address")->setValue(display_email); - if(display_email.size() > 30) - { - display_email.resize(30); - display_email += "..."; - } - getChild("send_im_to_email")->setLabelArg("[EMAIL]", display_email); + //getChild("email_address")->setValue(display_email); CHUI Merge LL removed this line, commenting out rest fror now + //if(display_email.size() > 30) + //{ + // display_email.resize(30); + // display_email += "..."; + //} + //getChild("send_im_to_email")->setLabelArg("[EMAIL]", display_email); // Show email address in preferences (FIRE-1071) - } void LLFloaterPreference::onUpdateSliderText(LLUICtrl* ctrl, const LLSD& name) { std::string ctrl_name = name.asString(); - if ((ctrl_name =="" )|| !hasChild(ctrl_name, true)) + if ((ctrl_name =="" )|| !hasChild(ctrl_name, TRUE)) return; LLTextBox* text_box = getChild(name.asString()); @@ -2029,14 +2155,17 @@ void LLFloaterPreference::onChangeMaturity() void LLFloaterPreference::onClickBlockList() { // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); + //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); CHUI Merge old + //LLFloaterSidePanelContainer::showPanel("people", "panel_people", CHUI Merge new, used in else for now + // LLSD().with("people_panel_tab_name", "blocked_panel")); CHUI Merge new if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) { LLFloaterReg::showInstance("fs_blocklist", LLSD()); } else { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); + LLFloaterSidePanelContainer::showPanel("people", "panel_people", + LLSD().with("people_panel_tab_name", "blocked_panel")); } // } @@ -2072,6 +2201,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("delete_transcripts"); + + if (!delete_transcripts_buttonp->getEnabled()) + { + delete_transcripts_buttonp->setEnabled(true); + } +} + void LLFloaterPreference::updateClickActionSettings() { const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); @@ -2144,6 +2300,35 @@ void LLFloaterPreference::setCacheLocation(const LLStringExplicit& location) cache_location_editor->setToolTip(location); } +void LLFloaterPreference::selectPanel(const LLSD& name) +{ + LLTabContainer * tab_containerp = getChild("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("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); + + // set 'enable' property for 'Delete transcripts...' button + updateDeleteTranscriptsButton(); + +} + //------------------------------Updater--------------------------------------- // FIRE-6795: Remove repetitive warning at every login @@ -2217,26 +2402,31 @@ LLPanelPreference::LLPanelPreference() //virtual BOOL LLPanelPreference::postBuild() { + ////////////////////// PanelGeneral /////////////////// + if (hasChild("display_names_check", TRUE)) + { + BOOL use_people_api = gSavedSettings.getBOOL("UsePeopleAPI"); + LLCheckBoxCtrl* ctrl_display_name = getChild("display_names_check"); + ctrl_display_name->setEnabled(use_people_api); + if (!use_people_api) + { + ctrl_display_name->setValue(FALSE); + } + } ////////////////////// PanelVoice /////////////////// - if (hasChild("voice_unavailable")) + if (hasChild("voice_unavailable", TRUE)) { BOOL voice_disabled = gSavedSettings.getBOOL("CmdLineDisableVoice"); getChildView("voice_unavailable")->setVisible( voice_disabled); getChildView("enable_voice_check")->setVisible( !voice_disabled); } - - //////////////////////PanelGraphics (hardware) /////////////////// - if (gGLManager.mIsIntel || gGLManager.mGLVersion < 3.f) - { //remove FSAA settings above "4x" - LLComboBox* combo = getChild("fsaa"); - combo->remove("8x"); - combo->remove("16x"); - } - + //////////////////////PanelSkins /////////////////// - /* - if (hasChild("skin_selection")) + + /* CHUI Merge no note given why this is comented out + if (hasChild("skin_selection", TRUE)) CHUI Merge new + if (hasChild("skin_selection")) CHUI Merge old { LLFloaterPreference::refreshSkin(this); @@ -2250,21 +2440,15 @@ BOOL LLPanelPreference::postBuild() } */ - if (hasChild("online_visibility") && hasChild("send_im_to_email")) - { - getChild("email_address")->setValue(getString("log_in_to_change") ); -// getChild("busy_response")->setValue(getString("log_in_to_change")); - } - //////////////////////PanelPrivacy /////////////////// - if (hasChild("media_enabled")) + if (hasChild("media_enabled", TRUE)) { bool media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); getChild("media_enabled")->set(media_enabled); getChild("autoplay_enabled")->setEnabled(media_enabled); } - if (hasChild("music_enabled")) + if (hasChild("music_enabled", TRUE)) { getChild("music_enabled")->set(gSavedSettings.getBOOL("AudioStreamingMusic")); } @@ -2272,17 +2456,17 @@ BOOL LLPanelPreference::postBuild() { getChild("media_filter")->set(gSavedSettings.getBOOL("MediaEnableFilter")); } - if (hasChild("voice_call_friends_only_check")) + if (hasChild("voice_call_friends_only_check", TRUE)) { getChild("voice_call_friends_only_check")->setCommitCallback(boost::bind(&showFriendsOnlyWarning, _1, _2)); } - if (hasChild("favorites_on_login_check")) + if (hasChild("favorites_on_login_check", TRUE)) { getChild("favorites_on_login_check")->setCommitCallback(boost::bind(&showFavoritesOnLoginWarning, _1, _2)); } - // Panel Advanced - if (hasChild("modifier_combo")) + //////////////////////PanelAdvanced /////////////////// + if (hasChild("modifier_combo", TRUE)) { //localizing if push2talk button is set to middle mouse if (MIDDLE_MOUSE_CV == getChild("modifier_combo")->getValue().asString()) @@ -2299,7 +2483,8 @@ BOOL LLPanelPreference::postBuild() //////////////////////PanelSetup /////////////////// // Add warning on high bandwidth settings - // if (hasChild("max_bandwidth")) + //if (hasChild("max_bandwidth"), TRUE) CHUI Merge new + // if (hasChild("max_bandwidth")) CHUI Merge old // Look for the layout widget on top level of this panel if (hasChild("max_bandwidth_layout")) // @@ -2503,7 +2688,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++); @@ -2792,7 +2977,7 @@ void LLFloaterPreferenceProxy::onChangeSocksSettings() otherHttpProxy->selectFirstItem(); } -}; +} // [SL:KB] - Patch: Viewer-CrashReporting | Checked: 2010-11-16 (Catznip-2.6.0a) | Added: Catznip-2.4.0b static LLRegisterPanelClassWrapper t_pref_crashreports("panel_preference_crashreports"); diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 5e2eff45cb..951541f0cf 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -35,8 +35,10 @@ #include "llfloater.h" #include "llavatarpropertiesprocessor.h" +#include "llconversationlog.h" #include "lllineeditor.h" // +class LLConversationLogObserver; class LLPanelPreference; class LLPanelLCD; class LLPanelDebug; @@ -47,6 +49,8 @@ class LLSD; class LLTextBox; class LLComboBox; +typedef std::map notifications_map; + typedef enum { GS_LOW_GRAPHICS, @@ -58,7 +62,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); @@ -70,20 +74,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(); @@ -93,16 +101,17 @@ protected: //void onClickClearCache(); // Clear viewer texture cache, vfs, and VO cache on next startup // AO: was protected, moved to public 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 "DoNotDisturbResponseChanged" in account settings depending on whether do not disturb response // Pie menu // make sure controls get greyed out or enabled when pie color override is toggled void onPieColorsOverrideChanged(); // Pie menu - // set value of "BusyResponseChanged" in account settings depending on whether busy 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); @@ -158,17 +167,16 @@ public: void onClickSetSounds(); void onClickPreviewUISound(const LLSD& ui_sound_id); // FIRE-8190: Preview function for "UI Sounds" Panel void setPreprocInclude(); -// void onClickSkipDialogs(); -// void onClickResetDialogs(); void onClickEnablePopup(); void onClickDisablePopup(); void resetAllIgnored(); void setAllIgnored(); void onClickLogPath(); + bool moveTranscriptsAndLog(); //[FIX FIRE-2765 : SJ] Making sure Reset button resets works void onClickResetLogPath(); 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(); @@ -197,9 +205,10 @@ 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); // Backup settings void onClickSetBackupSettingsPath(); @@ -214,12 +223,19 @@ public: void onQuitConfirmed(const LLSD& notification,const LLSD& response); // callback for finished restore dialog // 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 dbc3fbc9aa..b51675a464 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -704,8 +704,10 @@ void LLPanelRegionGeneralInfo::onClickKick() // this depends on the grandparent view being a floater // in order to set up floater dependency + LLView * button = findChild("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); @@ -1094,7 +1096,14 @@ BOOL LLPanelRegionDebugInfo::sendUpdate() void LLPanelRegionDebugInfo::onClickChooseAvatar() { - LLFloaterAvatarPicker::show(boost::bind(&LLPanelRegionDebugInfo::callbackAvatarID, this, _1, _2), FALSE, TRUE); + LLView * button = findChild("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); + } } @@ -1660,8 +1669,10 @@ void LLPanelEstateInfo::onClickKickUser() { // this depends on the grandparent view being a floater // in order to set up floater dependency + LLView * button = findChild("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); @@ -1836,8 +1847,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("add_allowed_avatar_btn"); + break; + + case ESTATE_ACCESS_BANNED_AGENT_ADD: + button = panel->findChild("add_banned_avatar_btn"); + break; + + case ESTATE_ACCESS_MANAGER_ADD: + button = panel->findChild("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 64fe7da143..0ae612cc8d 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() { } @@ -184,6 +185,11 @@ BOOL LLFloaterReporter::postBuild() // virtual LLFloaterReporter::~LLFloaterReporter() { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + // child views automatically deleted mObjectID = LLUUID::null; @@ -290,10 +296,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("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); } } @@ -315,11 +324,17 @@ void LLFloaterReporter::setFromAvatarID(const LLUUID& avatar_id) std::string avatar_link = LLSLURL("agent", mObjectID, "inspect").getSLURLString(); getChild("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 mMCDList; std::string mDefaultSummary; LLResourceData* mResourceDatap; + boost::signals2::connection mAvatarNameCacheConnection; }; #endif diff --git a/indra/newview/llfloaterscriptlimits.cpp b/indra/newview/llfloaterscriptlimits.cpp index 4d3c438fab..0012121df4 100644 --- a/indra/newview/llfloaterscriptlimits.cpp +++ b/indra/newview/llfloaterscriptlimits.cpp @@ -604,15 +604,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::iterator id_itor; for (id_itor = mObjectListItems.begin(); id_itor != mObjectListItems.end(); ++id_itor) @@ -715,10 +707,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 a9455f1ba4..9cf2d5805f 100644 --- a/indra/newview/llfloatersellland.cpp +++ b/indra/newview/llfloatersellland.cpp @@ -83,6 +83,7 @@ private: LLUUID mAuthorizedBuyer; bool mParcelSoldWithObjects; SelectionObserver mParcelSelectionObserver; + boost::signals2::connection mAvatarNameCacheConnection; void updateParcelInfo(); void refreshUI(); @@ -131,13 +132,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); } @@ -232,15 +238,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("sell_to_agent")->setValue(av_name.getCompleteName()); - getChild("sell_to_agent")->setToolTip(av_name.mUsername); + getChild("sell_to_agent")->setToolTip(av_name.getUserName()); } void LLFloaterSellLandUI::setBadge(const char* id, Badge badge) @@ -394,7 +405,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("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/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 0cbc8cb8be..64cb4d411b 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -338,18 +338,19 @@ void LLFloaterSettingsDebug::updateControl() // [RLVa:KB] - Checked: 2011-05-28 (RLVa-1.4.0a) | Modified: RLVa-1.4.0a // If "HideFromEditor" was toggled while the floater is open then we need to manually disable access to the control // NOTE: this runs per-frame so there's no need to explictly handle onCommitSettings() or onClickDefault() - mOldVisibility=mCurrentControlVariable->isHiddenFromSettingsEditor(); - mSpinner1->setEnabled(!mOldVisibility); - mSpinner2->setEnabled(!mOldVisibility); - mSpinner3->setEnabled(!mOldVisibility); - mSpinner4->setEnabled(!mOldVisibility); - mColorSwatch->setEnabled(!mOldVisibility); - mValText->setEnabled(!mOldVisibility); - mBooleanCombo->setEnabled(!mOldVisibility); - mDefaultButton->setEnabled(!mOldVisibility); + bool fEnable = !controlp->isHiddenFromSettingsEditor(); + spinner1->setEnabled(fEnable); + spinner2->setEnabled(fEnable); + spinner3->setEnabled(fEnable); + spinner4->setEnabled(fEnable); + color_swatch->setEnabled(fEnable); + childSetEnabled("val_text", fEnable); + childSetEnabled("boolean_combo", fEnable); + childSetEnabled("default_btn", fEnable); // [/RLVa:KB] + mCopyButton->setEnabled(TRUE); mSanityButton->setVisible(!mCurrentControlVariable->isSane()); diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp old mode 100755 new mode 100644 index aa4280d911..2101519af2 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -89,7 +89,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na if (!getVisible()) { - openFloater(); + openFloater(); } LLPanel* panel = NULL; @@ -97,10 +97,7 @@ LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_na LLSideTrayPanelContainer* container = dynamic_cast(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(view)) != NULL) @@ -127,8 +124,8 @@ void LLFloaterSidePanelContainer::showPanel(const std::string& floater_name, con { LLFloaterSidePanelContainer* floaterp = LLFloaterReg::getTypedInstance(floater_name); // if (floaterp) -// [RLVa:KB] - Checked: 2012-02-07 (RLVa-1.4.5) | Added: RLVa-1.4.5 - if ( (floaterp) && (mValidateSignal(floater_name, sMainPanelName, key)) ) +// [RLVa:KB] - Checked: 2013-04-16 (RLVa-1.4.8) + if ( (floaterp) && ((floaterp->getVisible()) || (LLFloaterReg::canShowInstance(floater_name, key))) && (canShowPanel(floater_name, key)) ) // [/RLVa:KB] { floaterp->openChildPanel(sMainPanelName, key); @@ -139,8 +136,8 @@ void LLFloaterSidePanelContainer::showPanel(const std::string& floater_name, con { LLFloaterSidePanelContainer* floaterp = LLFloaterReg::getTypedInstance(floater_name); // if (floaterp) -// [RLVa:KB] - Checked: 2012-02-07 (RLVa-1.4.5) | Added: RLVa-1.4.5 - if ( (floaterp) && (mValidateSignal(floater_name, panel_name, key)) ) +// [RLVa:KB] - Checked: 2013-04-16 (RLVa-1.4.8) + if ( (floaterp) && ((floaterp->getVisible()) || (LLFloaterReg::canShowInstance(floater_name, key))) && (canShowPanel(floater_name, panel_name, key)) ) // [/RLVa:KB] { floaterp->openChildPanel(panel_name, key); diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h old mode 100755 new mode 100644 diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index b606de0381..8f067741a0 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -797,7 +797,6 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) mRadioGroupEdit->setVisible( edit_visible ); //bool linked_parts = gSavedSettings.getBOOL("EditLinkedParts"); static LLCachedControl linked_parts(gSavedSettings, "EditLinkedParts"); - // Was removed from floater_tools.xml as part of SH-1917 SH-1935 //getChildView("RenderingCost")->setVisible( !linked_parts && (edit_visible || focus_visible || move_visible) && sShowObjectCost); mBtnLink->setVisible(edit_visible); diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index df434b7160..f6c41e4780 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -213,17 +213,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"; @@ -312,7 +304,7 @@ void LLFloaterTopObjects::updateSelectionInfo() LLAvatarName av_name; if (LLAvatarNameCache::get(object_id, &av_name)) { - bool isAvatar = (av_name.mDisplayName != OBJECT_NOT_AVATAR_NAME); + bool isAvatar = (av_name.getDisplayName() != OBJECT_NOT_AVATAR_NAME); getChild("profile_btn")->setEnabled(isAvatar); getChild("estate_kick_btn")->setEnabled(isAvatar && object_id != gAgentID); } @@ -604,7 +596,7 @@ void LLFloaterTopObjects::onAvatarCheck(const LLUUID& avatar_id, LLAvatarName av if (first_selected->getUUID() == avatar_id) { - bool isAvatar = (av_name.mDisplayName != OBJECT_NOT_AVATAR_NAME); + bool isAvatar = (av_name.getDisplayName() != OBJECT_NOT_AVATAR_NAME); getChild("profile_btn")->setEnabled(isAvatar); getChild("estate_kick_btn")->setEnabled(isAvatar && avatar_id != gAgentID); } diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index a1702c5b47..33f2c35239 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -29,7 +29,7 @@ #include "llfloatertranslationsettings.h" // Viewer includes -// #include "llnearbychatbar.h" // Remove floating chat bar +#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,9 +301,7 @@ void LLFloaterTranslationSettings::onBtnOK() gSavedSettings.setString("TranslationService", getSelectedService()); gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->showTranslationCheckbox(LLTranslate::isTranslationConfigured()); - // TODO: Add code to make this happen via control callback within LLNearbyChat - // + (LLFloaterReg::getTypedInstance("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("mute_btn")->setCommitCallback( + boost::bind(&LLFloaterVoiceVolume::onClickMuteVolume, this) ); + + getChild("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("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("mute_btn"); + LLUICtrl* volume_slider = getChild("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("avatar_name")->setValue(av_name.getCompleteName()); + mAvatarName = av_name; +} + +////////////////////////////////////////////////////////////////////////////// +// LLFloaterVoiceVolumeUtil +////////////////////////////////////////////////////////////////////////////// +void LLFloaterVoiceVolumeUtil::registerFloater() +{ + LLFloaterReg::add("floater_voice_volume", "floater_voice_volume.xml", + &LLFloaterReg::build); +} 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/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 getIcon() const = 0; - virtual LLPointer 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& 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 9b9ed12b1c..0000000000 --- a/indra/newview/llfolderviewitem.cpp +++ /dev/null @@ -1,3083 +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 "aoengine.h" // ## Zi: Animation Overrider -#include "fslslbridge.h" //-TT Client LSL Bridge -#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" - -#include "llinventorypanel.h" - -///---------------------------------------------------------------------------- -/// Class LLFolderViewItem -///---------------------------------------------------------------------------- - -static LLDefaultChildRegistry::Register r("folder_view_item"); - -// statics -std::map 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() -{ - static LLCachedControl FolderViewItemHeight(gSavedSettings, "FSFolderViewItemHeight"); - item_height = (S32)FolderViewItemHeight; -} - -// 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); - }; - - // Don't bother with tooltips in inventory - // 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() -{ - std::string oldLabel = mLabel; - - 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(); - // } - // } - - if( oldLabel.compare( mLabel ) ) - { - dirtyFilter(); - if (mParentFolder) - { - mParentFolder->requestSort(); - mParentFolder->requestArrange(); - } - } - else - dirtyFilter(); - - 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 LLFolderViewItem::getSelectionList() const -{ - std::set 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(); - 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(); - - // Begin Multi-substring inventory search - mStringMatchOffsets.clear(); - mStringMatchSizes.clear(); - for (U32 index = 0, max = filter.getFilterSubStringCount(); index < max; index++) - { - mStringMatchOffsets.push_back(filter.getFilterSubStringPos(index)); - mStringMatchSizes.push_back(filter.getFilterSubStringLen(index)); - } - // End Multi-substring inventory search - - filter.decrementFilterCount(); - -#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Reduce memory load for users with huge inventory by only creating member we really need - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -#endif // -} - -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(); - } - } - } -} - -std::string LLFolderViewItem::getSearchableLabel() const -{ - std::string searchable_label(mLabel); - searchable_label.append(mLabelSuffix); - LLStringUtil::toUpper(searchable_label); - return searchable_label; - - // return mSearchableLabel; -} - -// ## Zi: Extended Inventory Search -std::string LLFolderViewItem::getSearchableCreator() const -{ - LLViewerInventoryItem* item=getInventoryItem(); - std::string creator_name; - if(item) - if(gCacheName->getFullName(item->getCreatorUUID(),creator_name)) - LLStringUtil::toUpper( creator_name ); - - return creator_name; -} - -std::string LLFolderViewItem::getSearchableDescription() const -{ - std::string strDescr; - LLViewerInventoryItem* item=getInventoryItem(); - - if(item) - { - if(!item->getDescription().empty() ) - { - strDescr = item->getDescription(); - LLStringUtil::toUpper( strDescr ); - } - } - - return strDescr; -} - -std::string LLFolderViewItem::getSearchableUUID() const -{ - std::string strUUID; - LLViewerInventoryItem* item=getInventoryItem(); - if(item) - { - if(!item->getAssetUUID().isNull()) - { - strUUID = item->getAssetUUID().asString(); - LLStringUtil::toUpper( strUUID ); - } - - } - return strUUID; -} - -std::string LLFolderViewItem::getSearchableAll() const -{ - return getSearchableLabel()+"+"+getSearchableCreator()+"+"+getSearchableDescription()+"+"+getSearchableUUID(); -} - -LLViewerInventoryItem * LLFolderViewItem::getInventoryItem(void) const -{ - if (!getListener()) return NULL; - return gInventory.getItem(getListener()->getUUID()); -} - - -// ## Zi: Extended Inventory Search - -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() -{ - // Make inventory selection color independent from menu color - //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 sFgColor = LLUIColorTable::instance().getColor("InventoryItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("InventoryItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("InventoryItemHighlightFgColor", DEFAULT_WHITE); - // Make inventory selection color independent from menu color - 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 sProtectedColor = LLUIColorTable::instance().getColor("InventoryProtectedColor", DEFAULT_WHITE); // ## Zi: Animation Overrider - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - - const Params& default_params = LLUICtrlFactory::getDefaultParams(); - 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 LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Reduce memory load for users with huge inventory by only creating member we really need - 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; - } -#endif // - - //--------------------------------------------------------------------------------// - // Draw the actual label text - // - // FIRE-6714: Don't move objects to trash during cut&paste - // Don't hide cut items in inventory - if (!getRoot()->getFilter()->checkAgainstClipboard(mListener->getUUID())) - { - // Fade out item color to indicate it's being cut - color.mV[VALPHA] *= 0.5f; - } - // FIRE-6714: Don't move objects to trash during cut&paste - - 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); - } - - // ## Zi: Animation Overrider - //--------------------------------------------------------------------------------// - // Draw "protected" indicator - // - if((mListener->getUUID()==AOEngine::instance().getAOFolder() && gSavedPerAccountSettings.getBOOL("ProtectAOFolders")) -//-TT Client LSL Bridge - ||(mListener->getUUID()==FSLSLBridge::instance().getBridgeFolder() && gSavedPerAccountSettings.getBOOL("ProtectBridgeFolder")) -//-TT - ) - { - std::string locked_string = " (" + LLTrans::getString("ProtectedFolder") + ") "; - font->renderUTF8(locked_string, 0, right_x, y, sProtectedColor, - LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - S32_MAX, S32_MAX, &right_x, FALSE); - } - // ## Zi: Animation Overrider - - //--------------------------------------------------------------------------------// - // 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 - // - - // Begin Multi-substring inventory search -// if (mStringMatchOffsets.size()) - - // ## Zi: Extended Inventory Search - only highlight when search target is set to "name" - if (mStringMatchOffsets.size() && getRoot()->getFilter()->getFilterSubStringTarget()==LLInventoryFilter::SUBST_TARGET_NAME) - { - //std::vector mStringMatchOffsets; - //std::vector mStringMatchSizes; - std::vector::iterator offset_iter = mStringMatchOffsets.begin(); - std::vector::iterator size_iter = mStringMatchSizes.begin(); - - LLUIImage* box_image = default_params.selection_image; - - while (offset_iter < mStringMatchOffsets.end() && size_iter < mStringMatchSizes.end()) - { - // don't draw backgrounds for zero-length strings - S32 filter_string_length = *size_iter; - S32 filter_string_offset = *offset_iter; - if (filter_string_length > 0 && filter_string_offset != std::string::npos) - { - std::string combined_string = mLabel + mLabelSuffix; - S32 left = llround(text_left) + font->getWidth(combined_string, 0, filter_string_offset) - 1; - S32 right = left + font->getWidth(combined_string, filter_string_offset, filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; - - LLRect box_rect(left, top, right, bottom); - box_image->draw(box_rect, sFilterBGColor); - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_string_offset); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; - font->renderUTF8( combined_string, filter_string_offset, match_string_left, yy, - sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, - filter_string_length, S32_MAX, &right_x, FALSE ); - } - offset_iter++; - size_iter++; - } - } - // End Multi-substring inventory search -} - -bool LLFolderViewItem::isInSelection() const -{ - return mIsSelected || (mParentFolder && mParentFolder->isInSelection()); -} -// Don't bother with unneeded tooltips in inventor - -BOOL LLFolderViewItem::handleToolTip(S32 x, S32 y, MASK mask) -{ - if( childrenHandleToolTip( x, y, mask ) ) - return TRUE; - - int nStart = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation; - int nWidth = getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + nStart; - - if( getRoot()->getParentPanel()->getRect().getWidth() < nWidth ) // Label is truncated, display tooltip - { - setToolTip( mLabel ); - return LLView::handleToolTip( x, y, mask ); - } - else - setToolTip( LLStringExplicit("") ); - - // In case of root we always want to return TRUE, otherwise tooltip handling gets propagated one level up and we end with a tooltip like 'All Items'. - if( this == getRoot() ) - return TRUE; - - return FALSE; -} -// - -///---------------------------------------------------------------------------- -/// Class LLFolderViewFolder -///---------------------------------------------------------------------------- - -LLFolderViewFolder::LLFolderViewFolder( const LLFolderViewItem::Params& p ): - LLFolderViewItem( p ), // 0 = no create time - mIsOpen(FALSE), - mExpanderHighlighted(FALSE), - mCurHeight(0.f), - mTargetHeight(0.f), - mAutoOpenCountdown(0.f), - mSubtreeCreationDate(0), - mAmTrash(LLFolderViewFolder::UNKNOWN), - mLastArrangeGeneration( -1 ), - mLastCalculatedWidth(0), - mCompletedFilterGeneration(-1), - mMostFilteredDescendantGeneration(-1), - mNeedsSort(false), - mPassedFolderFilter(FALSE) -{ -} - -// Destroys the object -LLFolderViewFolder::~LLFolderViewFolder( void ) -{ - // The LLView base class takes care of object destruction. make sure that we - // don't have mouse or keyboard focus - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() -} - -void LLFolderViewFolder::setFilteredFolder(bool filtered, S32 filter_generation) -{ - mPassedFolderFilter = filtered; - mLastFilterGeneration = filter_generation; -} - -bool LLFolderViewFolder::getFilteredFolder(S32 filter_generation) -{ - return mPassedFolderFilter && mLastFilterGeneration >= getRoot()->getFilter()->getMinRequiredGeneration(); -} - -// addToFolder() returns TRUE if it succeeds. FALSE otherwise -BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root) -{ - if (!folder) - { - return FALSE; - } - mParentFolder = folder; - root->addItemID(getListener()->getUUID(), this); - return folder->addFolder(this); -} - -// Finds width and height of this object and its children. Also -// makes sure that this view and its children are the right size. -S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation) -{ - // sort before laying out contents - if (mNeedsSort) - { - mFolders.sort(mSortFunction); - mItems.sort(mSortFunction); - mNeedsSort = false; - } - - // evaluate mHasVisibleChildren - mHasVisibleChildren = false; - if (hasFilteredDescendants(filter_generation)) - { - // We have to verify that there's at least one child that's not filtered out - bool found = false; - // Try the items first - for (items_t::iterator iit = mItems.begin(); iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - found = (itemp->getFiltered(filter_generation)); - if (found) - break; - } - if (!found) - { - // If no item found, try the folders - for (folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - found = ( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); - if (found) - break; - } - } - - mHasVisibleChildren = found; - } - - // calculate height as a single item (without any children), and reshapes rectangle to match - LLFolderViewItem::arrange( width, height, filter_generation ); - - // clamp existing animated height so as to never get smaller than a single item - mCurHeight = llmax((F32)*height, mCurHeight); - - // initialize running height value as height of single item in case we have no children - *height = getItemHeight(); - F32 running_height = (F32)*height; - F32 target_height = (F32)*height; - - // are my children visible? - if (needsArrange()) - { - // set last arrange generation first, in case children are animating - // and need to be arranged again - mLastArrangeGeneration = getRoot()->getArrangeGeneration(); - if (mIsOpen) - { - // Add sizes of children - S32 parent_item_height = getRect().getHeight(); - - for(folders_t::iterator fit = mFolders.begin(); fit != mFolders.end(); ++fit) - { - LLFolderViewFolder* folderp = (*fit); - if (getRoot()->getDebugFilters()) - { - folderp->setVisible(TRUE); - } - else - { - folderp->setVisible( folderp->getListener() - && (folderp->getFiltered(filter_generation) - || (folderp->getFilteredFolder(filter_generation) - && folderp->hasFilteredDescendants(filter_generation)))); // passed filter or has descendants that passed filter - } - - if (folderp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += folderp->arrange( &child_width, &child_height, filter_generation ); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() ); - } - } - for(items_t::iterator iit = mItems.begin(); - iit != mItems.end(); ++iit) - { - LLFolderViewItem* itemp = (*iit); - if (getRoot()->getDebugFilters()) - { - itemp->setVisible(TRUE); - } - else - { - itemp->setVisible(itemp->getFiltered(filter_generation)); - } - - if (itemp->getVisible()) - { - S32 child_width = *width; - S32 child_height = 0; - S32 child_top = parent_item_height - llround(running_height); - - target_height += itemp->arrange( &child_width, &child_height, filter_generation ); - // don't change width, as this item is as wide as its parent folder by construction - itemp->reshape( itemp->getRect().getWidth(), child_height); - - running_height += (F32)child_height; - *width = llmax(*width, child_width); - itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() ); - } - } - } - - mTargetHeight = target_height; - // cache this width so next time we can just return it - mLastCalculatedWidth = *width; - } - else - { - // just use existing width - *width = mLastCalculatedWidth; - } - - // animate current height towards target height - if (llabs(mCurHeight - mTargetHeight) > 1.f) - { - mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT)); - - requestArrange(); - - // hide child elements that fall out of current animated height - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - // number of pixels that bottom of folder label is from top of parent folder - if (getRect().getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight() - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - // hide if beyond current folder height - (*fit)->setVisible(FALSE); - } - } - - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - // number of pixels that bottom of item label is from top of parent folder - if (getRect().getHeight() - (*iit)->getRect().mBottom - > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP) - { - (*iit)->setVisible(FALSE); - } - } - } - else - { - mCurHeight = mTargetHeight; - } - - // don't change width as this item is already as wide as its parent folder - reshape(getRect().getWidth(),llround(mCurHeight)); - - // pass current height value back to parent - *height = llround(mCurHeight); - - return llround(mTargetHeight); -} - -BOOL LLFolderViewFolder::needsArrange() -{ - return mLastArrangeGeneration < getRoot()->getArrangeGeneration(); -} - -void LLFolderViewFolder::requestSort() -{ - mNeedsSort = true; - // whenever item order changes, we need to lay things out again - requestArrange(); -} - -void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up) -{ - mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation); - mCompletedFilterGeneration = generation; - // only aggregate up if we are a lower (older) value - if (recurse_up - && mParentFolder - && generation < mParentFolder->getCompletedFilterGeneration()) - { - mParentFolder->setCompletedFilterGeneration(generation, TRUE); - } -} - -void LLFolderViewFolder::filter( LLInventoryFilter& filter) -{ - S32 filter_generation = filter.getCurrentGeneration(); - // if failed to pass filter newer than must_pass_generation - // you will automatically fail this time, so we only - // check against items that have passed the filter - S32 must_pass_generation = filter.getMustPassGeneration(); - - bool autoopen_folders = (filter.hasFilterString()); - - // if we have already been filtered against this generation, skip out - if (getCompletedFilterGeneration() >= filter_generation) - { - return; - } - - // filter folder itself - if (getLastFilterGeneration() < filter_generation) - { - if (getLastFilterGeneration() >= must_pass_generation // folder has been compared to a valid precursor filter - && !mPassedFilter) // and did not pass the filter - { - // go ahead and flag this folder as done - mLastFilterGeneration = filter_generation; - mStringMatchOffset = std::string::npos; - } - else // filter self only on first pass through - { - // filter against folder rules - filterFolder(filter); - // and then item rules - LLFolderViewItem::filter( filter ); - } - } - -#if LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Reduce memory load for users with huge inventory by only creating member we really need - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - mStatusText += llformat("(%d)", mCompletedFilterGeneration); - mStatusText += llformat("+%d", mMostFilteredDescendantGeneration); - } -#endif // - - // 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 LL_RELEASE_WITH_DEBUG_INFO || LL_DEBUG // Reduce memory load for users with huge inventory by only creating member we really need - if (getRoot()->getDebugFilters()) - { - mStatusText = llformat("%d", mLastFilterGeneration); - } -#endif // -} - -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 item_a_ancestors; - - LLFolderViewFolder* parent = item_a->getParentFolder(); - while(parent) - { - item_a_ancestors.push_back(parent); - parent = parent->getParentFolder(); - } - - std::deque 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& 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 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 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::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::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(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(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(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(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(a->getListener()))->getItem(); - LLViewerInventoryItem* bitem = (static_cast(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/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& items) +{ + std::vector types; + uuid_vec_t cargo_ids; + std::vector::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(*item_it)->startDrag(&type, &id); + + types.push_back(type); + cargo_ids.push_back(id); + } + + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, cargo_ids, + static_cast(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(folder->getViewModelItem()); + if (modelp->getUUID().isNull()) return; + + for (std::list::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((*child_folderp->getFoldersBegin())->getViewModelItem())->getCreationDate(); + LLFolderViewModelItemInventory* modelp = static_cast(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((*child_folderp->getItemsBegin())->getViewModelItem())->getCreationDate(); + + LLFolderViewModelItemInventory* modelp = static_cast(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(mFolderViewItem); + if (folderp) + { + folderp->requestArrange(); + } + if (static_cast(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(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 + { + Optional 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 +{ +public: + typedef LLFolderViewModel base_t; + + void setTaskID(const LLUUID& id) {mTaskID = id;} + + void sort(LLFolderViewFolder* folder); + bool contentsReady(); + bool startDrag(std::vector& 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 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 609a16fde0..5c6ce9d311 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 f2f49b096c..3af56563bf 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,10 +52,7 @@ #include "llviewermessage.h" #include "llvoavatarself.h" #include "llviewerstats.h" -// Remove floating chat bar -// #include "llnearbychatbar.h" -#include "fsnearbychathub.h" -// +#include "llfloaterimnearbychat.h" #include "llappearancemgr.h" #include "llgesturelistener.h" @@ -1011,10 +1009,8 @@ void LLGestureMgr::runStep(LLMultiGesture* gesture, LLGestureStep* step) const BOOL animate = FALSE; - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); - FSNearbyChat::instance().sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); - // + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate); gesture->mCurrentStep++; break; diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 3a44c5f929..07d90f9132 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -258,6 +258,13 @@ void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask) continue; } +// [RLVa:KB] - Checked: 2010-11-29 (RLVa-1.3.0c) | Added: RLVa-1.3.0c + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canEdit(vobjp)) ) + { + continue; + } +// [/RLVa:KB] + // [RLVa:KB] - Checked: 2010-11-29 (RLVa-1.3.0c) | Added: RLVa-1.3.0c if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canEdit(vobjp)) ) { diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 04ee238fa6..9ee4e75735 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -36,21 +36,19 @@ #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() // [FS communication UI] -//#include "llimfloater.h" +//#include "llimfloater.h" CHU merge LL removed this #include "fsfloaterim.h" // [FS communication UI] #include "groupchatlistener.h" -// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.3.0f) -#include "llslurl.h" -#include "rlvhandler.h" -// [/RLVa:KB] - -// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.3.0f) +// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.3.0) #include "llslurl.h" +#include "rlvactions.h" +#include "rlvcommon.h" #include "rlvhandler.h" // [/RLVa:KB] #include "exogroupmutelist.h" @@ -164,8 +162,8 @@ void LLGroupActions::startCall(const LLUUID& group_id) return; } -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(group_id)) && (!gIMMgr->hasSession(group_id)) ) +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + if ( (!RlvActions::canStartIM(group_id)) && (!RlvActions::hasOpenGroupSession(group_id)) ) { make_ui_sound("UISndInvalidOp"); RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("group", group_id, "about").getSLURLString())); @@ -470,8 +468,8 @@ LLUUID LLGroupActions::startIM(const LLUUID& group_id) { if (group_id.isNull()) return LLUUID::null; -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(group_id)) && (!gIMMgr->hasSession(group_id)) ) +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + if ( (!RlvActions::canStartIM(group_id)) && (!RlvActions::hasOpenGroupSession(group_id)) ) { make_ui_sound("UISndInvalidOp"); RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("group", group_id, "about").getSLURLString())); @@ -493,7 +491,8 @@ LLUUID LLGroupActions::startIM(const LLUUID& group_id) if (session_id != LLUUID::null) { // [FS communication UI] - //LLIMFloater::show(session_id); + //LLFloaterIMContainer::getInstance()->showConversation(session_id); CHUI Merge LL new + //LLIMFloater::show(session_id); CHUI Merge LL old FSFloaterIM::show(session_id); // [FS communication UI] } diff --git a/indra/newview/llgroupiconctrl.cpp b/indra/newview/llgroupiconctrl.cpp index 97f72b020c..2d0fc26c2a 100644 --- a/indra/newview/llgroupiconctrl.cpp +++ b/indra/newview/llgroupiconctrl.cpp @@ -39,7 +39,7 @@ #include "llcachename.h" #include "llagentdata.h" -#include "llimfloater.h" +#include "llfloaterimsession.h" */ static LLDefaultChildRegistry::Register g_i("group_icon"); diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 5a508cf6b4..eadf3bea20 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -107,17 +107,17 @@ void LLGroupList::enableForAgent(bool show_icons) // Listen for agent group changes. gAgent.addListener(this, "new group"); - // Set up context menu. - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + // Set up context menu. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; - registrar.add("People.Groups.Action", boost::bind(&LLGroupList::onContextMenuItemClick, this, _2)); - enable_registrar.add("People.Groups.Enable", boost::bind(&LLGroupList::onContextMenuItemEnable, this, _2)); + 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("menu_people_groups.xml", - gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if(context_menu) - mContextMenuHandle = context_menu->getHandle(); + LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_people_groups.xml", + gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(context_menu) + mContextMenuHandle = context_menu->getHandle(); } // virtual @@ -136,13 +136,13 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) if (mForAgent) { - LLMenuGL* context_menu = (LLMenuGL*)mContextMenuHandle.get(); - if (context_menu && size() > 0) - { - context_menu->buildDrawLabels(); - context_menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, context_menu, x, y); - } + LLToggleableMenu* context_menu = mContextMenuHandle.get(); + if (context_menu && size() > 0) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } } return handled; @@ -479,7 +479,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 02d4224413..501ffa13c5 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" /** @@ -47,8 +50,7 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener public: struct Params : public LLInitParam::Block { - Optional for_agent; - Params(); + Params(){}; }; LLGroupList(const Params& p); @@ -67,6 +69,8 @@ public: void setShowNone(bool show_none) { mShowNone = show_none; } void setGroups(const std::map< std::string,LLUUID> group_list); + LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); } + private: void setDirty(bool val = true) { mDirty = val; } void refresh(); @@ -78,7 +82,7 @@ private: bool onContextMenuItemClick(const LLSD& userdata); bool onContextMenuItemEnable(const LLSD& userdata); - LLHandle mContextMenuHandle; + LLHandle mContextMenuHandle; bool mShowIcons; bool mDirty; diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp index 4fd006e27e..9add316942 100644 --- a/indra/newview/llhudeffectlookat.cpp +++ b/indra/newview/llhudeffectlookat.cpp @@ -582,13 +582,13 @@ void LLHUDEffectLookAt::render() name = nameBuffer.getCompleteName(); break; case 2: // Display Name - name = nameBuffer.mDisplayName; + name = nameBuffer.getDisplayName(); break; case 3: // First Last name = nameBuffer.getLegacyName(); break; default: //user.name - name = nameBuffer.mUsername; + name = nameBuffer.getUserName(); break; } 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[] = + 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) { - bg_pos, - bg_pos + width_vec, - bg_pos + width_vec + height_vec, - bg_pos + height_vec, - }; - - if (debug_render) - { - 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(lltrunc(-mHeight / 2 + mOffsetY)), static_cast(lltrunc(mWidth)), static_cast(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]); + 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; - 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); - - // 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 &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 bd91dcd29e..0000000000 --- a/indra/newview/llimfloater.cpp +++ /dev/null @@ -1,1200 +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" - -#if 0 - -#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("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("slide_left_btn"); - slide_left->setVisible(mControlPanel->getParent()->getVisible()); - slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - LLButton* slide_right = getChild("slide_right_btn"); - slide_right->setVisible(!mControlPanel->getParent()->getVisible()); - slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - mInputEditor = getChild("chat_editor"); - mInputEditor->setMaxTextLength(1023); - // enable line history support for instant message bar - mInputEditor->setEnableLineHistory(TRUE); - // *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("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("slide_left_btn")->setVisible(mControlPanel->getParent()->getVisible()); - getChild("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(*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( - 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::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::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(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("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("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance("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 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::const_reverse_iterator iter = messages.rbegin(); - std::list::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::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(*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 errorWithContent(U32 statusNum, const std::string& reason, const LLSD& content) - { - llwarns << "Error inviting all agents to session [status:" - << statusNum << "]: " << content << 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(); -} - -#endif diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp deleted file mode 100644 index 8f5c89a904..0000000000 --- a/indra/newview/llimfloatercontainer.cpp +++ /dev/null @@ -1,168 +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" - -#if 0 - -#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(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(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("im_container"); -} - -LLIMFloaterContainer* LLIMFloaterContainer::getInstance() -{ - return LLFloaterReg::getTypedInstance("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 - -#endif diff --git a/indra/newview/llimfloatercontainer.h b/indra/newview/llimfloatercontainer.h deleted file mode 100644 index 404f78016a..0000000000 --- a/indra/newview/llimfloatercontainer.h +++ /dev/null @@ -1,74 +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$ - */ - -#if 0 - -#ifndef LL_LLIMFLOATERCONTAINER_H -#define LL_LLIMFLOATERCONTAINER_H - -#include -#include - -#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 avatarID_panel_map_t; - avatarID_panel_map_t mSessions; - boost::signals2::connection mNewMessageConnection; - - void onNewMessageReceived(const LLSD& data); -}; - -#endif // LL_LLIMFLOATERCONTAINER_H - -#endif 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; -//-------------------------------------------------------------------------- -LLIMHandler::LLIMHandler(e_notification_type type, const LLSD& id) -{ - mType = type; +extern void process_dnd_im(const LLSD& notification); +//-------------------------------------------------------------------------- +LLIMHandler::LLIMHandler() +: LLCommunicationNotificationHandler("IM Notifications", "notifytoast") +{ // 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; - } + 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; + } - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + // arrange a channel on a screen + if(!mChannel.get()->getVisible()) + { + initChannel(); + } - if(!notification) - return false; + LLSD substitutions = notification->getSubstitutions(); - // arrange a channel on a screen - if(!mChannel.get()->getVisible()) - { - initChannel(); - } + // 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; - if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") - { - LLSD substitutions = notification->getSubstitutions(); + 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(); - // 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* im_box = new LLToastIMPanel(im_p); - 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(); + 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(mChannel.get()); + if(channel) + channel->addToast(p); + } - 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(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; } -//-------------------------------------------------------------------------- -void LLIMHandler::onDeleteToast(LLToast* toast) -{ - // send a signal to the counter manager - mDelNotificationSignal(); -} - -//-------------------------------------------------------------------------- diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 763b669b1c..11c82205c4 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,7 +43,7 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" - +#include "llfloaterimsessiontab.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" @@ -49,8 +51,11 @@ #include "llcallingcard.h" #include "llchat.h" // [FS communication UI] -//#include "llimfloater.h" -#include "fsfloaterim.h" +//#include "llimfloater.h" CHUI Merge removed by LL +//#include "llfloaterimsession.h" CHUI Merge new +//#include "llfloaterimcontainer.h" CHUI Merge new +#include "fsfloaterimcontainer.h" // CHUI Merge fs new +//#include "fsfloaterim.h" // [FS communication UI] #include "llgroupiconctrl.h" #include "llmd5.h" @@ -60,22 +65,18 @@ #include "llviewerwindow.h" #include "llnotifications.h" #include "llnotificationsutil.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" #include "llspeakers.h" //for LLIMSpeakerMgr #include "lltextbox.h" #include "lltoolbarview.h" #include "llviewercontrol.h" #include "llviewerparcelmgr.h" -// [RLVa:KB] - Checked: 2010-04-09 (RLVa-1.2.0e) -#include "rlvhandler.h" +#include "llconversationlog.h" +#include "message.h" +// [RLVa:KB] - Checked: 2013-05-10 (RLVa-1.4.9) +#include "rlvactions.h" +#include "rlvcommon.h" // [/RLVa:KB] - #include "exogroupmutelist.h" #include "fsconsoleutils.h" #include "fscommon.h" @@ -115,6 +116,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("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) @@ -126,100 +168,186 @@ 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"]; + args["SESSION_TYPE"] = msg["session_type"]; // [FS communication UI] - //LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); + //LLNotificationsUtil::add("IMToast", args, args, boost::bind(&LLFloaterIMContainer::showConversation, LLFloaterIMContainer::getInstance(), msg["session_id"].asUUID())); CHUI Merge new + //LLNotificationsUtil::add("IMToast", args, LLSD(), boost::bind(&LLIMFloater::show, msg["session_id"].asUUID())); [FS communication UI] } -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; - } - - // Don't toast if the message is an announcement - if (msg["is_announcement"].asBoolean()) - { - return; - } - // Don't toast if the message is an announcement - - // (Group-)IMs in chat console - if (FSConsoleUtils::ProcessInstantMessage(msg["session_id"], msg["from_id"], msg["message"])) - { - return; - } - // (Group-)IMs in chat console - - // 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()) - { - return; - } - - // Skip toasting if we have open window of IM with this session id - // [FS communication UI] - //LLIMFloater* open_im_floater = LLIMFloater::findInstance(msg["session_id"]); - FSFloaterIM* open_im_floater = FSFloaterIM::findInstance(msg["session_id"]); - // [FS communication UI] - if (open_im_floater && open_im_floater->getVisible()) - { - return; - } - - LLAvatarNameCache::get(msg["from_id"].asUUID(), - boost::bind(&on_avatar_name_cache_toast, - _1, _2, msg)); -} - -void LLIMModel::setActiveSessionID(const LLUUID& session_id) +void on_new_message(const LLSD& msg) { - // check if such an ID really exists - if (!findIMSession(session_id)) + std::string user_preferences; + LLUUID participant_id = msg["from_id"].asUUID(); + LLUUID session_id = msg["session_id"].asUUID(); + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id); + + // do not show notification which goes from agent + if (gAgent.getID() == participant_id) + { + return; + } + + // determine state of conversations floater + enum {CLOSED, NOT_ON_TOP, ON_TOP, ON_TOP_AND_ITEM_IS_SELECTED} conversations_floater_status; + + + + LLFloaterIMContainer* im_box = LLFloaterReg::getTypedInstance("im_container"); + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(session_id); + + if (!LLFloater::isVisible(im_box) || im_box->isMinimized()) { - llwarns << "Trying to set as active a non-existent session!" << llendl; - return; + conversations_floater_status = CLOSED; + } + else if (!im_box->hasFocus() && + !(session_floater && LLFloater::isVisible(session_floater) + && !session_floater->isMinimized() && session_floater->hasFocus())) + { + conversations_floater_status = NOT_ON_TOP; + } + else if (im_box->getSelectedSession() != session_id) + { + conversations_floater_status = ON_TOP; + } + else + { + conversations_floater_status = ON_TOP_AND_ITEM_IS_SELECTED; } - mActiveSessionID = session_id; + // determine user prefs for this session + if (session_id.isNull()) + { + user_preferences = gSavedSettings.getString("NotificationNearbyChatOptions"); + } + else if(session->isP2PSessionType()) + { + if (LLAvatarTracker::instance().isBuddy(participant_id)) + { + user_preferences = gSavedSettings.getString("NotificationFriendIMOptions"); + } + else + { + user_preferences = gSavedSettings.getString("NotificationNonFriendIMOptions"); + } + } + else if(session->isAdHocSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationConferenceIMOptions"); + } + else if(session->isGroupSessionType()) + { + user_preferences = gSavedSettings.getString("NotificationGroupChatOptions"); + } + + // actions: + + // 0. nothing - exit + if (("none" == user_preferences || + ON_TOP_AND_ITEM_IS_SELECTED == conversations_floater_status) + && session_floater->isMessagePaneExpanded()) + { + return; + } + + // 1. open floater and [optional] surface it + if ("openconversations" == user_preferences && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status)) + { + if(!gAgent.isDoNotDisturb()) + { + // Open 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); + } + } + } + } + else + { + //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() + && !session_floater->isShown()) + { + LLAvatarNameCache::get(participant_id, boost::bind(&on_avatar_name_cache_toast, _1, _2, msg)); + } + } + } + + // 2. Flash line item + if ("openconversations" == user_preferences + || ON_TOP == conversations_floater_status + || ("toast" == user_preferences && ON_TOP != conversations_floater_status) + || ("flash" == user_preferences && CLOSED == conversations_floater_status)) + { + if(!LLMuteList::getInstance()->isMuted(participant_id)) + { + im_box->flashConversationItemWidget(session_id, true); + } + } + + // 3. Flash FUI button + if (("toast" == user_preferences || "flash" == user_preferences) && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status)) + { + if(!LLMuteList::getInstance()->isMuted(participant_id) + && !gAgent.isDoNotDisturb()) + { + gToolBarView->flashCommand(LLCommandId("chat"), true); + } + } + + // 4. Toast + if ((("toast" == user_preferences) && + (CLOSED == conversations_floater_status + || NOT_ON_TOP == conversations_floater_status)) + || !session_floater->isMessagePaneExpanded()) + + { + //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)); + } + } } LLIMModel::LLIMModel() { // [FS communication UI] - //addNewMsgCallback(boost::bind(&LLIMFloater::newIMCallback, _1)); + //addNewMsgCallback(boost::bind(&LLIMFloater::newIMCallback, _1)); CHUI Merge old + //addNewMsgCallback(boost::bind(&LLFloaterIMSession::newIMCallback, _1)); CHUI Merge new addNewMsgCallback(boost::bind(&FSFloaterIM::newIMCallback, _1)); // [FS communication UI] - addNewMsgCallback(boost::bind(&toast_callback, _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), @@ -231,7 +359,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; @@ -297,30 +426,22 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& } buildHistoryFileName(); - - if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) - { - std::list 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); @@ -416,6 +537,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES break; } } + default: + break; } // Update speakers list when connected if (LLVoiceChannel::STATE_CONNECTED == new_state) @@ -426,6 +549,11 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES LLIMModel::LLIMSession::~LLIMSession() { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } + delete mSpeakers; mSpeakers = NULL; @@ -491,11 +619,11 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list& 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 { @@ -504,8 +632,8 @@ void LLIMModel::LLIMSession::addMessagesFromHistory(const std::list& 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); @@ -529,10 +657,23 @@ void LLIMModel::LLIMSession::chatFromLogFile(LLLogChat::ELogLineType type, const } } +void LLIMModel::LLIMSession::loadHistory() +{ + mMsgs.clear(); + + if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) + { + std::list 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 @@ -574,7 +715,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; } @@ -594,19 +735,32 @@ bool LLIMModel::LLIMSession::isOtherParticipantAvaline() return !mOtherParticipantIsAvatar; } +LLUUID LLIMModel::LLIMSession::generateOutgouigAdHocHash() const +{ + LLUUID hash = LLUUID::null; + + if (mInitialTargetIDs.size()) + { + std::set sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); + hash = generateHash(sorted_uuids); + } + + return hash; +} + void LLIMModel::LLIMSession::onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name) { // if (av_name.mUsername.empty()) // { // // display names is off, use mDisplayName which will be the legacy name -// mHistoryFileName = LLCacheName::buildUsername(av_name.mDisplayName); +// mHistoryFileName = LLCacheName::buildUsername(av_name.getDisplayName()); // } // else // { // mHistoryFileName = av_name.mUsername; // } // [SL:KB] - Patch: Chat-Logs | Checked: 2011-01-16 (Catznip-2.4.0h) | Modified: Catznip-2.4.0h - if (!av_name.mIsTemporaryName) + if (!av_name.isValidName()) { LLIMModel::buildIMP2PLogFilename(avatar_id, mName, mHistoryFileName); @@ -653,7 +807,7 @@ void LLIMModel::LLIMSession::buildHistoryFileName() if (mInitialTargetIDs.size()) { std::set sorted_uuids(mInitialTargetIDs.begin(), mInitialTargetIDs.end()); - mHistoryFileName = mName + " hash" + generateHash(sorted_uuids); + mHistoryFileName = mName + " hash" + generateHash(sorted_uuids).asString(); } else { @@ -673,12 +827,25 @@ void LLIMModel::LLIMSession::buildHistoryFileName() // LLAvatarName av_name; // // For outgoing sessions we already have a cached name // // so no need for a callback in LLAvatarNameCache::get() +// CHUI Merge below is new if statement from LL +// if (LLAvatarNameCache::get(mOtherParticipantID, &av_name)) +// { +// mHistoryFileName = LLCacheName::buildUsername(av_name.getUserName()); +// } +// else +// { +// // Incoming P2P sessions include a name that we can use to build a history file name +// mHistoryFileName = LLCacheName::buildUsername(mName); +// } +// } +//} +// CHUI Merge below is the old if statement from LL // 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); +// mHistoryFileName = LLCacheName::buildUsername(av_name.getDisplayName()); // } // else // { @@ -699,7 +866,7 @@ void LLIMModel::LLIMSession::buildHistoryFileName() } //static -std::string LLIMModel::LLIMSession::generateHash(const std::set& sorted_uuids) +LLUUID LLIMModel::LLIMSession::generateHash(const std::set& sorted_uuids) { LLMD5 md5_uuid; @@ -713,7 +880,7 @@ std::string LLIMModel::LLIMSession::generateHash(const std::set& 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) @@ -727,12 +894,11 @@ 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); } // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(old_session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(old_session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(old_session_id); // [FS communication UI] if (im_floater) @@ -740,6 +906,11 @@ void LLIMModel::processSessionInitializedReply(const LLUUID& old_session_id, con im_floater->sessionInitReplyReceived(new_session_id); } + if (old_session_id != new_session_id) + { + gIMMgr->notifyObserverSessionIDUpdated(old_session_id, new_session_id); + } + // auto-start the call on session initialization? if (session->mStartCallOnInitialize) { @@ -775,7 +946,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()) { @@ -789,22 +960,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) @@ -815,6 +987,16 @@ bool LLIMModel::clearSession(const LLUUID& session_id) return true; } +void LLIMModel::getMessages(const LLUUID& session_id, std::list& 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& messages, int start_index) { LLIMSession* session = findIMSession(session_id); @@ -856,13 +1038,6 @@ void LLIMModel::sendNoUnreadMessages(const LLUUID& session_id) mNoUnreadMsgsSignal(arg); } -void LLIMModel::getMessages(const LLUUID& session_id, std::list& 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, BOOL is_announcement /* = FALSE */) { LLIMSession* session = findIMSession(session_id); @@ -927,12 +1102,12 @@ bool LLIMModel::buildIMP2PLogFilename(const LLUUID& idAgent, const std::string& if (!fLegacyFilenames) { // If display names are turned off mUserName will be empty and we have to construct it from the legacy name - strFilename = (!avName.mUsername.empty()) ? avName.mUsername : LLCacheName::buildUsername(avName.mDisplayName); + strFilename = (!avName.getUserName().empty()) ? avName.getUserName() : LLCacheName::buildUsername(avName.getDisplayName()); } else { // Use the legacy name to base the filename on - strFilename = LLCacheName::cleanFullName( (!avName.mLegacyFirstName.empty()) ? avName.getLegacyName() : avName.mDisplayName ); + strFilename = LLCacheName::cleanFullName( (!avName.LegacyFirstName.empty()) ? avName.getLegacyName() : avName.getDisplayName() ); } return true; } @@ -954,19 +1129,20 @@ bool LLIMModel::buildIMP2PLogFilename(const LLUUID& idAgent, const std::string& 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 @@ -1004,6 +1180,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; arg["is_announcement"] = is_announcement; // Ansariel: Indicator if it's an announcement mNewMsgSignal(arg); @@ -1054,7 +1231,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"); @@ -1080,7 +1257,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; } @@ -1621,7 +1798,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); @@ -1691,6 +1868,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; } @@ -1768,7 +1950,8 @@ LLIMMgr::onConfirmForceCloseError( LLUUID session_id = notification["payload"]["session_id"]; // [FS communication UI] - //LLFloater* floater = LLIMFloater::findInstance(session_id); + //LLFloater* floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLFloater* floater = LLIMFloater::findInstance(session_id); CHUI Merge old LLFloater* floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] if ( floater ) @@ -2128,7 +2311,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(); } } @@ -2230,7 +2413,8 @@ BOOL LLOutgoingCallDialog::postBuild() //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LLIncomingCallDialog::LLIncomingCallDialog(const LLSD& payload) : -LLCallDialog(payload) +LLCallDialog(payload), +mAvatarNameCacheConnection() { } @@ -2300,9 +2484,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); @@ -2328,7 +2514,6 @@ BOOL LLIncomingCallDialog::postBuild() getChildView("Start IM")->setVisible( is_avatar && notify_box_type != "VoiceInviteAdHoc" && notify_box_type != "VoiceInviteGroup"); setCanDrag(FALSE); - return TRUE; } @@ -2336,7 +2521,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("caller name"); @@ -2347,14 +2531,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 @@ -2362,18 +2547,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("question")->setValue(getString(key["question_type"].asString(), args)); - } - else - { - args["[CURRENT_CHAT]"] = getString("localchat"); - getChild("question")->setValue(getString(key["question_type"].asString(), args)); - } } //static @@ -2434,6 +2607,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); @@ -2637,7 +2814,8 @@ LLIMMgr::LLIMMgr() mPendingAgentListUpdates = LLSD::emptyMap(); // [FS communication UI] - //LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1)); + //LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLFloaterIMSession::sRemoveTypingIndicator, _1)); CHUI Merge new + //LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&LLIMFloater::sRemoveTypingIndicator, _1)); CHUI Merge old LLIMModel::getInstance()->addNewMsgCallback(boost::bind(&FSFloaterIM::sRemoveTypingIndicator, _1)); // [FS communication UI] } @@ -2648,6 +2826,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, @@ -2657,6 +2836,7 @@ void LLIMMgr::addMessage( BOOL is_announcement) // Ansariel: Special parameter indicating announcements { LLUUID other_participant_id = target_id; + LLUUID new_session_id = session_id; if (new_session_id.isNull()) { @@ -2666,9 +2846,11 @@ 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; } // Configurable IM sounds @@ -2679,6 +2861,13 @@ void LLIMMgr::addMessage( bool new_session = !hasSession(new_session_id); if (new_session) { + 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); + // Clear muted group chat early to prevent contacts floater // (re-)gaining focus; the server already knows the correct // session id, so we can leave it! @@ -2699,7 +2888,6 @@ void LLIMMgr::addMessage( } // - LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id); // 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 @@ -2737,6 +2925,9 @@ void LLIMMgr::addMessage( // Configurable IM sounds if(PlayModeUISndNewIncomingIMSession != 0 && dialog == IM_NOTHING_SPECIAL) { + //Play sound for new conversations + if (!gAgent.isDoNotDisturb() && (gSavedSettings.getBOOL("PlaySoundNewConversation") == TRUE)) + { make_ui_sound("UISndNewIncomingIMSession"); } else if(PlayModeUISndNewIncomingGroupIMSession != 0 && dialog != IM_NOTHING_SPECIAL) @@ -2776,27 +2967,48 @@ void LLIMMgr::addMessage( make_ui_sound("UISndNewIncomingGroupIMSession"); } } + } // - // FIRE-6650: Group chat for non-friends mutes when disabling voice chat from non-friends bool skip_message = false; - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); - if (session) + if (gSavedSettings.getBOOL("VoiceCallsFriendsOnly")) { - if (session->isAdHoc()) - { - skip_message = (other_participant_id != gAgentID && gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && - LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); - } + // 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 } + +// CHUI Merge LL seems to have fixed FIRE-6650 differently (above). below is our origonal fix + // FIRE-6650: Group chat for non-friends mutes when disabling voice chat from non-friends + //bool skip_message = false; + //LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(new_session_id); + //if (session) + //{ + // if (session->isAdHoc()) + // { + // skip_message = (other_participant_id != gAgentID && gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && + // LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); + // } + //} //bool skip_message = (gSavedSettings.getBOOL("VoiceCallsFriendsOnly") && // LLAvatarTracker::instance().getBuddyInfo(other_participant_id) == NULL); // FIRE-6650 if (!LLMuteList::getInstance()->isMuted(other_participant_id, LLMute::flagTextChat) && !skip_message) { - LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_announcement); + //LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg, true, is_announcement); CHUI Merge this got clobbered above + 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("im_container")-> + flashConversationItemWidget(new_session_id, true); + } } void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& message_name, const LLSD& args) @@ -2811,17 +3023,9 @@ void LLIMMgr::addSystemMessage(const LLUUID& session_id, const std::string& mess LLChat chat(message); chat.mSourceType = CHAT_SOURCE_SYSTEM; - - // Remove floating chat bar - // LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - // LLNearbyChat* nearby_chat = chat_bar->findChild("nearby_chat"); - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); - // [FS communication UI] - // - if(nearby_chat) + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (nearby_chat) { nearby_chat->addMessage(chat); } @@ -2835,6 +3039,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; @@ -2923,7 +3128,8 @@ LLUUID LLIMMgr::addSession( { LLDynamicArray 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 @@ -2932,7 +3138,8 @@ LLUUID LLIMMgr::addSession( const std::string& name, EInstantMessage dialog, const LLUUID& other_participant_id, - const LLDynamicArray& ids, bool voice) + const LLDynamicArray& ids, bool voice, + const LLUUID& floater_id) { if (0 == ids.getLength()) { @@ -2947,7 +3154,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()) @@ -2960,14 +3181,23 @@ 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; + llinfos << "LLIMMgr::addSession, new session added, name = " << name << ", session id = " << session_id << llendl; + //Per Plan's suggestion commented "explicit offline status warning" out to make Dessie happier (see EXT-3609) //*TODO After February 2010 remove this commented out line if no one will be missing that warning //noteOfflineUsers(session_id, floater, ids); @@ -2978,6 +3208,8 @@ LLUUID LLIMMgr::addSession( noteMutedUsers(session_id, ids); } + notifyObserverSessionVoiceOrIMStarted(session_id); + return session_id; } @@ -3001,6 +3233,8 @@ void LLIMMgr::removeSession(const LLUUID& session_id) LLIMModel::getInstance()->clearSession(session_id); + llinfos << "LLIMMgr::removeSession, session removed, session id = " << session_id << llendl; + notifyObserverSessionRemoved(session_id); } @@ -3080,12 +3314,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; @@ -3148,7 +3387,8 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) { // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] if ( im_floater ) @@ -3256,11 +3496,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); } } @@ -3367,7 +3623,7 @@ void LLIMMgr::noteOfflineUsers( } else if (gSavedSettings.getBOOL("UseDisplayNames")) { - offline.setArg("[NAME]", av_name.mDisplayName); + offline.setArg("[NAME]", av_name.getDisplayName()); } else { @@ -3508,7 +3764,8 @@ void LLIMMgr::processIMTypingCore(const LLIMInfo* im_info, BOOL typing) // // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] @@ -3557,7 +3814,8 @@ public: } // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] if ( im_floater ) @@ -3654,7 +3912,8 @@ public: { LLUUID session_id = input["body"]["session_id"].asUUID(); // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] if ( im_floater ) @@ -3701,25 +3960,23 @@ 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; } -// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c - if ( (gRlvHandler.hasBehaviour(RLV_BHVR_RECVIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_RECVIMFROM)) ) +// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) + if ( (RlvActions::hasBehaviour(RLV_BHVR_RECVIM)) || (RlvActions::hasBehaviour(RLV_BHVR_RECVIMFROM)) ) { if (gAgent.isInGroup(session_id)) // Group chat: don't accept the invite if not an exception { - if (!gRlvHandler.canReceiveIM(session_id)) + if (!RlvActions::canReceiveIM(session_id)) return; } - else if (!gRlvHandler.canReceiveIM(from_id)) // Conference chat: don't block; censor if not an exception + else if (!RlvActions::canReceiveIM(from_id)) // Conference chat: don't block; censor if not an exception { message = RlvStrings::getString(RLV_STRING_BLOCKED_RECVIM); } @@ -3792,6 +4049,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 dcd8067a47..03c8677e4d 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,12 +95,16 @@ 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 onAvatarNameCache(const LLUUID& avatar_id, const LLAvatarName& av_name); + void loadHistory(); + LLUUID mSessionID; std::string mName; EInstantMessage mType; @@ -135,22 +140,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& sorted_uuids); + static LLUUID generateHash(const std::set& 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 mId2SessionMap; @@ -183,22 +184,16 @@ 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 */ 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& messages, int start_index = 0); - /** * Sends no unread messages signal. */ @@ -207,7 +202,7 @@ public: /** * Populate supplied std::list with messages starting from index specified by start_index */ - void getMessages(const LLUUID& session_id, std::list& messages, int start_index = 0); + void getMessages(const LLUUID& session_id, std::list& 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 @@ -298,6 +293,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& messages, int start_index = 0); + /** * Add message to a list of message associated with session specified by session_id */ @@ -309,7 +310,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; }; @@ -336,6 +339,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, @@ -361,10 +365,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& ids, bool voice = false); + const LLDynamicArray& ids, bool voice = false, + const LLUUID& floater_id = LLUUID::null); /** * Creates a P2P session with the requisite handle for responding to voice calls. @@ -473,7 +479,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); @@ -555,7 +564,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); @@ -572,6 +588,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 aafc43b02d..9c6db3676f 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("add_friend_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickAddFriend, this) ); - - getChild("view_profile_btn")->setCommitCallback( - boost::bind(&LLInspectAvatar::onClickViewProfile, this) ); - getChild("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("gear_self_btn")->setVisible(self); - getChild("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("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("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("add_friend_btn")->setVisible(false); - getChild("im_btn")->setVisible(false); - } - else if (is_friend) - { - getChild("add_friend_btn")->setVisible(false); - getChild("im_btn")->setVisible(true); - } - else - { - getChild("add_friend_btn")->setVisible(true); - getChild("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("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) @@ -404,141 +292,11 @@ void LLInspectAvatar::processAvatarData(LLAvatarData* data) delete mPropertiesRequest; 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("gear_btn")->getMenu(); - LLToggleableMenu* gear_menu_self = getChild("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 self_speakerp = speaker_mgr->findSpeaker(gAgent.getID()); - LLPointer selected_speakerp = speaker_mgr->findSpeaker(mAvatarID); - - if(speaker_mgr->isVoiceActive() && selected_speakerp && - selected_speakerp->isInVoiceChannel() && - ((self_speakerp && self_speakerp->mIsModerator) || gAgent.isGodlike())) - { - getChild("enable_voice")->setVisible(selected_speakerp->mModeratorMutedVoice); - getChild("disable_voice")->setVisible(!selected_speakerp->mModeratorMutedVoice); - - enable_moderator_panel = true; - } - } - } - } - - if (enable_moderator_panel) - { - if (!getChild("moderator_panel")->getVisible()) - { - getChild("moderator_panel")->setVisible(true); - // stretch the floater so it can accommodate the moderator panel - reshape(getRect().getWidth(), getRect().getHeight() + getChild("moderator_panel")->getRect().getHeight()); - } - } - else if (getChild("moderator_panel")->getVisible()) - { - getChild("moderator_panel")->setVisible(false); - // shrink the inspector floater back to original size - reshape(getRect().getWidth(), getRect().getHeight() - getChild("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; - } - +/* +prep# virtual void errorWithContent(U32 status, const std::string& reason, const LLSD& content) - { llwarns << "MuteVoiceResponder error [status:" << status << "]: " << content << 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() { @@ -558,12 +316,11 @@ void LLInspectAvatar::updateVolumeSlider() getChild("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("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 +351,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 +374,13 @@ void LLInspectAvatar::onAvatarNameCache( const LLUUID& agent_id, const LLAvatarName& av_name) { + mAvatarNameCacheConnection.disconnect(); + if (agent_id == mAvatarID) { - getChild("user_name")->setValue(av_name.mDisplayName); - getChild("user_name_small")->setValue(av_name.mDisplayName); - getChild("user_slid")->setValue(av_name.mUsername); + getChild("user_name")->setValue(av_name.getDisplayName()); + getChild("user_name_small")->setValue(av_name.getDisplayName()); + getChild("user_slid")->setValue(av_name.getUserName()); mAvatarName = av_name; // show smaller display name if too long to display in regular size @@ -640,215 +399,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 old mode 100755 new mode 100644 index 772a92706f..dd2173d75f --- 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" @@ -74,11 +75,7 @@ #include "llwearablelist.h" #include "lllandmarkactions.h" // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1) -#include "rlvhandler.h" -#include "rlvlocks.h" -// [/RLVa:KB] - -// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) +#include "rlvactions.h" #include "rlvhandler.h" #include "rlvlocks.h" // [/RLVa:KB] @@ -111,21 +108,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 // Patch: ReplaceWornItemsOnly void wear_inventory_category_on_avatar(LLInventoryCategory* category); @@ -136,10 +118,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 @@ -187,7 +169,6 @@ public: { if (clear_observer) { - dec_busy_count(); gInventory.removeObserver(this); delete this; } @@ -210,7 +191,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(); @@ -229,7 +211,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 @@ -248,9 +234,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 { @@ -268,6 +269,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 @@ -307,7 +313,7 @@ void LLInvFVBridge::showProperties() */ } -void LLInvFVBridge::removeBatch(LLDynamicArray& batch) +void LLInvFVBridge::removeBatch(std::vector& batch) { // Deactivate gestures when moving them into Trash LLInvFVBridge* bridge; @@ -316,11 +322,11 @@ void LLInvFVBridge::removeBatch(LLDynamicArray& 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) @@ -333,7 +339,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray& 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) @@ -351,7 +357,7 @@ void LLInvFVBridge::removeBatch(LLDynamicArray& batc removeBatchNoCheck(batch); } -void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray& batch) +void LLInvFVBridge::removeBatchNoCheck(std::vector& batch) { // this method moves a bunch of items and folders to the trash. As // per design guidelines for the inventory model, the message is @@ -367,14 +373,14 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArrayisItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -387,7 +393,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArrayisItemRemovable()) continue; item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID()); if(item) @@ -428,7 +434,7 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArrayisItemRemovable()) continue; LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID()); if(cat) @@ -522,8 +528,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; } @@ -947,6 +955,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(); @@ -1034,7 +1048,7 @@ BOOL LLInvFVBridge::isProtectedFolder() 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()) { @@ -1074,7 +1088,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; } @@ -1106,6 +1120,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) @@ -1356,10 +1371,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(object_folderp->getViewModelItem())->isLoading(); } } @@ -1367,7 +1382,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) { @@ -1377,7 +1392,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); } } } @@ -1389,14 +1404,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 @@ -1405,6 +1436,7 @@ LLInvFVBridge* LLInventoryFVBridgeBuilder::createBridge(LLAssetType::EType asset actual_asset_type, inv_type, inventory, + view_model, root, uuid, flags); @@ -1461,12 +1493,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(); - // FIRE-6714: Don't move objects to trash during cut&paste - //LLFolderView::removeCutItems(); - // FIRE-6714: Don't move objects to trash during cut&paste - mRoot->setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, false); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -1479,10 +1506,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) @@ -1491,10 +1518,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)) @@ -1504,7 +1531,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) @@ -1645,6 +1672,15 @@ LLUIImagePtr LLItemBridge::getIcon() const return LLInventoryIcon::getIcon(LLInventoryType::ICONNAME_OBJECT); } +LLUIImagePtr LLItemBridge::getIconOverlay() const +{ + if (getItem() && getItem()->getIsLinkType()) + { + return LLUI::getUIImage("Inv_Link"); + } + return NULL; +} + PermissionMask LLItemBridge::getPermissionMask() const { LLViewerInventoryItem* item = getItem(); @@ -1653,27 +1689,28 @@ 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); - } - return mDisplayName; -} - -void LLItemBridge::buildDisplayName(LLInventoryItem* item, std::string& name) -{ - if(item) - { - name.assign(item->getName()); + mDisplayName.assign(getItem()->getName()); } else { - name.assign(LLStringUtil::null); - } + mDisplayName.assign(LLStringUtil::null); } + mSearchableName.assign(mDisplayName); + mSearchableName.append(getLabelSuffix()); + LLStringUtil::toUpper(mSearchableName); + + //Name set, so trigger a sort + if(mParent) +{ + mParent->requestSort(); + } + } + LLFontGL::StyleFlags LLItemBridge::getLabelStyle() const { U8 font = LLFontGL::NORMAL; @@ -1795,18 +1832,17 @@ BOOL LLItemBridge::renameItem(const std::string& new_name) { LLPointer 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()) @@ -1814,7 +1850,6 @@ BOOL LLItemBridge::removeItem() return FALSE; } - // move it to the trash LLPreview::hide(mUUID, TRUE); LLInventoryModel* model = getInventoryModel(); @@ -1971,6 +2006,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 @@ -1979,11 +2107,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; }; @@ -1997,7 +2125,7 @@ BOOL LLFolderBridge::isItemRemovable() const } LLInventoryPanel* panel = mInventoryPanel.get(); - LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getRootFolder()->getItemByID(mUUID) : NULL); + LLFolderViewFolder* folderp = dynamic_cast(panel ? panel->getItemByID(mUUID) : NULL); if (folderp) { LLIsItemRemovable folder_test; @@ -2236,7 +2364,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(); @@ -2456,7 +2584,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // Check whether the folder being dragged from active inventory panel // passes the filter of the destination panel. // FIRE-7219: Allow DnD operation on filtered folder views - //is_movable = check_category(model, cat_id, active_folder_view, filter); + //is_movable = check_category(model, cat_id, active_panel, filter); } } } @@ -2538,7 +2666,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); } @@ -2715,7 +2843,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; } @@ -2729,7 +2856,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; } @@ -2800,7 +2926,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); } */ @@ -2819,7 +2944,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); } } @@ -2898,7 +3022,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if ("open" == action) { - LLFolderViewFolder *f = dynamic_cast(mRoot->getItemByID(mUUID)); + LLFolderViewFolder *f = dynamic_cast(mInventoryPanel.get()->getItemByID(mUUID)); if (f) { f->setOpen(TRUE); @@ -2958,12 +3082,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(); - // FIRE-6714: Don't move objects to trash during cut&paste - //LLFolderView::removeCutItems(); - // FIRE-6714: Don't move objects to trash during cut&paste - mRoot->setSelection(item_to_select, item_to_select ? item_to_select->isOpen() : false, false); + gInventory.removeObject(mUUID); return; } else if ("copy" == action) @@ -3019,7 +3138,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 @@ -3129,17 +3248,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); @@ -3203,6 +3329,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(mParent)->updateHierarchyCreationDate(date); + } + } +} + void LLFolderBridge::pasteFromClipboard() { LLInventoryModel* model = getInventoryModel(); @@ -3220,7 +3359,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) { @@ -3243,7 +3382,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); } } @@ -3285,7 +3424,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); } } @@ -3295,6 +3435,7 @@ void LLFolderBridge::pasteFromClipboard() llassert(viitem); if (viitem) { + //changeItemParent() implicity calls dirtyFilter changeItemParent(model, viitem, parent_id, FALSE); } } @@ -3433,7 +3574,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); @@ -3445,33 +3586,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() // moved from buildContextMenu after merge @@ -3489,40 +3630,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")); } } } @@ -3559,22 +3700,45 @@ 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")); } // Don't offer sharing of trash folder (FIRE-1642, FIRE-6547) //if (!isOutboxFolder()) if (!isOutboxFolder() && mUUID != trash_id) { - 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(); @@ -3604,21 +3768,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 @@ -3633,7 +3797,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) @@ -3641,22 +3805,22 @@ 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")); // Patch: ReplaceWornItemsOnly mItems.push_back(std::string("Wear Items")); // } - 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)) // [SL:KB] - Patch: Appearance-Misc | Checked: 2010-11-24 (Catznip-2.4) @@ -3664,9 +3828,9 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) ((!is_outfit) && (gAgentWearables.isCOFChangeInProgress())) ) // [/SL:KB] { - 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")); } } @@ -3675,49 +3839,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; @@ -3807,25 +3950,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); @@ -3891,6 +4015,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) { @@ -4005,9 +4147,10 @@ void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) LLPointer 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(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( @@ -4059,7 +4202,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryPanel* destination_panel = mInventoryPanel.get(); if (!destination_panel) return false; - LLInventoryFilter* filter = destination_panel->getFilter(); + LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); @@ -4193,15 +4336,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // Check whether the item being dragged from active inventory panel // passes the filter of the destination panel. // Allow drag and drop in inventory regardless of filter (e.g. Recent) + // CHUI merge LL changed below, kept commented out //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()); //} // @@ -4214,6 +4355,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(); @@ -4231,8 +4374,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(itemp->getViewModelItem())->getUUID(); + LLFavoritesOrderStorage::instance().rearrangeFavoriteLandmarks(srcItemId, destItemId); } } @@ -4264,7 +4407,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()); } @@ -4423,13 +4566,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) @@ -4469,10 +4609,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)) @@ -4492,13 +4632,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; } @@ -4507,7 +4647,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; } @@ -4518,15 +4658,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()); } // +=================================================+ @@ -4628,15 +4768,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; @@ -4864,7 +4995,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(); @@ -4880,23 +5011,27 @@ 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(); + } + +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) + if ( (!RlvActions::canStartIM(item->getCreatorUUID())) && (!RlvActions::hasOpenP2PSession(item->getCreatorUUID())) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", item->getCreatorUUID(), "completename").getSLURLString())); + return; } -// LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); -// if (session_id != LLUUID::null) -// { -// LLIMFloater::show(session_id); -// } -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - LLAvatarActions::startIM(item->getCreatorUUID()); // [/RLVa:KB] + + LLUUID session_id = gIMMgr->addSession(callingcard_name, IM_NOTHING_SPECIAL, item->getCreatorUUID()); + if (session_id != LLUUID::null) + { + LLFloaterIMContainer::getInstance()->showConversation(session_id); + } } } else if ("lure" == action) @@ -5764,6 +5899,7 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) p.on_enable.parameter = cbparams; LLView* parent = attachment->getIsHUDAttachment() ? attach_hud_menu : attach_menu; LLUICtrlFactory::create(p, parent); + items.push_back(p.name); } } } @@ -5785,11 +5921,10 @@ BOOL LLObjectBridge::renameItem(const std::string& new_name) { LLPointer 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()) { @@ -6213,16 +6348,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; @@ -6311,14 +6436,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(); } } @@ -6662,9 +6788,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()); @@ -6676,41 +6801,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; - } /************************************************************************/ @@ -6775,5 +6888,4 @@ LLInvFVBridge* LLWornInventoryBridgeBuilder::createBridge( } - // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 1f609efbc4..d7a0d5b459 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 "llviewerwearable.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 menuentry_vec_t; @@ -56,7 +58,7 @@ typedef std::vector 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& batch); - virtual void move(LLFolderViewEventListener* new_parent_bridge) {} + virtual void removeBatch(std::vector& 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,8 +148,8 @@ 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? @@ -167,30 +177,36 @@ protected: LLViewerInventoryCategory* item, const LLUUID& new_parent, BOOL restamp); - void removeBatchNoCheck(LLDynamicArray& batch); + void removeBatchNoCheck(std::vector& batch); protected: LLHandle 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; @@ -212,7 +228,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; @@ -221,18 +236,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 @@ -240,15 +254,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(); @@ -258,7 +275,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); @@ -266,11 +285,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, @@ -283,20 +303,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 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); @@ -319,8 +343,6 @@ protected: //-TT 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); @@ -332,10 +354,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 mHandle; }; @@ -365,7 +388,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*); }; @@ -552,7 +574,6 @@ class LLMeshBridge : public LLItemBridge public: virtual LLUIImagePtr getIcon() const; virtual void openItem(); - virtual void previewItem(); virtual void buildContextMenu(LLMenuGL& menu, U32 flags); protected: @@ -628,7 +649,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. @@ -673,6 +694,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 de2619f475..03eafe3456 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" @@ -46,60 +46,49 @@ #include "llappearancemgr.h" // needed to query whether we are in COF 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), +LLInventoryFilter::LLInventoryFilter(const Params& p) +: mName(p.name), + mFilterModified(FILTER_NONE), + mEmptyLookupMessage("InventoryNoMatchingItems"), mFilterSubStringTarget(SUBST_TARGET_NAME), // ## Zi: Extended Inventory Search - 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(); // Begin Multi-substring inventory search mSubStringMatchOffsets.clear(); mFilterSubStrings.clear(); // End Multi-substring inventory search - 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() -{ -} - // ## Zi: Extended Inventory Search void LLInventoryFilter::setFilterSubStringTarget(const std::string& targetName) { @@ -141,23 +130,27 @@ std::string LLInventoryFilter::getSearchableTarget(const LLFolderViewItem* item) } // ## Zi: Extended Inventory Search -BOOL LLInventoryFilter::check(const LLFolderViewItem* item) +bool LLInventoryFilter::check(const LLFolderViewModelItem* item) { + const LLFolderViewModelItemInventory* listener = dynamic_cast(item); + // Clipboard cut items are *always* filtered so we need this value upfront // FIRE-6714: Don't move objects to trash during cut&paste // Don't hide cut items in inventory - //const LLFolderViewEventListener* listener = item->getListener(); + //const LLFolderViewEventListener* listener = item->getListener(); CHUI merge LL removed thie line //const BOOL passed_clipboard = (listener ? checkAgainstClipboard(listener->getUUID()) : TRUE); const BOOL passed_clipboard = TRUE; // FIRE-6714: Don't move objects to trash during cut&paste // If it's a folder and we're showing all folders, return automatically. - const BOOL is_folder = (dynamic_cast(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; CHUI Merge LL origonal removed in FS, replaced with enhanced search + //std::string::size_type string_offset = mFilterSubString.size() ? listener->getSearchableName().find(mFilterSubString) : std::string::npos; CHUI Merge LL new line // Begin Multi-substring inventory search mSubStringMatchOffset = std::string::npos; if (mFilterSubStrings.size()) @@ -190,21 +183,18 @@ BOOL LLInventoryFilter::check(const LLFolderViewItem* item) } // End Multi-substring inventory search - 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); @@ -213,28 +203,20 @@ bool LLInventoryFilter::check(const LLInventoryItem* item) //const BOOL passed_clipboard = checkAgainstClipboard(item->getUUID()); const BOOL passed_clipboard = TRUE; // Don't filter cut items - 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(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; } @@ -245,6 +227,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 // FIRE-6714: Don't move objects to trash during cut&paste // Don't hide cut items in inventory @@ -257,14 +246,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; @@ -273,9 +262,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(); @@ -375,7 +363,7 @@ BOOL LLInventoryFilter::checkAgainstFilterType(const LLFolderViewItem* item) con } } } - + return TRUE; } @@ -454,13 +442,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(item->getListener()); + const LLInvFVBridge *bridge = dynamic_cast(listener); if (bridge && bridge->isLink()) { const LLUUID& linked_uuid = gInventory.getLinkedItemID(bridge->getUUID()); @@ -482,9 +469,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(); @@ -504,20 +490,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); @@ -529,11 +515,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 @@ -547,16 +533,9 @@ BOOL LLInventoryFilter::isActive() const || mFilterOps.mHoursAgo != 0; } -BOOL LLInventoryFilter::isModified() const +bool LLInventoryFilter::isModified() const { - return mModified; -} - -BOOL LLInventoryFilter::isModifiedAndClear() -{ - BOOL ret = mModified; - mModified = FALSE; - return ret; + return mFilterModified != FILTER_NONE; } void LLInventoryFilter::updateFilterTypes(U64 types, U64& current_types) @@ -757,9 +736,10 @@ void LLInventoryFilter::setDateRange(time_t min_date, time_t max_date) void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) { + static LLCachedControl s_last_logoff(gSavedPerAccountSettings, "LastLogoff", 0); if (sl && !isSinceLogoff()) { - setDateRange(mLastLogoff, time_max()); + setDateRange(s_last_logoff(), time_max()); setModified(); } if (!sl && isSinceLogoff()) @@ -778,17 +758,18 @@ void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl) } } -BOOL LLInventoryFilter::isSinceLogoff() const +bool LLInventoryFilter::isSinceLogoff() const { - return (mFilterOps.mMinDate == (time_t)mLastLogoff) && + static LLCachedControl 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) @@ -895,15 +876,6 @@ void LLInventoryFilter::setFilterWorn(BOOL sl) mFilterOps.mFilterTypes |= FILTERTYPE_WORN; } -void LLInventoryFilter::setSortOrder(U32 order) -{ - if (mOrder != order) - { - mOrder = order; - setModified(); - } -} - void LLInventoryFilter::markDefault() { mDefaultFilterOps = mFilterOps; @@ -915,83 +887,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; @@ -999,140 +956,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; } @@ -1148,7 +1085,6 @@ const std::string& LLInventoryFilter::getFilterText() } else { - //mFilterText += "No "; mFilterText += LLTrans::getString("No Filters"); mFilterText += not_filtered_types; } @@ -1158,66 +1094,55 @@ const std::string& LLInventoryFilter::getFilterText() if (isSinceLogoff()) { - //mFilterText += " - Since Logoff"; mFilterText += LLTrans::getString("Since Logoff"); } return mFilterText; } -void LLInventoryFilter::toLLSD(LLSD& data) const + +LLInventoryFilter& LLInventoryFilter::operator=( const LLInventoryFilter& other ) { - 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(); + 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; } -void LLInventoryFilter::fromLLSD(LLSD& data) + +void LLInventoryFilter::toParams(Params& params) const { - if(data.has("filter_types")) + 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(); +} + +void LLInventoryFilter::fromParams(const Params& params) +{ + if (!params.validateBlock()) { - setFilterObjectTypes((U64)data["filter_types"].asInteger()); + return; } - 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()); - } - - if(data.has("permissions")) - { - setFilterPermissions((PermissionMask)data["permissions"].asInteger()); - } - - if(data.has("substring")) - { - setFilterSubString(std::string(data["substring"].asString())); - } - - if(data.has("sort_order")) - { - setSortOrder((U32)data["sort_order"].asInteger()); - } - - 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 @@ -1230,11 +1155,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; @@ -1261,14 +1196,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) { @@ -1286,15 +1213,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) @@ -1302,9 +1229,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); } @@ -1315,6 +1245,30 @@ bool LLInventoryFilter::areDateLimitsSet() || mFilterOps.mHoursAgo != 0; } +bool LLInventoryFilter::showAllResults() const +{ + return hasFilterString(); +} + + + +bool LLInventoryFilter::FilterOps::DateRange::validateBlock( bool emit_errors /*= true*/ ) const +{ + bool valid = LLInitParam::Block::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; +} + // Begin Multi-substring inventory search // For use by LLFolderViewItem for highlighting diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 34e6cd0ccf..ada56a2740 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -29,6 +29,7 @@ #include "llinventorytype.h" #include "llpermissionsflags.h" +#include "llfolderviewmodel.h" // Begin Multi-substring inventory search #include @@ -38,7 +39,7 @@ class LLFolderViewItem; class LLFolderViewFolder; class LLInventoryItem; -class LLInventoryFilter +class LLInventoryFilter : public LLFolderViewFilter { public: enum EFolderShow @@ -48,14 +49,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 @@ -63,7 +56,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 FILTERTYPE_WORN = 0x1 << 6, // search by wearable type }; @@ -93,16 +86,92 @@ public: }; // ## Zi: Extended Inventory Search - LLInventoryFilter(const std::string& name); - virtual ~LLInventoryFilter(); + struct FilterOps + { + struct DateRange : public LLInitParam::Block + { + Optional 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 + { + Optional types; + Optional object_types, + wearable_types, + category_types; + Optional links; + Optional uuid; + Optional date_range; + Optional hours_ago; + Optional show_folder_state; + Optional 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 + { + Optional name; + Optional filter_ops; + Optional substring; + Optional 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); @@ -113,7 +182,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; // Begin Multi-substring inventory search // For use by LLFolderViewItem for highlighting @@ -142,19 +211,15 @@ 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; // ## Zi: Extended Inventory Search void setFilterSubStringTarget(const std::string& targetName); EFilterSubstringTarget getFilterSubStringTarget() const; @@ -167,24 +232,20 @@ public: 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 @@ -196,8 +257,8 @@ public: // +-------------------------------------------------------------------+ // + Default // +-------------------------------------------------------------------+ - BOOL isDefault() const; - BOOL isNotDefault() const; + bool isDefault() const; + bool isNotDefault() const; void markDefault(); void resetDefault(); @@ -205,43 +266,30 @@ 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; // Begin Multi-substring inventory search @@ -253,16 +301,14 @@ private: 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 17ef3a0c53..c97ee67e4e 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" @@ -1034,7 +1037,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) @@ -1069,7 +1072,7 @@ void LLSaveFolderState::doFolder(LLFolderViewFolder* folder) void LLOpenFilteredFolders::doItem(LLFolderViewItem *item) { - if (item->getFiltered()) + if (item->passedFilter()) { item->getParentFolder()->setOpenArrangeRecursively(TRUE, LLFolderViewFolder::RECURSE_UP); } @@ -1077,12 +1080,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); } @@ -1090,7 +1093,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()) @@ -1103,14 +1106,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; } } @@ -1130,3 +1131,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(root->getViewModelItem()); + LLViewerInventoryCategory *cat = model->getCategory(inventory_item->getUUID()); + if (!cat) return; + cat->changeType(new_folder_type); + return; + } + + + std::set 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::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((*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 d26d381d50..69bb97cc81 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -423,21 +423,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 { @@ -447,49 +432,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); + + static void onItemsRemovalConfirmation(const LLSD& notification, const LLSD& response, LLFolderView* root); + static void removeItemFromDND(LLFolderView* root); }; -class LLOpenFilteredFolders : public LLFolderViewFunctor -{ -public: - LLOpenFilteredFolders() {} - virtual ~LLOpenFilteredFolders() {} - virtual void doFolder(LLFolderViewFolder* folder); - virtual void doItem(LLFolderViewItem* item); -}; - -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 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 659448143d..2197c53bb8 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 old mode 100755 new mode 100644 index 537f9d67e6..9809d5dc75 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -392,13 +392,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; @@ -421,7 +420,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()) { @@ -644,7 +680,10 @@ void LLInventoryModel::collectDescendentsIf(const LLUUID& id, { cats.put(cat); } - collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add); +// [RLVa:KB] - Checked: 2013-04-15 (RLVa-1.4.8) + collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add, follow_folder_links); +// [/RLVa:KB] +// collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add); } } @@ -1016,6 +1055,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat) cat_array->put(old_cat); } mask |= LLInventoryObserver::STRUCTURE; + mask |= LLInventoryObserver::INTERNAL; } if(old_cat->getName() != cat->getName()) { @@ -1355,14 +1395,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); + } } } } @@ -2057,8 +2116,9 @@ bool LLInventoryModel::loadSkeleton( { LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); cat->setVersion(NO_VERSION); - llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + LL_DEBUGS("Inventory") << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; } + LL_INFOS("Inventory") << "Invalidated " << invalid_categories.size() << " categories due to invalid descendents cache" << llendl; // At this point, we need to set the known descendents for each // category which successfully cached so that we do not @@ -3378,6 +3438,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; @@ -3399,7 +3460,7 @@ void LLInventoryModel::saveItemsOrder(const LLInventoryModel::item_array_t& item notifyObservers(); } - +*/ // See also LLInventorySort where landmarks in the Favorites folder are sorted. //-TT Patch: ReplaceWornItemsOnly @@ -3619,14 +3680,15 @@ public: * * @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); -} +//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; @@ -3643,7 +3705,7 @@ void LLInventoryModel::rearrangeFavoriteLandmarks(const LLUUID& source_item_id, saveItemsOrder(items); } - +*/ //---------------------------------------------------------------------------- // *NOTE: DEBUG functionality diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index 657f4d0a7d..41368fba66 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -251,8 +251,10 @@ public: // 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" 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; @@ -389,14 +391,11 @@ 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); + //void saveItemsOrder(const LLInventoryModel::item_array_t& items); //-------------------------------------------------------------------- // Creation diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 37a608cea5..d83d5d6cf5 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -38,24 +38,38 @@ #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" #include "llviewerfoldertype.h" #include "llvoavatarself.h" +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) +#include "rlvactions.h" +#include "rlvcommon.h" +// [/RLVa:KB] static LLDefaultChildRegistry::Register 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 LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; // const makes GCC >= 4.6 very angry about not user defined default ctor. +static const LLInventoryFolderViewModelBuilder INVENTORY_BRIDGE_BUILDER; // CHUI merge changed +//static LLInventoryFVBridgeBuilder INVENTORY_BRIDGE_BUILDER; // const makes GCC >= 4.6 very angry about not user defined default ctor. +// 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 +149,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(p); } void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { - mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves - - buildFolderView(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.popScope(); + 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); + 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(scroller_params); addChild(mScroller); @@ -212,7 +236,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 +250,7 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { initializeViews(); } + gIdleCallbacks.addFunction(onIdle, (void*)this); if (mSortOrderSetting != INHERIT_SORT_ORDER) @@ -240,19 +264,19 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) // hide inbox // Optional hiding of Received Items folder aka Inbox - //getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); + //getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); if (!gSavedSettings.getBOOL("FSShowInboxFolder")) { - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); } gSavedSettings.getControl("FSShowInboxFolder")->getSignal()->connect(boost::bind(&LLInventoryPanel::updateShowInboxFolder, this, _2)); // Optional hiding of Received Items folder aka Inbox - getFilter()->setFilterCategoryTypes(getFilter()->getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_OUTBOX)); + 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 @@ -262,19 +286,18 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) gSavedSettings.getControl("DebugHideEmptySystemFolders")->getSignal()->connect(boost::bind(&LLInventoryPanel::updateHideEmptySystemFolders, this, _2)); // 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); @@ -290,68 +313,53 @@ 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); } // ## Zi: Extended Inventory Search @@ -368,16 +376,17 @@ LLInventoryFilter::EFilterSubstringTarget LLInventoryPanel::getFilterSubStringTa 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(); } @@ -385,27 +394,22 @@ 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); } // ## Zi: Filter Links Menu @@ -417,12 +421,12 @@ U64 LLInventoryPanel::getFilterLinks() 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) @@ -444,11 +448,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(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(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(view_item); + } ////////////////////////////// // LABEL Operation @@ -458,7 +471,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(); @@ -473,11 +486,15 @@ void LLInventoryPanel::modelChanged(U32 mask) // Destroy and regenerate the UI. if (mask & LLInventoryObserver::REBUILD) { - 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(view_item ? view_item->getViewModelItem() : NULL); view_folder = dynamic_cast(view_item); } @@ -499,7 +516,7 @@ void LLInventoryPanel::modelChanged(U32 mask) { if (view_folder) { - view_folder->requestSort(); + view_folder->getViewModelItem()->requestSort(); } } @@ -532,20 +549,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(); @@ -557,9 +578,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(); } } @@ -571,6 +593,43 @@ LLFolderView* LLInventoryPanel::getRootFolder() return mFolderRoot; } +LLUUID LLInventoryPanel::getRootFolderID() +{ + if (mFolderRoot && mFolderRoot->getViewModelItem()) + { + return static_cast(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) @@ -590,16 +649,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; @@ -609,14 +725,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); @@ -624,100 +740,51 @@ 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(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; // Don't bother with tooltips in inventory + params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); + params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); + return LLUICtrlFactory::create(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; // Don't bother with tooltips in inventory + + params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); + params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); return LLUICtrlFactory::create(params); } -LLFolderViewItem* LLInventoryPanel::buildNewViews(const LLUUID& id ) +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) @@ -735,16 +802,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 @@ -755,28 +818,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; @@ -793,7 +856,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(); @@ -806,7 +869,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. @@ -817,8 +880,8 @@ void LLInventoryPanel::openStartFolderOrMyInventory() { LLFolderViewFolder *fchild = dynamic_cast(child); if (fchild - && fchild->getListener() - && fchild->getListener()->getUUID() == gInventory.getRootFolderID()) + && fchild->getViewModelItem() + && fchild->getViewModelItem()->getName() == "My Inventory") { fchild->setOpen(TRUE); break; @@ -835,7 +898,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(); } @@ -949,7 +1012,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& items, BOOL user_action)>& cb) @@ -962,7 +1025,7 @@ void LLInventoryPanel::setSelectCallback(const boost::functionclearSelection(); + mSelectThisID.setNull(); } void LLInventoryPanel::onSelectionChange(const std::deque& items, BOOL user_action) @@ -971,7 +1034,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque& it mCompletionObserver->reset(); for (std::deque::const_iterator it = items.begin(); it != items.end(); ++it) { - LLUUID id = (*it)->getListener()->getUUID(); + LLUUID id = static_cast((*it)->getViewModelItem())->getUUID(); LLViewerInventoryItem* inv_item = mInventory->getItem(id); if (inv_item && !inv_item->isFinished()) @@ -991,20 +1054,15 @@ void LLInventoryPanel::onSelectionChange(const std::deque& 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 selected_items = mFolderRoot->getSelectionList(); + std::set selected_items = mFolderRoot->getSelectionList(); std::string name; @@ -1014,20 +1072,23 @@ bool LLInventoryPanel::beginIMSession() // [/RLVa:KB] // EInstantMessage type = IM_SESSION_CONFERENCE_START; - std::set::const_iterator iter; +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) + bool fRlvCanStartIM = true; +// [/RLVa:KB] + + std::set::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(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; @@ -1054,19 +1115,17 @@ bool LLInventoryPanel::beginIMSession() id = item_array.get(i)->getCreatorUUID(); if(at.isBuddyOnline(id)) { -// members.put(id); -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - members.push_back(id); +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) + fRlvCanStartIM &= RlvActions::canStartIM(id); // [/RLVa:KB] + members.put(id); } } } } 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) { @@ -1079,10 +1138,10 @@ bool LLInventoryPanel::beginIMSession() if(at.isBuddyOnline(id)) { -// members.put(id); -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - members.push_back(id); +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) + fRlvCanStartIM &= RlvActions::canStartIM(id); // [/RLVa:KB] + members.put(id); } } } //if IT_CALLINGCARD @@ -1093,6 +1152,15 @@ bool LLInventoryPanel::beginIMSession() // the session_id is randomly generated UUID which will be replaced later // with a server side generated number +// [RLVa:KB] - Checked: 2013-05-08 (RLVa-1.4.9) + if (!fRlvCanStartIM) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF); + return true; + } +// [/RLVa:KB] + if (name.empty()) { name = LLTrans::getString("conference-title"); @@ -1101,7 +1169,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); // } // [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h LLAvatarActions::startConference(members); @@ -1113,13 +1181,13 @@ bool LLInventoryPanel::beginIMSession() bool LLInventoryPanel::attachObject(const LLSD& userdata) { // Copy selected item UUIDs to a vector. - std::set selected_items = mFolderRoot->getSelectionList(); + std::set selected_items = mFolderRoot->getSelectionList(); uuid_vec_t items; - for (std::set::const_iterator set_iter = selected_items.begin(); + for (std::set::const_iterator set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) { - items.push_back(*set_iter); + items.push_back(static_cast((*set_iter)->getViewModelItem())->getUUID()); } // Attach selected items. @@ -1132,7 +1200,7 @@ bool LLInventoryPanel::attachObject(const LLSD& userdata) BOOL LLInventoryPanel::getSinceLogoff() { - return getFilter()->isSinceLogoff(); + return getFilter().isSinceLogoff(); } // Optional hiding of empty system folders @@ -1310,14 +1378,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::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(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 selection_set = mFolderRoot->getSelectionList(); + if (!selection_set.empty()) + { + can_delete = true; + for (std::set::iterator iter = selection_set.begin(); + iter != selection_set.end(); + ++iter) + { + LLFolderViewItem *item = *iter; + const LLFolderViewModelItemInventory *listener = static_cast(item->getViewModelItem()); + if (!listener) + { + can_delete = false; + } + else + { + can_delete &= listener->isItemRemovable() && !listener->isItemInTrash(); + } + } + } + } + return can_delete; +} /************************************************************************/ /* Recent Inventory Panel related class */ @@ -1337,7 +1541,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: @@ -1384,3 +1588,34 @@ LLInventoryWornItemsPanel::LLInventoryWornItemsPanel( const Params& params) } +namespace LLInitParam +{ + void TypeValues::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 4a71d00bcc..8eda9ff71a 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,20 @@ #include "lluictrlfactory.h" #include -class LLFolderView; -class LLFolderViewFolder; -class LLFolderViewItem; -class LLInventoryFilter; -class LLInventoryModel; class LLInvFVBridge; +class LLInventoryFolderViewModelBuilder; class LLInventoryFVBridgeBuilder; -class LLMenuBarGL; -class LLCheckBoxCtrl; -class LLSpinCtrl; -class LLTextBox; -class LLIconCtrl; -class LLSaveFolderState; -class LLFilterEditor; -class LLTabContainer; class LLInvPanelComplObserver; +class LLFolderViewModelInventory; + +namespace LLInitParam +{ + template<> + struct TypeValues : public TypeValuesHelper + { + static void declareValues(); + }; +} class LLInventoryPanel : public LLPanel { @@ -74,6 +74,19 @@ public: {} }; + struct StartFolder : public LLInitParam::ChoiceBlock + { + Alternative name; + Alternative id; + Alternative type; + + StartFolder() + : name("name"), + id("id"), + type("type") + {} + }; + struct Params : public LLInitParam::Block { @@ -82,12 +95,14 @@ public: Optional allow_multi_select; Optional show_item_link_overlays; Optional filter; - Optional start_folder; + Optional start_folder; Optional use_label_suffix; Optional show_empty_message; - Optional show_load_status; Optional scroll; Optional accepts_drag_and_drop; + Optional folder_view; + Optional folder; + Optional item; Params() : sort_order_setting("sort_order_setting"), @@ -98,27 +113,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 + { + Mandatory filter; + Mandatory 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, @@ -138,8 +164,9 @@ public: void setSelection(const LLUUID& obj_id, BOOL take_keyboard_focus); void setSelectCallback(const boost::function& 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); @@ -162,6 +189,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 &items, BOOL user_action); @@ -173,6 +201,7 @@ public: void doCreate(const LLSD& userdata); bool beginIMSession(); bool attachObject(const LLSD& userdata); + static void idle(void* user_data); // Optional hiding of empty system folders void updateHideEmptySystemFolders(const LLSD &data); @@ -193,30 +222,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 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; //-------------------------------------------------------------------- @@ -229,7 +272,6 @@ public: void setSortOrder(U32 order); U32 getSortOrder() const; - void requestSort(); // private: // Make this available so we can save sort order based on sort_order_setting in XUI std::string mSortOrderSetting; @@ -242,27 +284,28 @@ 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 public: void setWorn(BOOL sl); 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: * diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index 5b45e1507c..6158ce171d 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 #include #include +#include #if LL_MSVC #pragma warning(push) @@ -59,10 +62,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"); @@ -85,6 +89,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}\\]).*"); const static boost::regex TIMESTAMP_AND_STUFF_SEC("^(\\[\\d{4}/\\d{1,2}/\\d{1,2}\\s+\\d{1,2}:\\d{2}:\\d{2}\\]\\s+|\\[\\d{1,2}:\\d{2}:\\d{2}\\]\\s+)?(.*)$"); /** @@ -120,6 +125,15 @@ const static int IDX_TEXT = 3; using namespace boost::posix_time; using namespace boost::gregorian; +void append_to_last_message(std::list& 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 { public: @@ -204,15 +218,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 matches; + * Testing for in bound and out bound ad-hoc file names + * if it is then skip date stamping. + **/ + + boost::match_results matches; bool inboundConf = boost::regex_match(filename, matches, INBOUND_CONFERENCE); bool outboundConf = boost::regex_match(filename, matches, OUTBOUND_CONFERENCE); if (!(inboundConf || outboundConf)) @@ -233,17 +249,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) { @@ -255,20 +271,14 @@ 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") + "]"; if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) { timeStr += ":[" @@ -278,7 +288,7 @@ std::string LLLogChat::timestamp(bool withdate) else { timeStr = "[" + LLTrans::getString("TimeHour") + "]:[" - + LLTrans::getString ("TimeMin")+"]"; + + LLTrans::getString ("TimeMin")+"]"; if (gSavedSettings.getBOOL("FSSecondsinChatTimestamps")) { timeStr += ":[" @@ -286,6 +296,9 @@ std::string LLLogChat::timestamp(bool withdate) } } + LLSD substitution; + substitution["datetime"] = (S32)time_corrected(); + LLStringUtil::format (timeStr, substitution); return timeStr; } @@ -293,9 +306,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) { if(LLStartUp::getStartupState() <= STATE_LOGIN_CLEANUP) { @@ -339,108 +352,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()) + if (NULL != sSaveHistorySignal) { - llwarns << "Filename is Empty!" << llendl; - return ; + (*sSaveHistorySignal)(); } - - LLFILE* fptr = LLFile::fopen(makeLogFileName(filename), "r"); /*Flawfinder: ignore*/ - if (!fptr) - { - callback(LOG_EMPTY, LLSD(), userdata); - return; //No previous conversation with this name. - } - else - { - 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); - } -} - -void append_to_last_message(std::list& 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& messages) +void LLLogChat::loadChatHistory(const std::string& file_name, std::list& 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)) { @@ -476,9 +422,9 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list& 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); } @@ -486,9 +432,259 @@ void LLLogChat::loadAllHistory(const std::string& file_name, std::list& 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 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& 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 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& list_of_transcriptions) +{ + // create search pattern + std::string pattern = "*." + LL_TRANSCRIPT_FILE_EXTENSION; + findTranscriptFiles(pattern, list_of_transcriptions); +} + +// static +void LLLogChat::getListOfTranscriptBackupFiles(std::vector& 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& listOfFilesToMove, + std::vector& 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(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& listOfFilesToMove) +{ + std::vector listOfFilesMoved; + return moveTranscripts(currentDirectory, newDirectory, listOfFilesToMove, listOfFilesMoved); +} + +//static +void LLLogChat::deleteTranscripts() +{ + std::vector 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 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 " +//Example, an object's name can be written like "Object " void LLChatLogFormatter::format(const LLSD& im, std::ostream& ostr) const { if (!im.isMap()) @@ -497,19 +693,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 " - if (im[IM_FROM].isDefined()) + //Example, an object's name can be written like "Object " + 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()) { @@ -517,9 +713,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); @@ -527,10 +723,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 @@ -552,13 +749,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; @@ -581,8 +783,8 @@ bool LLChatLogParser::parse(std::string& raw, LLSD& im) U32 divider_pos = stuff.find(NAME_TEXT_DIVIDER, 3); 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; } } @@ -596,8 +798,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 @@ -606,8 +808,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; } } @@ -615,7 +817,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 } @@ -627,45 +829,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 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& list_of_transcriptions); + static void getListOfTranscriptFiles(std::vector& list); + static void getListOfTranscriptBackupFiles(std::vector& 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& messages, const LLSD& load_params = LLSD()); + + typedef boost::signals2::signal 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& listOfFilesToMove, + std::vector& listOfFilesMoved); + static bool moveTranscripts(const std::string currentDirectory, + const std::string newDirectory, + std::vector& listOfFilesToMove); + + static void deleteTranscripts(); + static bool isTranscriptExist(const LLUUID& avatar_id); - static void loadAllHistory(const std::string& file_name, std::list& 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/llmanip.cpp b/indra/newview/llmanip.cpp index a4a9445562..a93980ea97 100644 --- a/indra/newview/llmanip.cpp +++ b/indra/newview/llmanip.cpp @@ -53,6 +53,7 @@ #include "llresmgr.h" #include "pipeline.h" #include "llglheaders.h" +#include "lluiimage.h" #include "llviewerregion.h" // Aurora Sim // Local constants... diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index b0fbad33b0..7f396b7b7e 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 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 3ac0565761..271802d48a 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 da9d5c2f12..0000000000 --- a/indra/newview/llnearbychat.cpp +++ /dev/null @@ -1,342 +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" - -#if 0 - -#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 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("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("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(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::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("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 history; - LLLogChat::loadAllHistory("chat", history); - - std::list::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("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(); -} - -#endif \ No newline at end of file diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h deleted file mode 100644 index c17bc7d6e1..0000000000 --- a/indra/newview/llnearbychat.h +++ /dev/null @@ -1,85 +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$ - */ - -#if 0 - -#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 mPopupMenuHandle; - LLChatHistory* mChatHistory; - - std::vector mMessageArchive; -}; - -#endif - -#endif diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp deleted file mode 100644 index d3f6176cf6..0000000000 --- a/indra/newview/llnearbychatbar.cpp +++ /dev/null @@ -1,687 +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" - -#if 0 - -#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("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("show_nearby_chat"); - show_btn->setCommitCallback(boost::bind(&LLNearbyChatBar::onToggleNearbyChatPanel, this)); - show_btn->setToggleState(show_nearby_chat); - - mOutputMonitor = getChild("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("chat_bar"); -} - -void LLNearbyChatBar::showHistory() -{ - openFloater(); - - if (!getChildView("nearby_chat")->getVisible()) - { - onToggleNearbyChatPanel(); - } -} - -void LLNearbyChatBar::showTranslationCheckbox(BOOL show) -{ - getChild("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("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 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; - - -#endif diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 89fe7bb3c2..9d2810cb37 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" @@ -37,13 +38,17 @@ #include "lltoastalertpanel.h" +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) +#include "rlvactions.h" +// [/RLVa:KB] + 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 +73,94 @@ 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"]; + +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + // Don't spawn an IM session for non-chat related events: + // - LLHandlerUtil::logToIMP2P() below will still be called with to_file_only == false + // - LLHandlerUtil::logToIM() will eventually be called as a result and without an open IM session it will log the + // same message as it would for an open session whereas to_file_only == true would take a different code path + if ( (RlvActions::hasOpenP2PSession(from_id)) || (RlvActions::canStartIM(from_id)) ) { - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - - LLUUID from_id = notification->getPayload()["from_id"]; - +// [/RLVa:KB] // firstly create session... LLHandlerUtil::spawnIMSession(name, from_id); - - // ...then log message to have IM Well notified about new message - LLHandlerUtil::logToIMP2P(notification); +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) } +// [/RLVa:KB] - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = dynamic_cast(alert_dialog); - p.enable_hide_btn = false; - p.can_fade = false; - p.is_modal = mIsModal; - p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); + // ...then log message to have IM Well notified about new message + LLHandlerUtil::logToIMP2P(notification); + } - // 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); + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLToast::Params p; + p.notif_id = notification->getID(); + p.notification = notification; + p.panel = dynamic_cast(alert_dialog); + p.enable_hide_btn = false; + p.can_fade = false; + p.is_modal = mIsModal; + p.on_delete_toast = boost::bind(&LLAlertHandler::onDeleteToast, this, _1); - LLScreenChannel* channel = dynamic_cast(mChannel.get()); - if(channel) - channel->addToast(p); - } - else if (notify["sigtype"].asString() == "change") - { - LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); - LLScreenChannel* channel = dynamic_cast(mChannel.get()); - if(channel) - channel->modifyToastByNotificationID(notification->getID(), (LLToastPanel*)alert_dialog); - } - else - { - LLScreenChannel* channel = dynamic_cast(mChannel.get()); - if(channel) - channel->killToastByNotificationID(notification->getID()); - } + // 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(mChannel.get()); + if(channel) + channel->addToast(p); + return false; } -//-------------------------------------------------------------------------- - -void LLAlertHandler::onDeleteToast(LLToast* toast) +void LLAlertHandler::onChange( LLNotificationPtr notification ) { + LLToastAlertPanel* alert_dialog = new LLToastAlertPanel(notification, mIsModal); + LLScreenChannel* channel = dynamic_cast(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) +{ +} + +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 41b53f63d6..9d5e09f598 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,74 +62,39 @@ 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(mChannel.get()); - if(channel) - channel->addToast(p); + LLScreenChannel* channel = dynamic_cast(mChannel.get()); + if(channel) + channel->addToast(p); - // send a signal to the counter manager - mNewNotificationSignal(); + // Standalone group floaters + //LLGroupActions::refresh_notices(); + LLGroupActions::refresh_notices(notification->getPayload()["group_id"].asUUID()); - // Standalone group floaters - //LLGroupActions::refresh_notices(); - LLGroupActions::refresh_notices(notification->getPayload()["group_id"].asUUID()); - } - 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 cdc9b9b823..3cb4e51cfa 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -27,10 +27,11 @@ #ifndef LL_LLNOTIFICATIONHANDLER_H #define LL_LLNOTIFICATIONHANDLER_H +#include #include "llwindow.h" -//#include "llnotificationsutil.h" +#include "llnotifications.h" #include "llchannelmanager.h" #include "llchat.h" #include "llinstantmessage.h" @@ -39,28 +40,13 @@ #include "llavatarname.h" // [FS communication UI] -//class LLIMFloater; +//class LLIMFloater; CHUI Merge old +//class LLFloaterIMSession; CHUI Merge new class FSFloaterIM; // [FS communication UI] 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, - NT_MONEYCHAT, - NT_RADARCHAT -} ENotificationType; /** * Handler of notification events. @@ -88,21 +74,8 @@ class LLEventHandler public: virtual ~LLEventHandler() {}; - // callbacks for counters - typedef boost::function notification_callback_t; - typedef boost::signals2::signal notification_signal_t; - notification_signal_t mNewNotificationSignal; - notification_signal_t mDelNotificationSignal; - boost::signals2::connection setNewNotificationCallback(notification_callback_t cb) { return mNewNotificationSignal.connect(cb); } - boost::signals2::connection setDelNotification(notification_callback_t cb) { return mDelNotificationSignal.connect(cb); } - // callback for notification/toast - typedef boost::function notification_id_callback_t; - typedef boost::signals2::signal notification_id_signal_t; - notification_id_signal_t mNotificationIDSignal; - boost::signals2::connection setNotificationIDCallback(notification_id_callback_t cb) { return mNotificationIDSignal.connect(cb); } - protected: - virtual void onDeleteToast(LLToast* toast)=0; + 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 @@ -111,8 +84,6 @@ protected: virtual void initChannel()=0; LLHandle mChannel; - e_notification_type mType; - }; // LLSysHandler and LLChatHandler are more specific base classes @@ -122,24 +93,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 > 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 { @@ -153,17 +137,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(); }; @@ -171,18 +152,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(); }; @@ -190,167 +168,143 @@ 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 +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 +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 +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. */ @@ -362,12 +316,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. diff --git a/indra/newview/llnotificationhandlerutil.cpp b/indra/newview/llnotificationhandlerutil.cpp index f950148ef0..64cec55a2c 100644 --- a/indra/newview/llnotificationhandlerutil.cpp +++ b/indra/newview/llnotificationhandlerutil.cpp @@ -35,17 +35,12 @@ #include "llagent.h" // [FS communication UI] -//#include "llimfloater.h" +//#include "llfloaterimsession.h" CHUI Merge new +//#include "llimfloater.h" CHUI Merge old #include "fsfloaterim.h" // [FS communication UI] #include "llimview.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" // [SL:KB] - Patch: Chat-Logs | Checked: 2010-11-18 (Catznip-2.4.0c) | Added: Catznip-2.4.0c @@ -59,282 +54,31 @@ using namespace LLNotificationsUI; -// static -std::list< std::set > LLSysHandler::sExclusiveNotificationGroups; +LLNotificationHandler::LLNotificationHandler(const std::string& name, const std::string& notification_type, const std::string& parentName) +: LLNotificationChannel(name, parentName, LLNotificationFilters::filterBy(&LLNotification::getType, notification_type)) +{} -// static -void LLSysHandler::init() -{ - std::set online_offline_group; - online_offline_group.insert("FriendOnline"); - online_offline_group.insert("FriendOffline"); +LLSystemNotificationHandler::LLSystemNotificationHandler(const std::string& name, const std::string& notification_type) + : LLNotificationHandler(name, notification_type, "System") +{} - sExclusiveNotificationGroups.push_back(online_offline_group); -} - -LLSysHandler::LLSysHandler() -{ - if(sExclusiveNotificationGroups.empty()) - { - init(); - } -} - -void LLSysHandler::removeExclusiveNotifications(const LLNotificationPtr& notif) -{ - LLScreenChannel* channel = dynamic_cast(mChannel.get()); - if (channel == NULL) - { - return; - } - - class ExclusiveMatcher: public LLScreenChannel::Matcher - { - public: - ExclusiveMatcher(const std::set& excl_group, - const std::string& from_name) : - mExclGroup(excl_group), mFromName(from_name) - { - } - bool matches(const LLNotificationPtr notification) const - { - for (std::set::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& mExclGroup; - const std::string& mFromName; - }; - - - for (exclusive_notif_sets::iterator it = sExclusiveNotificationGroups.begin(); it - != sExclusiveNotificationGroups.end(); it++) - { - std::set group = *it; - std::set::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"), -// Additional Firestorm notifications - RADAR_ALERT("RadarAlert"), - STREAM_METADATA("StreamMetadata"), - STREAM_METADATA_NA("StreamMetadataNoArtist"); -// - - -// 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() -/// Don't log RadarAlert or StreamMetadata notifications to nearby chat. -/// If the user elects to log them, they can use the Nearby chat logging modes. - && RADAR_ALERT != notification->getName() - && STREAM_METADATA != notification->getName() - && STREAM_METADATA_NA != 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(); -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a -// return -// (canEmbedNotificationInIM(notification)) && -// ( (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()) ); -// [/SL:KB] -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h - return - (canEmbedNotificationInIM(notification)) && - ( (!rlv_handler_t::isEnabled()) || (gRlvHandler.canStartIM(notification->getPayload()["from_id"].asUUID())) ) && - ( (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()) ); -// [/RLVa:KB] -} - -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a -// static -bool LLHandlerUtil::canEmbedNotificationInIM(const LLNotificationPtr& notification) -{ - switch (gSavedSettings.getS32("NotificationCanEmbedInIM")) - { - case 1: // Focused - return isIMFloaterFocused(notification); - case 2: // Never - return false; - case 0: // Always (Viewer 2 default) - default: - return true; - } -} -// [/SL:KB] - -// 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(); -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a - return - (canEmbedNotificationInIM(notification)) && - ( (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()) ); -// [/SL:KB] -} - -// 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(); -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a - return - (canEmbedNotificationInIM(notification)) && - ( (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()) ); -// [/SL:KB] -} - -// static -bool LLHandlerUtil::canSpawnSessionAndLogToIM(const LLNotificationPtr& notification) -{ - return canLogToIM(notification) && canSpawnIMSession(notification); -} - -// static -bool LLHandlerUtil::canSpawnToast(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); -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a - // Force a toast if the user opted not to embed notifications panels in IMs - return (!canEmbedNotificationInIM(notification)) || (!isIMFloaterOpened(notification)); -// [/SL:KB] - } - - return true; -} - -// static -// [FS communication UI] -//LLIMFloater* LLHandlerUtil::findIMFloater(const LLNotificationPtr& notification) -FSFloaterIM* LLHandlerUtil::findIMFloater(const LLNotificationPtr& notification) -// [FS communication UI] -{ - LLUUID from_id = notification->getPayload()["from_id"]; - LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); - // [FS communication UI] - //return LLFloaterReg::findTypedInstance("impanel", session_id); - return LLFloaterReg::findTypedInstance("fs_impanel", session_id); - // [FS communication UI] -} +LLCommunicationNotificationHandler::LLCommunicationNotificationHandler(const std::string& name, const std::string& notification_type) + : LLNotificationHandler(name, notification_type, "Communication") +{} // static bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) { bool res = false; + LLUUID from_id = notification->getPayload()["from_id"]; + LLUUID session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, from_id); // [FS communication UI] - //LLIMFloater* im_floater = findIMFloater(notification); - FSFloaterIM* im_floater = findIMFloater(notification); + //LLFloaterIMSession* im_floater = LLFloaterReg::findTypedInstance("impanel", session_id); CHUI Merge new + //return LLFloaterReg::findTypedInstance("impanel", session_id); CHUI Merge old + return LLFloaterReg::findTypedInstance("fs_impanel", session_id); // [FS communication UI] + if (im_floater != NULL) { res = im_floater->getVisible() == TRUE; @@ -343,22 +87,6 @@ bool LLHandlerUtil::isIMFloaterOpened(const LLNotificationPtr& notification) return res; } -bool LLHandlerUtil::isIMFloaterFocused(const LLNotificationPtr& notification) -{ - bool res = false; - - // [FS communication UI] - //LLIMFloater* im_floater = findIMFloater(notification); - FSFloaterIM* im_floater = findIMFloater(notification); - // [FS communication UI] - 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, @@ -397,13 +125,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, @@ -414,25 +135,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) // @@ -455,9 +160,6 @@ void log_name_callback(const LLUUID& agent_id, const LLAvatarName& av_name, // 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()) @@ -482,7 +184,6 @@ void LLHandlerUtil::logToIMP2P(const LLNotificationPtr& notification, bool to_fi // [/SL:KB] } } -} // static void LLHandlerUtil::logGroupNoticeToIMGroup( @@ -514,10 +215,11 @@ void LLHandlerUtil::logGroupNoticeToIMGroup( void LLHandlerUtil::logToNearbyChat(const LLNotificationPtr& notification, EChatSourceType type) { // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); + //LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); CHUI Merge new (including odd white space) + //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); CHUI Merge old FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); // [FS communication UI] - if(nearby_chat) + if (nearby_chat) { LLChat chat_msg(notification->getMessage()); chat_msg.mSourceType = type; @@ -606,7 +308,8 @@ void LLHandlerUtil::addNotifPanelToIM(const LLNotificationPtr& notification) void LLHandlerUtil::updateIMFLoaterMesages(const LLUUID& session_id) { // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); + //LLFloaterIMSession* im_floater = LLFloaterIMSession::findInstance(session_id); CHUI Merge new + //LLIMFloater* im_floater = LLIMFloater::findInstance(session_id); CHUI Merge old FSFloaterIM* im_floater = FSFloaterIM::findInstance(session_id); // [FS communication UI] if (im_floater != NULL && im_floater->getVisible()) @@ -615,15 +318,15 @@ void LLHandlerUtil::updateIMFLoaterMesages(const LLUUID& session_id) } } -// static -void LLHandlerUtil::updateVisibleIMFLoaterMesages(const LLNotificationPtr& notification) -{ - const std::string name = LLHandlerUtil::getSubstitutionName(notification); - LLUUID from_id = notification->getPayload()["from_id"]; - LLUUID session_id = spawnIMSession(name, from_id); - - updateIMFLoaterMesages(session_id); -} +//// static CHUI Merge check this, wasn't disabled in FS, but was in RLVa +//void LLHandlerUtil::updateVisibleIMFLoaterMesages(const LLNotificationPtr& notification) +//{ +// const std::string name = LLHandlerUtil::getSubstitutionName(notification); +// LLUUID from_id = notification->getPayload()["from_id"]; +// LLUUID session_id = spawnIMSession(name, from_id); +// +// updateIMFLoaterMesages(session_id); +//} // static void LLHandlerUtil::decIMMesageCounter(const LLNotificationPtr& notification) @@ -632,14 +335,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--; @@ -648,3 +347,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) { - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + LLHints::show(p); +} - std::string sigtype = notify["sigtype"].asString(); - if (sigtype == "add" || sigtype == "load") - { - LLHints::show(notification); - } - else if (sigtype == "delete") - { - LLHints::hide(notification); - } +void LLHintHandler::onDelete(LLNotificationPtr p) +{ + LLHints::hide(p); +} + +bool LLHintHandler::processNotification(const LLNotificationPtr& p) +{ return false; } diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index ff452d9cd2..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 @@ -42,119 +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(&LLNotification::getType, "notify")); - LLNotificationChannel::buildChannel("NotificationTips", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "notifytip")); - LLNotificationChannel::buildChannel("Group Notifications", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "groupnotify")); - LLNotificationChannel::buildChannel("Alerts", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("AlertModal", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alertmodal")); - LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "notifytoast")); - LLNotificationChannel::buildChannel("Offer", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "offer")); - LLNotificationChannel::buildChannel("Hints", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "hint")); - LLNotificationChannel::buildChannel("Browser", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "browser")); - LLNotificationChannel::buildChannel("Outbox", "Visible", LLNotificationFilters::filterBy(&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(new LLScriptHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["notifytip"] = boost::shared_ptr(new LLTipHandler(NT_NOTIFY, LLSD())); - mNotifyHandlers["groupnotify"] = boost::shared_ptr(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); - mNotifyHandlers["alert"] = boost::shared_ptr(new LLAlertHandler(NT_ALERT, LLSD())); - mNotifyHandlers["alertmodal"] = boost::shared_ptr(new LLAlertHandler(NT_ALERT, LLSD())); - static_cast(mNotifyHandlers["alertmodal"].get())->setAlertMode(true); - mNotifyHandlers["notifytoast"] = boost::shared_ptr(new LLIMHandler(NT_IMCHAT, LLSD())); - - mNotifyHandlers["nearbychat"] = boost::shared_ptr(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); - mNotifyHandlers["offer"] = boost::shared_ptr(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(mNotifyHandlers[notification_type].get()); - - if(!handle) - return false; - - // If we don't return false, everything that follows breaks. - handle->processNotification(notify); - return false; + mChatHandler = boost::shared_ptr(new LLFloaterIMNearbyChatHandler()); } //-------------------------------------------------------------------------- void LLNotificationManager::onChat(const LLChat& msg, const LLSD &args) { - // check ENotificationType argument - switch(args["type"].asInteger()) - { - // Process L$ balance changes in nearby chat - case NT_MONEYCHAT: - // - case NT_NEARBYCHAT: - { - LLNearbyChatHandler* handle = dynamic_cast(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 >::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 { typedef std::pair eventhandlers; - typedef std::pair 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 > mNotifyHandlers; - // cruft std::map mChatHandlers; - - std::map mChannelListeners; + boost::shared_ptr mChatHandler; + std::vector mChannels; }; } diff --git a/indra/newview/llnotificationofferhandler.cpp b/indra/newview/llnotificationofferhandler.cpp index 37965dce2c..dc16aea84d 100644 --- a/indra/newview/llnotificationofferhandler.cpp +++ b/indra/newview/llnotificationofferhandler.cpp @@ -36,20 +36,21 @@ #include "llscriptfloater.h" #include "llimview.h" #include "llnotificationsutil.h" +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) +#include "rlvactions.h" +// [/RLVa:KB] 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,193 +69,145 @@ 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) { - - - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) - { - // This is an original inventory offer, so add a script floater - LLScriptFloaterManager::instance().onAddNotification(notification->getID()); - } - else - { - notification->setReusable(LLHandlerUtil::isNotificationReusable(notification)); - - LLUUID session_id; -// if (LLHandlerUtil::canSpawnIMSession(notification)) -// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Modified: RLVa-1.3.0h - // Don't spawn a new IM session for inventory offers if this notification was subject to @shownames=n - bool spawn_session = (LLHandlerUtil::canSpawnIMSession(notification)) && (!notification->getPayload().has("rlv_shownames")); -// [/RLVa:KB] -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Modified: Catznip-2.5.0a -// bool spawn_session = LLHandlerUtil::canSpawnIMSession(notification); - if (spawn_session) -// [/SL:KB] - { - 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); -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Modified: Catznip-2.5.0a - // NOTE: add_notid_to_im needs to be FALSE if we suppressed spawning an IM because in that case the notification needs to - // be routed to the "syswell" or the inventory offer floater will dissapear and the user won't be able to accept it - bool add_notid_to_im = (spawn_session) && (LLHandlerUtil::canAddNotifPanelToIM(notification)); -// [/SL:KB] - 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(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 -// [RLVa:KB] - Checked: 2010-04-20 (RLVa-1.2.2a) | Added: RLVa-1.2.0f - if (notification->getPayload().has("rlv_shownames")) - { - // Log to chat history if this notification was subject to @shownames=n - LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - } - else if (add_notid_to_im && LLHandlerUtil::isIMFloaterOpened(notification)) -// [/RLVa:KB] -// if (add_notid_to_im && LLHandlerUtil::isIMFloaterOpened(notification)) - { - LLHandlerUtil::logToIMP2P(notification, true); - } - else - { - LLHandlerUtil::logToIMP2P(notification); - } - } - } + // This is an original inventory offer, so add a script floater + LLScriptFloaterManager::instance().onAddNotification(notification->getID()); } - else if (notify["sigtype"].asString() == "delete") + else { - if( notification->getPayload().has("give_inventory_notification") - && !notification->getPayload()["give_inventory_notification"] ) + bool add_notif_to_im = notification->canLogToIM() && notification->hasFormElements(); + + if (add_notif_to_im) { - // Remove original inventory offer script floater - LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); - } - else - { -// if (LLHandlerUtil::canAddNotifPanelToIM(notification) -// && !LLHandlerUtil::isIMFloaterOpened(notification)) -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Modified: Catznip-2.5.0a - // LLHandlerUtil::canAddNotifPanelToIM() won't necessarily tell us whether the notification went into an IM or to the syswell - // -> the one and only time we need to decrease the unread IM count is when we've clicked any of the buttons on the *toast* - // -> since LLIMFloater::updateMessages() hides the toast when we open the IM (which resets the unread count to 0) we should - // *only* decrease the unread IM count if there's a visible toast since the unread count will be at 0 otherwise anyway - LLScreenChannel* pChannel = dynamic_cast(mChannel.get()); - LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification->getID()) : NULL; - if ( (pToast) && (!pToast->getCanBeStored()) ) -// [/SL:KB] + 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(); + } + +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) + // Don't spawn an IM session for non-chat related events + if ( (RlvActions::hasOpenP2PSession(from_id)) || (RlvActions::canStartIM(from_id)) ) { - LLHandlerUtil::decIMMesageCounter(notification); +// [/RLVa:KB] + LLHandlerUtil::spawnIMSession(name, from_id); + LLHandlerUtil::addNotifPanelToIM(notification); +// [RLVa:KB] - Checked: 2013-05-09 (RLVa-1.4.9) } - mChannel.get()->killToastByNotificationID(notification->getID()); + else + { + // Since we didn't add this notification to an IM session we want it to get routed to the notification syswell + add_notif_to_im = false; + } +// [/RLVa:KB] + } + + if (!notification->canShowToast()) + { + LLNotificationsUtil::cancel(notification); + } + else if(!notification->canLogToIM() || !LLHandlerUtil::isIMFloaterOpened(notification)) + { + 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(mChannel.get()); + if(channel) + channel->addToast(p); + + } + + if (notification->canLogToIM()) + { + // 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())) -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Modified: Catznip-2.5.0a - if (toast->getCanBeStored()) -// [/SL:KB] + LLToastNotifyPanel* panelp = LLToastNotifyPanel::getInstance(p->getID()); + if (panelp) { - // 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 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)) -// { -// LLNotifications::instance().cancel(notification); -// } -// [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Modified: Catznip-2.5.0a - // NOTE: this will be fired from LLScreenChannel::killToastByNotificationID() which treats visible and stored toasts differently - if ( (notification) && (!notification->isCancelled()) && - (LLNotificationManager::getInstance()->getHandlerForNotification(notification->getType()) == this) ) - { - LLScreenChannel* pChannel = dynamic_cast(mChannel.get()); - LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification->getID()) : NULL; - if ( (!pToast) || (pToast->getCanBeStored()) ) + // + // HACK: if we're dealing with a notification embedded in IM, update it + // otherwise remove its toast + // + if (dynamic_cast(panelp)) { - LLNotifications::instance().cancel(notification); + panelp->updateNotification(); + } + else + { + // if notification has changed, hide it + mChannel.get()->removeToastByNotificationID(p->getID()); } } -// [/SL:KB] } + + +/*virtual*/ void LLOfferHandler::onDelete(LLNotificationPtr notification) +{ + if( notification->getPayload().has("give_inventory_notification") + && !notification->getPayload()["give_inventory_notification"] ) + { + // Remove original inventory offer script floater + LLScriptFloaterManager::instance().onRemoveNotification(notification->getID()); + } + else + { +// if (notification->canLogToIM() +// && notification->hasFormElements() +// && !LLHandlerUtil::isIMFloaterOpened(notification)) +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + // The above test won't necessarily tell us whether the notification went into an IM or to the notification syswell + // -> the one and only time we need to decrease the unread IM count is when we've clicked any of the buttons on the *toast* + // -> since LLIMFloater::updateMessages() hides the toast when we open the IM (which resets the unread count to 0) we should + // *only* decrease the unread IM count if there's a visible toast since the unread count will be at 0 otherwise anyway + LLScreenChannel* pChannel = dynamic_cast(mChannel.get()); + LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification->getID()) : NULL; + if ( (pToast) && (!pToast->getCanBeStored()) ) +// [/SL:KB] + { + 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(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; } - if(SCRIPT_DIALOG == notification->getName() || SCRIPT_DIALOG_GROUP == notification->getName() || SCRIPT_LOAD_URL == notification->getName()) + LLScreenChannel* channel = dynamic_cast(mChannel.get()); + if(channel) { - LLScriptFloaterManager::getInstance()->onAddNotification(notification->getID()); - } - else - { - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); - - LLToast::Params p; - p.notif_id = notification->getID(); - p.notification = notification; - p.panel = notify_box; - p.on_delete_toast = boost::bind(&LLScriptHandler::onDeleteToast, this, _1); - - LLScreenChannel* channel = dynamic_cast(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 d876ceff20..a2f37fba49 100644 --- a/indra/newview/llnotificationstorage.cpp +++ b/indra/newview/llnotificationstorage.cpp @@ -25,230 +25,120 @@ */ #include "llviewerprecompiledheaders.h" // must be first include + #include "llnotificationstorage.h" -#include "llxmlnode.h" // for linux compilers +#include +#include -#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" #include "llsyswellwindow.h" -////////////////////////////////////////////////////////////////////////// +typedef boost::function responder_constructor_t; -class LLResponderRegistry +class LLResponderRegistry : public LLRegistrySingleton { -public: - - static void registerResponders(); - - static LLNotificationResponderInterface* createResponder(const std::string& notification_name, const LLSD& params); - -private: - - template - static LLNotificationResponderInterface* create(const LLSD& params) - { - RESPONDER_TYPE* responder = new RESPONDER_TYPE(); - responder->fromLLSD(params); - return responder; - } - - typedef boost::function responder_constructor_t; - - static void add(const std::string& notification_name, const responder_constructor_t& ctr); - -private: - - typedef std::map build_map_t; - - static build_map_t sBuildMap; + public: + template static LLNotificationResponderInterface * create(const LLSD& pParams); + LLNotificationResponderInterface * createResponder(const std::string& pNotificationName, const LLSD& pParams); }; -////////////////////////////////////////////////////////////////////////// - -LLPersistentNotificationStorage::LLPersistentNotificationStorage() +template LLNotificationResponderInterface * LLResponderRegistry::create(const LLSD& pParams) { - mFileName = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml" ); + RESPONDER_TYPE* responder = new RESPONDER_TYPE(); + responder->fromLLSD(pParams); + return responder; } -bool LLPersistentNotificationStorage::onPersistentChannelChanged(const LLSD& payload) + +LLNotificationResponderInterface * LLResponderRegistry::createResponder(const std::string& pNotificationName, const LLSD& pParams) { - // 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; + responder_constructor_t * factoryFunc = (LLResponderRegistry::getValue(pNotificationName)); + + if(factoryFunc) + { + return (*factoryFunc)(pParams); + } + + return NULL; } -// 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; +LLResponderRegistry::StaticRegistrar sRegisterObjectGiveItem("ObjectGiveItem", &LLResponderRegistry::create); +LLResponderRegistry::StaticRegistrar sRegisterUserGiveItem("UserGiveItem", &LLResponderRegistry::create); +LLResponderRegistry::StaticRegistrar sRegisterOfferInfo("offer_info", &LLResponderRegistry::create); -void LLPersistentNotificationStorage::saveNotifications() + +LLNotificationStorage::LLNotificationStorage(std::string pFileName) + : mFileName(pFileName) { - // TODO - think about save optimization. - - llofstream notify_file(mFileName.c_str()); - if (!notify_file.is_open()) - { - llwarns << "Failed to open " << mFileName << llendl; - return; - } - - 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) - { - 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 formatter = new LLSDXMLFormatter(); - formatter->format(output, notify_file, LLSDFormatter::OPTIONS_PRETTY); } -void LLPersistentNotificationStorage::loadNotifications() +LLNotificationStorage::~LLNotificationStorage() { - LLResponderRegistry::registerResponders(); +} - llifstream notify_file(mFileName.c_str()); - if (!notify_file.is_open()) +bool LLNotificationStorage::writeNotifications(const LLSD& pNotificationData) const +{ + + 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; + } + else + { + LLPointer formatter = new LLSDXMLFormatter(); + formatter->format(pNotificationData, notifyFile, LLSDFormatter::OPTIONS_PRETTY); } - LLSD input; - LLPointer parser = new LLSDXMLParser(); - if (parser->parse(notify_file, input, LLSDSerialize::SIZE_UNLIMITED) < 0) - { - llwarns << "Failed to parse open notifications" << llendl; - return; - } + return didFileOpen; +} - if (input.isUndefined()) - { - return; - } - - LLSD& data = input["data"]; - if (data.isUndefined()) - { - return; - } - - using namespace LLNotificationsUI; - LLScreenChannel* notification_channel = dynamic_cast(LLChannelManager::getInstance()-> - findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID")))); - - LLNotifications& instance = LLNotifications::instance(); - S32 processed_notifications = 0; +bool LLNotificationStorage::readNotifications(LLSD& pNotificationData) const LLNotificationWellWindow::getInstance()->lockWindowUpdate(); - for (LLSD::array_const_iterator notification_it = data.beginArray(); - notification_it != data.endArray(); - ++notification_it) +{ + LL_INFOS("LLNotificationStorage") << "starting read '" << mFileName << "'" << LL_ENDL; + + bool didFileRead; + + pNotificationData.clear(); + + llifstream notifyFile(mFileName.c_str()); + didFileRead = notifyFile.is_open(); + if (!didFileRead) { - LLSD notification_params = *notification_it; - - if (instance.templateExists(notification_params["name"].asString())) - { - 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 open file '" << mFileName << "'" << LL_ENDL; } + else + { + LLPointer parser = new LLSDXMLParser(); + didFileRead = (parser->parse(notifyFile, pNotificationData, LLSDSerialize::SIZE_UNLIMITED) >= 0); + if (!didFileRead) + { + LL_WARNS("LLNotificationStorage") << "Failed to parse open notifications from file '" << mFileName + << "'" << LL_ENDL; + } LLNotificationWellWindow::getInstance()->unlockWindowUpdate(); - - LLNotifications::instance().getChannel("Persistent")-> - connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1)); -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -LLResponderRegistry::build_map_t LLResponderRegistry::sBuildMap; - -void LLResponderRegistry::registerResponders() -{ - sBuildMap.clear(); - - add("ObjectGiveItem", &create); - add("UserGiveItem", &create); -} - -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); + + LL_INFOS("LLNotificationStorage") << "ending read '" << mFileName << "'" << LL_ENDL; + + return didFileRead; } -void LLResponderRegistry::add(const std::string& notification_name, const responder_constructor_t& ctr) +LLNotificationResponderInterface * LLNotificationStorage::createResponder(const std::string& pNotificationName, const LLSD& pParams) const { - if(sBuildMap.find(notification_name) != sBuildMap.end()) - { - llwarns << "Responder is already registered : " << notification_name << llendl; - llassert(!"Responder already registered"); - } - sBuildMap[notification_name] = ctr; + return LLResponderRegistry::getInstance()->createResponder(pNotificationName, pParams); } - -// 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" +#include -// 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 +#include "llerror.h" + +class LLNotificationResponderInterface; +class LLSD; + +class LLNotificationStorage { - LOG_CLASS(LLPersistentNotificationStorage); + LOG_CLASS(LLNotificationStorage); public: + LLNotificationStorage(std::string pFileName); + ~LLNotificationStorage(); - LLPersistentNotificationStorage(); +protected: + bool writeNotifications(const LLSD& pNotificationData) const; + bool readNotifications(LLSD& pNotificationData) const; - void saveNotifications(); - - void loadNotifications(); + LLNotificationResponderInterface* createResponder(const std::string& pNotificationName, const LLSD& pParams) const; private: - - bool onPersistentChannelChanged(const LLSD& payload); - std::string mFileName; }; diff --git a/indra/newview/llnotificationtiphandler.cpp b/indra/newview/llnotificationtiphandler.cpp index 9a28828176..7a66972c7b 100644 --- a/indra/newview/llnotificationtiphandler.cpp +++ b/indra/newview/llnotificationtiphandler.cpp @@ -28,14 +28,8 @@ #include "llviewerprecompiledheaders.h" // must be first include #include "llfloaterreg.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// #include "llnearbychatbar.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" +#include "llfloaterimnearbychat.h" #include "llnotificationhandler.h" #include "llnotifications.h" #include "lltoastnotifypanel.h" @@ -43,20 +37,18 @@ #include "llviewerwindow.h" #include "llnotificationmanager.h" #include "llpaneltiptoast.h" -#include "lggcontactsets.h" +#include "lggcontactsets.h" // [FIRE-3522 : SJ] 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(); } } @@ -75,73 +67,58 @@ 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)) + if (notification->canLogToChat()) + { + LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); + + // don't show toast if Nearby Chat is opened + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat->isChatVisible()) { - LLHandlerUtil::logToNearbyChat(notification, CHAT_SOURCE_SYSTEM); - - // don't show toast if Nearby Chat is opened - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); - // [FS communication UI] - // Remove floating chat bar - // LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); - // if (!nearby_chat_bar->isMinimized() && nearby_chat_bar->getVisible() && nearby_chat->getVisible()) - if (nearby_chat->getVisible()) - // - { - return false; - } + return false; } + } - std::string session_name = notification->getPayload()["SESSION_NAME"]; - const std::string name = notification->getSubstitutions()["NAME"]; - const LLUUID agent_id = notification->getSubstitutions()["AGENT-ID"]; + std::string session_name = notification->getPayload()["SESSION_NAME"]; + const std::string name = notification->getSubstitutions()["NAME"]; + const LLUUID agent_id = notification->getSubstitutions()["AGENT-ID"]; // [FIRE-3522 : SJ] - 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 (session_name.empty()) + { + session_name = name; + } + LLUUID from_id = notification->getPayload()["from_id"]; + if (notification->canLogToIM()) + { +// LLHandlerUtil::logToIM(IM_NOTHING_SPECIAL, session_name, name, +// notification->getMessage(), from_id, from_id); // [SL:KB] - Patch: Chat-Logs | Checked: 2010-11-18 (Catznip-2.4.0c) | Added: Catznip-2.4.0c LLHandlerUtil::logToIMP2P(notification, false); // [/SL:KB] - } + } - if (LLHandlerUtil::canSpawnIMSession(notification)) - { - LLHandlerUtil::spawnIMSession(name, from_id); - } + if (notification->canLogToIM() && notification->hasFormElements()) + { + LLHandlerUtil::spawnIMSession(name, from_id); + } - // don't spawn toast for inventory accepted/declined offers if respective IM window is open (EXT-5909) - if (!LLHandlerUtil::canSpawnToast(notification)) - { - return false; + if (notification->canLogToIM() && LLHandlerUtil::isIMFloaterOpened(notification)) + { + return false; } // [FIRE-3522 : SJ] Only show Online/Offline toast when ChatOnlineNotification is enabled or the Friend is one you want to have on/offline notices from @@ -152,46 +129,20 @@ bool LLTipHandler::processNotification(const LLSD& notify) { return false; } - } + } - LLToastPanel* notify_box = LLToastPanel::buidPanelFromNotification(notification); + 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; + 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(mChannel.get()); - if(channel) - channel->addToast(p); - } - else if (notify["sigtype"].asString() == "delete") - { - mChannel.get()->killToastByNotificationID(notification->getID()); - } + LLScreenChannel* channel = dynamic_cast(mChannel.get()); + if(channel) + channel->addToast(p); return false; } - -//-------------------------------------------------------------------------- -void LLTipHandler::onDeleteToast(LLToast* toast) -{ -} - -//-------------------------------------------------------------------------- - -void LLTipHandler::onRejectToast(const 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/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index 9e695634df..88b67d71bd 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 @@ -74,8 +75,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); @@ -116,26 +117,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 @@ -160,6 +141,24 @@ void LLOutputMonitorCtrl::draw() } } + if ((mPower == 0.f && !mIsTalking) && mShowParticipantsSpeaking) + { + std::set participant_uuids; + LLVoiceClient::instance().getParticipantList(participant_uuids); + std::set::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 icon; // Centralized voice power level //if (mIsMuted) @@ -271,14 +270,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()) { @@ -286,6 +305,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); @@ -294,12 +314,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); } } @@ -308,32 +328,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(); - } + 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; + } - // 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; - } + } } ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index fab2b5ac98..86ef06b739 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 mImageMute; LLPointer mImageOff; LLPointer 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; }; ////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelblockedlist.cpp b/indra/newview/llpanelblockedlist.cpp index 73d0b6be82..115114bb53 100644 --- a/indra/newview/llpanelblockedlist.cpp +++ b/indra/newview/llpanelblockedlist.cpp @@ -30,17 +30,22 @@ // 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 "llavataractions.h" #include "llviewercontrol.h" static LLRegisterPanelClassWrapper t_panel_blocked_list("panel_block_list_sidetray"); @@ -57,54 +62,54 @@ 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)); - // Profile button - mCommitCallbackRegistrar.add("Block.ClickProfile", boost::bind(&LLPanelBlockedList::onProfileBtnClick, 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("blocked"); + mBlockedList = getChild("blocked"); mBlockedList->setCommitOnSelectionChange(TRUE); - // Profile button - mBlockedList->setCommitCallback(boost::bind(&LLPanelBlockedList::onSelectionChanged, this)); - mBlockedList->setDoubleClickCallback(boost::bind(&LLPanelBlockedList::onProfileBtnClick, this)); - mBlockedList->sortByColumn("item_name", TRUE); - mBlockedList->setSearchColumn(mBlockedList->getColumn("item_name")->mIndex); + this->setVisibleCallback(boost::bind(&LLPanelBlockedList::removePicker, this)); - // Make sure user can go back blocked user list if it's in a skin without - // sidebar (getParent()); - if(parent) + switch (gSavedSettings.getU32("BlockPeopleSortOrder")) { - childSetCommitCallback("back", boost::bind(&LLPanelBlockedList::onBackBtnClick, this), NULL); - getChild("back_button_container")->setVisible(TRUE); - } - else - { - getChild("back_button_container")->setVisible(FALSE); - } - // + case E_SORT_BY_NAME: + mBlockedList->sortByName(); + break; - LLMuteList::getInstance()->addObserver(this); - - refreshBlockedList(); + 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("blocked_gear_btn")->setMenu(blocked_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); + } + + getChild("unblock_btn")->setCommitCallback(boost::bind(&LLPanelBlockedList::unblockItem, this)); + getChild("blocked_filter_input")->setCommitCallback(boost::bind(&LLPanelBlockedList::onFilterEdit, this, _2)); return LLPanel::postBuild(); } void LLPanelBlockedList::draw() { - // Only update if selection changes - //updateButtons(); + updateButtons(); LLPanel::draw(); } @@ -118,200 +123,112 @@ void LLPanelBlockedList::onOpen(const LLSD& key) void LLPanelBlockedList::selectBlocked(const LLUUID& mute_id) { - mBlockedList->selectByID(mute_id); - mBlockedList->scrollToShowSelected(); + mBlockedList->selectItemByUUID(mute_id); } void LLPanelBlockedList::showPanelAndSelect(const LLUUID& idToSelect) { - // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD().with(BLOCKED_PARAM_NAME, idToSelect)); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterReg::showInstance("fs_blocklist", LLSD().with(BLOCKED_PARAM_NAME, idToSelect)); - } - else - { - 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() -{ - mBlockedList->deleteAllItems(); - - std::vector mutes = LLMuteList::getInstance()->getMutes(); - std::vector::iterator it; - for (it = mutes.begin(); it != mutes.end(); ++it) - { - 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); - } -} - void LLPanelBlockedList::updateButtons() { - bool hasSelected = NULL != mBlockedList->getFirstSelected(); - getChildView("Unblock")->setEnabled(hasSelected); + bool hasSelected = NULL != mBlockedList->getSelectedItem(); + getChildView("unblock_btn")->setEnabled(hasSelected); + getChildView("blocked_gear_btn")->setEnabled(hasSelected); } - - -void LLPanelBlockedList::onBackBtnClick() +void LLPanelBlockedList::unblockItem() { - LLSideTrayPanelContainer* parent = dynamic_cast(getParent()); - if(parent) + LLBlockedListItem* item = mBlockedList->getBlockedItem(); + if (item) { - parent->openPreviousPanel(); + LLMute mute(item->getUUID(), item->getName()); + LLMuteList::instance().remove(mute); } } -void LLPanelBlockedList::onRemoveBtnClick() +void LLPanelBlockedList::onCustomAction(const LLSD& userdata) { - //std::string name = mBlockedList->getSelectedItemLabel(); - //LLUUID id = mBlockedList->getStringUUIDSelectedItem(); - //LLMute mute(id, name); - // - //// Keep scroll position - //S32 scroll_pos = mBlockedList->getScrollPos(); + const std::string command_name = userdata.asString(); - //S32 last_selected = mBlockedList->getFirstSelectedIndex(); - //if (LLMuteList::getInstance()->remove(mute)) - //{ - // // 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); - // } - // // Only update if selection changes - // onSelectionChanged(); - - // // Keep scroll position - // mBlockedList->setScrollPos(scroll_pos); - //} - - // Allow bulk removals - S32 scroll_pos = mBlockedList->getScrollPos(); - S32 last_selected = mBlockedList->getFirstSelectedIndex(); - - // Remove observer before bulk operation or it would refresh the - // list after each removal, sending us straight into a crash! - LLMuteList::getInstance()->removeObserver(this); - - std::vector selected_items = mBlockedList->getAllSelected(); - for (std::vector::iterator it = selected_items.begin(); it != selected_items.end(); it++) + if ("block_obj_by_name" == command_name) { - std::string name = (*it)->getColumn(0)->getValue().asString(); - LLUUID id = (*it)->getUUID(); - LLMute mute(id, name); - LLMuteList::getInstance()->remove(mute); + blockObjectByName(); } - - LLMuteList::getInstance()->addObserver(this); - refreshBlockedList(); - - if (last_selected == mBlockedList->getItemCount()) + else if ("block_res_by_name" == command_name) { - // we were on the last item, so select the last item again - mBlockedList->selectNthItem(last_selected - 1); + blockResidentByName(); } - else + else if ("sort_by_name" == command_name) { - // else select the item after the last item previously selected - mBlockedList->selectNthItem(last_selected); + mBlockedList->sortByName(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_NAME); + } + else if ("sort_by_type" == command_name) + { + mBlockedList->sortByType(); + gSavedSettings.setU32("BlockPeopleSortOrder", E_SORT_BY_TYPE); } - onSelectionChanged(); - mBlockedList->setScrollPos(scroll_pos); - // } -void LLPanelBlockedList::onPickBtnClick() +BOOL LLPanelBlockedList::isActionChecked(const LLSD& userdata) +{ + 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) + { + return E_SORT_BY_TYPE == sort_order; + } + + return false; +} + +void LLPanelBlockedList::blockResidentByName() { const BOOL allow_multiple = FALSE; const BOOL close_on_select = TRUE; - // Standalone blocklist floater - /*LLFloaterAvatarPicker* picker = *///LLFloaterAvatarPicker::show(boost::bind(&LLPanelBlockedList::callbackBlockPicked, this, _1, _2), allow_multiple, close_on_select); + + LLView * button = findChild("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); + } - // *TODO: mantipov: should LLFloaterAvatarPicker be closed when panel is closed? - // old Floater dependency is not enable in panel - // addDependentFloater(picker); - - // Standalone blocklist floater - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelBlockedList::callbackBlockPicked, this, _1, _2), allow_multiple, close_on_select); - LLFloater* parent = dynamic_cast(getParent()); - if (parent) - { - parent->addDependentFloater(picker); - } - } - else - { - LLFloaterAvatarPicker::show(boost::bind(&LLPanelBlockedList::callbackBlockPicked, this, _1, _2), allow_multiple, close_on_select); - } - // + mPicker = picker->getHandle(); } -void LLPanelBlockedList::onBlockByNameClick() +void LLPanelBlockedList::blockObjectByName() { - // Standalone blocklist floater - //LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterGetBlockedObjectName* picker = LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); - LLFloater* parent = dynamic_cast(getParent()); - if (parent) - { - parent->addDependentFloater(picker); - } - } - else - { - LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); - } - // + LLFloaterGetBlockedObjectName::show(&LLPanelBlockedList::callbackBlockByName); } -// Profile button -void LLPanelBlockedList::onSelectionChanged() +void LLPanelBlockedList::onFilterEdit(const std::string& search_string) { - updateButtons(); - LLMute mute = LLMuteList::getInstance()->getMute(mBlockedList->getStringUUIDSelectedItem()); - getChildView("Profile")->setEnabled(mBlockedList->getNumSelected() == 1 && mute.mID.notNull() && mute.mType == LLMute::AGENT); -} + std::string filter = search_string; + LLStringUtil::trimHead(filter); -void LLPanelBlockedList::onProfileBtnClick() -{ - LLMute mute = LLMuteList::getInstance()->getMute(mBlockedList->getStringUUIDSelectedItem()); - if (mBlockedList->getNumSelected() == 1 && mute.mID.notNull() && mute.mType == LLMute::AGENT) - { - LLAvatarActions::showProfile(mute.mID); - } + mBlockedList->setNameFilter(filter); } -// void LLPanelBlockedList::callbackBlockPicked(const uuid_vec_t& ids, const std::vector 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 54d74219ed..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 -// 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,29 +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(); - // Profile button - void onProfileBtnClick(); - void onSelectionChanged(); - // + 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 names); static void callbackBlockByName(const std::string& text); private: - LLScrollListCtrl* mBlockedList; + LLBlockList* mBlockedList; + LLHandle mPicker; }; //----------------------------------------------------------------------------- diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index e4201da52b..dd3566a5cf 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -85,6 +85,7 @@ LLPanelGroupGeneral::LLPanelGroupGeneral() mCtrlListGroup(NULL), mActiveTitleLabel(NULL), mComboActiveTitle(NULL), + mAvatarNameCacheConnection(), mCtrlReceiveGroupChat(NULL) // { @@ -92,6 +93,10 @@ LLPanelGroupGeneral::LLPanelGroupGeneral() LLPanelGroupGeneral::~LLPanelGroupGeneral() { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } BOOL LLPanelGroupGeneral::postBuild() @@ -795,9 +800,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)); } } @@ -837,6 +845,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 9bbd0c1b0a..3ba4e4d4f8 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -119,6 +119,7 @@ private: LLCheckBoxCtrl *mCtrlReceiveGroupChat; // LLGroupMgrGroupData::member_list_t::iterator mMemberProgress; + boost::signals2::connection mAvatarNameCacheConnection; LLUUID mIteratorGroup; // FIRE-6074; UUID of the group mMemberProgress belongs to. // For storing group name for copy name button diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index ce9bce2b70..c9cf075c03 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& 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("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 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 names; uuid_vec_t agent_ids; agent_ids.push_back(agent_id); @@ -473,8 +502,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 @@ -482,7 +510,7 @@ void LLPanelGroupInvite::addUsers(uuid_vec_t& agent_ids) } else { - names.push_back(av_name.getLegacyName()); + names.push_back(av_name.getAccountName()); } } } @@ -495,7 +523,7 @@ void LLPanelGroupInvite::addUserCallback(const LLUUID& id, const LLAvatarName& a std::vector 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 145e70b3f2..29fbfdeaaa 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -547,10 +547,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 bf16367d3f..1808f5f7a2 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" - #if 0 #include "llfloaterreg.h" @@ -41,394 +40,8 @@ #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("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("volume_slider")->setCommitCallback(boost::bind(&LLPanelIMControlPanel::onVolumeChange, this, _2)); - - getChildView("add_friend_btn")->setEnabled(!LLAvatarActions::isFriend(getChild("avatar_icon")->getAvatarId())); - - setFocusReceivedCallback(boost::bind(&LLPanelIMControlPanel::onFocusReceived, this)); - - return LLPanelChatControlPanel::postBuild(); -} - -void LLPanelIMControlPanel::draw() -{ - bool is_muted = LLMuteList::getInstance()->isMuted(mAvatarID); - - getChild("block_btn_panel")->setVisible(!is_muted); - getChild("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("mute_btn"); - mute_btn->setValue( is_muted_voice ); - - LLUICtrl* volume_slider = getChild("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("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("avatar_name")->getText(), LLMute::AGENT); - - LLMuteList::getInstance()->add(mute); -} - -void LLPanelIMControlPanel::onClickUnblock() -{ - LLMute mute(mAvatarID, getChild("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("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("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("avatar_name")->setValue(im_session->mName); - getChild("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("avatar_name")->setValue(avatar_name); - getChild("avatar_name")->setToolTip(avatar_name); - - bool is_linden = LLStringUtil::endsWith(full_name, " Linden"); - getChild("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("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(); -} #endif diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index 3cdf1a1992..1944ac570a 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -30,14 +30,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() : @@ -46,21 +44,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; } @@ -71,41 +54,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: @@ -123,9 +71,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 6201de13f8..55a701a3e7 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" @@ -105,7 +106,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()) { @@ -141,7 +142,7 @@ private: // virtual void LLOpenFolderByID::doFolder(LLFolderViewFolder* folder) { - if (folder->getListener() && folder->getListener()->getUUID() == mFolderID) + if (folder->getViewModelItem() && static_cast(folder->getViewModelItem())->getUUID() == mFolderID) { if (!folder->isOpen()) { @@ -180,7 +181,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); @@ -252,12 +253,6 @@ void LLLandmarksPanel::onSearchEdit(const std::string& string) // LLPlacesInventoryPanel* inventory_list = dynamic_cast(tab->getAccordionView()); // if (NULL == inventory_list) continue; - // if (inventory_list->getFilter()) - // { - // filter_list(inventory_list, string); - // } - //} - if (mFavoritesInventoryPanel && mFavoritesInventoryPanel->getFilter()) { filter_list(mFavoritesInventoryPanel, string); @@ -265,7 +260,6 @@ void LLLandmarksPanel::onSearchEdit(const std::string& string) if (mLandmarksInventoryPanel && mLandmarksInventoryPanel->getFilter()) { filter_list(mLandmarksInventoryPanel, string); - } if (mMyInventoryPanel && mMyInventoryPanel->getFilter()) { filter_list(mMyInventoryPanel, string); @@ -304,28 +298,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) + LLFolderViewModelItemInventory* view_model_item = getCurSelectedViewModelItem(); + if (view_model_item && view_model_item->getInventoryType() == LLInventoryType::IT_LANDMARK) { - llwarns << "There are no selected list. No actions are performed." << llendl; - return; - } - - LLFolderViewEventListener* listenerp = current_item->getListener(); - if (listenerp && listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) - { - listenerp->openItem(); + view_model_item->openItem(); } } @@ -336,8 +323,7 @@ bool LLLandmarksPanel::isSingleItemSelected() if (mCurrentSelectedList != NULL) { - LLPlacesFolderView* root_view = - static_cast(mCurrentSelectedList->getRootFolder()); + LLFolderView* root_view = mCurrentSelectedList->getRootFolder(); if (root_view->getSelectedCount() == 1) { @@ -383,7 +369,7 @@ void LLLandmarksPanel::onSelectorButtonClicked() LLFolderViewItem* cur_item = mFavoritesInventoryPanel->getRootFolder()->getCurSelectedItem(); if (!cur_item) return; - LLFolderViewEventListener* listenerp = cur_item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast(cur_item->getViewModelItem()); if (listenerp->getInventoryType() == LLInventoryType::IT_LANDMARK) { LLSD key; @@ -406,10 +392,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); @@ -450,14 +433,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 @@ -474,10 +457,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); @@ -490,6 +473,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(cur_item->getViewModelItem()); + } + return NULL; +} + + LLFolderViewItem* LLLandmarksPanel::selectItemInAccordionTab(LLPlacesInventoryPanel* inventory_list, const std::string& tab_name, const LLUUID& obj_id, @@ -500,7 +494,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; @@ -542,12 +536,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)); } } @@ -577,7 +571,7 @@ void LLLandmarksPanel::initFavoritesInventoryPanel() mFavoritesInventoryPanel = getChild("favorites_list"); initLandmarksPanel(mFavoritesInventoryPanel); - mFavoritesInventoryPanel->getFilter()->setEmptyLookupMessage("FavoritesNoMatchingItems"); + mFavoritesInventoryPanel->getFilter().setEmptyLookupMessage("FavoritesNoMatchingItems"); initAccordion("tab_favorites", mFavoritesInventoryPanel, true); } @@ -588,12 +582,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)); @@ -617,7 +606,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); @@ -629,12 +618,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)); @@ -699,20 +683,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(); } } @@ -765,14 +749,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; @@ -810,6 +789,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) { @@ -842,24 +824,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 (folder_bridge), LLSD( "category"), gInventory.findCategoryUUIDForType( LLFolderType::FT_LANDMARK)); @@ -867,7 +849,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) @@ -885,9 +867,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) { @@ -899,7 +881,7 @@ void LLLandmarksPanel::onClipboardAction(const LLSD& userdata) const } else { - mCurrentSelectedList->getRootFolder()->doToSelected(mCurrentSelectedList->getModel(),command_name); + mCurrentSelectedList->doToSelected(command_name); } } @@ -944,7 +926,7 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata) { if(mCurrentSelectedList) { - mCurrentSelectedList->getRootFolder()->doToSelected(&gInventory, userdata); + mCurrentSelectedList->doToSelected(userdata); } } } @@ -966,8 +948,9 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { std::string command_name = userdata.asString(); - LLPlacesFolderView* root_folder_view = mCurrentSelectedList ? - static_cast(mCurrentSelectedList->getRootFolder()) : NULL; + LLFolderView* root_folder_view = mCurrentSelectedList + ? mCurrentSelectedList->getRootFolder() + : NULL; if ("collapse_all" == command_name) { @@ -1028,18 +1011,13 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (!root_folder_view) return false; - std::set selected_uuids = root_folder_view->getSelectionList(); + std::set selected_uuids = root_folder_view->getSelectionList(); // Allow to execute the command only if it can be applied to all selected items. - for (std::set::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set::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; @@ -1063,10 +1041,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(cur_item->getInventoryObject()); if (!inv_item) return false; LLUUID asset_uuid = inv_item->getAssetUUID(); @@ -1100,7 +1078,7 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const { if (mCurrentSelectedList) { - std::set selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); + std::set selection = mCurrentSelectedList->getRootFolder()->getSelectionList(); if (!selection.empty()) { return ( 1 == selection.size() && !LLAgentPicksInfo::getInstance()->isPickLimitReached() ); @@ -1162,27 +1140,23 @@ void LLLandmarksPanel::onMenuVisibilityChange(LLUICtrl* ctrl, const LLSD& param) { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - std::set selected_uuids = root_folder_view->getSelectionList(); + std::set 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::const_iterator iter = selected_uuids.begin(); iter != selected_uuids.end(); ++iter) + for (std::set::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(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. @@ -1221,7 +1195,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(item->getViewModelItem())->getInventoryType() == LLInventoryType::IT_LANDMARK) { can_be_modified = true; @@ -1259,7 +1233,7 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold if (can_be_modified) { - LLFolderViewEventListener* listenerp = item->getListener(); + LLFolderViewModelItemInventory* listenerp = static_cast(item->getViewModelItem()); if ("cut" == command_name) { @@ -1319,8 +1293,9 @@ bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType carg LLInventoryItem* item = static_cast(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) { @@ -1448,7 +1423,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 @@ -1462,7 +1437,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 c6c267d4e1..3b2fca3934 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -48,12 +48,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" // #include "lltrans.h" @@ -120,7 +122,7 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p) //mCommitCallbackRegistrar.add("Inventory.NewWindow", boost::bind(&LLPanelMainInventory::newWindow, this)); mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this)); mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this)); - mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars)); + mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); // ## Zi: Filter Links Menu mCommitCallbackRegistrar.add("Inventory.FilterLinks.Set", boost::bind(&LLPanelMainInventory::onFilterLinksChecked, this, _2)); @@ -182,7 +184,7 @@ BOOL LLPanelMainInventory::postBuild() mFilterTabs = getChild("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("All Items"); @@ -190,7 +192,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; @@ -204,7 +206,7 @@ BOOL LLPanelMainInventory::postBuild() recent_items_panel->setSortOrder(gSavedSettings.getU32(LLInventoryPanel::RECENTITEMS_SORT_ORDER)); // 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)); } @@ -246,11 +248,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); } } @@ -321,24 +326,28 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) LLInventoryPanel* all_items_panel = getChild("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("Recent Items"); - if (recent_items_panel) + LLInventoryPanel* panel = findChild("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; } } @@ -411,7 +420,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() @@ -433,13 +442,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(); @@ -595,7 +604,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); @@ -753,15 +762,15 @@ void LLPanelMainInventory::onFilterSelected() if (!gSavedSettings.getBOOL("FSSplitInventorySearchOverTabs")) { setFilterSubString(mFilterSubString); + LLInventoryFilter& filter = mActivePanel->getFilter(); // CHUI Merge new, check location } // Seperate search for inventory tabs from Satomi Ahn (FIRE-913 & FIRE-6862) - 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(); @@ -892,6 +901,8 @@ void LLPanelMainInventory::onFocusReceived() void LLPanelMainInventory::setFilterTextFromFilter() { + //mFilterText = mActivePanel->getFilter()->getFilterText(); CHUI Merge old + //mFilterText = mActivePanel->getFilter().getFilterText(); CHUI Merge new // ## Zi: Filter dropdown // this method gets called by the filter subwindow (once every frame), so we update our combo box here LLInventoryFilter* filter=mActivePanel->getFilter(); @@ -947,7 +958,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(); @@ -1273,7 +1284,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) @@ -1284,7 +1295,7 @@ void LLPanelMainInventory::saveTexture(const LLSD& userdata) return; } - const LLUUID& item_id = current_item->getListener()->getUUID(); + const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance("preview_texture", LLSD(item_id), TAKE_FOCUS_YES); if (preview_texture) { @@ -1361,7 +1372,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - const LLUUID item_id = current_item->getListener()->getUUID(); + const LLUUID item_id = static_cast(current_item->getViewModelItem())->getUUID(); LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item) { @@ -1376,7 +1387,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) { return; } - current_item->getListener()->performAction(getActivePanel()->getModel(), "goto"); + static_cast(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); } if (command_name == "find_links") @@ -1386,17 +1397,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(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); } } @@ -1405,11 +1416,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(static_cast(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(current_item->getViewModelItem())->getInventoryType(); return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT); } } @@ -1421,28 +1432,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 selection_set = root->getSelectionList(); - if (selection_set.empty()) return FALSE; - for (std::set::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") { @@ -1452,7 +1442,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(current_item->getViewModelItem())->getUUID(); const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsLinkType() && !item->getIsBrokenLink()) { @@ -1464,11 +1454,11 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "find_links") { LLFolderView* root = getActivePanel()->getRootFolder(); - std::set selection_set = root->getSelectionList(); + std::set 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(current_item->getViewModelItem())->getUUID(); const LLInventoryObject *obj = gInventory.getObject(item_id); if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType())) { @@ -1481,7 +1471,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(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 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(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(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(item->getViewModelItem()); + LLFolderBridge * bridge = static_cast(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(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(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 @@ -46,9 +45,6 @@ public: LLInboxInventoryPanel(const Params& p); ~LLInboxInventoryPanel(); - // virtual - void buildFolderView(const LLInventoryPanel::Params& params); - // virtual LLFolderViewFolder * createFolderViewFolder(LLInvFVBridge * bridge); LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); @@ -63,13 +59,13 @@ public: Optional 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 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 r1("outbox_inventory_panel"); -static LLDefaultChildRegistry::Register 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(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(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() {} - }; - - 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() {} - }; - - 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/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 73b9ca64d9..f61663b30a 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -481,6 +481,14 @@ void LLPanelObject::getState( ) enable_rotate = FALSE; } +// [RLVa:KB] - Checked: 2010-03-31 (RLVa-1.2.0c) | Modified: RLVa-1.0.0g + if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SITTP))) ) + { + if ( (isAgentAvatarValid()) && (gAgentAvatarp->isSitting()) && (gAgentAvatarp->getRoot() == objectp->getRootEdit()) ) + enable_move = enable_scale = enable_rotate = FALSE; + } +// [/RLVa:KB] + // [RLVa:KB] - Checked: 2010-03-31 (RLVa-1.2.0c) | Modified: RLVa-1.0.0g if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_UNSIT)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SITTP))) ) { diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index b34a677348..35223f2451 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -71,17 +71,20 @@ #include "rlvlocks.h" // [/RLVa:KB] +const LLColor4U DEFAULT_WHITE(255, 255, 255); #include "tea.h" // + ///---------------------------------------------------------------------------- /// 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; @@ -107,26 +110,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& batch); - virtual void move(LLFolderViewEventListener* parent_listener); + virtual void removeBatch(std::vector& batch); + virtual void move(LLFolderViewModelItem* parent_listener); virtual BOOL isItemCopyable() const; virtual BOOL copyToClipboard() const; virtual BOOL cutToClipboard() const; @@ -136,11 +142,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, @@ -152,7 +162,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), @@ -337,15 +348,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); @@ -359,11 +382,6 @@ void LLTaskInvFVBridge::openItem() lldebugs << "LLTaskInvFVBridge::openItem()" << llendl; } -void LLTaskInvFVBridge::previewItem() -{ - openItem(); -} - BOOL LLTaskInvFVBridge::isItemRenameable() const { // [RLVa:KB] - Checked: 2010-09-28 (RLVa-1.2.1f) | Modified: RLVa-1.0.5a @@ -374,6 +392,14 @@ BOOL LLTaskInvFVBridge::isItemRenameable() const } // [/RLVa:KB] +// [RLVa:KB] - Checked: 2010-09-28 (RLVa-1.2.1f) | Modified: RLVa-1.0.5a +// LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); + if ( (rlv_handler_t::isEnabled()) && (object) && (gRlvAttachmentLocks.isLockedAttachment(object->getRootEdit())) ) + { + return FALSE; + } +// [/RLVa:KB] + if(gAgent.isGodlike()) return TRUE; // LLViewerObject* object = gObjectList.findObject(mPanel->getTaskUUID()); if(object) @@ -523,7 +549,7 @@ BOOL LLTaskInvFVBridge::removeItem() return FALSE; } -void LLTaskInvFVBridge::removeBatch(LLDynamicArray& batch) +void LLTaskInvFVBridge::removeBatch(std::vector& batch) { if (!mPanel) { @@ -563,7 +589,7 @@ void LLTaskInvFVBridge::removeBatch(LLDynamicArray& } } -void LLTaskInvFVBridge::move(LLFolderViewEventListener* parent_listener) +void LLTaskInvFVBridge::move(LLFolderViewModelItem* parent_listener) { } @@ -800,7 +826,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, @@ -808,6 +834,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( @@ -830,15 +857,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; @@ -866,7 +885,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. @@ -1600,7 +1619,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 @@ -1625,7 +1644,7 @@ BOOL LLPanelObjectInventory::postBuild() void LLPanelObjectInventory::doToSelected(const LLSD& userdata) { - mFolders->doToSelected(&gInventory, userdata); + LLInventoryAction::doToSelected(&gInventory, mFolders, userdata.asString()); } void LLPanelObjectInventory::clearContents() @@ -1637,6 +1656,8 @@ void LLPanelObjectInventory::clearContents() LLToolDragAndDrop::getInstance()->endDrag(); } + clearItemIDs(); + if( mScroller ) { // removes mFolders @@ -1652,21 +1673,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(p); - // this ensures that we never say "searching..." or "no items found" - mFolders->getFilter()->setShowFolderState(LLInventoryFilter::SHOW_ALL_FOLDERS); + mFolders->setCallbackRegistrar(&mCommitCallbackRegistrar); if (hasFocus()) @@ -1711,7 +1737,7 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, iter != inventory->end(); ) { LLInventoryObject* item = *iter++; - LLFloaterProperties* floater = LLFloaterReg::findTypedInstance("properites", item->getUUID()); + LLFloaterProperties* floater = LLFloaterReg::findTypedInstance("properties", item->getUUID()); if(floater) { floater->refresh(); @@ -1726,15 +1752,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 selected_items; + std::vector selected_item_ids; + std::set 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::iterator it = selected_items.begin(), end_it = selected_items.end(); + it != end_it; + ++it) + { + selected_item_ids.push_back(static_cast((*it)->getViewModelItem())->getUUID()); + } LLViewerObject* objectp = gObjectList.findObject(mTaskUUID); if (objectp) @@ -1742,19 +1773,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 { @@ -1764,11 +1797,12 @@ void LLPanelObjectInventory::updateInventory() } // restore previous selection - std::set::iterator selection_it; - BOOL first_item = TRUE; - for (selection_it = selected_items.begin(); selection_it != selected_items.end(); ++selection_it) + std::vector::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 @@ -1784,7 +1818,10 @@ void LLPanelObjectInventory::updateInventory() } } - mFolders->requestArrange(); + if (mFolders) + { + mFolders->requestArrange(); + } mInventoryNeedsUpdate = FALSE; // Edit menu handler is set in onFocusReceived } @@ -1805,19 +1842,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(p); - new_folder->addToFolder(mFolders, mFolders); + p.font_color = item_color; + p.font_highlight_color = item_color; + + LLFolderViewFolder* new_folder = LLUICtrlFactory::create(p); + new_folder->addToFolder(mFolders); new_folder->toggleOpen(); - createViewsForCategory(&contents, inventory_root, new_folder); + if (!contents.empty()) + { + createViewsForCategory(&contents, inventory_root, new_folder); + } } } @@ -1827,6 +1869,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 child_categories; LLTaskInvFVBridge* bridge; @@ -1849,11 +1893,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(p); child_categories.put(new obj_folder_pair(obj, (LLFolderViewFolder*)view)); @@ -1862,15 +1906,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 (params); } - view->addToFolder(folder, mFolders); + view->addToFolder(folder); + addItemID(obj->getUUID(), view); } } @@ -1938,6 +1984,7 @@ void LLPanelObjectInventory::refresh() removeVOInventoryListener(); clearContents(); } + mInventoryViewModel.setTaskID(mTaskUUID); //llinfos << "LLPanelObjectInventory::refresh() " << mTaskUUID << llendl; } @@ -2025,7 +2072,10 @@ void LLPanelObjectInventory::idle(void* user_data) { LLPanelObjectInventory* self = (LLPanelObjectInventory*)user_data; - + if (self->mFolders) + { + self->mFolders->update(); + } if (self->mInventoryNeedsUpdate) { self->updateInventory(); @@ -2050,3 +2100,32 @@ void LLPanelObjectInventory::onFocusReceived() LLPanel::onFocusReceived(); } + + +LLFolderViewItem* LLPanelObjectInventory::getItemByID( const LLUUID& id ) +{ + std::map::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 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 7771796b30..64f9bb6036 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -172,7 +172,7 @@ public: return menu; } - + private: static void onCreate(const LLSD& param) { @@ -189,11 +189,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) { @@ -206,11 +203,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(p, parent); } } @@ -279,7 +272,7 @@ private: if (inventory_panel->getVisible()) { - inventory_panel->setSortOrder(sort_order); + inventory_panel->getFolderViewModel()->setSorter(sort_order); } else { @@ -751,7 +744,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); @@ -898,13 +891,13 @@ LLPanelOutfitEdit::selection_info_t LLPanelOutfitEdit::getAddMorePanelSelectionT { if (mInventoryItemsPanel != NULL && mInventoryItemsPanel->getVisible()) { - std::set selected_uuids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set 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((*selected_items.begin())->getViewModelItem())->getUUID()); } } else if (mWearableItemsList != NULL && mWearableItemsList->getVisible()) @@ -1323,7 +1316,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(curr_item->getViewModelItem()); if (!listenerp) return; selected_id = listenerp->getUUID(); @@ -1340,9 +1333,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 item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); - - std::for_each(item_set.begin(), item_set.end(), boost::bind( tmp, &uuid_list, _1)); + std::set item_set = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + for (std::set::iterator it = item_set.begin(), end_it = item_set.end(); + it != end_it; + ++it) + { + uuid_list.push_back(static_cast((*it)->getViewModelItem())->getUUID()); + } } else if (mWearablesListViewPanel->getVisible()) { @@ -1387,13 +1384,13 @@ void LLPanelOutfitEdit::saveListSelection() { if(mWearablesListViewPanel->getVisible()) { - std::set selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); + std::set selected_ids = mInventoryItemsPanel->getRootFolder()->getSelectionList(); if(!selected_ids.size()) return; - for (std::set::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) + for (std::set::const_iterator item_id = selected_ids.begin(); item_id != selected_ids.end(); ++item_id) { - mWearableItemsList->selectItemByUUID(*item_id, true); + mWearableItemsList->selectItemByUUID(static_cast((*item_id)->getViewModelItem())->getUUID(), true); } mWearableItemsList->scrollToShowFirstSelectedItem(); } @@ -1411,7 +1408,7 @@ void LLPanelOutfitEdit::saveListSelection() for(std::vector::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 47e6b88d60..d73f53c386 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -64,6 +64,9 @@ #include "llvoiceclient.h" #include "llworld.h" #include "llspeakers.h" +// [RLVa:KB] - Checked: 2010-06-04 (RLVa-1.2.2a) +#include "rlvhandler.h" +// [/RLVa:KB] // Firestorm includes #include "fscontactsfloater.h" @@ -82,6 +85,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"; @@ -505,19 +509,12 @@ 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), // Firestorm radar //mMiniMap(NULL) mMiniMap(NULL), @@ -529,7 +526,25 @@ LLPanelPeople::LLPanelPeople() //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() @@ -547,13 +562,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) @@ -573,17 +581,31 @@ void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LL } } + +void LLPanelPeople::removePicker() +{ + if(mPicker.get()) + { + mPicker.get()->closeFloater(); + } +} + BOOL LLPanelPeople::postBuild() { - mFilterEditor = getChild("filter_input"); - mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); + getChild("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); mTabContainer = getChild("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); + mSavedFilters.resize(mTabContainer->getTabCount()); + mSavedOriginalFilters.resize(mTabContainer->getTabCount()); LLPanel* friends_tab = getChild(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("avatars_online"); mAllFriendList = friends_tab->getChild("avatars_all"); mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online")); @@ -641,25 +663,18 @@ BOOL LLPanelPeople::postBuild() //mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu); mRadarList->setContextMenu(&FSFloaterRadarMenu::gFSRadarMenu); // - 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); // Firestorm radar //setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); - LLPanel* groups_panel = getChild(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(FRIENDS_TAB_NAME); - friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); - friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); // Firestorm radar friends_panel->childSetAction("GlobalOnlineStatusToggle", boost::bind(&LLPanelPeople::onGlobalVisToggleButtonClicked, this)); - mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1)); // Firestorm radar @@ -686,6 +701,19 @@ BOOL LLPanelPeople::postBuild() mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this)); mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this)); + LLMenuButton* groups_gear_btn = getChild("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("tab_all"); accordion_tab->setDropDownStateChangedCallback( boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList)); @@ -694,82 +722,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)); - // Firestorm radar - registrar.add("Radar.Gear.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, 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)); - - // Firestorm radar options - registrar.add("Radar.NameFmt", boost::bind(&FSRadar::onRadarNameFmtClicked, _2)); - registrar.add("Radar.ReportTo", boost::bind(&FSRadar::onRadarReportToClicked, _2)); // Milkshake-style Radar Alerts - enable_registrar.add("Radar.NameFmtCheck", boost::bind(&FSRadar::radarNameFmtCheck, _2)); - enable_registrar.add("Radar.ReportToCheck", boost::bind(&FSRadar::radarReportToCheck, _2)); // Milkshake-style Radar Alerts - // - - mNearbyGearButton = getChild("nearby_view_sort_btn"); - mFriendsGearButton = getChild("friends_viewsort_btn"); - mGroupsGearButton = getChild("groups_viewsort_btn"); - mRecentGearButton = getChild("recent_viewsort_btn"); - - LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGroupPlusMenuHandle = plus_menu->getHandle(); - - // Use Firestorm gear menu - //LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - LLToggleableMenu* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile("menu_fs_radar_gear.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("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("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("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 @@ -807,9 +762,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)); } } @@ -893,31 +850,12 @@ 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("button_bar")->getChild(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("button_bar")->getChild(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("button_bar")->getChild(btn_name); - button->setClickedCallback(cb); -} - void LLPanelPeople::updateButtons() { std::string cur_tab = getActiveTabName(); - bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); +// [RLVa:KB] - Checked: 2013-05-06 (RLVa-1.4.9) + bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); +// [/RLVa:KB] 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); @@ -931,88 +869,62 @@ 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); - // Firestorm changes - //buttonSetVisible("teleport_btn", friends_tab_active); - buttonSetVisible("teleport_btn", nearby_tab_active || friends_tab_active); - // Firestorm changes - 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("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected // groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected // [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.4.1a) | Added: RLVa-1.3.0f - groups_panel->getChildView("activate_btn")->setEnabled( - item_selected && !cur_group_active && !gRlvHandler.hasBehaviour(RLV_BHVR_SETGROUP)); // "none" or a non-active group selected +// groups_panel->getChildView("activate_btn")->setEnabled( +// item_selected && !cur_group_active && !gRlvHandler.hasBehaviour(RLV_BHVR_SETGROUP)); // "none" or a non-active group selected // [/RLVa:KB] - groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); } 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) { + if (cur_panel->hasChild("add_friend_btn", TRUE)) + cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend && !is_self); + // cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend); // [RLVa:KB] - Checked: 2010-07-20 (RLVa-1.2.2a) | Added: RLVa-1.2.0h - if (nearby_tab_active) - { - cur_panel->getChildView("add_friend_btn_nearby")->setEnabled(!is_friend && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)); - } - else if (recent_tab_active) - { - cur_panel->getChildView("add_friend_btn_recent")->setEnabled(!is_friend); - } - +// cur_panel->getChildView("add_friend_btn")->setEnabled( +// !is_friend && ((!nearby_tab_active) || (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)))); // [/RLBa:KB] 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(); - // [RLVa:KB] - Checked: 2010-06-04 (RLVa-1.2.2a) | Modified: RLVa-1.2.0d if ( (nearby_tab_active) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ) { item_selected = multiple_selected = false; } // [/RLBa:KB] - - 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) */ ); // LO - Dont block the TP button at all. - - 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 @@ -1058,6 +970,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; } @@ -1088,6 +1003,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"); @@ -1158,52 +1075,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. - // Changed for FS specific radar - // mNearbyList->setNameFilter(mFilterSubStringOrig); - mRadarList->setFilterString(mFilterSubStringOrig); - // Changed for FS specific radar - 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) { @@ -1226,10 +1151,6 @@ void LLPanelPeople::onTabSelected(const LLSD& param) } // - if (GROUP_TAB_NAME == tab_name) - mFilterEditor->setLabel(getString("groups_filter_label")); - else - mFilterEditor->setLabel(getString("people_filter_label")); } void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) @@ -1241,6 +1162,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); @@ -1281,12 +1206,6 @@ void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) updateButtons(); } -void LLPanelPeople::onViewProfileButtonClicked() -{ - LLUUID id = getCurrentItemID(); - LLAvatarActions::showProfile(id); -} - void LLPanelPeople::onAddFriendButtonClicked() { LLUUID id = getCurrentItemID(); @@ -1314,8 +1233,12 @@ bool LLPanelPeople::isItemsFreeOfFriends(const uuid_vec_t& uuids) void LLPanelPeople::onAddFriendWizButtonClicked() { + LLPanel* cur_panel = mTabContainer->getCurrentPanel(); + LLView * button = cur_panel->findChild("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; @@ -1323,11 +1246,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() @@ -1345,11 +1270,6 @@ void LLPanelPeople::onDeleteFriendButtonClicked() } } -void LLPanelPeople::onGroupInfoButtonClicked() -{ - LLGroupActions::show(getCurrentItemID()); -} - void LLPanelPeople::onChatButtonClicked() { LLUUID group_id = getCurrentItemID(); @@ -1357,6 +1277,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; @@ -1373,11 +1304,6 @@ void LLPanelPeople::onImButtonClicked() } } -void LLPanelPeople::onActivateButtonClicked() -{ - LLGroupActions::activate(mGroupList->getSelectedUUID()); -} - // static void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector names) { @@ -1385,19 +1311,15 @@ void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vectorshowPermissions(show_permissions); mOnlineFriendList->showPermissions(show_permissions); } - else if (chosen_item == "panel_block_list_sidetray") - { - // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterReg::showInstance("fs_blocklist", LLSD()); - } - else - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } - // - } } void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata) @@ -1489,22 +1397,8 @@ void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) //{ // setSortOrder(mNearbyList, E_SORT_BY_DISTANCE); //} - //else if (chosen_item == "panel_block_list_sidetray") - if (chosen_item == "panel_block_list_sidetray") // Firestorm radar - { - // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterReg::showInstance("fs_blocklist", LLSD()); - } - else - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } - // - } + } bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata) @@ -1538,20 +1432,6 @@ void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { mRecentList->toggleIcons(); } - else if (chosen_item == "panel_block_list_sidetray") - { - // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterReg::showInstance("fs_blocklist", LLSD()); - } - else - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } - // - } } bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata) @@ -1580,43 +1460,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); - // Firestorm change - //LLAvatarActions::offerTeleport(selected_uuids); - LLAvatarActions::offerTeleport(LLAvatarActions::canOfferTeleport(selected_uuids)); - // Firestorm change -} - -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 01aed84839..a3cdf359c7 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -80,6 +80,8 @@ private: E_SORT_BY_RECENT_SPEAKERS = 4, } ESortOrder; + void removePicker(); + // methods indirectly called by the updaters void updateFriendListHelpText(); void updateFriendList(); @@ -92,31 +94,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); @@ -125,8 +118,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); @@ -152,7 +143,6 @@ private: void onGlobalVisToggleButtonClicked(); // Firestorm callback handler - LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; LLAvatarList* mOnlineFriendList; LLAvatarList* mAllFriendList; @@ -164,11 +154,8 @@ private: LLGroupList* mGroupList; LLNetMap* mMiniMap; - LLHandle mGroupPlusMenuHandle; - LLHandle mNearbyViewSortMenuHandle; - LLHandle mFriendsViewSortMenuHandle; - LLHandle mGroupsViewSortMenuHandle; - LLHandle mRecentViewSortMenuHandle; + std::vector mSavedOriginalFilters; + std::vector mSavedFilters; Updater* mFriendListUpdater; // Firestorm radar @@ -176,14 +163,7 @@ private: // Firestorm radar Updater* mRecentListUpdater; Updater* mButtonsUpdater; - - LLMenuButton* mNearbyGearButton; - LLMenuButton* mFriendsGearButton; - LLMenuButton* mGroupsGearButton; - LLMenuButton* mRecentGearButton; - - std::string mFilterSubString; - std::string mFilterSubStringOrig; + LLHandle< LLFloater > mPicker; // Firestorm radar boost::signals2::connection mNearbyUpdateSignalConnection; diff --git a/indra/newview/llpanelpeoplemenus.cpp b/indra/newview/llpanelpeoplemenus.cpp index a224ace582..2471d09f6a 100644 --- a/indra/newview/llpanelpeoplemenus.cpp +++ b/indra/newview/llpanelpeoplemenus.cpp @@ -37,21 +37,26 @@ #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" #include "rlvhandler.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 ) { @@ -63,40 +68,88 @@ 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)); // Firestorm additions registrar.add("Avatar.GroupInvite", boost::bind(&LLAvatarActions::inviteToGroup, id)); - enable_registrar.add("Avatar.EnableItem", boost::bind(&NearbyMenu::enableContextMenuItem, this, _2)); - enable_registrar.add("Avatar.CheckItem", boost::bind(&NearbyMenu::checkContextMenuItem, this, _2)); + 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; } -bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) +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 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 @@ -163,6 +216,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(); @@ -184,10 +243,19 @@ bool NearbyMenu::enableContextMenuItem(const LLSD& userdata) return (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWINV)); } // + 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(); @@ -200,11 +268,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 2d0f1bea94..35efaa721f 100644 --- a/indra/newview/llpanelplaceprofile.cpp +++ b/indra/newview/llpanelplaceprofile.cpp @@ -516,9 +516,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()) @@ -540,9 +538,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 899011c143..e9fd1c29dc 100644 --- a/indra/newview/llpaneltopinfobar.cpp +++ b/indra/newview/llpaneltopinfobar.cpp @@ -237,7 +237,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/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index a4f3aeebe6..6efc635e42 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,40 +26,20 @@ #include "llviewerprecompiledheaders.h" -// common includes -#include "lltrans.h" -#include "llavataractions.h" -#include "llagent.h" +#include "llavatarnamecache.h" // [SL:KB] - Patch: Chat-GroupSessionEject | Checked: 2012-02-04 (Catznip-3.2.1) #include "llgroupactions.h" // [/SL:KB] #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(avatar_list->getItemByValue(avatar_uuid)); - if (item) - { - LLOutputMonitorCtrl* indicator = item->getChild("speaking_indicator"); - indicator->setIsMuted(is_muted); - } -} - - // See EXT-4301. /** * class LLAvalineUpdater - observe the list of voice participants in session and check @@ -199,15 +179,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) { @@ -218,36 +192,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; @@ -266,142 +220,36 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, mModeratorToRemoveList.insert(speakerp->mID); } } - // we need to exclude agent id for non group chat - sort(); + + // Identify and store what kind of session we are + LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(data_source->getSessionID()); + if (im_session) + { + // By default, sessions that can't be identified as group or ad-hoc will be considered P2P (i.e. 1 on 1) + mConvType = CONV_SESSION_1_ON_1; + if (im_session->isAdHocSessionType()) + { + mConvType = CONV_SESSION_AD_HOC; + } + else if (im_session->isGroupSessionType()) + { + mConvType = CONV_SESSION_GROUP; + item->setState(LLAvatarListItem::IS_ONLINE); + } + } + else + { + // That's the only session that doesn't get listed in the LLIMModel as a session... + mConvType = CONV_SESSION_NEARBY; + item->setState(LLAvatarListItem::IS_GROUPMOD); + } } 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(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(ctrl); - if (list) - { - const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); - const std::size_t moderator_indicator_len = moderator_indicator.length(); - - // Firstly remove moderators indicator - std::set::const_iterator - moderator_list_it = mModeratorToRemoveList.begin(), - moderator_list_end = mModeratorToRemoveList.end(); - for (;moderator_list_it != moderator_list_end; ++moderator_list_it) - { - LLAvatarListItem* item = dynamic_cast (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); - } - item->setState(LLAvatarListItem::IS_ONLINE); - } - } - - 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 (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); - } - item->setState(LLAvatarListItem::IS_GROUPMOD); - } - } - - // update voice mute state of all items. See EXT-7235 - LLSpeakerMgr::speaker_list_t speaker_list; - - // Use also participants which are not in voice session now (the second arg is TRUE). - // They can already have mModeratorMutedVoice set from the previous voice session - // and LLSpeakerVoiceModerationEvent will not be sent when speaker manager is updated next time. - mSpeakerMgr->getSpeakerList(&speaker_list, TRUE); - for(LLSpeakerMgr::speaker_list_t::iterator it = speaker_list.begin(); it != speaker_list.end(); it++) - { - const LLPointer& speakerp = *it; - - if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - update_speaker_indicator(list, speakerp->mID, speakerp->mModeratorMutedVoice); - } - } - } -} - /* Seems this method is not necessary after onAvalineCallerRemoved was implemented; @@ -415,31 +263,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); } @@ -451,23 +279,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; @@ -476,19 +287,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 event, const LLSD& userdata) @@ -501,27 +299,34 @@ bool LLParticipantList::onAddItemEvent(LLPointer event, co } addAvatarIDExceptAgent(uu_id); - sort(); return true; } bool LLParticipantList::onRemoveItemEvent(LLPointer 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 event, const LLSD& userdata) { - uuid_vec_t& group_members = mAvatarList->getIDs(); - group_members.clear(); - mAvatarList->setDirty(); + clearParticipants(); + return true; +} + +bool LLParticipantList::onSpeakerUpdateEvent(LLPointer 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; } @@ -545,9 +350,7 @@ bool LLParticipantList::onModeratorUpdateEvent(LLPointer 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; @@ -561,60 +364,45 @@ bool LLParticipantList::onSpeakerMuteEvent(LLPointer 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() -{ - if ( !mAvatarList ) - return; - - switch ( getSortOrder() ) - { - 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; - } -} - void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) { - if (mExcludeAgent && gAgent.getID() == avatar_id) return; - if (mAvatarList->contains(avatar_id)) return; + // 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)) + { + 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); } @@ -633,12 +421,12 @@ void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer 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 speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id); - if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT) + if (speaker.isNull() || (speaker->mType == LLSpeaker::SPEAKER_OBJECT)) { return false; } @@ -661,6 +449,14 @@ bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + return mParent.onSpeakerUpdateEvent(event, userdata); +} + // // LLParticipantList::SpeakerModeratorListener // @@ -674,389 +470,4 @@ bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointergetSessionID(), mUUIDs.front())); -// [SL:KB] - - 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); - // Hide SortBy separator - main_menu->setItemVisible("Sort Separator", is_sort_visible); - // - 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(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 speakerp = mParent.mSpeakerMgr->findSpeaker(speaker_id); - if (speakerp.isNull()) - { - LL_WARNS("Speakers") << "Speaker " << speaker_id << " not found" << llendl; - return; - } - LLAvatarListItem* item = dynamic_cast(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 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(mParent.mSpeakerMgr); - if (mgr) - { - mgr->moderateVoiceParticipant(avatar_id, unmute); - } -} - -void LLParticipantList::LLParticipantListMenu::moderateVoiceAllParticipants(bool unmute) -{ - LLIMSpeakerMgr* mgr = dynamic_cast(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 ( - 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; - } -// [SL:KB] - Patch: Chat-GroupSessionEject | Checked: 2012-02-04 (Catznip-3.2.1) | Added: Catznip-3.2.1 - else if (item == "can_eject") - { - return LLGroupActions::canEjectFromGroup(mParent.mSpeakerMgr->getSessionID(), mUUIDs.front()); - } -// [/SL:KB] - - 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 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 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 lhs = mParent.mSpeakerMgr->findSpeaker(avatar_item1->getAvatarId()); - LLPointer 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 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 @@ -65,12 +51,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. */ @@ -93,13 +73,9 @@ protected: bool onRemoveItemEvent(LLPointer event, const LLSD& userdata); bool onClearListEvent(LLPointer event, const LLSD& userdata); bool onModeratorUpdateEvent(LLPointer event, const LLSD& userdata); + bool onSpeakerUpdateEvent(LLPointer event, const LLSD& userdata); bool onSpeakerMuteEvent(LLPointer 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 @@ -134,6 +110,13 @@ protected: /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); }; + class SpeakerUpdateListener : public BaseSpeakerListener + { + public: + SpeakerUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + class SpeakerModeratorUpdateListener : public BaseSpeakerListener { public: @@ -149,98 +132,7 @@ protected: /*virtual*/ bool handleEvent(LLPointer 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 mModeratorList; std::set mModeratorToRemoveList; @@ -262,25 +151,10 @@ private: LLPointer mSpeakerAddListener; LLPointer mSpeakerRemoveListener; LLPointer mSpeakerClearListener; + LLPointer mSpeakerUpdateListener; LLPointer mSpeakerModeratorListener; LLPointer 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 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..666f10df96 --- /dev/null +++ b/indra/newview/llpersistentnotificationstorage.cpp @@ -0,0 +1,149 @@ +/** +* @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() + , 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 history_channel = boost::dynamic_pointer_cast(LLNotifications::instance().getChannel("Persistent")); + if (!history_channel) + { + return; + } + + LLSD output = LLSD::emptyMap(); + LLSD& data = output["data"]; + + for ( std::vector::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); + + LL_INFOS("LLPersistentNotificationStorage") << "start loading notifications" << LL_ENDL; + + 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(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()); + } + } + + LL_INFOS("LLPersistentNotificationStorage") << "finished loading notifications" << LL_ENDL; +} + +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, 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(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 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() + {} + }; + + 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 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 > 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 items; + std::vector disabled_items; + + LLInventoryPanel* inv_panel = mInventoryPanel.get(); + bool is_open = false; + if (inv_panel) { - std::vector items; - std::vector disabled_items; - - LLInventoryPanel* inv_panel = mInventoryPanel.get(); - bool is_open = false; - if (inv_panel) - { - LLFolderViewFolder* folder = dynamic_cast(inv_panel->getRootFolder()->getItemByID(mUUID)); - is_open = (NULL != folder) && folder->isOpen(); - } - - // 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::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 - - // repeat parent functionality - sSelf = getHandle(); // necessary for "New Folder" functionality - - hide_context_entries(menu, items, disabled_items); + LLFolderViewFolder* folder = dynamic_cast(inv_panel->getItemByID(mUUID)); + is_open = (NULL != folder) && folder->isOpen(); } + + // 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::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 + + // repeat parent functionality + sSelf = getHandle(); // necessary for "New Folder" functionality + + 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(inv_panel->getRootFolder()->getItemByID(mUUID)); + folder = dynamic_cast(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 15324880a2..d2ca28ba0e 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" @@ -58,44 +59,35 @@ LLPlacesInventoryPanel::~LLPlacesInventoryPanel() delete mSavedFolderState; } -void LLPlacesInventoryPanel::buildFolderView(const LLInventoryPanel::Params& params) + +LLFolderView * LLPlacesInventoryPanel::createFolderRoot(LLUUID root_id ) { - // 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); + 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"; - 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(p); + return LLUICtrlFactory::create(p); } - // save current folder open state void LLPlacesInventoryPanel::saveFolderState() { @@ -130,76 +122,21 @@ 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); -} - -BOOL LLPlacesFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) -{ - // Don't accept anything except landmarks and folders to be dropped - // in places folder view. See STORM-296. - if (cargo_type != DAD_LANDMARK && cargo_type != DAD_CATEGORY) - { - *accept = ACCEPT_NO; - return FALSE; - } - - return LLFolderView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, - accept, tooltip_msg); -} - -void LLPlacesFolderView::setupMenuHandle(LLInventoryType::EType asset_type, LLHandle menu_handle) -{ - mMenuHandlesByInventoryType[asset_type] = menu_handle; -} - -// EOF +//} CHUI Merge error +// +//BOOL LLPlacesFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, +// EDragAndDropType cargo_type, +// void* cargo_data, +// EAcceptance* accept, +// std::string& tooltip_msg) +//{ +// // Don't accept anything except landmarks and folders to be dropped +// // in places folder view. See STORM-296. +// if (cargo_type != DAD_LANDMARK && cargo_type != DAD_CATEGORY) +// { +// *accept = ACCEPT_NO; +// return FALSE; +// } +// +// return LLFolderView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, +// accept, tooltip_msg); \ No newline at end of file diff --git a/indra/newview/llplacesinventorypanel.h b/indra/newview/llplacesinventorypanel.h index 459d599192..b2ed72a234 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,42 +56,10 @@ 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 ); - /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); - void setupMenuHandle(LLInventoryType::EType asset_type, LLHandle 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 > 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 b0c59d11df..52674955d2 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -194,6 +194,12 @@ bool LLLiveLSLFile::loadFile() return mOnChangeCallback(filename()); } +/// --------------------------------------------------------------------------- +/// LLFloaterScriptSearch +/// --------------------------------------------------------------------------- + +// Removed from FS + /// --------------------------------------------------------------------------- /// LLScriptEdCore /// --------------------------------------------------------------------------- diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index c656596412..fd98c26a26 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -151,7 +151,7 @@ BOOL LLProgressView::postBuild() getChild("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)); @@ -341,7 +341,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 7527cf827f..a8dc9aa198 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" @@ -294,12 +294,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; } @@ -412,7 +426,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()); } } @@ -451,49 +465,27 @@ 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); } redrawToasts(); } -//-------------------------------------------------------------------------- -void LLScreenChannel::removeStoredToastByNotificationID(LLUUID id) -{ - // *TODO: may be remove this function - std::vector::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::iterator it = find(mToastList.begin(), mToastList.end(), id); + LLNotificationPtr notification = LLNotifications::instance().find(id); + if (!notification) return; if( it != mToastList.end()) { @@ -506,42 +498,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::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 to_delete = findToasts(matcher); @@ -562,11 +579,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(); } @@ -732,7 +749,7 @@ void LLScreenChannel::showToastsCentre() return; } - LLRect toast_rect; + LLRect toast_rect; S32 bottom = (getRect().mTop - getRect().mBottom)/2 + toast->getRect().getHeight()/2; std::vector::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 store_tost_callback_t; - typedef boost::signals2::signal store_tost_signal_t; - store_tost_signal_t mOnStoreToast; - boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } - // signal on rejecting of a toast event - typedef boost::function reject_tost_callback_t; - typedef boost::signals2::signal reject_tost_signal_t; - reject_tost_signal_t mRejectToastSignal; boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mRejectToastSignal.connect(cb); } + typedef boost::signals2::signal 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 c5009bd50d..338bddef42 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" ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -96,7 +96,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; } @@ -243,10 +248,14 @@ void LLScriptFloater::setVisible(BOOL visible) if(!visible) { - LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(getNotificationId()); - if(chiclet) + LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel(); + if (NULL != chiclet_panelp) { - chiclet->setToggleState(false); + LLIMChiclet * chicletp = chiclet_panelp->findChiclet(getNotificationId()); + if(NULL != chicletp) + { + chicletp->setToggleState(false); + } } } } @@ -255,15 +264,19 @@ void LLScriptFloater::onMouseDown() { if(getNotificationId().notNull()) { - // Remove new message icon - LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(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(getNotificationId()); + // Remove new message icon + if (NULL == chicletp) + { + llerror("Dock chiclet for LLScriptFloater doesn't exist", 0); + } + else + { + chicletp->setShowNewMessagesIcon(false); + } } } } @@ -314,7 +327,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); + } } } @@ -323,7 +340,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); + } } } @@ -331,39 +352,41 @@ void LLScriptFloater::dockToChiclet(bool dock) { if (getDockControl() == NULL) { - LLChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(getNotificationId()); - if (chiclet == NULL) + LLChicletPanel * chiclet_panelp = LLChicletBar::getInstance()->getChicletPanel(); + if (NULL != chiclet_panelp) { - llwarns << "Dock chiclet for LLScriptFloater doesn't exist" << llendl; - return; - } - else - { - LLChicletBar::getInstance()->getChicletPanel()->scrollToChiclet(chiclet); - } + LLChiclet * chicletp = chiclet_panelp->findChiclet(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); + + // Stop saving position while we dock floater + bool save = getSavePosition(); + setSavePosition(false); // Group notices, IMs and chiclets position //setDockControl(new LLDockControl(chiclet, this, getDockTongue(), // LLDockControl::BOTTOM)); if (gSavedSettings.getBOOL("InternalShowGroupNoticesTopRight")) { - setDockControl(new LLDockControl(chiclet, this, getDockTongue(), + setDockControl(new LLDockControl(chicletp, this, getDockTongue(), // CHUI Merge LL changed chiclet to chicletp, check this is correct here LLDockControl::BOTTOM)); } else { - setDockControl(new LLDockControl(chiclet, this, getDockTongue(), + setDockControl(new LLDockControl(chicletp, this, getDockTongue(), // CHUI Merge LL changed chiclet to chicletp, check this is correct here LLDockControl::TOP)); } // Group notices, IMs and chiclets position - setDocked(dock); + setDocked(dock); - // Restore saving - setSavePosition(save); + // Restore saving + setSavePosition(save); + } } } @@ -451,11 +474,15 @@ void LLScriptFloaterManager::onAddNotification(const LLUUID& notification_id) if(it != mNotifications.end()) { - LLIMChiclet* chiclet = LLChicletBar::getInstance()->getChicletPanel()->findChiclet(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(it->first); + if(NULL != chicletp) + { + // Pass the new_message icon state further. + set_new_message = chicletp->getShowNewMessagesIcon(); + } } LLScriptFloater* floater = LLFloaterReg::findTypedInstance("script_floater", it->first); @@ -471,14 +498,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(notification_id); - } - else - { - LLChicletBar::getInstance()->getChicletPanel()->createChiclet(notification_id); + // Create inventory offer chiclet for offer type notifications + if( OBJ_GIVE_INVENTORY == obj_type ) + { + chiclet_panelp->createChiclet(notification_id); + } + else + { + chiclet_panelp->createChiclet(notification_id); + } } LLIMWellWindow::getInstance()->addObjectRow(notification_id, set_new_message); @@ -514,7 +545,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 1ab1d48014..2098e8568b 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" @@ -269,11 +269,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(); } } @@ -380,7 +380,7 @@ void LLSidepanelAppearance::toggleOutfitEditPanel(BOOL visible, BOOL disable_cam if (!disable_camera_switch) // if we're just switching between outfit and wearable editing, don't end customization. { LLVOAvatarSelf::onCustomizeEnd(disable_camera_switch); - LLAppearanceMgr::getInstance()->setOutfitDirty( FALSE ); + LLAppearanceMgr::getInstance()->updateIsDirty(); } } } @@ -415,7 +415,7 @@ void LLSidepanelAppearance::toggleWearableEditPanel(BOOL visible, LLViewerWearab { // Save changes if closing. mEditWearable->saveChanges(); - LLAppearanceMgr::getInstance()->setOutfitDirty( FALSE ); + LLAppearanceMgr::getInstance()->updateIsDirty(); if (!disable_camera_switch) // if we're just switching between outfit and wearable editing, don't end customization. { LLVOAvatarSelf::onCustomizeEnd(disable_camera_switch); @@ -458,7 +458,6 @@ void LLSidepanelAppearance::editWearable(LLViewerWearable *wearable, LLView *dat LLSidepanelAppearance *panel = dynamic_cast(data); if (panel) { - panel->showOutfitsInventoryPanel(); panel->showWearableEditPanel(wearable, disable_camera_switch); } } diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index 3cea3653a8..097702b07d 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" @@ -268,9 +269,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()) @@ -408,10 +408,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()); @@ -478,7 +478,7 @@ void LLSidepanelInventory::onInfoButtonClicked() void LLSidepanelInventory::onShareButtonClicked() { - LLAvatarActions::shareWithAvatars(); + LLAvatarActions::shareWithAvatars(this); } void LLSidepanelInventory::onShopButtonClicked() @@ -502,7 +502,7 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action) } } - current_item->getListener()->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); + static_cast(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); } void LLSidepanelInventory::onWearButtonClicked() @@ -692,7 +692,7 @@ LLInventoryItem *LLSidepanelInventory::getSelectedItem() return NULL; } } - const LLUUID &item_id = current_item->getListener()->getUUID(); + const LLUUID &item_id = static_cast(current_item->getViewModelItem())->getUUID(); LLInventoryItem *item = gInventory.getItem(item_id); return item; } @@ -701,7 +701,7 @@ U32 LLSidepanelInventory::getSelectedCount() { int count = 0; - std::set selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); + std::set selection_list = mPanelMainInventory->getActivePanel()->getRootFolder()->getSelectionList(); count += selection_list.size(); if ((count == 0) && mInboxEnabled && (mInventoryPanelInbox != NULL)) @@ -752,9 +752,9 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox) updateVerbs(); } -std::set LLSidepanelInventory::getInboxSelectionList() +std::set LLSidepanelInventory::getInboxSelectionList() { - std::set inventory_selected_uuids; + std::set inventory_selected_uuids; if (mInboxEnabled && (mInventoryPanelInbox != NULL)) { diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index 51af00979a..db1ffb3d28 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 getInboxSelectionList(); + std::set getInboxSelectionList(); void showItemInfoPanel(); void showTaskInfoPanel(); diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index e84ff37826..fb2a4da0f1 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -2748,7 +2748,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); @@ -3999,7 +3999,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 fc8cff487c..469770269a 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 remove_delay ("SpeakerParticipantRemoveDelay", 10.0); mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); @@ -263,7 +326,11 @@ LLSpeakerMgr::~LLSpeakerMgr() LLPointer 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 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_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 participants; - LLVoiceClient::getInstance()->getParticipantList(participants); - // add new participants to our list of known speakers - for (std::set::iterator participant_it = participants.begin(); - participant_it != participants.end(); - ++participant_it) + std::set participants; + LLVoiceClient::getInstance()->getParticipantList(participants); + // If we are, add all voice client participants to our list of known speakers + for (std::set::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 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"); } } @@ -717,44 +853,10 @@ void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) } } } - -class ModerationResponder : public LLHTTPClient::Responder -{ -public: - ModerationResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - +/*prep# virtual void errorWithContent(U32 status, const std::string& reason, const LLSD& content) - { llwarns << "ModerationResponder error [status:" << status << "]: " << content << 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 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 > 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 speaker_ids_t; typedef std::multimap speaking_indicators_mmap_t; @@ -93,16 +103,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 * @@ -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 64e9e85f31..bc5d04098a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -55,7 +55,8 @@ #include "llfocusmgr.h" #include "llhttpsender.h" // [FS communication UI] -//#include "llimfloater.h" +//#include "llfloaterimsession.h" CHUI Merge new +//#include "llimfloater.h" CHUI Merge old #include "fsfloaterim.h" // [FS communication UI] #include "lllocationhistory.h" @@ -66,13 +67,8 @@ #include "llmemorystream.h" #include "llmessageconfig.h" #include "llmoveview.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimcontainer.h" +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llteleporthistory.h" @@ -103,6 +99,7 @@ #include "llcallingcard.h" #include "llconsole.h" #include "llcontainerview.h" +#include "llconversationlog.h" #include "lldebugview.h" #include "lldrawable.h" #include "lleventnotifier.h" @@ -1122,6 +1119,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) @@ -1561,6 +1565,8 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); + LLConversationLog::getInstance(); + return FALSE; } @@ -1671,6 +1677,10 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, LLVoiceClient::getInstance()->updateSettings(); display_startup(); + // create a container's instance for start a controlling conversation windows + // by the voice's events + //LLFloaterIMContainer::getInstance(); CHUI Merge new in LL, looks similer to below, check if needed + // FIRE-3066: Force creation or FSFLoaterContacts here, this way it will register with LLAvatarTracker early enough. // Otherwise it is only create if isChatMultriTab() == true and LLIMFloaterContainer::getInstance is called // Moved here from llfloaternearbyvchat.cpp by Zi, to make this work even if LogShowHistory is FALSE @@ -1691,17 +1701,10 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, llinfos << "Radar initialized" << llendl; // - //gCacheName is required for nearby chat history loading - //so I just moved nearby history loading a few states further - if (gSavedPerAccountSettings.getBOOL("LogShowHistory")) - { // [FS communication UI] //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); + //FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); CHUI Merge LL removed this, needs checking // [FS communication UI] - if (nearby_chat) nearby_chat->loadHistory(); - } - display_startup(); // *Note: this is where gWorldMap used to be initialized. @@ -1812,7 +1815,7 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, } //--------------------------------------------------------------------- - // Agent Send + // World Wait //--------------------------------------------------------------------- if(STATE_WORLD_WAIT == LLStartUp::getStartupState()) { @@ -2167,6 +2170,10 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, // 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(); @@ -2522,7 +2529,6 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, } // - // 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. @@ -2546,7 +2552,7 @@ LLWorld::getInstance()->addRegion(gFirstSimHandle, gFirstSim, first_sim_size_x, // [FS communication UI] //LLIMFloater::initIMFloater(); - FSFloaterIM::initIMFloater(); + //FSFloaterIM::initIMFloater(); CHUI Merge LL removed this, check if still needed // [FS communication UI] display_startup(); @@ -3196,7 +3202,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")); // FIRE-6659: Legacy "Resident" name toggle LLCacheName::sDontTrimLegacyNames = gSavedSettings.getBOOL("DontTrimLegacyNames"); diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index dfb03cc4cd..cb6d4c608d 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -109,7 +109,6 @@ #include "llviewernetwork.h" #endif // OPENSIM // - // // Globals // diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 682ef8118e..035aa17b4f 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 @@ -179,11 +170,6 @@ void LLSysWellWindow::setVisible(BOOL visible) mChannel->updateShowToastsState(); mChannel->redrawToasts(); } - - if (visible) - { - releaseNewMessagesState(); - } } //--------------------------------------------------------------------------------- @@ -233,135 +219,12 @@ void LLSysWellWindow::reshapeWindow() } } -void LLSysWellWindow::releaseNewMessagesState() -{ - if (NULL != mSysWellChiclet) - { - mSysWellChiclet->setNewMessagesState(false); - } -} - //--------------------------------------------------------------------------------- bool LLSysWellWindow::isWindowEmpty() { return mMessageList->size() == 0; } -/************************************************************************/ -/* 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("group_chiclet"); - break; - case LLIMChiclet::TYPE_AD_HOC: - mChiclet = getChild("adhoc_chiclet"); - break; - case LLIMChiclet::TYPE_UNKNOWN: // assign mChiclet a non-null value anyway - case LLIMChiclet::TYPE_IM: - mChiclet = getChild("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("contact_name"); - contactName->setValue(name); - } - - mCloseBtn = getChild("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("contact_name"); - contactName->setValue( av_name.getCompleteName() ); -} - -//--------------------------------------------------------------------------------- -void LLIMWellWindow::RowPanel::onChicletSizeChanged(LLChiclet* ctrl, const LLSD& param) -{ - LLTextBox* text = getChild("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 */ /************************************************************************/ @@ -447,13 +310,19 @@ BOOL LLIMWellWindow::ObjectRowPanel::handleRightMouseDown(S32 x, S32 y, MASK mas ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS -LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) -: LLSysWellWindow(key) +LLNotificationWellWindow::WellNotificationChannel::WellNotificationChannel(LLNotificationWellWindow* well_window) +: LLNotificationChannel(LLNotificationChannel::Params().name(well_window->getPathname())), + mWellWindow(well_window) { - // init connections to the list's update events - connectListUpdaterToSignal("notify"); - connectListUpdaterToSignal("groupnotify"); - connectListUpdaterToSignal("offer"); + connectToChannel("Notifications"); + connectToChannel("Group Notifications"); + connectToChannel("Offer"); +} + +LLNotificationWellWindow::LLNotificationWellWindow(const LLSD& key) +: LLSysWellWindow(key) +{ + mNotificationUpdates.reset(new WellNotificationChannel(this)); mUpdateLocked = false; } @@ -499,7 +368,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)); } @@ -551,7 +419,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)); } } @@ -578,20 +446,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(); @@ -606,7 +460,10 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) mChannel->killToastByNotificationID(id); } - +void LLNotificationWellWindow::onAdd( LLNotificationPtr notify ) +{ + removeItemByID(notify->getID()); +} /************************************************************************/ /* LLIMWellWindow implementation */ @@ -617,12 +474,10 @@ void LLNotificationWellWindow::onItemClose(LLSysWellItem* item) LLIMWellWindow::LLIMWellWindow(const LLSD& key) : LLSysWellWindow(key) { - LLIMMgr::getInstance()->addSessionObserver(this); } LLIMWellWindow::~LLIMWellWindow() { - LLIMMgr::getInstance()->removeSessionObserver(this); } // static @@ -643,47 +498,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; @@ -700,85 +519,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(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(); @@ -789,14 +536,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; } @@ -809,21 +549,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 @@ -868,13 +593,6 @@ void LLIMWellWindow::closeAllImpl() { LLPanel* panel = mMessageList->getItemByValue(*iter); - RowPanel* im_panel = dynamic_cast (panel); - if (im_panel) - { - gIMMgr->leaveSession(*iter); - continue; - } - ObjectRowPanel* obj_panel = dynamic_cast (panel); if (obj_panel) { @@ -899,4 +617,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 4d037f6793..8faed6a74a 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -27,29 +27,26 @@ #ifndef LL_LLSYSWELLWINDOW_H #define LL_LLSYSWELLWINDOW_H -#include "llsyswellitem.h" - -#include "lltransientdockablefloater.h" -#include "llbutton.h" -#include "llscreenchannel.h" -#include "llscrollcontainer.h" #include "llimview.h" - -#include "boost/shared_ptr.hpp" +#include "llnotifications.h" +#include "llscreenchannel.h" +#include "llsyswellitem.h" +#include "lltransientdockablefloater.h" 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; @@ -113,7 +109,7 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void setVisible(BOOL visible); - + /*virtual*/ void onAdd(LLNotificationPtr notify); // Operating with items void addItem(LLSysWellItem::Params p); @@ -125,6 +121,18 @@ public: void unlockWindowUpdate(); 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: @@ -132,12 +140,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); @@ -152,7 +156,7 @@ private: * * It contains a list list of all active IM sessions. */ -class LLIMWellWindow : public LLSysWellWindow, LLIMSessionObserver, LLInitClass +class LLIMWellWindow : public LLSysWellWindow, LLInitClass { public: LLIMWellWindow(const LLSD& key); @@ -164,57 +168,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 dc9ff31881..b5c7e26e3d 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" @@ -188,7 +189,7 @@ protected: F32 mContextConeOpacity; LLSaveFolderState mSavedFolderState; BOOL mSelectedItemPinned; - + LLRadioGroup* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; @@ -378,7 +379,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); @@ -461,7 +462,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); @@ -645,11 +646,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(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 @@ -659,26 +659,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; @@ -836,7 +822,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::dequegetListener()->getUUID()); + LLInventoryItem* itemp = gInventory.getItem(static_cast(first_item->getViewModelItem())->getUUID()); mNoCopyTextureSelected = FALSE; if (itemp) { @@ -1041,7 +1027,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); @@ -1385,7 +1371,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 ); @@ -1397,10 +1383,10 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) } else { - mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); - lldebugs << "mImageItemID: " << mImageItemID << llendl; - mImageAssetID = floaterp->getAssetID(); - lldebugs << "mImageAssetID: " << mImageAssetID << llendl; + mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); + lldebugs << "mImageItemID: " << mImageItemID << llendl; + mImageAssetID = floaterp->getAssetID(); + lldebugs << "mImageAssetID: " << mImageAssetID << llendl; } if (op == TEXTURE_SELECT && mOnSelectCallback) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index a7ff98b9d9..5a075567e6 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -505,6 +505,7 @@ private: }; static const char* sStateDescs[]; e_state mState; + void setState(e_state new_state); e_write_to_cache_state mWriteToCacheState; LLTextureFetch* mFetcher; LLPointer mFormattedImage; @@ -1031,7 +1032,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); if ((prioritize && mState == INIT) || mState == DONE) { - mState = INIT; + setState(INIT); U32 work_priority = mWorkPriority | LLWorkerThread::PRIORITY_HIGH; setPriority(work_priority); } @@ -1095,12 +1096,14 @@ bool LLTextureFetchWorker::doWork(S32 param) { if (mState == INIT || mState == LOAD_FROM_NETWORK || mState == LOAD_FROM_SIMULATOR) { + LL_DEBUGS("Texture") << mID << " abort: mImagePriority < F_ALMOST_ZERO" << llendl; return true; // abort } } if(mState > CACHE_POST && !mCanUseNET && !mCanUseHTTP) { //nowhere to get data, abort. + LL_WARNS("Texture") << mID << " abort, nowhere to get data" << llendl; return true ; } @@ -1152,7 +1155,7 @@ bool LLTextureFetchWorker::doWork(S32 param) clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); mCacheWriteHandle = LLTextureCache::nullHandle(); - mState = LOAD_FROM_TEXTURE_CACHE; + setState(LOAD_FROM_TEXTURE_CACHE); mInCache = FALSE; mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) @@ -1169,7 +1172,7 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 size = mDesiredSize - offset; if (size <= 0) { - mState = CACHE_POST; + setState(CACHE_POST); return false; } mFileSize = 0; @@ -1187,7 +1190,9 @@ bool LLTextureFetchWorker::doWork(S32 param) offset, size, responder); mCacheReadTimer.reset(); } - else if ((mUrl.empty()||mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache()) +/* SH-3980 - disabling caching of server bakes until we can fix the blurring problems */ +/* else if ((mUrl.empty()||mFTType==FTT_SERVER_BAKE) && mFetcher->canLoadFromCache()) */ + else if (mUrl.empty() && mFetcher->canLoadFromCache()) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it @@ -1200,12 +1205,12 @@ bool LLTextureFetchWorker::doWork(S32 param) else if(!mUrl.empty() && mCanUseHTTP) { setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = WAIT_HTTP_RESOURCE; + setState(WAIT_HTTP_RESOURCE); } else { setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = LOAD_FROM_NETWORK; + setState(LOAD_FROM_NETWORK); } } @@ -1215,7 +1220,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mFetcher->mTextureCache->readComplete(mCacheReadHandle, false)) { mCacheReadHandle = LLTextureCache::nullHandle(); - mState = CACHE_POST; + setState(CACHE_POST); // fall through } else @@ -1223,6 +1228,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // //This should never happen // + LL_DEBUGS("Texture") << mID << " this should never happen" << llendl; return false; } } @@ -1241,7 +1247,12 @@ bool LLTextureFetchWorker::doWork(S32 param) // we have enough data, decode it llassert_always(mFormattedImage->getDataSize() > 0); mLoadedDiscard = mDesiredDiscard; - mState = DECODE_IMAGE; + if (mLoadedDiscard < 0) + { + LL_WARNS("Texture") << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << llendl; + } + setState(DECODE_IMAGE); mInCache = TRUE; mWriteToCacheState = NOT_WRITE ; LL_DEBUGS("Texture") << mID << ": Cached. Bytes: " << mFormattedImage->getDataSize() @@ -1254,13 +1265,14 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mUrl.compare(0, 7, "file://") == 0) { // failed to load local file, we're done. + LL_WARNS("Texture") << mID << ": abort, failed to load local file " << mUrl << LL_ENDL; return true; } // need more data else { LL_DEBUGS("Texture") << mID << ": Not in Cache" << LL_ENDL; - mState = LOAD_FROM_NETWORK; + setState(LOAD_FROM_NETWORK); } // fall through @@ -1301,13 +1313,15 @@ bool LLTextureFetchWorker::doWork(S32 param) mCanUseHTTP = false; } } +#if 0 /* SH-3980 - disabling caching of server bakes until we can fix the blurring problems */ if (mFTType == FTT_SERVER_BAKE) { mWriteToCacheState = CAN_WRITE; } +#endif if (mCanUseHTTP && !mUrl.empty()) { - mState = WAIT_HTTP_RESOURCE; + setState(WAIT_HTTP_RESOURCE); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1337,6 +1351,8 @@ bool LLTextureFetchWorker::doWork(S32 param) //mFetcher->addToNetworkQueue(this); //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + + LL_DEBUGS("Texture") << mID << " does this happen?" << llendl; return false; } } @@ -1355,10 +1371,16 @@ bool LLTextureFetchWorker::doWork(S32 param) { // processSimulatorPackets() failed // llwarns << "processSimulatorPackets() failed to load buffer" << llendl; + LL_WARNS("Texture") << mID << " processSimulatorPackets() failed to load buffer" << llendl; return true; // failed } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; + if (mLoadedDiscard < 0) + { + LL_WARNS("Texture") << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << llendl; + } + setState(DECODE_IMAGE); mWriteToCacheState = SHOULD_WRITE; recordTextureDone(false); } @@ -1382,14 +1404,14 @@ bool LLTextureFetchWorker::doWork(S32 param) // Otherwise, advance into the HTTP states. if (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore()) { - mState = WAIT_HTTP_RESOURCE2; + setState(WAIT_HTTP_RESOURCE2); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); mFetcher->addHttpWaiter(this->mID); ++mResourceWaitCount; return false; } - mState = SEND_HTTP_REQ; + setState(SEND_HTTP_REQ); // *NOTE: You must invoke releaseHttpSemaphore() if you transition // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort // the request. @@ -1406,6 +1428,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (! mCanUseHTTP) { releaseHttpSemaphore(); + LL_WARNS("Texture") << mID << " abort: SEND_HTTP_REQ but !mCanUseHTTP" << llendl; return true; // abort } @@ -1422,13 +1445,19 @@ bool LLTextureFetchWorker::doWork(S32 param) // We already have all the data, just decode it mLoadedDiscard = mFormattedImage->getDiscardLevel(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; + if (mLoadedDiscard < 0) + { + LL_WARNS("Texture") << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << llendl; + } + setState(DECODE_IMAGE); releaseHttpSemaphore(); return false; } else { releaseHttpSemaphore(); + LL_WARNS("Texture") << mID << " SEND_HTTP_REQ abort: cur_size " << cur_size << " <=0" << llendl; return true; // abort. } } @@ -1486,7 +1515,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mFetcher->addToHTTPQueue(mID); recordTextureStart(true); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; + setState(WAIT_HTTP_REQ); // fall through } @@ -1504,8 +1533,9 @@ bool LLTextureFetchWorker::doWork(S32 param) { if(mWriteToCacheState == NOT_WRITE) //map tiles { - mState = DONE; + setState(DONE); releaseHttpSemaphore(); + LL_DEBUGS("Texture") << mID << " abort: WAIT_HTTP_REQ not found" << llendl; return true; // failed, means no map tile on the empty region. } @@ -1514,7 +1544,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // roll back to try UDP if (mCanUseNET) { - mState = INIT; + setState(INIT); mCanUseHTTP = false; mUrl.clear(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); @@ -1545,15 +1575,21 @@ bool LLTextureFetchWorker::doWork(S32 param) // Use available data mLoadedDiscard = mFormattedImage->getDiscardLevel(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; + if (mLoadedDiscard < 0) + { + LL_WARNS("Texture") << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << llendl; + } + setState(DECODE_IMAGE); releaseHttpSemaphore(); return false; } // Fail harder resetFormattedData(); - mState = DONE; + setState(DONE); releaseHttpSemaphore(); + LL_WARNS("Texture") << mID << " abort: fail harder" << llendl; return true; // failed } @@ -1562,6 +1598,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // next time the texture is requested, even if the data have already been fetched. if(mWriteToCacheState != NOT_WRITE) { + // Why do we want to keep url if NOT_WRITE - is this a proxy for map tiles? mUrl.clear(); } @@ -1575,7 +1612,8 @@ bool LLTextureFetchWorker::doWork(S32 param) } // abort. - mState = DONE; + setState(DONE); + LL_WARNS("Texture") << mID << " abort: no data received" << llendl; releaseHttpSemaphore(); return true; } @@ -1593,7 +1631,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " << mID << ". Aborting load." << LL_ENDL; - mState = DONE; + setState(DONE); releaseHttpSemaphore(); return true; } @@ -1641,7 +1679,12 @@ bool LLTextureFetchWorker::doWork(S32 param) mHttpReplyOffset = 0; mLoadedDiscard = mRequestedDiscard; - mState = DECODE_IMAGE; + if (mLoadedDiscard < 0) + { + LL_WARNS("Texture") << mID << " mLoadedDiscard is " << mLoadedDiscard + << ", should be >=0" << llendl; + } + setState(DECODE_IMAGE); if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; @@ -1672,31 +1715,34 @@ bool LLTextureFetchWorker::doWork(S32 param) if (textures_decode_disabled) { // for debug use, don't decode - mState = DONE; + setState(DONE); return true; } if (mDesiredDiscard < 0) { // We aborted, don't decode - mState = DONE; + setState(DONE); + LL_DEBUGS("Texture") << mID << " DECODE_IMAGE abort: desired discard " << mDesiredDiscard << "<0" << llendl; return true; } if (mFormattedImage->getDataSize() <= 0) { - //llerrs << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl; + llwarns << "Decode entered with invalid mFormattedImage. ID = " << mID << llendl; //abort, don't decode - mState = DONE; + setState(DONE); + LL_DEBUGS("Texture") << mID << " DECODE_IMAGE abort: (mFormattedImage->getDataSize() <= 0)" << llendl; return true; } if (mLoadedDiscard < 0) { - //llerrs << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl; + llwarns << "Decode entered with invalid mLoadedDiscard. ID = " << mID << llendl; //abort, don't decode - mState = DONE; + setState(DONE); + LL_DEBUGS("Texture") << mID << " DECODE_IMAGE abort: mLoadedDiscard < 0" << llendl; return true; } @@ -1706,7 +1752,7 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 discard = mHaveAllData ? 0 : mLoadedDiscard; U32 image_priority = LLWorkerThread::PRIORITY_NORMAL | mWorkPriority; mDecoded = FALSE; - mState = DECODE_IMAGE_UPDATE; + setState(DECODE_IMAGE_UPDATE); LL_DEBUGS("Texture") << mID << ": Decoding. Bytes: " << mFormattedImage->getDataSize() << " Discard: " << discard << " All Data: " << mHaveAllData << LL_ENDL; mDecodeHandle = mFetcher->mImageDecodeThread->decodeImage(mFormattedImage, image_priority, discard, mNeedsAux, @@ -1734,13 +1780,13 @@ bool LLTextureFetchWorker::doWork(S32 param) mFormattedImage = NULL; ++mRetryAttempt; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = INIT; + setState(INIT); return false; } else { // llwarns << "UNABLE TO LOAD TEXTURE: " << mID << " RETRIES: " << mRetryAttempt << llendl; - mState = DONE; // failed + setState(DONE); // failed } } else @@ -1749,7 +1795,7 @@ bool LLTextureFetchWorker::doWork(S32 param) LL_DEBUGS("Texture") << mID << ": Decoded. Discard: " << mDecodedDiscard << " Raw Image: " << llformat("%dx%d",mRawImage->getWidth(),mRawImage->getHeight()) << LL_ENDL; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = WRITE_TO_CACHE; + setState(WRITE_TO_CACHE); } // fall through } @@ -1765,7 +1811,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { // If we're in a local cache or we didn't actually receive any new data, // or we failed to load anything, skip - mState = DONE; + setState(DONE); return false; } S32 datasize = mFormattedImage->getDataSize(); @@ -1784,7 +1830,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it U32 cache_priority = mWorkPriority; mWritten = FALSE; - mState = WAIT_ON_WRITE; + setState(WAIT_ON_WRITE); ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, @@ -1797,7 +1843,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { if (writeToCacheComplete()) { - mState = DONE; + setState(DONE); // fall through } else @@ -1818,7 +1864,10 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mDecodedDiscard >= 0 && mDesiredDiscard < mDecodedDiscard) { // More data was requested, return to INIT - mState = INIT; + setState(INIT); + LL_DEBUGS("Texture") << mID << " more data requested, returning to INIT: " + << " mDecodedDiscard " << mDecodedDiscard << ">= 0 && mDesiredDiscard " << mDesiredDiscard + << "<" << " mDecodedDiscard " << mDecodedDiscard << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); return false; } @@ -1858,10 +1907,10 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe bool partial = false; LLCore::HttpStatus status(response->getStatus()); - lldebugs << "HTTP COMPLETE: " << mID - << " status: " << status.toHex() - << " '" << status.toString() << "'" - << llendl; + LL_DEBUGS("Texture") << "HTTP COMPLETE: " << mID + << " status: " << status.toHex() + << " '" << status.toString() << "'" + << llendl; // unsigned int offset(0), length(0), full_length(0); // response->getRange(&offset, &length, &full_length); // llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle @@ -2445,6 +2494,7 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const std::string exten = gDirUtilp->getExtension(url); if (!url.empty() && (!exten.empty() && LLImageBase::getCodecFromExtension(exten) != IMG_CODEC_J2C)) { + LL_DEBUGS("Texture") << "full request for " << id << " exten is not J2C: " << exten << llendl; // Only do partial requests for J2C at the moment desired_size = MAX_IMAGE_DATA_SIZE; desired_discard = 0; @@ -2487,7 +2537,7 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const worker->setCanUseHTTP(can_use_http) ; if (!worker->haveWork()) { - worker->mState = LLTextureFetchWorker::INIT; + worker->setState(LLTextureFetchWorker::INIT); worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); @@ -2511,7 +2561,7 @@ bool LLTextureFetch::createRequest(FTType f_type, const std::string& url, const worker->unlockWorkMutex(); // -Mw } -// llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; + LL_DEBUGS("Texture") << "REQUESTED: " << id << " Discard: " << desired_discard << " size " << desired_size << llendl; return true; } @@ -3180,6 +3230,30 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +void LLTextureFetchWorker::setState(e_state new_state) +{ + static const char* e_state_name[] = + { + "INVALID", + "INIT", + "LOAD_FROM_TEXTURE_CACHE", + "CACHE_POST", + "LOAD_FROM_NETWORK", + "LOAD_FROM_SIMULATOR", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", + "SEND_HTTP_REQ", + "WAIT_HTTP_REQ", + "DECODE_IMAGE", + "DECODE_IMAGE_UPDATE", + "WRITE_TO_CACHE", + "WAIT_ON_WRITE", + "DONE" + }; + LL_DEBUGS("Texture") << "id: " << mID << " FTType: " << mFTType << " disc: " << mDesiredDiscard << " sz: " << mDesiredSize << " state: " << e_state_name[mState] << " => " << e_state_name[new_state] << llendl; + mState = new_state; +} + // Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) @@ -3236,7 +3310,7 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 llassert_always(data_size == FIRST_PACKET_SIZE || data_size == worker->mFileSize); res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; + worker->setState(LLTextureFetchWorker::LOAD_FROM_SIMULATOR); worker->unlockWorkMutex(); // -Mw return res; } @@ -3286,7 +3360,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 (worker->mState == LLTextureFetchWorker::LOAD_FROM_NETWORK)) { worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); - worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; + worker->setState(LLTextureFetchWorker::LOAD_FROM_SIMULATOR); } else { @@ -3549,7 +3623,7 @@ void LLTextureFetch::releaseHttpWaiters() break; } - worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setState(LLTextureFetchWorker::SEND_HTTP_REQ); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->unlockWorkMutex(); // -Mw 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 mHandle; - LLPanel* mWrapperPanel; + LLPanel* mWrapperPanel; // timer counts a lifetime of a toast std::auto_ptr 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 0c18db29ac..48f978e919 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -55,7 +55,7 @@ const S32 LLToastGroupNotifyPanel::DEFAULT_MESSAGE_MAX_LINE_COUNT = 7; -LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification) +LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(const LLNotificationPtr& notification) : LLToastPanel(notification), mInventoryOffer(NULL) { @@ -75,10 +75,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::string from; LLStringUtil::format_map_t args; args["[SENDER]"] = from_name; @@ -123,7 +121,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 684b97999b..e7b3922d0f 100644 --- a/indra/newview/lltoastgroupnotifypanel.h +++ b/indra/newview/lltoastgroupnotifypanel.h @@ -47,14 +47,11 @@ class LLToastGroupNotifyPanel public: void close(); - static bool onNewNotification(const LLSD& notification); - F32 getCurrentTransparency(); - // 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 a5f8b52f60..cb6a07f799 100644 --- a/indra/newview/lltoastimpanel.cpp +++ b/indra/newview/lltoastimpanel.cpp @@ -109,9 +109,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 417dc24dea..3511b8e51c 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -41,13 +41,17 @@ #include "llnotificationsutil.h" #include "llviewermessage.h" // [FS communication UI] -//#include "llimfloater.h" +//#include "llfloaterimsession.h" CHUI Merge new +//#include "llimfloater.h" CHUI Merge old #include "fsfloaterim.h" // [FS communication UI] 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; @@ -55,176 +59,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(notification->getID()) { - // is it a script dialog? - mIsScriptDialog = (notification->getName() == "ScriptDialog" || notification->getName() == "ScriptDialogGroup"); - - if (mIsScriptDialog) - buildFromFile( "panel_notification_script.xml"); - else - buildFromFile( "panel_notification.xml"); - if(rect != LLRect::null) - { - this->setShape(rect); - } - mInfoPanel = getChild("info_panel"); - mControlPanel = getChild("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 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(); + init(rect, show_images); } - // 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("caution_text_box"); - } - else - { - mTextBox = getChild("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 buttons; - buttons.reserve(mNumOptions); - S32 buttons_width = 0; - // create all buttons and accumulate they total width to reshape mControlPanel - for (S32 i = 0; i < mNumOptions; i++) - { - LLSD form_element = form->getElement(i); - if (form_element["type"].asString() != "button") - { - // not a button. - continue; - } - if (form_element["name"].asString() == TEXTBOX_MAGIC_TOKEN) - { - // a textbox pretending to be a button. - continue; - } - LLButton* new_button = createButton(form_element, TRUE); - buttons_width += new_button->getRect().getWidth(); - S32 index = form_element["index"].asInteger(); - buttons.push_back(index_button_pair_t(index,new_button)); - } - if (buttons.empty()) - { - addDefaultButton(); - } - else - { - const S32 button_panel_width = mControlPanel->getRect().getWidth();// do not change width of the panel - S32 button_panel_height = mControlPanel->getRect().getHeight(); - //try get an average h_pad to spread out buttons - S32 h_pad = (button_panel_width - buttons_width) / (S32(buttons.size())); - if(h_pad < 2*HPAD) - { - /* - * Probably it is a scriptdialog toast - * for a scriptdialog toast h_pad can be < 2*HPAD if we have a lot of buttons. - * In last case set default h_pad to avoid heaping of buttons - */ - S32 button_per_row = button_panel_width / BUTTON_WIDTH; - h_pad = (button_panel_width % BUTTON_WIDTH) / (button_per_row - 1);// -1 because we do not need space after last button in a row - if(h_pad < 2*HPAD) // still not enough space between buttons ? - { - h_pad = 2*HPAD; - } - } - if (mIsScriptDialog) - { - // we are using default width for script buttons so we can determinate button_rows - //to get a number of rows we divide the required width of the buttons to button_panel_width - S32 button_rows = llceil(F32(buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width); - //S32 button_rows = (buttons.size() - 1) * (BUTTON_WIDTH + h_pad) / button_panel_width; - //reserve one row for the ignore_btn - button_rows++; - //calculate required panel height for scripdialog notification. - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + IGNORE_BTN_TOP_DELTA + BOTTOM_PAD; - } - else - { - // in common case buttons can have different widths so we need to calculate button_rows according to buttons_width - //S32 button_rows = llceil(F32(buttons.size()) * (buttons_width + h_pad) / button_panel_width); - S32 button_rows = llceil(F32((buttons.size() - 1) * h_pad + buttons_width) / button_panel_width); - //calculate required panel height - button_panel_height = button_rows * (BTN_HEIGHT + VPAD) + BOTTOM_PAD; - } - - // we need to keep min width and max height to make visible all buttons, because width of the toast can not be changed - adjustPanelForScriptNotice(button_panel_width, button_panel_height); - updateButtonsLayout(buttons, h_pad); - // save buttons for later use in disableButtons() - mButtons.assign(buttons.begin(), buttons.end()); - } - } - // adjust panel's height to the text size - mInfoPanel->setFollowsAll(); - snapToMessageHeight(mTextBox, MAX_LENGTH); - - if(notification->isReusable()) - { - mButtonClickConnection = sButtonClickSignal.connect( - boost::bind(&LLToastNotifyPanel::onToastPanelButtonClicked, this, _1, _2)); - - if(notification->isRespondedTo()) - { - // User selected an option in toast, now disable required buttons in IM window - disableRespondedOptions(notification); - } - } -} void LLToastNotifyPanel::addDefaultButton() { LLSD form_element; @@ -242,7 +82,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() : ""; @@ -252,14 +91,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")); @@ -294,16 +134,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& buttons, S32 h_pad) { @@ -391,104 +226,6 @@ void LLToastNotifyPanel::adjustPanelForTipNotice() } } -typedef std::set button_name_set_t; -typedef std::map disable_button_map_t; - -disable_button_map_t initUserGiveItemDisableButtonMap() -{ - // see EXT-5905 for disable rules - - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Show"); - disable_map.insert(std::make_pair("Show", buttons)); - - buttons.insert("Discard"); - disable_map.insert(std::make_pair("Discard", buttons)); - - buttons.insert("Mute"); - disable_map.insert(std::make_pair("Mute", buttons)); - - return disable_map; -} - -disable_button_map_t initTeleportOfferedDisableButtonMap() -{ - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Teleport"); - buttons.insert("Cancel"); - - disable_map.insert(std::make_pair("Teleport", buttons)); - disable_map.insert(std::make_pair("Cancel", buttons)); - - return disable_map; -} - -disable_button_map_t initFriendshipOfferedDisableButtonMap() -{ - disable_button_map_t disable_map; - button_name_set_t buttons; - - buttons.insert("Accept"); - buttons.insert("Decline"); - - disable_map.insert(std::make_pair("Accept", buttons)); - disable_map.insert(std::make_pair("Decline", buttons)); - - return disable_map; -} - -button_name_set_t getButtonDisableList(const std::string& notification_name, const std::string& button_name) -{ - static disable_button_map_t user_give_item_disable_map = initUserGiveItemDisableButtonMap(); - static disable_button_map_t teleport_offered_disable_map = initTeleportOfferedDisableButtonMap(); - static disable_button_map_t friendship_offered_disable_map = initFriendshipOfferedDisableButtonMap(); - - disable_button_map_t::const_iterator it; - disable_button_map_t::const_iterator it_end; - disable_button_map_t search_map; - - if("UserGiveItem" == notification_name) - { - search_map = user_give_item_disable_map; - } - else if(("TeleportOffered" == notification_name) || ("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) -{ - button_name_set_t buttons = getButtonDisableList(notification_name, selected_button); - - std::vector::const_iterator it = mButtons.begin(); - for ( ; it != mButtons.end(); it++) - { - LLButton* btn = it->second; - if(buttons.find(btn->getName()) != buttons.end()) - { - btn->setEnabled(FALSE); - } - } -} - // static void LLToastNotifyPanel::onClickButton(void* data) { @@ -501,103 +238,266 @@ void LLToastNotifyPanel::onClickButton(void* data) { 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(self->mNotification->getResponder()); - LLOfferInfo* new_info = NULL; - if(is_reusable && old_info) - { - new_info = new LLOfferInfo(*old_info); - self->mNotification->setResponder(new_info); - } + // disable all buttons + self->mControlPanel->setEnabled(FALSE); + + // this might repost notification with new form data/enabled buttons self->mNotification->respond(response); - - if(is_reusable) - { - sButtonClickSignal(self->mNotification->getID(), button_name); - } - else - { - // disable all buttons - self->mControlPanel->setEnabled(FALSE); - } } -void LLToastNotifyPanel::onToastPanelButtonClicked(const LLUUID& notification_id, const std::string btn_name) +void LLToastNotifyPanel::init( LLRect rect, bool show_images ) { - if(mNotification->getID() == notification_id) + 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("info_panel"); + + mControlPanel = getChild("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("caution_text_box"); + } + else + { + mTextBox = getChild("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 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); + + // reshape the panel to its previous size + if (current_rect.notEmpty()) { - disableButtons(mNotification->getName(), btn_name); + reshape(current_rect.getWidth(), current_rect.getHeight()); } } -void LLToastNotifyPanel::disableRespondedOptions(const LLNotificationPtr& notification) -{ - LLSD response = notification->getResponse(); - for (LLSD::map_const_iterator response_it = response.beginMap(); - response_it != response.endMap(); ++response_it) - { - if (response_it->second.isBoolean() && response_it->second.asBoolean()) - { - // that after multiple responses there can be many pressed buttons - // need to process them all - disableButtons(notification->getName(), response_it->first); - } - } -} - - ////////////////////////////////////////////////////////////////////////// LLIMToastNotifyPanel::LLIMToastNotifyPanel(LLNotificationPtr& pNotification, const LLUUID& session_id, const LLRect& rect /* = LLRect::null */, - bool show_images /* = true */) - : mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images) + bool show_images /* = true */, LLTextBase* parent_text) +: mSessionID(session_id), LLToastNotifyPanel(pNotification, rect, show_images), + mParentText(parent_text) { - mTextBox->setFollowsAll(); + compactButtons(); } LLIMToastNotifyPanel::~LLIMToastNotifyPanel() { - // We shouldn't delete notification when IM floater exists - // since that notification will be reused by IM floater. - // This may happened when IM floater reloads messages, exactly when user - // changes layout of IM chat log(disable/enable plaintext mode). - // See EXT-6500 - // [FS communication UI] - //LLIMFloater* im_floater = LLIMFloater::findInstance(mSessionID); - FSFloaterIM* im_floater = FSFloaterIM::findInstance(mSessionID); - // [FS communication UI] - if (im_floater != NULL && !im_floater->isDead()) - { - mCloseNotificationOnDestroy = false; - } } void LLIMToastNotifyPanel::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) { - S32 text_height = mTextBox->getTextBoundingRect().getHeight(); - S32 widget_height = mTextBox->getRect().getHeight(); - S32 delta = text_height - widget_height; - LLRect rc = getRect(); - - 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); + snapToMessageHeight(); +} - // Notification height required to display the text message depends on - // the width of the text box thus if panel width is changed the text box - // width is also changed then reshape() is called to adjust proper height. - if (is_width_changed) +void LLIMToastNotifyPanel::snapToMessageHeight() +{ + if(!mTextBox) { - reshape(width, height, called_from_parent); + return; + } + + //Add message height if it is visible + if (mTextBox->getVisible()) + { + S32 new_panel_height = computeSnappedToMessageHeight(mTextBox, MAX_LENGTH); + + //reshape the panel with new height + if (new_panel_height != getRect().getHeight()) + { + LLToastNotifyPanel::reshape( getRect().getWidth(), new_panel_height); + } } } +void LLIMToastNotifyPanel::compactButtons() +{ + //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++) + { + LLButton * button = dynamic_cast (*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(); + } + } + + if (mParentText) + { + mParentText->needsReflow(); + } +} + +void LLIMToastNotifyPanel::updateNotification() + { + init(LLRect(), true); + } + +void LLIMToastNotifyPanel::init( LLRect rect, bool show_images ) +{ + LLToastNotifyPanel::init(LLRect(), show_images); + + compactButtons(); +} + // EOF + diff --git a/indra/newview/lltoastnotifypanel.h b/indra/newview/lltoastnotifypanel.h index db517ec858..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 { 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 mBtnCallbackData; - bool mCloseNotificationOnDestroy; - typedef std::pair 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 mButtons; + //std::vector 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 e2127a9e40..49dd1887c7 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -61,6 +61,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) { @@ -72,22 +91,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); + } } } @@ -101,7 +111,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 -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 2c4d96c3f6..eec6acdb3c 100644 --- a/indra/newview/lltoastscripttextbox.cpp +++ b/indra/newview/lltoastscripttextbox.cpp @@ -72,7 +72,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 11637f8ede..378923a65b 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -671,7 +671,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(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 ee282e8263..33ad0ac4f1 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" // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1) #include "rlvhandler.h" #include "rlvlocks.h" @@ -662,33 +661,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); } } @@ -705,20 +712,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) @@ -735,17 +749,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 @@ -765,17 +789,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); + } } } @@ -788,7 +820,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++) @@ -2614,7 +2646,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. @@ -2650,6 +2688,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/lltoolface.cpp b/indra/newview/lltoolface.cpp index b513d3a7fa..e4e6238484 100644 --- a/indra/newview/lltoolface.cpp +++ b/indra/newview/lltoolface.cpp @@ -110,6 +110,15 @@ void LLToolFace::pickCallback(const LLPickInfo& pick_info) return; } +// [RLVa:KB] - Checked: 2010-11-29 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c + if ( (rlv_handler_t::isEnabled()) && + ( (!gRlvHandler.canEdit(hit_obj)) || + ((gRlvHandler.hasBehaviour(RLV_BHVR_FARTOUCH)) && (!gRlvHandler.canTouch(hit_obj, pick_info.mObjectOffset))) ) ) + { + return; + } +// [/RLVa:KB] + // [RLVa:KB] - Checked: 2010-11-29 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c if ( (rlv_handler_t::isEnabled()) && ( (!gRlvHandler.canEdit(hit_obj)) || diff --git a/indra/newview/lltoolgun.cpp b/indra/newview/lltoolgun.cpp index 1c4e923106..1c4ccfc3c8 100644 --- a/indra/newview/lltoolgun.cpp +++ b/indra/newview/lltoolgun.cpp @@ -42,7 +42,7 @@ #include "llhudmanager.h" #include "lltoolmgr.h" #include "lltoolgrab.h" - +#include "lluiimage.h" // Linden library includes #include "llwindow.h" // setMouseClipping() diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index cd7405b3f5..c629fbd557 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -1051,9 +1051,8 @@ 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; // Build group prefix -Zi std::string group_title; @@ -1068,36 +1067,17 @@ BOOL LLToolPie::handleTooltipObject( LLViewerObject* hover_object, std::string l } } - 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(); // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a - final_name = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ? group_title + av_name.getCompleteName() : RlvStrings::getAnonym(av_name); + final_name = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ? av_name.getCompleteName() : RlvStrings::getAnonym(av_name); // [/RLVa:KB] } else { -// final_name = full_name; -// [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a - final_name = (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ? group_title + full_name : RlvStrings::getAnonym(full_name); -// [/RLVa:KB] + final_name = LLTrans::getString("TooltipPerson");; } // *HACK: We may select this object, so pretend it was clicked diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp old mode 100755 new mode 100644 index ed768eb093..aaa81c57d4 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -30,6 +30,7 @@ #include "llregionhandle.h" #include "stdtypes.h" +#include "llvoavatar.h" /* * Classes and utility functions for per-thread and per-region @@ -126,6 +127,8 @@ LLViewerAssetStats::PerRegionStats::merge(const LLViewerAssetStats::PerRegionSta mFPS.merge(src.mFPS); } + // Avatar stats - data all comes from main thread, so leave alone. + // Requests for (int i = 0; i < LL_ARRAY_SIZE(mRequests); ++i) { diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h old mode 100755 new mode 100644 index 3381c01ed5..e4581d2120 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -36,6 +36,7 @@ #include "llviewerassetstorage.h" #include "llsimplestat.h" #include "llsd.h" +#include "llvoavatar.h" /** * @class LLViewerAssetStats @@ -181,6 +182,9 @@ public: // Frames-Per-Second Samples void recordFPS(F32 fps); + // Avatar-related statistics + void recordAvatarStats(); + // Merge a source instance into a destination instance. This is // conceptually an 'operator+=()' method: // - counts are added 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 f737eba69c..1d1f7b98ce 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" @@ -441,6 +442,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/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 825588b56d..81e5c4cc53 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -551,15 +551,6 @@ bool handleVelocityInterpolate(const LLSD& newvalue) return true; } -// [SL:KB] - Patch: UI-DndButtonCommit | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c -bool handleSettingF32Change(const LLSD& sdValue, F32* pValue) -{ - if (pValue) - *pValue = sdValue.asReal(); - return true; -} -// [/SL:KB] - // ## Zi: Moved Avatar Z offset from RLVa to here bool handleAvatarZOffsetChanged(const LLSD& sdValue) { @@ -888,8 +879,6 @@ void settings_setup_listeners() gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&handleSpellCheckChanged)); gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged)); gSavedSettings.getControl("LoginLocation")->getSignal()->connect(boost::bind(&handleLoginLocationChanged)); -// [SL:KB] - Patch: UI-DndButtonCommit | Checked: 2011-06-19 (Catznip-2.6.0c) | Added: Catznip-2.6.0c - gSavedSettings.getControl("DragAndDropCommitDelay")->getSignal()->connect(boost::bind(&handleSettingF32Change, _2, &DELAY_DRAG_HOVER_COMMIT)); // FIRE-9759 - Temporarily remove AvatarZOffset since it's broken //gSavedPerAccountSettings.getControl("AvatarZOffset")->getSignal()->connect(boost::bind(&handleAvatarZOffsetChanged, _2)); // ## Zi: Moved Avatar Z offset from RLVa to here gSavedSettings.getControl("FSUseV1Menus")->getSignal()->connect(boost::bind(&show_v1_menus)); // V1 menu system -WoLf diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index 91ac1b8251..b8bdb8805b 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -335,6 +335,24 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) // Logic for forcing window updates if we're in drone mode. // + // *TODO: Investigate running display() during gHeadlessClient. See if this early exit is needed DK 2011-02-18 + if (gHeadlessClient) + { +#if LL_WINDOWS + static F32 last_update_time = 0.f; + if ((gFrameTimeSeconds - last_update_time) > 1.f) + { + InvalidateRect((HWND)gViewerWindow->getPlatformWindow(), NULL, FALSE); + last_update_time = gFrameTimeSeconds; + } +#elif LL_DARWIN + // MBW -- Do something clever here. +#endif + // Not actually rendering, don't bother. + return; + } + + // // Bail out if we're in the startup state and don't want to try to // render the world. @@ -1153,7 +1171,7 @@ void render_hud_attachments() gPipeline.pushRenderTypeMask(); // turn off everything - gPipeline.clearAllRenderTypes(); + gPipeline.andRenderTypeMask(LLPipeline::END_RENDER_TYPES); // turn on HUD gPipeline.toggleRenderType(LLPipeline::RENDER_TYPE_HUD); // turn on HUD particles diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp index 2fe1e50781..ab5ed4cf77 100644 --- a/indra/newview/llviewerdisplayname.cpp +++ b/indra/newview/llviewerdisplayname.cpp @@ -57,6 +57,7 @@ namespace LLViewerDisplayName sNameChangedSignal.connect(cb); } + void doNothing() { } } class LLSetDisplayNameResponder : public LLHTTPClient::Responder @@ -103,7 +104,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; @@ -145,9 +146,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 ); } @@ -195,8 +196,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(); if (LGGContactSets::getInstance()->hasPseudonym(agent_id)) { LLSD payload; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index 6afb840962..e217d0bd40 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -33,8 +33,8 @@ #include "llfloaterautoreplacesettings.h" #include "llcompilequeue.h" // [FS communication UI] -//#include "llcallfloater.h" -#include "fsfloatervoicecontrols.h" +//#include "llcallfloater.h" CHUI Merge LL removed this +//#include "fsfloatervoicecontrols.h" // [FS communication UI] #include "llfasttimerview.h" #include "llfloaterabout.h" @@ -53,6 +53,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" @@ -72,7 +75,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" @@ -117,23 +120,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" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" #include "llpanelblockedlist.h" #include "llpanelclassified.h" #include "llpreviewanim.h" @@ -146,7 +144,6 @@ #include "llscriptfloater.h" #include "llfloatermodelpreview.h" #include "llcommandhandler.h" -// #include "llnearbychatbar.h" // Remove floating chat bar // *NOTE: Please add files in alphabetical order to keep merges easy. // ND: And for FS please put yours after this line, for easier merges too @@ -180,8 +177,9 @@ #include "NACLfloaterexploresounds.h" #include "particleeditor.h" #include "quickprefs.h" +// [RLVa:KB] - Checked: 2010-03-11 #include "rlvfloaters.h" - +// [/RLVa:KB] // handle secondlife:///app/openfloater/{NAME} URLs class LLFloaterOpenHandler : public LLCommandHandler @@ -232,15 +230,10 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("bumps", "floater_bumps.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - // Remove floating chat bar - // LLFloaterReg::add("chat_bar", "floater_chat_bar.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - // [FS communication UI] - //LLFloaterReg::add("nearby_chat", "floater_nearby_chat.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("fs_nearby_chat", "floater_fs_nearby_chat.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - // [FS communication UI] - // - + LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -262,12 +255,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("help_browser", "floater_help_browser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("hud", "floater_hud.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - // [FS communication UI] - //LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - //LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("fs_impanel", "floater_fs_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("fs_im_container", "floater_fs_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - // [FS communication UI] + LLFloaterReg::add("impanel", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("im_container", "floater_im_container.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("im_well_window", "floater_sys_well.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("incoming_call", "floater_incoming_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -276,6 +265,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); LLInspectRemoteObjectUtil::registerFloater(); + LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); LLFloaterDisplayNameUtil::registerFloater(); @@ -320,6 +310,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); + LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); @@ -374,8 +365,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("upload_sound", "floater_sound_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "upload"); // [FS communication UI] - //LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("fs_voice_controls", "floater_fs_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + //LLFloaterReg::add("voice_controls", "floater_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); CHUI Merge LL removed this + //LLFloaterReg::add("fs_voice_controls", "floater_fs_voice_controls.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); // [FS communication UI] LLFloaterReg::add("voice_effect", "floater_voice_effect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index 0ed0109933..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,10 +41,7 @@ #include "llviewermessage.h" // send_guid_sound_trigger #include "llviewernetwork.h" #include "llagent.h" -// Remove floating chat bar -// #include "llnearbychatbar.h" -#include "fsnearbychathub.h" -// +#include "llfloaterimnearbychat.h" // Globals LLViewerGestureList gGestureList; @@ -133,9 +131,8 @@ void LLViewerGesture::doTrigger( BOOL send_chat ) { // Don't play nodding animation, since that might not blend // with the gesture animation. - // Remove floating chat bar - // LLNearbyChatBar::getInstance()->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); - FSNearbyChat::instance().sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); + (LLFloaterReg::getTypedInstance("nearby_chat"))-> + sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE); } } diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp old mode 100755 new mode 100644 index 757f119cb3..4b9311d8fb --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -64,6 +64,7 @@ #include "llavatarnamecache.h" #include "llavataractions.h" #include "lllogininstance.h" +#include "llfavoritesbar.h" #include "llviewernetwork.h" // FIRE-10122 - User@grid stored_favorites.xml - getGrid() @@ -1016,12 +1017,6 @@ void create_gesture_cb(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; @@ -1299,7 +1294,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(); @@ -1323,7 +1318,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) { @@ -1366,7 +1361,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 @@ -1440,349 +1435,16 @@ 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 "@" prefix. - * Data are stored in user home directory. - */ -class LLFavoritesOrderStorage : public LLSingleton - , public LLDestroyClass -{ - 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() : 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 sort_index_map_t; - sort_index_map_t mSortIndexes; - - typedef std::map 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 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 ); -// FIRE-10122 - User@grid stored_favorites.xml - //lldebugs << "Saved favorites for " << av_name.getLegacyName() << llendl; - //fav_llsd[av_name.getLegacyName()] = user_llsd; - std::string name = av_name.getLegacyName() + " @ " + LLGridManager::getInstance()->getGridLabel(); - lldebugs << "Saved favorites for " << name << llendl; - fav_llsd[name] = 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 ); -// FIRE-10122 - User@grid stored_favorites.xml - //lldebugs << "Removed favorites for " << av_name.getLegacyName() << llendl; - //if (fav_llsd.has(av_name.getLegacyName())) - //{ - // fav_llsd.erase(av_name.getLegacyName()); - //} - std::string name = av_name.getLegacyName() + " @ " + LLGridManager::getInstance()->getGridLabel(); - lldebugs << "Removed favorites for " << name << llendl; - if (fav_llsd.has(name)) - { - fav_llsd.erase(name); - } -// - - 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::setSortField(S32 sortField) +//{ +// LLFavoritesOrderStorage::instance().setSortIndex(mUUID, sortField); +// getSLURL(); +//} void LLViewerInventoryItem::getSLURL() { diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h old mode 100755 new mode 100644 index ebfe0079c7..a17edb7f5b --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -34,6 +34,7 @@ #include // boost::signals2::trackable +class LLInventoryPanel; class LLFolderView; class LLFolderBridge; class LLViewerInventoryCategory; @@ -61,7 +62,7 @@ public: 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 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 @@ -376,7 +377,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/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index e153a62c2e..beb2d63f6f 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -27,15 +27,12 @@ #include "llviewerprecompiledheaders.h" #include "llappviewer.h" +#include "llfloaterreg.h" #include "llviewerkeyboard.h" #include "llmath.h" #include "llagent.h" #include "llagentcamera.h" -// Remove floating chat bar -// #include "llnearbychatbar.h" -#include "fsnearbychathub.h" -#include "lllineeditor.h" -// +#include "llfloaterimnearbychat.h" #include "llviewercontrol.h" #include "llfocusmgr.h" #include "llmorphview.h" @@ -574,10 +571,7 @@ void stop_moving( EKeystate s ) void start_chat( EKeystate s ) { // start chat - // Remove floating chat bar - // LLNearbyChatBar::startChat(NULL); - FSNearbyChat::instance().showDefaultChatBar(TRUE); - // + LLFloaterIMNearbyChat::startChat(NULL); } void start_gesture( EKeystate s ) @@ -603,18 +597,18 @@ void start_gesture( EKeystate s ) ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) { // Changed for new chatbar - //if (LLNearbyChat::getInstance()->getCurrentChat().empty()) + // ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) CHUI Merge new //{ // // No existing chat in chat editor, insert '/' - // LLNearbyChat::startChat("/"); + // LLFloaterIMNearbyChat::startChat("/"); CHUI Merge new //} //else //{ // // Don't overwrite existing text in chat editor - // LLNearbyChat::startChat(NULL); + // LLFloaterIMNearbyChat::startChat(NULL); CHUI Merge new //} - FSNearbyChat::instance().showDefaultChatBar(TRUE,"/"); + FSNearbyChat::instance().showDefaultChatBar(TRUE,"/"); // CHUI Merge Check this // } } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp old mode 100755 new mode 100644 index 38c366c7d2..1109471d48 --- 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" @@ -122,6 +124,10 @@ #include "llwindow.h" #include "llpathfindingmanager.h" #include "boost/unordered_map.hpp" +// [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) +#include "rlvhandler.h" +#include "rlvlocks.h" +// [/RLVa:KB] // Firestorm includes // [RLVa:KB] - Checked: 2011-05-22 (RLVa-1.3.1a) @@ -217,11 +223,8 @@ PieMenu* gPieDetachScreenMenu = NULL; PieMenu* gPieDetachBodyPartMenus[10]; // ## Zi: Pie menu -LLMenuItemCallGL* gAFKMenu = NULL; -LLMenuItemCallGL* gBusyMenu = NULL; LLMenuItemCallGL* gAutorespondMenu = NULL; LLMenuItemCallGL* gAutorespondNonFriendsMenu = NULL; - // // Local prototypes @@ -582,8 +585,6 @@ void init_menus() gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", upload_cost); gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", upload_cost); - gAFKMenu = gMenuBarView->getChild("Set Away", TRUE); - gBusyMenu = gMenuBarView->getChild("Set Busy", TRUE); gAutorespondMenu = gMenuBarView->getChild("Set Autorespond", TRUE); gAutorespondNonFriendsMenu = gMenuBarView->getChild("Set Autorespond to non-friends", TRUE); gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE); @@ -3900,15 +3901,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); @@ -4213,7 +4205,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); } @@ -6461,26 +6454,6 @@ void show_v1_menus() // V1 menu system -WoLf // gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects..."); // } // - -class LLCommunicateBlockList : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - // Optional standalone blocklist floater - //LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - if (gSavedSettings.getBOOL("FSUseStandaloneBlocklistFloater")) - { - LLFloaterReg::showInstance("fs_blocklist", LLSD()); - } - else - { - LLFloaterSidePanelContainer::showPanel("people", "panel_block_list_sidetray", LLSD()); - } - // - return true; - } -}; - class LLWorldSetHomeLocation : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -6563,18 +6536,18 @@ class LLWorldGetAway : 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; } @@ -6875,7 +6848,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(); @@ -6908,12 +6881,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 @@ -9135,6 +9108,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 = @@ -9396,6 +9383,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) @@ -9661,11 +9664,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(); } @@ -9694,6 +9693,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) @@ -9911,8 +9915,7 @@ void initialize_menus() view_listener_t::addEnable(new LLUploadCostCalculator(), "Upload.CalculateCosts"); - - 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)); @@ -9961,17 +9964,24 @@ 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"); // Add reset camera angles menu view_listener_t::addMenu(new LLViewResetCameraAngles(), "View.ResetCameraAngles"); // - + // 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"); @@ -9979,14 +9989,14 @@ 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 LLWorldSetDoNotDisturb(), "World.SetDoNotDisturb"); view_listener_t::addMenu(new LLWorldGetAway(), "World.GetAway"); //[SJ FIRE-2177] - view_listener_t::addMenu(new LLWorldSetBusy(), "World.SetBusy"); view_listener_t::addMenu(new LLWorldGetBusy(), "World.GetBusy"); //[SJ FIRE-2177] view_listener_t::addMenu(new LLWorldSetAutorespond(), "World.SetAutorespond"); view_listener_t::addMenu(new LLWorldGetAutorespond(), "World.GetAutorespond"); //[SJ FIRE-2177] view_listener_t::addMenu(new LLWorldSetAutorespondNonFriends(), "World.SetAutorespondNonFriends"); view_listener_t::addMenu(new LLWorldGetAutorespondNonFriends(), "World.GetAutorespondNonFriends"); //[SJ FIRE-2177] - +// CHUI Merge check above view_listener_t::addMenu(new LLWorldEnableCreateLandmark(), "World.EnableCreateLandmark"); // [RLVa:KB] enable.add("World.EnablePlaceProfile", boost::bind(&enable_place_profile)); @@ -10110,6 +10120,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 35c7e5cbff..91d5b28481 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; @@ -229,8 +229,6 @@ extern PieMenu* gPieAttachBodyPartMenus[10]; extern PieMenu* gPieDetachBodyPartMenus[10]; // ## Zi: Pie Menu -extern LLMenuItemCallGL* gAFKMenu; -extern LLMenuItemCallGL* gBusyMenu; extern LLMenuItemCallGL* gAutorespondMenu; extern LLMenuItemCallGL* gAutorespondNonFriendsMenu; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 606bf91902..6a5bda5135 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -65,16 +65,11 @@ #include "llfloatersnapshot.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" +#include "llimview.h" #include "llinventoryfunctions.h" #include "llinventoryobserver.h" #include "llinventorypanel.h" -// Remove floating chat bar -// #include "llnearbychat.h" -// [FS communication UI] -//#include "llfloaternearbychat.h" -#include "fsfloaternearbychat.h" -// [FS communication UI] -// +#include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llpanelgrouplandmoney.h" @@ -118,6 +113,7 @@ #include "llpanelplaceprofile.h" #include "llviewerregion.h" // [RLVa:KB] - Checked: 2010-03-09 (RLVa-1.2.0a) +#include "rlvactions.h" #include "rlvhandler.h" #include "rlvinventory.h" #include "rlvui.h" @@ -160,6 +156,8 @@ const static boost::regex NEWLINES("\\n{1}"); #pragma warning (disable:4702) #endif +extern void on_new_message(const LLSD& msg); + // // Constants // @@ -232,78 +230,92 @@ bool friendship_offer_callback(const LLSD& notification, const LLSD& response) S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLMessageSystem* msg = gMessageSystem; const LLSD& payload = notification["payload"]; + LLNotificationPtr notification_ptr = LLNotifications::instance().find(notification["id"].asUUID()); - // add friend to recent people list - LLRecentPeople::instance().add(payload["from_id"]); + // 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"]); + switch(option) + { + case 0: + { + // accept + LLAvatarTracker::formFriendship(payload["from_id"]); - const LLUUID fid = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + 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())); + // 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 + 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); +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + // Assume that any offer notification with "getCanBeStored() == true" is the result of RLVa routing it to the notifcation syswell + /*const*/ LLNotificationsUI::LLScreenChannel* pChannel = LLNotificationsUI::LLChannelManager::instance().getNotificationScreenChannel(); + /*const*/ LLNotificationsUI::LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification["id"].asUUID()) : NULL; + if ( (!pToast) || (!pToast->getCanBeStored()) ) { - // 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; - } +// [/SL:KB] + notification_ptr->repost(); +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + } +// [/SL:KB] + } 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 // @@ -771,7 +783,7 @@ static void highlight_inventory_objects_in_panel(const std::vector& 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(); @@ -859,7 +871,13 @@ private: mSelectedItems.clear(); if (LLInventoryPanel::getActiveInventoryPanel()) { - mSelectedItems = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + std::set selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + mSelectedItems.insert(static_cast((*it)->getViewModelItem())->getUUID()); + } } mSelectedItems.erase(mMoveIntoFolderID); } @@ -894,7 +912,15 @@ private: } // get selected items (without destination folder) - selected_items_t selected_items = active_panel->getRootFolder()->getSelectionList(); + selected_items_t selected_items; + + std::set selection = LLInventoryPanel::getActiveInventoryPanel()->getRootFolder()->getSelectionList(); + for (std::set::iterator it = selection.begin(), end_it = selection.end(); + it != end_it; + ++it) + { + selected_items.insert(static_cast((*it)->getViewModelItem())->getUUID()); + } selected_items.erase(mMoveIntoFolderID); // compare stored & current sets of selected items @@ -1214,7 +1240,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) { @@ -1424,6 +1450,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) @@ -1469,6 +1497,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; @@ -1593,16 +1622,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)); @@ -1615,8 +1643,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: @@ -1680,6 +1708,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) @@ -1692,9 +1725,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: { @@ -1724,18 +1762,19 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { opener = discard_agent_offer; } - + // Optional V1-like inventory accept messages if (gSavedSettings.getBOOL("FSUseLegacyInventoryAcceptMessages")) { send_decline_response(); } // Optional V1-like inventory accept messages - - if (busy && (!mFromGroup && !mFromObject)) + if (modified_form != NULL) { - busy_message(gMessageSystem, mFromID); + modified_form->setElementEnabled("Show", false); + modified_form->setElementEnabled("Discard", false); } + break; } default: @@ -1755,6 +1794,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& { delete this; } + return false; } @@ -1860,7 +1900,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(); // [RLVa:KB] - Checked: 2010-09-23 (RLVa-1.2.1e) | Added: RLVa-1.2.1e bool fRlvNotifyAccepted = false; @@ -1981,9 +2021,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; } @@ -2174,6 +2214,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); @@ -2197,7 +2238,12 @@ void inventory_offer_handler(LLOfferInfo* info) } // Optional V1-like inventory accept messages - if( !bAutoAccept ) // Nicky D. if we auto accept, do not pester the user with stuff in the chicklet. + if (gAgent.isDoNotDisturb()) + { + send_do_not_disturb_message(gMessageSystem, info->mFromID); + } + + if( !bAutoAccept ) // Nicky D. if we auto accept, do not pester the user with stuff in the chicklet. // Inform user that there is a script floater via toast system { payload["give_inventory_notification"] = TRUE; @@ -2266,6 +2312,29 @@ 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); + +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + // Assume that any offer notification with "getCanBeStored() == true" is the result of RLVa routing it to the notifcation syswell + /*const*/ LLNotificationsUI::LLScreenChannel* pChannel = LLNotificationsUI::LLChannelManager::instance().getNotificationScreenChannel(); + /*const*/ LLNotificationsUI::LLToast* pToast = (pChannel) ? pChannel->getToastByNotificationID(notification["id"].asUUID()) : NULL; + if ( (!pToast) || (!pToast->getCanBeStored()) ) + { +// [/SL:KB] + notification_ptr->repost(); +// [SL:KB] - Patch: UI-Notifications | Checked: 2013-05-09 (Catznip-3.5) + } +// [/SL:KB] + } + return false; } static LLNotificationFunctorRegistration lure_callback_reg("TeleportOffered", lure_callback); @@ -2427,7 +2496,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: @@ -2468,16 +2537,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; @@ -2485,13 +2545,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); } @@ -2509,7 +2569,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(); @@ -2519,15 +2579,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; - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); - // [FS communication UI] - if(nearby_chat) + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); + if (nearby_chat) { nearby_chat->addMessage(chat); } - } void process_improved_im(LLMessageSystem *msg, void **user_data) @@ -2624,19 +2680,18 @@ 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_autorespond = gAgent.getAutorespond(); BOOL is_autorespond_nonfriends = gAgent.getAutorespondNonFriends(); BOOL is_autorespond_muted = gSavedPerAccountSettings.getBOOL("FSSendMutedAvatarResponse"); 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; static LLCachedControl accept_im_from_only_friend(gSavedSettings, "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; @@ -2654,7 +2709,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; @@ -2665,7 +2720,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LLPostponedNotification::add(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() @@ -2674,7 +2729,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // do nothing -- don't distract newbies in // Prelude with global IMs } -// [RLVa:KB] - Checked: 2011-05-28 (RLVa-1.4.0a) | Modified: RLVa-1.4.0a +// [RLVa:KB] - Checked: 2011-05-28 (RLVa-1.4.0) else if ( (rlv_handler_t::isEnabled()) && (offline == IM_ONLINE) && ("@version" == message) && (!is_muted) && ((!accept_im_from_only_friend) || (is_friend)) ) { @@ -2707,33 +2762,35 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // 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; - std::string response; - LLAgentUI::buildFullname(my_name); - if (is_busy) - { - response = gSavedPerAccountSettings.getString("BusyModeResponse"); - } - else if (is_autorespond_nonfriends && !is_friend) - { - response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse"); - } - else if (is_autorespond) - { - response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse"); - } - 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); + // CHUI Merge LL changed this + //std::string response; + //LLAgentUI::buildFullname(my_name); + //if (is_busy) + //{ + // response = gSavedPerAccountSettings.getString("BusyModeResponse"); + //} + //else if (is_autorespond_nonfriends && !is_friend) + //{ + // response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse"); + //} + //else if (is_autorespond) + //{ + // response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse"); + //} + //pack_instant_message( CHUI Merge LL removed this and below, comented out above to be looked at later + // gMessageSystem, + // gAgent.getID(), + // FALSE, + // gAgent.getSessionID(), + // from_id, + // my_name, + // response, + // IM_ONLINE, + // IM_BUSY_AUTO_RESPONSE, + // session_id); + //gAgent.sendReliableMessage(); + // CHUI Merge End } // checkfor and process reqinfo @@ -2755,6 +2812,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -2809,12 +2867,19 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) bool mute_im = is_muted; 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, IM_OFFLINE == offline); + gIMMgr->addNotifiedNonFriendSessionID(session_id); + } + mute_im = true; } -// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c +// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) // Don't block offline IMs, or IMs from Lindens - if ( (rlv_handler_t::isEnabled()) && (offline != IM_OFFLINE) && (!is_linden) && (!gRlvHandler.canReceiveIM(from_id)) ) + if ( (rlv_handler_t::isEnabled()) && (offline != IM_OFFLINE) && (!RlvActions::canReceiveIM(from_id)) && (!LLMuteList::getInstance()->isLinden(original_name) )) { if (!mute_im) RlvUtil::sendBusyMessage(from_id, RlvStrings::getString(RLV_STRING_BLOCKED_RECVIM_REMOTE), session_id); @@ -2833,21 +2898,19 @@ 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 (!gIMMgr->isNonFriendSessionNotified(session_id)) - { - std::string message = LLTrans::getString("IM_unblock_only_groups_friends"); - gIMMgr->addMessage(session_id, from_id, name, message); - gIMMgr->addNotifiedNonFriendSessionID(session_id); - } - - mute_im = true; - } -*/ - if (!mute_im || is_linden) +// bool mute_im = is_muted; +// 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, IM_OFFLINE == offline); +// gIMMgr->addNotifiedNonFriendSessionID(session_id); +// } +// +// mute_im = true; +// } + if (!mute_im) { // checkfor and process reqinfo message = FSData::getInstance()->processRequestForInfo(from_id,message,name,session_id); @@ -2858,6 +2921,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) from_id, name, buffer, + IM_OFFLINE == offline, LLStringUtil::null, dialog, parent_estate_id, @@ -3023,7 +3087,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; @@ -3046,11 +3113,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 { @@ -3134,7 +3199,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) @@ -3145,9 +3210,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); } @@ -3202,69 +3269,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; -// } -// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c - LLIMModel::LLIMSession* pIMSession = LLIMModel::instance().findIMSession(session_id); - if (!pIMSession) - { - return; - } - - if ( (gRlvHandler.hasBehaviour(RLV_BHVR_RECVIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_RECVIMFROM)) ) - { - switch (pIMSession->mSessionType) - { - case LLIMModel::LLIMSession::GROUP_SESSION: // Group chat - if ( (from_id != gAgent.getID()) && (!gRlvHandler.canReceiveIM(session_id)) ) - return; - break; - case LLIMModel::LLIMSession::ADHOC_SESSION: // Conference chat - if ( (from_id != gAgent.getID()) && (!gRlvHandler.canReceiveIM(from_id)) ) - message = RlvStrings::getString(RLV_STRING_BLOCKED_RECVIM); - break; - default: - RLV_ASSERT(false); - return; - } - } -// [/RLVa:KB] - - // standard message, not from system - std::string saved; - if(offline == IM_OFFLINE) - { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); - } - buffer = saved + message; - 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; } @@ -3339,16 +3346,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. - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); - // [FS communication UI] + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("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); @@ -3387,8 +3390,78 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) LLPostponedNotification::add(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; } @@ -3399,19 +3472,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 - // Then let's do that! :) - //buffer = llformat("%s (%s): %s", name.c_str(), "busy response", message.c_str()); - buffer = llformat("%s (%s): %s", name.c_str(), LLTrans::getString("BusyResponse").c_str(), message.c_str()); - gIMMgr->addMessage(session_id, from_id, name, buffer); + gIMMgr->addMessage(session_id, from_id, name, message); } break; @@ -3429,12 +3498,12 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { return; } -// else if (is_busy) +// else if (is_do_not_disturb) // [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Added: RLVa-1.2.2c - else if ( (is_busy) && (!fRlvSummon) ) + else if ( (is_do_not_disturb) && (!fRlvSummon) ) // [/RLVa:KB] { - busy_message(msg,from_id); + send_do_not_disturb_message(msg, from_id); } else { @@ -3496,13 +3565,13 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if (!gRlvHandler.canTeleportViaLure(from_id)) { RlvUtil::sendBusyMessage(from_id, RlvStrings::getString(RLV_STRING_BLOCKED_TPLURE_REMOTE)); - if (is_busy) - busy_message(msg,from_id); + if (is_do_not_disturb) + send_do_not_disturb_message(msg, from_id); return; } // Censor lure message if: 1) restricted from receiving IMs from the sender, or 2) @showloc=n restricted - if ( (!gRlvHandler.canReceiveIM(from_id)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ) + if ( (!RlvActions::canReceiveIM(from_id)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ) { message = RlvStrings::getString(RLV_STRING_HIDDEN); } @@ -3553,8 +3622,8 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) if ( (rlv_handler_t::isEnabled()) && ((gRlvHandler.hasBehaviour(RLV_BHVR_ACCEPTTP)) || (fRlvSummon)) ) { gRlvHandler.setCanCancelTp(false); - if (is_busy) - busy_message(msg,from_id); + if (is_do_not_disturb) + send_do_not_disturb_message(msg, from_id); LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); } else @@ -3695,17 +3764,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); + } // [SL:KB] - Patch: UI-Notifications | Checked: 2011-04-11 (Catznip-2.5.0a) | Added: Catznip-2.5.0a args["NAME_LABEL"] = LLSLURL("agent", from_id, "completename").getSLURLString(); // [/SL:KB] @@ -3743,12 +3811,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(¬ification_display_name_callback, - _1, - _2, - "FriendshipAccepted", - args, - payload)); + LLAvatarNameCache::get(from_id, boost::bind(¬ification_display_name_callback,_1,_2,"FriendshipAccepted",args,payload)); } break; @@ -3766,15 +3829,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(), @@ -3782,7 +3845,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(); } } @@ -3817,7 +3881,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 @@ -3865,7 +3929,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 @@ -4005,7 +4069,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 { @@ -4037,7 +4101,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // } - BOOL is_busy = gAgent.getBusy(); + BOOL is_do_not_disturb = gAgent.isDoNotDisturb(); BOOL is_muted = FALSE; BOOL is_linden = FALSE; @@ -4076,7 +4140,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) @@ -4390,7 +4454,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 = ""; @@ -4432,7 +4496,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) @@ -4451,6 +4514,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); } } @@ -4986,14 +5054,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()) @@ -6783,11 +6851,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)); } } @@ -7549,10 +7615,7 @@ void notify_cautioned_script_question(const LLSD& notification, const LLSD& resp // [RLVa:KB] - Checked: 2012-07-28 (RLVa-1.4.7) if (caution) { - // [FS communication UI] - //LLFloaterNearbyChat* nearby_chat = LLFloaterNearbyChat::getInstance(); - FSFloaterNearbyChat* nearby_chat = FSFloaterNearbyChat::getInstance(); - // [FS communication UI] + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance("nearby_chat"); if(nearby_chat) { LLChat chat_msg(notice.getString()); @@ -8192,14 +8255,14 @@ bool handle_lure_callback(const LLSD& notification, const LLSD& response) if(0 == option) { -// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c - if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIMTO)) ) +// [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) + if ( (RlvActions::hasBehaviour(RLV_BHVR_SENDIM)) || (RlvActions::hasBehaviour(RLV_BHVR_SENDIMTO)) ) { // Filter the lure message if one of the recipients of the lure can't be sent an IM to for (LLSD::array_const_iterator it = notification["payload"]["ids"].beginArray(); it != notification["payload"]["ids"].endArray(); ++it) { - if (!gRlvHandler.canSendIM(it->asUUID())) + if (!RlvActions::canSendIM(it->asUUID())) { text = RlvStrings::getString(RLV_STRING_HIDDEN); break; @@ -8236,7 +8299,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. @@ -8373,7 +8435,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 ae0d5a074b..f1a14b33e3 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); @@ -158,6 +157,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); @@ -233,6 +234,7 @@ public: void forceResponse(InventoryOfferResponse response); + static std::string mResponderType; EInstantMessage mIM; LLUUID mFromID; BOOL mFromGroup; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp old mode 100755 new mode 100644 index ea5ca5d2a4..67ebf1e41d --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2752,7 +2752,8 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS if(ft && (0 == error_code) && (object = gObjectList.findObject(ft->mTaskID))) { - object->loadTaskInvFile(ft->mFilename); + if (object->loadTaskInvFile(ft->mFilename)) + { // Crashfix, not sure why object->mInventory can be 0 if( !object->mInventory ) @@ -2763,22 +2764,30 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS } // - LLInventoryObject::object_list_t::iterator it = object->mInventory->begin(); - LLInventoryObject::object_list_t::iterator end = object->mInventory->end(); - std::list& pending_lst = object->mPendingInventoryItemsIDs; + LLInventoryObject::object_list_t::iterator it = object->mInventory->begin(); + LLInventoryObject::object_list_t::iterator end = object->mInventory->end(); + std::list& pending_lst = object->mPendingInventoryItemsIDs; - for (; it != end && pending_lst.size(); ++it) - { - LLViewerInventoryItem* item = dynamic_cast(it->get()); - if(item && item->getType() != LLAssetType::AT_CATEGORY) + for (; it != end && pending_lst.size(); ++it) { - std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.begin(), item->getAssetUUID()); - if (id_it != pending_lst.end()) + LLViewerInventoryItem* item = dynamic_cast(it->get()); + if(item && item->getType() != LLAssetType::AT_CATEGORY) { - pending_lst.erase(id_it); + std::list::iterator id_it = std::find(pending_lst.begin(), pending_lst.begin(), item->getAssetUUID()); + if (id_it != pending_lst.end()) + { + pending_lst.erase(id_it); + } } } } + else + { + // MAINT-2597 - crash when trying to edit a no-mod object + // Somehow get an contents inventory response, but with an invalid stream (possibly 0 size?) + // Stated repro was specific to no-mod objects so failing without user interaction should be safe. + llwarns << "Trying to load invalid task inventory file. Ignoring file contents." << llendl; + } } else { @@ -2790,7 +2799,7 @@ void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code, LLExtS delete ft; } -void LLViewerObject::loadTaskInvFile(const std::string& filename) +BOOL LLViewerObject::loadTaskInvFile(const std::string& filename) { std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename); llifstream ifs(filename_and_local_path); @@ -2837,8 +2846,11 @@ void LLViewerObject::loadTaskInvFile(const std::string& filename) { llwarns << "unable to load task inventory: " << filename_and_local_path << llendl; + return FALSE; } doInventoryCallback(); + + return TRUE; } void LLViewerObject::doInventoryCallback() diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index c7f50f76bb..75e478c0d0 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -34,7 +34,7 @@ #include "llhudicon.h" #include "llinventory.h" #include "llrefcount.h" -#include "llprimitive.h" +#include "../llprimitive/llprimitive.h" #include "lluuid.h" #include "llvoinventorylistener.h" #include "object_flags.h" @@ -673,7 +673,7 @@ protected: // static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status); - void loadTaskInvFile(const std::string& filename); + BOOL loadTaskInvFile(const std::string& filename); void doInventoryCallback(); BOOL isOnMap(); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index f6aa0d393f..6d73d2917b 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -1011,14 +1011,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/llviewerregion.cpp b/indra/newview/llviewerregion.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 69a5192d4f..d8a9a0273c 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -41,6 +41,7 @@ #include "lltexturefetch.h" #include "llviewerobjectlist.h" #include "llviewertexturelist.h" +#include "lltexlayer.h" #include "lltexlayerparams.h" #include "llsurface.h" #include "llvlmanager.h" @@ -54,7 +55,6 @@ #include "llviewerregion.h" #include "llvoavatar.h" #include "llvoavatarself.h" -#include "llviewertexlayer.h" #include "llviewerwindow.h" // *TODO: remove, only used for width/height #include "llworld.h" #include "llfeaturemanager.h" @@ -417,7 +417,7 @@ F32 gWorstLandCompression = 0.f, gWorstWaterCompression = 0.f; U32 gTotalWorldBytes = 0, gTotalObjectBytes = 0, gTotalTextureBytes = 0, gSimPingCount = 0; U32 gObjectBits = 0; F32 gAvgSimPing = 0.f; -U32 gTotalTextureBytesPerBoostLevel[LLGLTexture::MAX_GL_IMAGE_CATEGORY] = {0}; +U32 gTotalTextureBytesPerBoostLevel[LLViewerTexture::MAX_GL_IMAGE_CATEGORY] = {0}; extern U32 gVisCompared; extern U32 gVisTested; @@ -530,10 +530,10 @@ class ViewerStatsResponder : public LLHTTPClient::Responder public: ViewerStatsResponder() { } - void errorWithContent(U32 statusNum, const std::string& reason, const LLSD& content) + void error(U32 statusNum, const std::string& reason) { - llwarns << "ViewerStatsResponder error [status:" << statusNum << "]: " - << content << llendl; + llinfos << "ViewerStatsResponder::error " << statusNum << " " + << reason << llendl; } void result(const LLSD& content) @@ -733,57 +733,41 @@ void send_stats() LLHTTPClient::post(url, body, new ViewerStatsResponder()); } -LLViewerStats::PhaseMap::PhaseMap() -{ -} - -LLTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name) +LLFrameTimer& LLViewerStats::PhaseMap::getPhaseTimer(const std::string& phase_name) { phase_map_t::iterator iter = mPhaseMap.find(phase_name); if (iter == mPhaseMap.end()) { - LLTimer timer; + LLFrameTimer timer; mPhaseMap[phase_name] = timer; } - LLTimer& timer = mPhaseMap[phase_name]; + LLFrameTimer& timer = mPhaseMap[phase_name]; return timer; } void LLViewerStats::PhaseMap::startPhase(const std::string& phase_name) { - LLTimer& timer = getPhaseTimer(phase_name); + LLFrameTimer& timer = getPhaseTimer(phase_name); lldebugs << "startPhase " << phase_name << llendl; - timer.start(); + timer.unpause(); } -void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name) +void LLViewerStats::PhaseMap::stopAllPhases() { - phase_map_t::iterator iter = mPhaseMap.find(phase_name); - if (iter != mPhaseMap.end()) + for (phase_map_t::iterator iter = mPhaseMap.begin(); + iter != mPhaseMap.end(); ++iter) { + const std::string& phase_name = iter->first; if (iter->second.getStarted()) { - // Going from started to stopped state - record stats. - iter->second.stop(); + // Going from started to paused state - record stats. + recordPhaseStat(phase_name,iter->second.getElapsedTimeF32()); } + lldebugs << "stopPhase (all) " << phase_name << llendl; + iter->second.pause(); } } -bool LLViewerStats::PhaseMap::getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed) -{ - phase_map_t::iterator iter = mPhaseMap.find(phase_name); - if (iter != mPhaseMap.end()) - { - elapsed = iter->second.getElapsedTimeF32(); - completed = !iter->second.getStarted(); - return true; - } - else - { - return false; - } -} - void LLViewerStats::PhaseMap::clearPhases() { lldebugs << "clearPhases" << llendl; @@ -802,3 +786,59 @@ LLSD LLViewerStats::PhaseMap::dumpPhases() } return result; } + +// static initializer +//static +LLViewerStats::phase_stats_t LLViewerStats::PhaseMap::sStats; + +LLViewerStats::PhaseMap::PhaseMap() +{ +} + + +void LLViewerStats::PhaseMap::stopPhase(const std::string& phase_name) +{ + phase_map_t::iterator iter = mPhaseMap.find(phase_name); + if (iter != mPhaseMap.end()) + { + if (iter->second.getStarted()) + { + // Going from started to stopped state - record stats. + iter->second.stop(); + } + } +} +// static +LLViewerStats::StatsAccumulator& LLViewerStats::PhaseMap::getPhaseStats(const std::string& phase_name) +{ + phase_stats_t::iterator it = sStats.find(phase_name); + if (it == sStats.end()) + { + LLViewerStats::StatsAccumulator new_stats; + sStats[phase_name] = new_stats; + } + return sStats[phase_name]; +} + +// static +void LLViewerStats::PhaseMap::recordPhaseStat(const std::string& phase_name, F32 value) +{ + LLViewerStats::StatsAccumulator& stats = getPhaseStats(phase_name); + stats.push(value); +} + + +bool LLViewerStats::PhaseMap::getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed) +{ + phase_map_t::iterator iter = mPhaseMap.find(phase_name); + if (iter != mPhaseMap.end()) + { + elapsed = iter->second.getElapsedTimeF32(); + completed = !iter->second.getStarted(); + return true; + } + else + { + return false; + } +} diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h old mode 100755 new mode 100644 index e74fb36e97..6b2461be41 --- a/indra/newview/llviewerstats.h +++ b/indra/newview/llviewerstats.h @@ -279,24 +279,29 @@ public: // Phase tracking (originally put in for avatar rezzing), tracking // progress of active/completed phases for activities like outfit changing. - typedef std::map phase_map_t; + typedef std::map phase_map_t; typedef std::map phase_stats_t; class PhaseMap { private: phase_map_t mPhaseMap; + static phase_stats_t sStats; public: PhaseMap(); - LLTimer& getPhaseTimer(const std::string& phase_name); + LLFrameTimer& getPhaseTimer(const std::string& phase_name); bool getPhaseValues(const std::string& phase_name, F32& elapsed, bool& completed); void startPhase(const std::string& phase_name); void stopPhase(const std::string& phase_name); + void stopAllPhases(); void clearPhases(); LLSD dumpPhases(); + static StatsAccumulator& getPhaseStats(const std::string& phase_name); + static void recordPhaseStat(const std::string& phase_name, F32 value); phase_map_t::iterator begin() { return mPhaseMap.begin(); } phase_map_t::iterator end() { return mPhaseMap.end(); } }; + private: F64 mStats[ST_COUNT]; diff --git a/indra/newview/llviewertexlayer.cpp b/indra/newview/llviewertexlayer.cpp old mode 100755 new mode 100644 index f3c9921819..777e1f9c76 --- a/indra/newview/llviewertexlayer.cpp +++ b/indra/newview/llviewertexlayer.cpp @@ -363,7 +363,7 @@ BOOL LLViewerTexLayerSetBuffer::requestUpdateImmediate() void LLViewerTexLayerSetBuffer::doUpload() { LLViewerTexLayerSet* layer_set = getViewerTexLayerSet(); - llinfos << "Uploading baked " << layer_set->getBodyRegionName() << llendl; + LL_DEBUGS("Avatar") << "Uploading baked " << layer_set->getBodyRegionName() << llendl; LLViewerStats::getInstance()->incStat(LLViewerStats::ST_TEX_BAKES); // Don't need caches since we're baked now. (note: we won't *really* be baked diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp old mode 100755 new mode 100644 index e0b8b37172..2437f1525b --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -917,6 +917,10 @@ LLViewerFetchedTexture::LLViewerFetchedTexture(const LLUUID& id, FTType f_type, { init(TRUE) ; mFTType = f_type; + if (mFTType == FTT_HOST_BAKE) + { + mCanUseHTTP = false; + } generateGLTexture() ; } @@ -1900,7 +1904,10 @@ bool LLViewerFetchedTexture::updateFetch() // We finished but received no data if (current_discard < 0) { - llwarns << "!mIsFetching, setting as missing" << llendl; + llwarns << "!mIsFetching, setting as missing, decode_priority " << decode_priority + << " mRawDiscardLevel " << mRawDiscardLevel + << " current_discard " << current_discard + << llendl; setIsMissingAsset(); desired_discard = -1; } @@ -2033,11 +2040,13 @@ bool LLViewerFetchedTexture::updateFetch() } else if (mHasFetcher && !mIsFetching) { - // Only delete requests that haven't receeived any network data for a while + // Only delete requests that haven't received any network data + // for a while. Note - this is the normal mechanism for + // deleting requests, not just a place to handle timeouts. const F32 FETCH_IDLE_TIME = 5.f; if (mLastPacketTimer.getElapsedTimeF32() > FETCH_IDLE_TIME) { -// llinfos << "Deleting request: " << getID() << " Discard: " << current_discard << " <= min:" << mMinDiscardLevel << " or priority == 0: " << decode_priority << llendl; + LL_DEBUGS("Texture") << "exceeded idle time " << FETCH_IDLE_TIME << ", deleting request: " << getID() << llendl; LLAppViewer::getTextureFetch()->deleteRequest(getID(), true); mHasFetcher = FALSE; } diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerwearable.cpp b/indra/newview/llviewerwearable.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index baa3b9b51b..fe296a96f2 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -37,9 +37,11 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llcommunicationchannel.h" #include "llfloaterreg.h" #include "lllogininstance.h" // #include "llmeshrepository.h" +#include "llnotificationhandler.h" #include "llpanellogin.h" #include "llviewerkeyboard.h" #include "llviewermenu.h" @@ -57,6 +59,7 @@ // linden library includes #include "llaudioengine.h" // mute on minimize +#include "llchatentry.h" #include "indra_constants.h" #include "llassetstorage.h" #include "llerrorcontrol.h" @@ -130,6 +133,7 @@ #include "llmorphview.h" #include "llmoveview.h" #include "llnavigationbar.h" +#include "llnotificationhandler.h" #include "llpaneltopinfobar.h" #include "llpopupview.h" #include "llpreviewtexture.h" @@ -190,7 +194,7 @@ #include "llviewerjoystick.h" #include "llviewernetwork.h" #include "llpostprocess.h" -// #include "llnearbychatbar.h" // Remove floating chat bar +#include "llfloaterimnearbychat.h" #include "llagentui.h" #include "llwearablelist.h" @@ -200,10 +204,6 @@ #include "llfloaternotificationsconsole.h" -// Remove floating chat bar -// #include "llnearbychat.h" -#include "fsnearbychathub.h" -// #include "llwindowlistener.h" #include "llviewerwindowlistener.h" #include "llpaneltopinfobar.h" @@ -774,7 +774,7 @@ public: if(log_texture_traffic) { U32 old_y = ypos ; - for(S32 i = LLGLTexture::BOOST_NONE; i < LLGLTexture::MAX_GL_IMAGE_CATEGORY; i++) + for(S32 i = LLViewerTexture::BOOST_NONE; i < LLViewerTexture::MAX_GL_IMAGE_CATEGORY; i++) { if(gTotalTextureBytesPerBoostLevel[i] > 0) { @@ -1441,6 +1441,43 @@ void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item) BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height) { + // *TODO: Enable similar information output for other platforms? DK 2011-02-18 +#if LL_WINDOWS + if (gHeadlessClient) + { + HWND window_handle = (HWND)window->getPlatformWindow(); + PAINTSTRUCT ps; + HDC hdc; + + RECT wnd_rect; + wnd_rect.left = 0; + wnd_rect.top = 0; + wnd_rect.bottom = 200; + wnd_rect.right = 500; + + hdc = BeginPaint(window_handle, &ps); + //SetBKColor(hdc, RGB(255, 255, 255)); + FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); + + std::string temp_str; + temp_str = llformat( "FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ + LLViewerStats::getInstance()->mFPSStat.getMeanPerSec(), + LLViewerStats::getInstance()->mSimPhysicsFPS.getPrev(0), + LLViewerStats::getInstance()->mSimTimeDilation.getPrev(0)); + S32 len = temp_str.length(); + TextOutA(hdc, 0, 0, temp_str.c_str(), len); + + + LLVector3d pos_global = gAgent.getPositionGlobal(); + temp_str = llformat( "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]); + len = temp_str.length(); + TextOutA(hdc, 0, 25, temp_str.c_str(), len); + + TextOutA(hdc, 0, 50, "Set \"HeadlessClient FALSE\" in settings.ini file to reenable", 61); + EndPaint(window_handle, &ps); + return TRUE; + } +#endif return FALSE; } @@ -1571,16 +1608,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(&LLNotification::getType, "alert")); - LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy(&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. @@ -1588,13 +1626,23 @@ LLViewerWindow::LLViewerWindow(const Params& p) LLViewerWindow::sMovieBaseName = "SLmovie"; resetSnapshotLoc(); + + /* + LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, + BOOL fullscreen, + BOOL clearBg, + BOOL disable_vsync, + BOOL ignore_pixel_depth, + U32 fsaa_samples) + */ // create window - const BOOL clear_bg = FALSE; mWindow = LLWindowManager::createWindow(this, p.title, p.name, p.x, p.y, p.width, p.height, 0, p.fullscreen, - clear_bg, + gHeadlessClient, gSavedSettings.getBOOL("DisableVerticalSync"), + !gHeadlessClient, p.ignore_pixel_depth, gSavedSettings.getBOOL("RenderDeferred") ? 0 : gSavedSettings.getU32("RenderFSAASamples")); //don't use window level anti-aliasing if FBOs are enabled @@ -1701,8 +1749,7 @@ LLViewerWindow::LLViewerWindow(const Params& p) // Init the image list. Must happen after GL is initialized and before the images that // LLViewerWindow needs are requested. - const BOOL SKIP_ANALYZE_ALPHA=FALSE; - LLImageGL::initClass(LLGLTexture::MAX_GL_IMAGE_CATEGORY, SKIP_ANALYZE_ALPHA) ; + LLImageGL::initClass(LLViewerTexture::MAX_GL_IMAGE_CATEGORY) ; gTextureList.init(); LLViewerTextureManager::init() ; gBumpImageList.init(); @@ -1871,8 +1918,8 @@ void LLViewerWindow::initBase() gDebugView->init(); gToolTipView = getRootView()->getChild("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("progress_view"); @@ -2692,40 +2739,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 ) { - static LLCachedControl ArrowKeysAlwaysMove(gSavedSettings, "ArrowKeysAlwaysMove"); // Attempt to speed up things a little - - // Remove floating chat bar - // LLNearbyChatBar* nearby_chat = LLFloaterReg::findTypedInstance("chat_bar"); - - // if (nearby_chat) - // { - // LLLineEditor* chat_editor = nearby_chat->getChatBox(); - - - // arrow keys move avatar while chatting hack - // if (chat_editor && chat_editor->hasFocus()) - // { - // // If text field is empty, there's no point in trying to move - // // cursor with arrow keys, so allow movement - // if (chat_editor->getText().empty() - // || gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) - if(FSNearbyChat::instance().defaultChatBarHasFocus() && - (FSNearbyChat::instance().defaultChatBarIsIdle() || - // Attempt to speed up things a little - // gSavedSettings.getBOOL("ArrowKeysAlwaysMove"))) - ArrowKeysAlwaysMove)) - // - // + if ((focusedFloaterName == "nearby_chat") || (focusedFloaterName == "im_container") || (focusedFloaterName == "impanel")) + { + if (gSavedSettings.getBOOL("ArrowKeysAlwaysMove")) { // let Control-Up and Control-Down through for chat line history, - // Control-Right and Control-Left too for chat line editing if (!(key == KEY_UP && mask == MASK_CONTROL) && !(key == KEY_DOWN && mask == MASK_CONTROL) - && !(key == KEY_LEFT && mask == MASK_CONTROL) - && !(key == KEY_RIGHT && mask == MASK_CONTROL)) + && !(key == KEY_UP && mask == MASK_ALT) + && !(key == KEY_DOWN && mask == MASK_ALT)) { switch(key) { @@ -2743,11 +2770,9 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) break; } } - // Remove floating chat bar - // } - // } - // } + } + if (keyboard_focus->handleKey(key, mask, FALSE)) { return TRUE; @@ -2783,18 +2808,21 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // !keyboard_focus && key < 0x80 && (mask == MASK_NONE || mask == MASK_SHIFT) ) { - // Remove floating chat bar - // LLLineEditor* chat_editor = LLFloaterReg::getTypedInstance("chat_bar")->getChatBox(); + // Initialize nearby chat if it's missing + LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance("nearby_chat"); + if (!nearby_chat) + { + LLSD name("im_container"); + LLFloaterReg::toggleInstanceOrBringToFront(name); + } - // if (chat_editor) - // { - // // passing NULL here, character will be added later when it is handled by character handler. - // LLNearbyChatBar::getInstance()->startChat(NULL); - // return TRUE; - // } - FSNearbyChat::instance().showDefaultChatBar(TRUE); - return TRUE; - // + LLChatEntry* chat_editor = LLFloaterReg::findTypedInstance("nearby_chat")->getChatBox(); + if (chat_editor) + { + // passing NULL here, character will be added later when it is handled by character handler. + nearby_chat->startChat(NULL); + return TRUE; + } } // give menus a chance to handle unmodified accelerator keys @@ -2821,7 +2849,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 @@ -5423,20 +5454,6 @@ LLRect LLViewerWindow::getChatConsoleRect() //---------------------------------------------------------------------------- -//static -bool LLViewerWindow::onAlert(const LLSD& notify) -{ - LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); - - // 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 dad1a570af..8abf37199f 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" @@ -417,7 +418,6 @@ public: private: bool shouldShowToolTipFor(LLMouseHandler *mh); - static bool onAlert(const LLSD& notify); void switchToolByMask(MASK mask); void destroyWindow(); @@ -434,6 +434,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 b2e3edeed0..69254fdba9 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -59,6 +59,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; enum ERenderName { @@ -219,6 +223,25 @@ struct LLTextureMaskData ** **/ +//------------------------------------------------------------------------ +// LLVOAvatarBoneInfo +// Trans/Scale/Rot etc. info about each avatar bone. Used by LLVOAvatarSkeleton. +//------------------------------------------------------------------------ +struct LLVOAvatarCollisionVolumeInfo : public LLInitParam::Block +{ + LLVOAvatarCollisionVolumeInfo() + : name("name"), + pos("pos"), + rot("rot"), + scale("scale") + {} + + Mandatory name; + Mandatory pos, + rot, + scale; +}; + struct LLAppearanceMessageContents { LLAppearanceMessageContents(): @@ -237,6 +260,50 @@ struct LLAppearanceMessageContents std::vector mParams; }; +struct LLVOAvatarChildJoint : public LLInitParam::ChoiceBlock + { + Alternative > bone; + Alternative collision_volume; + + LLVOAvatarChildJoint() + : bone("bone"), + collision_volume("collision_volume") + {} +}; + + + +struct LLVOAvatarBoneInfo : public LLInitParam::Block +{ + LLVOAvatarBoneInfo() + : pivot("pivot") + {} + + Mandatory pivot; + Multiple children; +}; + +//------------------------------------------------------------------------ +// LLVOAvatarSkeletonInfo +// Overall avatar skeleton +//------------------------------------------------------------------------ +struct LLVOAvatarSkeletonInfo : public LLInitParam::Block +{ + LLVOAvatarSkeletonInfo() + : skeleton_root(""), + num_bones("num_bones"), + num_collision_volumes("num_collision_volumes"), + version("version") + {} + + Mandatory version; + Mandatory num_bones, + num_collision_volumes; + Mandatory skeleton_root; +}; + + + //----------------------------------------------------------------------------- // class LLBodyNoiseMotion //----------------------------------------------------------------------------- @@ -622,10 +689,10 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mBelowWater(FALSE), mLastAppearanceBlendTime(0.f), mAppearanceAnimating(FALSE), - mNameString(), + mNameIsSet(false), mTitle(), mNameAway(false), - mNameBusy(false), + mNameDoNotDisturb(false), mNameMute(false), mNameAppearance(false), mNameFriend(false), @@ -649,7 +716,9 @@ LLVOAvatar::LLVOAvatar(const LLUUID& id, mLastRezzedStatus(-1), mIsEditingAppearance(FALSE), mUseLocalAppearance(FALSE), - mUseServerBakes(FALSE) // FIXME DRANO consider using boost::optional, defaulting to unknown. + mUseServerBakes(FALSE), // FIXME DRANO consider using boost::optional, defaulting to unknown. + mLastUpdateRequestCOFVersion(-1), + mLastUpdateReceivedCOFVersion(-1) { //VTResume(); // VTune @@ -1129,7 +1198,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 ); @@ -1537,6 +1606,7 @@ LLViewerObject* LLVOAvatar::lineSegmentIntersectRiggedAttachments(const LLVector return hit; } + LLVOAvatar* LLVOAvatar::asAvatar() { return this; @@ -1642,15 +1712,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); + } + } } } @@ -1837,11 +1907,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) { @@ -2154,7 +2224,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { LLVector3 tagPos = mRoot->getWorldPosition(); tagPos[VZ] -= mPelvisToFoot; - tagPos[VZ] += ( mBodySize[VZ] + mAvatarOffset[VZ] + 0.125f ); + tagPos[VZ] += ( mBodySize[VZ] + 0.125f ); mVoiceVisualizer->setVoiceSourceWorldPosition( tagPos ); } }//if ( voiceEnabled ) @@ -2396,10 +2466,10 @@ void LLVOAvatar::idleUpdateLoadingEffect() { LL_INFOS("Avatar") << avString() << "self isFullyLoaded, mFirstFullyVisible" << LL_ENDL; mFirstFullyVisible = FALSE; - LLAppearanceMgr::instance().onFirstFullyVisible(); + LLAppearanceMgr::instance().onFirstFullyVisible(); AOEngine::instance().onLoginComplete(); // ## Zi: Animation Overrider - } + } if (isFullyLoaded() && mFirstFullyVisible && !isSelf()) { LL_INFOS("Avatar") << avString() << "other isFullyLoaded, mFirstFullyVisible" << LL_ENDL; @@ -2585,7 +2655,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) mVisibleTyping = visible_typing; new_name = TRUE; } - + // [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.2a) | Added: RLVa-0.2.0b if (fRlvShowNames) { @@ -2639,7 +2709,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last) if (!mNameText) { mNameText = static_cast( 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); @@ -2648,10 +2718,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); // Wolfspirit: Following thing is already handled in LLHUDNameTag::lineSegmentIntersect // Fixing bubblechat alpha flashing with commenting this out. @@ -2677,9 +2746,17 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) bool fRlvShowNames = gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES); // [/RLVa:KB] 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 = (!isSelf() && LLMuteList::getInstance()->isMuted(getID())); + bool is_muted; + if (isSelf()) + { + is_muted = false; + } + else + { + is_muted = LLMuteList::getInstance()->isMuted(getID()); + } // bool is_friend = LLAvatarTracker::instance().isBuddy(getID()); // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Added: RLVa-1.2.2a bool is_friend = (!fRlvShowNames) && (LLAvatarTracker::instance().isBuddy(getID())); @@ -2763,12 +2840,12 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // // Rebuild name tag if state change detected - if (mNameString.empty() + if (!mNameIsSet || 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 @@ -2794,7 +2871,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) @@ -2802,9 +2879,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) @@ -2825,7 +2902,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 @@ -2837,7 +2914,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) std::string title_str = title->getString(); LLStringFn::replace_ascii_controlchars(title_str,LL_UNKNOWN_CHAR); addNameTagLine(title_str, name_tag_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } static LLUICachedControl show_display_names("NameTagShowDisplayNames"); @@ -2845,14 +2922,14 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) static LLUICachedControl colorize_username("FSColorUsername"); // FIRE-1061 static LLUICachedControl show_legacynames("FSNameTagShowLegacyUsernames"); - 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::invalidateNameTag, _1)); + // Force a rebuild at next idle + // Note: do not connect a callback on idle(). + clearNameTag(); } // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a @@ -2865,17 +2942,17 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) if (mClientTagData.has("name") && !mClientTagData["name"].asString().empty()) { - addNameTagLine(av_name.mDisplayName+" (" + mClientTagData["name"].asString() + ")",name_tag_color,LLFontGL::NORMAL, LLFontGL::getFontSansSerif()); + addNameTagLine(av_name.getDisplayName()+" (" + mClientTagData["name"].asString() + ")",name_tag_color,LLFontGL::NORMAL, LLFontGL::getFontSansSerif()); } else { - 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 // FIRE-1061 @@ -2887,13 +2964,13 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // // Show user name as legacy name if selected -- TS - std::string username = av_name.mUsername; + std::string username = av_name.getUserName(); if (show_legacynames) { username = LLCacheName::buildFullName( firstname->getString(), lastname->getString() ); } addNameTagLine(username, username_color, LLFontGL::NORMAL, - LLFontGL::getFontSansSerifSmall()); + LLFontGL::getFontSansSerifSmall()); } // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a } @@ -2906,8 +2983,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) else // DISPLAY NAMES OFF { 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() ); // [RLVa:KB] - Checked: 2010-10-31 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a if ( (fRlvShowNames) && (!isSelf()) ) { @@ -2937,7 +3013,7 @@ void LLVOAvatar::idleUpdateNameTagText(BOOL new_name) // Show distance in tag mNameAway = is_away; - mNameBusy = is_busy; + mNameDoNotDisturb = is_do_not_disturb; mNameMute = is_muted; mNameAppearance = is_appearance; mNameFriend = is_friend; @@ -2956,7 +3032,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); - + std::deque::iterator chat_iter = mChats.begin(); mNameText->clearString(); @@ -2987,13 +3063,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; } @@ -3021,13 +3097,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; } @@ -3053,18 +3129,18 @@ void LLVOAvatar::addNameTagLine(const std::string& line, const LLColor4& color, { mNameText->addLine(line, color, (LLFontGL::StyleFlags)style, font); } - mNameString += line; - mNameString += '\n'; + mNameIsSet |= !line.empty(); } void LLVOAvatar::clearNameTag() { - mNameString.clear(); + mNameIsSet = false; if (mNameText) { mNameText->setLabel(""); - mNameText->setString( "" ); + mNameText->setString(""); } + mTimeVisible.reset(); } //static @@ -3090,38 +3166,49 @@ void LLVOAvatar::invalidateNameTags() if (avatar->isDead()) continue; avatar->clearNameTag(); - } } // Compute name tag position during idle update -LLVector3 LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) +void LLVOAvatar::idleUpdateNameTagPosition(const LLVector3& root_pos_last) { LLQuaternion root_rot = mRoot->getWorldRotation(); + LLQuaternion inv_root_rot = ~root_rot; LLVector3 pixel_right_vec; LLVector3 pixel_up_vec; LLViewerCamera::getInstance()->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec); LLVector3 camera_to_av = root_pos_last - LLViewerCamera::getInstance()->getOrigin(); camera_to_av.normalize(); - LLVector3 local_camera_at = camera_to_av * ~root_rot; + LLVector3 local_camera_at = camera_to_av * inv_root_rot; LLVector3 local_camera_up = camera_to_av % LLViewerCamera::getInstance()->getLeftAxis(); local_camera_up.normalize(); - local_camera_up = local_camera_up * ~root_rot; + local_camera_up = local_camera_up * inv_root_rot; - local_camera_up.scaleVec((mBodySize + mAvatarOffset) * 0.5f); - local_camera_at.scaleVec((mBodySize + mAvatarOffset) * 0.5f); + LLVector3 avatar_ellipsoid(mBodySize.mV[VX] * 0.4f, + mBodySize.mV[VY] * 0.4f, + mBodySize.mV[VZ] * NAMETAG_VERT_OFFSET_WEIGHT); - LLVector3 name_position = mRoot->getWorldPosition(); - name_position[VZ] -= mPelvisToFoot; - name_position[VZ] += ((mBodySize[VZ] + mAvatarOffset[VZ])* 0.55f); + local_camera_up.scaleVec(avatar_ellipsoid); + local_camera_at.scaleVec(avatar_ellipsoid); + + LLVector3 head_offset = (mHeadp->getLastWorldPosition() - mRoot->getLastWorldPosition()) * inv_root_rot; + + if (dist_vec(head_offset, mTargetRootToHeadOffset) > NAMETAG_UPDATE_THRESHOLD) + { + mTargetRootToHeadOffset = head_offset; + } + + mCurRootToHeadOffset = lerp(mCurRootToHeadOffset, mTargetRootToHeadOffset, LLCriticalDamp::getInterpolant(0.2f)); + + LLVector3 name_position = mRoot->getLastWorldPosition() + (mCurRootToHeadOffset * root_rot); name_position += (local_camera_up * root_rot) - (projected_vec(local_camera_at * root_rot, camera_to_av)); - name_position += pixel_up_vec * 15.f; + name_position += pixel_up_vec * NAMETAG_VERTICAL_SCREEN_OFFSET; + //mNameText->setPositionAgent(name_position); // Ansariel: Optional Z-offset correction for name tags static LLCachedControl fsNameTagOffset(gSavedSettings, "FSNameTagZOffsetCorrection"); name_position[VZ] += fsNameTagOffset / 10.f; - return name_position; } void LLVOAvatar::idleUpdateNameTagAlpha(BOOL new_name, F32 alpha) @@ -3145,13 +3232,11 @@ LLColor4 LLVOAvatar::getNameTagColor() // ...not using display names LLColor4 color = LLUIColorTable::getInstance()->getColor("NameTagLegacy"); - if (LLAvatarNameCache::useDisplayNames()) + 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 = LLUIColorTable::getInstance()->getColor("NameTagMatch"); } @@ -3205,9 +3290,9 @@ bool LLVOAvatar::isVisuallyMuted() const static LLCachedControl max_attachment_bytes(gSavedSettings, "RenderAutoMuteByteLimit"); static LLCachedControl 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); } //------------------------------------------------------------------------ @@ -3239,11 +3324,11 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mUseServerBakes, central_bake_version); std::string origin_string = bakedTextureOriginInfo(); debug_line += " [" + origin_string + "]"; + S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion(); + S32 last_request_cof_version = mLastUpdateRequestCOFVersion; + S32 last_received_cof_version = mLastUpdateReceivedCOFVersion; if (isSelf()) { - S32 curr_cof_version = LLAppearanceMgr::instance().getCOFVersion(); - S32 last_request_cof_version = LLAppearanceMgr::instance().getLastUpdateRequestCOFVersion(); - S32 last_received_cof_version = LLAppearanceMgr::instance().getLastAppearanceUpdateCOFVersion(); debug_line += llformat(" - cof: %d req: %d rcv:%d", curr_cof_version, last_request_cof_version, last_received_cof_version); // Use LLCachedControl @@ -3255,6 +3340,10 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) debug_line += " FORCING ERRS"; } } + else + { + debug_line += llformat(" - cof rcv:%d", last_received_cof_version); + } addDebugText(debug_line); } // Use LLCachedControl @@ -3295,8 +3384,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } } - LLVector3d root_pos_global; - if (!mIsBuilt) { return FALSE; @@ -3311,7 +3398,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) mTimeVisible.reset(); } - //-------------------------------------------------------------------- // the rest should only be done occasionally for far away avatars //-------------------------------------------------------------------- @@ -3444,7 +3530,7 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) } root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition()); - root_pos.mdV[VZ] += getVisualParamWeight(11001); + root_pos.mdV[VZ] += getVisualParamWeight(AVATAR_HOVER); resolveHeightGlobal(root_pos, ground_under_pelvis, normal); @@ -3721,10 +3807,6 @@ BOOL LLVOAvatar::updateCharacter(LLAgent &agent) if ( playSound && PlayModeUISndFootsteps ) // { -// 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(); @@ -3941,13 +4023,6 @@ void LLVOAvatar::updateVisibility() { releaseMeshData(); } - // this breaks off-screen chat bubbles - //if (mNameText) - //{ - // mNameText->markDead(); - // mNameText = NULL; - // sNumVisibleChatBubbles--; - //} } mVisible = visible; @@ -3963,46 +4038,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() //----------------------------------------------------------------------------- @@ -4023,11 +4058,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) @@ -4081,13 +4116,13 @@ U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass) if (face) { LLVertexBuffer* vb = face->getVertexBuffer(); - if (vb) - { - vb->flush(); - } + if (vb) + { + vb->flush(); } } } + } else { mNeedsSkin = FALSE; @@ -4700,7 +4735,7 @@ void LLVOAvatar::updateTextures() void LLVOAvatar::addLocalTextureStats( ETextureIndex idx, LLViewerFetchedTexture* imagep, - F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked, U32 index ) + F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked) { // No local texture stats for non-self avatars return; @@ -4774,11 +4809,7 @@ void LLVOAvatar::addBakedTextureStats( LLViewerFetchedTexture* imagep, F32 pixel //the texture pipeline will stop fetching this texture. imagep->resetTextureStats(); - // TODO: currently default to HTTP texture and fall back to UDP if cannot be found there. - // Once server messaging is in place, we should call setCanUseHTTP(false) for old style - // appearance requests // This we never want to do for OpenSim. Keep as true when LL changes this. - imagep->setCanUseHTTP(true); imagep->setMaxVirtualSizeResetInterval(MAX_TEXTURE_VIRTURE_SIZE_RESET_INTERVAL); imagep->resetMaxVirtualSizeResetCounter() ; @@ -5607,7 +5638,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()) @@ -5642,7 +5672,6 @@ BOOL LLVOAvatar::updateJointLODs() dirtyMesh(2); return TRUE; } - } return FALSE; } @@ -5949,14 +5978,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 @@ -5971,8 +5995,6 @@ void LLVOAvatar::cleanupAttachedMesh( LLViewerObject* pVO ) } } } - } -} //----------------------------------------------------------------------------- // detachObject() //----------------------------------------------------------------------------- @@ -6135,11 +6157,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); //KC: revoke perms on sit @@ -6236,7 +6254,6 @@ BOOL LLVOAvatar::isWearingWearableType(LLWearableType::EType type) const const LLAvatarAppearanceDictionary::TextureEntry *texture_dict = tex_iter->second; if (texture_dict->mWearableType == type) { - // If you're checking another avatar's clothing, you don't have component textures. // Thus, you must check to see if the corresponding baked texture is defined. // NOTE: this is a poor substitute if you actually want to know about individual pieces of clothing // this works for detecting a skirt (most important), but is ineffective at any piece of clothing that @@ -6442,6 +6459,11 @@ void LLVOAvatar::stopPhase(const std::string& phase_name, bool err_check) void LLVOAvatar::logPendingPhases() { + if (!isAgentAvatarValid()) + { + return; + } + for (LLViewerStats::phase_map_t::iterator it = getPhases().begin(); it != getPhases().end(); ++it) @@ -6476,6 +6498,11 @@ void LLVOAvatar::logPendingPhasesAllAvatars() void LLVOAvatar::logMetricsTimerRecord(const std::string& phase_name, F32 elapsed, bool completed) { + if (!isAgentAvatarValid()) + { + return; + } + LLSD record; record["timer_name"] = phase_name; record["avatar_id"] = getID(); @@ -6547,8 +6574,8 @@ BOOL LLVOAvatar::processFullyLoadedChange(bool loading) mFullyLoaded = (mFullyLoadedTimer.getElapsedTimeF32() > PAUSE); - if (!mPreviousFullyLoaded && !loading && mFullyLoaded) - { + if (!mPreviousFullyLoaded && !loading && mFullyLoaded) + { debugAvatarRezTime("AvatarRezNotification","fully loaded"); } @@ -7064,16 +7091,12 @@ LLBBox LLVOAvatar::getHUDBBox() const return bbox; } -void LLVOAvatar::rebuildHUD() -{ -} - //----------------------------------------------------------------------------- // onFirstTEMessageReceived() //----------------------------------------------------------------------------- void LLVOAvatar::onFirstTEMessageReceived() { - LL_INFOS("Avatar") << avString() << LL_ENDL; + LL_DEBUGS("Avatar") << avString() << LL_ENDL; if( !mFirstTEMessageReceived ) { mFirstTEMessageReceived = TRUE; @@ -7179,7 +7202,7 @@ void dump_visual_param(LLAPRFile::tFiletype* file, LLVisualParam* viewer_param, void LLVOAvatar::dumpAppearanceMsgParams( const std::string& dump_prefix, - const LLAppearanceMessageContents& contents) + const LLAppearanceMessageContents& contents) { std::string outfilename = get_sequential_numbered_file_name(dump_prefix,".xml"); const std::vector& params_for_dump = contents.mParamWeights; @@ -7243,7 +7266,7 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe U8 av_u8; mesgsys->getU8Fast(_PREHASH_AppearanceData, _PREHASH_AppearanceVersion, av_u8, 0); contents.mAppearanceVersion = av_u8; - llinfos << "appversion set by AppearanceData field: " << contents.mAppearanceVersion << llendl; + LL_DEBUGS("Avatar") << "appversion set by AppearanceData field: " << contents.mAppearanceVersion << llendl; mesgsys->getS32Fast(_PREHASH_AppearanceData, _PREHASH_CofVersion, contents.mCOFVersion, 0); // For future use: //mesgsys->getU32Fast(_PREHASH_AppearanceData, _PREHASH_Flags, appearance_flags, 0); @@ -7290,7 +7313,7 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT if (num_blocks != expected_tweakable_count) { - llinfos << "Number of params in AvatarAppearance msg (" << num_blocks << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl; + LL_DEBUGS("Avatar") << "Number of params in AvatarAppearance msg (" << num_blocks << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl; } } else @@ -7301,7 +7324,7 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe } else { - llinfos << "AvatarAppearance msg received without any parameters, object: " << getID() << llendl; + LL_DEBUGS("Avatar") << "AvatarAppearance msg received without any parameters, object: " << getID() << llendl; } } @@ -7312,7 +7335,6 @@ void LLVOAvatar::parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMe if (it != contents.mParams.end()) { S32 index = it - contents.mParams.begin(); - llinfos << "index: " << index << llendl; contents.mParamAppearanceVersion = llround(contents.mParamWeights[index]); LL_DEBUGS("Avatar") << "appversion req by appearance_version param: " << contents.mParamAppearanceVersion << llendl; } @@ -7361,7 +7383,6 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) static LLCachedControl enable_verbose_dumps(gSavedSettings, "DebugAvatarAppearanceMessage"); // std::string dump_prefix = getFullname() + "_" + (isSelf()?"s":"o") + "_"; - //if (enable_verbose_dumps) { dumpArchetypeXML(dump_prefix + "process_start"); } if (gSavedSettings.getBOOL("BlockAvatarAppearanceMessages")) { llwarns << "Blocking AvatarAppearance message" << llendl; @@ -7384,7 +7405,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) return; } S32 this_update_cof_version = contents.mCOFVersion; - S32 last_update_request_cof_version = LLAppearanceMgr::instance().mLastUpdateRequestCOFVersion; + S32 last_update_request_cof_version = mLastUpdateRequestCOFVersion; // Only now that we have result of appearance_version can we decide whether to bail out. if( isSelf() ) @@ -7393,8 +7414,6 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) << " last_update_request_cof_version " << last_update_request_cof_version << " my_cof_version " << LLAppearanceMgr::instance().getCOFVersion() << llendl; - LLAppearanceMgr::instance().setLastAppearanceUpdateCOFVersion(this_update_cof_version); - if (getRegion() && (getRegion()->getCentralBakeVersion()==0)) { llwarns << avString() << "Received AvatarAppearance message for self in non-server-bake region" << llendl; @@ -7421,7 +7440,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) if (isSelf() && isEditingAppearance()) { - llinfos << "ignoring appearance message while in appearance edit" << llendl; + LL_DEBUGS("Avatar") << "ignoring appearance message while in appearance edit" << llendl; return; } @@ -7432,10 +7451,12 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // appearance version, which may cause us to look for baked // textures in the wrong place and flag them as missing // assets. - llinfos << "ignoring appearance message due to lack of params" << llendl; + LL_DEBUGS("Avatar") << "ignoring appearance message due to lack of params" << llendl; return; } + mLastUpdateReceivedCOFVersion = this_update_cof_version; + setIsUsingServerBakes(appearance_version > 0); applyParsedTEMessage(contents.mTEContents); @@ -7456,7 +7477,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) mClientTagData["tex_color"] = tex->getColor().getValue(); //WS: Clear mNameString to force a rebuild - mNameString.clear(); + mNameIsSet = false; // // prevent the overwriting of valid baked textures with invalid baked textures @@ -7477,7 +7498,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived; mFirstAppearanceMessageReceived = TRUE; - LL_INFOS("Avatar") << avString() << "processAvatarAppearance start " << mID + LL_DEBUGS("Avatar") << avString() << "processAvatarAppearance start " << mID << " first? " << is_first_appearance_message << " self? " << isSelf() << LL_ENDL; if (is_first_appearance_message ) @@ -7517,7 +7538,7 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) const S32 expected_tweakable_count = getVisualParamCountInGroup(VISUAL_PARAM_GROUP_TWEAKABLE); // don't worry about VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT if (num_params != expected_tweakable_count) { - llinfos << "Number of params in AvatarAppearance msg (" << num_params << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl; + LL_DEBUGS("Avatar") << "Number of params in AvatarAppearance msg (" << num_params << ") does not match number of tweakable params in avatar xml file (" << expected_tweakable_count << "). Processing what we can. object: " << getID() << llendl; } if (params_changed) @@ -7696,7 +7717,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; @@ -7747,7 +7768,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 ) { - for (U32 i = 0; i < mBakedTextureDatas.size(); i++) { LLViewerTexture* image_baked = getImage( mBakedTextureDatas[i].mTextureIndex, 0 ); diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h old mode 100755 new mode 100644 index 757c8db4d7..d6123c9fd0 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -69,8 +69,11 @@ class LLVoiceVisualizer; class LLHUDNameTag; class LLHUDEffectSpiral; class LLTexGlobalColor; -class LLViewerJoint; +struct LLVOAvatarBoneInfo; +struct LLVOAvatarChildJoint; +//class LLViewerJoint; struct LLAppearanceMessageContents; +struct LLVOAvatarSkeletonInfo; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // LLVOAvatar @@ -245,7 +248,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); // Colorize tags //LLColor4 getNameTagColor(bool is_friend); @@ -361,6 +364,8 @@ public: F32 mLastPelvisToFoot; F32 mPelvisFixup; F32 mLastPelvisFixup; + LLVector3 mCurRootToHeadOffset; + LLVector3 mTargetRootToHeadOffset; S32 mLastSkeletonSerialNum; @@ -381,7 +386,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); @@ -545,7 +549,7 @@ protected: //-------------------------------------------------------------------- protected: virtual void setLocalTexture(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerTexture* tex, BOOL baked_version_exits, U32 index = 0); - virtual void addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index = 0); + virtual void addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex type, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked); // MULTI-WEARABLE: make self-only? virtual void setBakedReady(LLAvatarAppearanceDefines::ETextureIndex type, BOOL baked_version_exists, U32 index = 0); @@ -726,7 +730,6 @@ public: public: BOOL hasHUDAttachment() const; LLBBox getHUDBBox() const; - void rebuildHUD(); void resetHUDAttachments(); BOOL canAttachMoreObjects() const; BOOL canAttachMoreObjects(U32 n) const; @@ -870,12 +873,12 @@ protected: static void getAnimLabels(LLDynamicArray* labels); static void getAnimNames(LLDynamicArray* names); private: - std::string mNameString; // UTF-8 title + name + status + bool mNameIsSet; LLSD mClientTagData; bool mHasClientTagColor; std::string mTitle; bool mNameAway; - bool mNameBusy; + bool mNameDoNotDisturb; bool mNameMute; bool mNameAppearance; bool mNameFriend; @@ -981,6 +984,17 @@ protected: LLFrameTimer mRuthDebugTimer; // For tracking how long it takes for av to rez LLFrameTimer mDebugExistenceTimer; // Debugging for how long the avatar has been in memory. + //-------------------------------------------------------------------- + // COF monitoring + //-------------------------------------------------------------------- + +public: + // COF version of last viewer-initiated appearance update request. For non-self avs, this will remain at default. + S32 mLastUpdateRequestCOFVersion; + + // COF version of last appearance message received for this av. + S32 mLastUpdateReceivedCOFVersion; + /** Diagnostics ** ** *******************************************************************************/ diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 3e14053250..24d1e3b056 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -2898,13 +2898,17 @@ BOOL LLVOAvatarSelf::canGrabBakedTexture(EBakedTextureIndex baked_index) const } void LLVOAvatarSelf::addLocalTextureStats( ETextureIndex type, LLViewerFetchedTexture* imagep, - F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked, U32 index ) + F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked) { if (!isIndexLocalTexture(type)) return; - if (!covered_by_baked) + // Sunshine - ignoring covered_by_baked will force local textures + // to always load. Fix for SH-4001 and many related issues. Do + // not restore this without some more targetted fix for the local + // textures failing to load issue. + //if (!covered_by_baked) { - if (getLocalTextureID(type, index) != IMG_DEFAULT_AVATAR) + if (imagep->getID() != IMG_DEFAULT_AVATAR) { imagep->setNoDelete(); if (imagep->getDiscardLevel() != 0) @@ -3101,7 +3105,7 @@ void LLVOAvatarSelf::outputRezDiagnostics() const void LLVOAvatarSelf::outputRezTiming(const std::string& msg) const { - LL_INFOS("Avatar") + LL_DEBUGS("Avatar") << avString() << llformat("%s. Time from avatar creation: %.2f", msg.c_str(), mDebugSelfLoadTimer.getElapsedTimeF32()) << LL_ENDL; @@ -3319,8 +3323,8 @@ void LLVOAvatarSelf::onCustomizeEnd(bool disable_camera_switch) gAgentCamera.changeCameraToDefault(); gAgentCamera.resetView(); } - - LLAppearanceMgr::instance().updateAppearanceFromCOF(); + + LLAppearanceMgr::instance().updateAppearanceFromCOF(); } } diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h old mode 100755 new mode 100644 index 26470f8202..f33a579ea4 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -219,7 +219,7 @@ protected: /*virtual*/ void setBakedReady(LLAvatarAppearanceDefines::ETextureIndex type, BOOL baked_version_exists, U32 index); void localTextureLoaded(BOOL succcess, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata); void getLocalTextureByteCount(S32* gl_byte_count) const; - /*virtual*/ void addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex i, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked, U32 index); + /*virtual*/ void addLocalTextureStats(LLAvatarAppearanceDefines::ETextureIndex i, LLViewerFetchedTexture* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked); LLLocalTextureObject* getLocalTextureObject(LLAvatarAppearanceDefines::ETextureIndex i, U32 index) const; private: diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 181735ee30..ac2a34ba1e 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 state_changed_signal_t; + typedef boost::signals2::signal state_changed_signal_t; // on current channel changed signal typedef boost::function channel_changed_callback_t; diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index c96bd22cdf..b60564b6aa 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -546,6 +546,7 @@ void LLVoiceClient::setMuteMic(bool muted) { mMuteMic = muted; updateMicMuteLogic(); + mMicroChangedSignal(); } @@ -556,6 +557,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 05d5df026a..04f898a013 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -315,6 +315,9 @@ public: LLVoiceClient(); ~LLVoiceClient(); + typedef boost::signals2::signal 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 @@ -419,6 +422,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 @@ -478,6 +483,7 @@ protected: LLVoiceModuleInterface* mVoiceModule; LLPumpIO *m_servicePump; + LLCachedControl mVoiceEffectEnabled; LLCachedControl mVoiceEffectDefault; diff --git a/indra/newview/llvoicevisualizer.cpp b/indra/newview/llvoicevisualizer.cpp index 2241537aaf..9281334d81 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 2bf88dbc8b..ed8356f0ca 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; @@ -292,6 +296,7 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mCaptureDeviceDirty(false), mRenderDeviceDirty(false), mSpatialCoordsDirty(false), + mIsInitialized(false), mMuteMic(false), mMuteMicDirty(false), @@ -316,7 +321,9 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mCaptureBufferRecording(false), mCaptureBufferRecorded(false), mCaptureBufferPlaying(false), - mPlayRequestCount(0) + mPlayRequestCount(0), + + mAvatarNameCacheConnection() { mSpeakerVolume = scale_speaker_volume(0); @@ -349,6 +356,10 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : LLVivoxVoiceClient::~LLVivoxVoiceClient() { + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } //--------------------------------------------------- @@ -521,7 +532,7 @@ void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) { LLViewerRegion *region = gAgent.getRegion(); - if ( region && mVoiceEnabled ) + if ( region && (mVoiceEnabled || !mIsInitialized)) { std::string url = region->getCapability("ProvisionVoiceAccountRequest"); @@ -692,7 +703,7 @@ void LLVivoxVoiceClient::stateMachine() setVoiceEnabled(false); } - if(mVoiceEnabled) + if(mVoiceEnabled || !mIsInitialized) { updatePosition(); } @@ -737,7 +748,7 @@ void LLVivoxVoiceClient::stateMachine() //MARK: stateDisabled case stateDisabled: - if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) + if(mTuningMode || ((mVoiceEnabled || !mIsInitialized) && !mAccountName.empty())) { setState(stateStart); } @@ -892,7 +903,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); @@ -1086,7 +1097,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); @@ -1104,7 +1115,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); @@ -1248,7 +1259,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); @@ -1264,7 +1275,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 @@ -1284,7 +1295,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); @@ -1365,7 +1376,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); @@ -1412,7 +1423,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); @@ -1432,7 +1443,7 @@ void LLVivoxVoiceClient::stateMachine() //MARK: stateRunning case stateRunning: // steady state // Disabling voice or disconnect requested. - if(!mVoiceEnabled || mSessionTerminateRequested) + if((!mVoiceEnabled && mIsInitialized) || mSessionTerminateRequested) { leaveAudioSession(); } @@ -1479,6 +1490,8 @@ void LLVivoxVoiceClient::stateMachine() mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); sendPositionalUpdate(); } + + mIsInitialized = true; } break; @@ -1512,7 +1525,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); @@ -1540,7 +1553,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); @@ -2660,33 +2673,19 @@ void LLVivoxVoiceClient::checkFriend(const LLUUID& id) { buddyListEntry *buddy = findBuddy(id); - // Make sure we don't add a name before it's been looked up. + // Make sure we don't add a name before it's been looked up in the avatar name cache LLAvatarName av_name; - if(LLAvatarNameCache::get(id, &av_name)) + if (LLAvatarNameCache::get(id, &av_name)) { - // *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(); - - const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); - bool canSeeMeOnline = false; - if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) - canSeeMeOnline = true; + // *NOTE: We feed legacy names to Vivox because we 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.getAccountName(); - // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary. - - if(buddy) + if (buddy) { - // This buddy is already in both lists. - - if(name != buddy->mDisplayName) - { - // The buddy is in the list with the wrong name. Update it with the correct name. - LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; - buddy->mDisplayName = name; - buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent. - } + // This buddy is already in both lists (vivox buddies and avatar cache). + // Trust the avatar cache more for the display name (vivox display name are notoriously wrong) + buddy->mDisplayName = name; } else { @@ -2695,20 +2694,19 @@ void LLVivoxVoiceClient::checkFriend(const LLUUID& id) buddy->mUUID = id; } - // In all the above cases, the buddy is in the SL friends list (which is how we got here). - buddy->mInSLFriends = true; - buddy->mCanSeeMeOnline = canSeeMeOnline; + const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); + buddy->mCanSeeMeOnline = (relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)); + // In all the above cases, the buddy is in the SL friends list and tha name has been resolved (which is how we got here). buddy->mNameResolved = true; - + buddy->mInSLFriends = true; } else { - // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has. - if(buddy) + // This name hasn't been looked up yet in the avatar cache. Don't do anything with this buddy list entry until it has. + if (buddy) { buddy->mNameResolved = false; } - // Initiate a lookup. // The "lookup completed" callback will ensure that the friends list is rechecked after it completes. lookupName(id); @@ -2816,13 +2814,12 @@ void LLVivoxVoiceClient::sendFriendsListUpdates() { std::ostringstream stream; - if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) + if(buddy->mInSLFriends && !buddy->mInVivoxBuddies) { if(mNumberOfAliases > 0) { // Add (or update) this entry in the vivox buddy list buddy->mInVivoxBuddies = true; - buddy->mNeedsNameUpdate = false; LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; stream << "" @@ -3714,8 +3711,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(); @@ -3941,7 +3937,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); LLChat chat; @@ -3954,9 +3950,9 @@ void LLVivoxVoiceClient::messageEvent( chat.mFromName = session->mName; chat.mSourceType = CHAT_SOURCE_AGENT; - if(is_busy && !is_linden) + if(is_do_not_disturb && !is_linden) { - // 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 +3960,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 @@ -5846,7 +5843,6 @@ LLVivoxVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : mNameResolved = false; mInVivoxBuddies = false; mInSLFriends = false; - mNeedsNameUpdate = false; } void LLVivoxVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) @@ -5871,25 +5867,21 @@ LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::stri buddyListEntry *result = NULL; buddyListMap::iterator iter = mBuddyListMap.find(uri); - if(iter != mBuddyListMap.end()) + if (iter != mBuddyListMap.end()) { // Found a matching buddy already in the map. LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; result = iter->second; } - if(!result) + if (!result) { // participant isn't already in one list or the other. LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; result = new buddyListEntry(uri); result->mDisplayName = displayName; - if(IDFromName(uri, result->mUUID)) - { - // Extracted UUID from name successfully. - } - else + if (!IDFromName(uri, result->mUUID)) { LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; } @@ -6189,18 +6181,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 +6722,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(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(); ) @@ -7162,7 +7251,7 @@ void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) void LLVivoxProtocolParser::EndTag(const char *tag) { const std::string& string = textBuffer; - + responseDepth--; if (ignoringTags) @@ -7261,6 +7350,8 @@ void LLVivoxProtocolParser::EndTag(const char *tag) } else if (!stricmp("Buddy", tag)) { + // NOTE : Vivox does *not* give reliable display name for Buddy tags + // We don't take those very seriously as a result... LLVivoxVoiceClient::getInstance()->processBuddyListEntry(uriString, displayNameString); } else if (!stricmp("BlockRule", tag)) diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 70452a1137..7cda855061 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: ////////////////////// @@ -582,7 +584,6 @@ protected: bool mNameResolved; bool mInSLFriends; bool mInVivoxBuddies; - bool mNeedsNameUpdate; }; typedef std::map buddyListMap; @@ -641,6 +642,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 +743,8 @@ private: std::string mRenderDevice; bool mCaptureDeviceDirty; bool mRenderDeviceDirty; + + bool mIsInitialized; bool checkParcelChanged(bool update = false); @@ -851,6 +855,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 6e43a86602..bbd11fa2bc 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/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 5c8de9b59b..8d68ceb2b2 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -1702,7 +1702,7 @@ void LLWorld::getAvatars(uuid_vec_t* avatar_ids, std::vector* 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/llworldmap.cpp b/indra/newview/llworldmap.cpp index 3c5aa55183..5fa100c079 100644 --- a/indra/newview/llworldmap.cpp +++ b/indra/newview/llworldmap.cpp @@ -34,6 +34,7 @@ #include "lluistring.h" #include "llviewertexturelist.h" #include "lltrans.h" +#include "llgltexture.h" // Aurora Sim #include "llappviewer.h" diff --git a/indra/newview/llworldmap.h b/indra/newview/llworldmap.h index f7004f3b4f..59a864d7e6 100644 --- a/indra/newview/llworldmap.h +++ b/indra/newview/llworldmap.h @@ -36,6 +36,7 @@ #include "llsingleton.h" #include "llviewerregion.h" #include "llviewertexture.h" +#include "llgltexture.h" // Description of objects like hubs, events, land for sale, people and more (TBD). // Note: we don't store a "type" in there so we need to store instances of this class in @@ -105,7 +106,7 @@ public: void setSize(U16 sizeX, U16 sizeY) { mSizeX = sizeX; mSizeY = sizeY; } // Aurora Sim void setAccess (U32 accesscode) { mAccess = accesscode; } - void setRegionFlags (U64 region_flags) { mRegionFlags = region_flags; } + void setRegionFlags (U32 region_flags) { mRegionFlags = region_flags; } void setLandForSaleImage (LLUUID image_id); // void setWaterHeight (F32 water_height) { mWaterHeight = water_height; } @@ -167,7 +168,7 @@ private: bool mFirstAgentRequest; // Init agent request flag U32 mAccess; // Down/up and maturity rating of the region - U64 mRegionFlags; // Tell us if the siminfo has been received (if non 0) and what kind of region it is (Sandbox, allow damage) + U32 mRegionFlags; // Tell us if the siminfo has been received (if non 0) and what kind of region it is (Sandbox, allow damage) // Currently not used but might prove useful one day so we comment out // F32 mWaterHeight; // Water height on the region (not actively used) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 3dc90e4e86..590b1c978b 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -5355,11 +5355,6 @@ void LLPipeline::rebuildPools() } max_count--; } - - if (isAgentAvatarValid()) - { - gAgentAvatarp->rebuildHUD(); - } } void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp ) @@ -5844,7 +5839,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(&(*(mNearbyLights.rbegin())))); if (light->dist < farthest_light->dist) { if (farthest_light->fade >= 0.f) @@ -6902,7 +6897,7 @@ void LLPipeline::resetVertexBuffers(LLDrawable* drawable) } void LLPipeline::resetVertexBuffers() -{ +{ mResetVertexBuffers = true; } diff --git a/indra/newview/rlvactions.cpp b/indra/newview/rlvactions.cpp new file mode 100644 index 0000000000..8f91f5f9f7 --- /dev/null +++ b/indra/newview/rlvactions.cpp @@ -0,0 +1,81 @@ +/** + * + * Copyright (c) 2009-2013, Kitty Barnett + * + * The source code in this file is provided to you under the terms of the + * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt + * + * By copying, modifying or distributing this software, you acknowledge that + * you have read and understood your obligations described above, and agree to + * abide by those obligations. + * + */ + +#include "llviewerprecompiledheaders.h" +#include "llimview.h" +#include "rlvactions.h" +#include "rlvhandler.h" + +// ============================================================================ +// RlvActions member functions +// + +// Checked: 2010-11-30 (RLVa-1.3.0) +bool RlvActions::canReceiveIM(const LLUUID& idSender) +{ + // User can receive an IM from "sender" (could be an agent or a group) if: + // - not generally restricted from receiving IMs (or the sender is an exception) + // - not specifically restricted from receiving an IM from the sender + return + (!rlv_handler_t::isEnabled()) || + ( ( (!gRlvHandler.hasBehaviour(RLV_BHVR_RECVIM)) || (gRlvHandler.isException(RLV_BHVR_RECVIM, idSender)) ) && + ( (!gRlvHandler.hasBehaviour(RLV_BHVR_RECVIMFROM)) || (!gRlvHandler.isException(RLV_BHVR_RECVIMFROM, idSender)) ) ); +} + +// Checked: 2010-11-30 (RLVa-1.3.0) +bool RlvActions::canSendIM(const LLUUID& idRecipient) +{ + // User can send an IM to "recipient" (could be an agent or a group) if: + // - not generally restricted from sending IMs (or the recipient is an exception) + // - not specifically restricted from sending an IM to the recipient + return + (!rlv_handler_t::isEnabled()) || + ( ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)) || (gRlvHandler.isException(RLV_BHVR_SENDIM, idRecipient)) ) && + ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SENDIMTO)) || (!gRlvHandler.isException(RLV_BHVR_SENDIMTO, idRecipient)) ) ); +} + +// Checked: 2011-04-12 (RLVa-1.3.0) +bool RlvActions::canStartIM(const LLUUID& idRecipient) +{ + // User can start an IM session with "recipient" (could be an agent or a group) if: + // - not generally restricted from starting IM sessions (or the recipient is an exception) + // - not specifically restricted from starting an IM session with the recipient + return + (!rlv_handler_t::isEnabled()) || + ( ( (!gRlvHandler.hasBehaviour(RLV_BHVR_STARTIM)) || (gRlvHandler.isException(RLV_BHVR_STARTIM, idRecipient)) ) && + ( (!gRlvHandler.hasBehaviour(RLV_BHVR_STARTIMTO)) || (!gRlvHandler.isException(RLV_BHVR_STARTIMTO, idRecipient)) ) ); +} + +// Checked: 2013-05-10 (RLVa-1.4.9) +bool RlvActions::hasBehaviour(ERlvBehaviour eBhvr) +{ + return gRlvHandler.hasBehaviour(eBhvr); +} + +// Checked: 2013-05-09 (RLVa-1.4.9) +bool RlvActions::hasOpenP2PSession(const LLUUID& idAgent) +{ + const LLUUID idSession = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, idAgent); + return (idSession.notNull()) && (LLIMMgr::instance().hasSession(idSession)); +} + +// Checked: 2013-05-09 (RLVa-1.4.9) +bool RlvActions::hasOpenGroupSession(const LLUUID& idGroup) +{ + return (idGroup.notNull()) && (LLIMMgr::instance().hasSession(idGroup)); +} + +// ============================================================================ diff --git a/indra/newview/rlvactions.h b/indra/newview/rlvactions.h new file mode 100644 index 0000000000..b586e2dbc1 --- /dev/null +++ b/indra/newview/rlvactions.h @@ -0,0 +1,60 @@ +/** + * + * Copyright (c) 2009-2013, Kitty Barnett + * + * The source code in this file is provided to you under the terms of the + * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt + * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt + * + * By copying, modifying or distributing this software, you acknowledge that + * you have read and understood your obligations described above, and agree to + * abide by those obligations. + * + */ + +#ifndef RLV_ACTIONS_H +#define RLV_ACTIONS_H + +#include "rlvdefines.h" + +// ============================================================================ +// RlvActions class declaration - developer-friendly non-RLVa code facing class, use in lieu of RlvHandler whenever possible +// + +class RlvActions +{ +public: + + /* + * Returns true if the user is allowed to receive IMs from the specified sender (can be an avatar or a group) + */ + static bool canReceiveIM(const LLUUID& idSender); + + /* + * Returns true if the user is allowed to send IMs to the specified recipient (can be an avatar or a group) + */ + static bool canSendIM(const LLUUID& idRecipient); + + /* + * Returns true if the user is allowed to start a - P2P or group - conversation with the specified UUID. + */ + static bool canStartIM(const LLUUID& idRecipient); // @startim and @startimto + + /* + * Convenience function to check for a behaviour without having to include rlvhandler.h. + * Do NOT call this function if speed is important (i.e. per-frame) + */ + static bool hasBehaviour(ERlvBehaviour eBhvr); + + /* + * Returns true if a - P2P or group - IM session is open with the specified UUID. + */ + static bool hasOpenP2PSession(const LLUUID& idAgent); + static bool hasOpenGroupSession(const LLUUID& idGroup); +}; + +// ============================================================================ + +#endif // RLV_ACTIONS_H diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index 856cabe041..3ae2c3806d 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -20,7 +20,6 @@ #include "llavatarnamecache.h" #include "llinstantmessage.h" #include "llnotificationsutil.h" -#include "lltrans.h" #include "lluictrlfactory.h" #include "llversionviewer.h" #include "llviewerparcelmgr.h" @@ -126,6 +125,15 @@ void RlvSettings::initClass() } #endif // RLV_EXTENSION_STARTLOCATION +// Checked: 2013-04-17 (RLVa-1.4.8) +bool RlvSettings::onChangedAvatarOffset(const LLSD& sdValue) +{ + if ( (isAgentAvatarValid()) && (!gAgentAvatarp->isUsingServerBakes()) ) + { + gAgentAvatarp->computeBodySize(); + } + return true; +} // Checked: 2011-08-16 (RLVa-1.4.0b) | Added: RLVa-1.4.0b bool RlvSettings::onChangedMenuLevel() @@ -315,27 +323,21 @@ void RlvUtil::filterNames(std::string& strUTF8Text, bool fFilterLegacy) LLAvatarName avName; if (LLAvatarNameCache::get(idAgents[idxAgent], &avName)) { - const std::string& strAnonym = RlvStrings::getAnonym(avName.mDisplayName); - - // NOTE: if the legacy first and last name are empty we get a legacy name of " " which would replace all spaces in the string - std::string strLegacyName; - if ( (fFilterLegacy) && (!avName.mLegacyFirstName.empty()) && - ((!avName.mIsDisplayNameDefault) || (LLCacheName::getDefaultLastName() == avName.mLegacyLastName)) ) - { - strLegacyName = avName.getLegacyName(); - } + const std::string& strDisplayName = avName.getDisplayName(); + const std::string& strLegacyName = avName.getLegacyName(); + const std::string& strAnonym = RlvStrings::getAnonym(avName); // If the display name is a subset of the legacy name we need to filter that first, otherwise it's the other way around - if (boost::icontains(strLegacyName, avName.mDisplayName)) + if (boost::icontains(strLegacyName, strDisplayName)) { - if (!strLegacyName.empty()) + if (fFilterLegacy) boost::ireplace_all(strUTF8Text, strLegacyName, strAnonym); - boost::ireplace_all(strUTF8Text, avName.mDisplayName, strAnonym); + boost::ireplace_all(strUTF8Text, strDisplayName, strAnonym); } else { - boost::ireplace_all(strUTF8Text, avName.mDisplayName, strAnonym); - if (!strLegacyName.empty()) + boost::ireplace_all(strUTF8Text, strDisplayName, strAnonym); + if (fFilterLegacy) boost::ireplace_all(strUTF8Text, strLegacyName, strAnonym); } } @@ -438,7 +440,7 @@ void RlvUtil::sendBusyMessage(const LLUUID& idTo, const std::string& strMsg, con LLAgentUI::buildFullname(strFullName); pack_instant_message(gMessageSystem, gAgent.getID(), FALSE, gAgent.getSessionID(), idTo, strFullName, - strMsg, IM_ONLINE, IM_BUSY_AUTO_RESPONSE, idSession); + strMsg, IM_ONLINE, IM_DO_NOT_DISTURB_AUTO_RESPONSE, idSession); gAgent.sendReliableMessage(); } @@ -479,7 +481,8 @@ bool rlvMenuToggleEnabled() gSavedSettings.setBOOL(RLV_SETTING_MAIN, !rlv_handler_t::isEnabled()); LLSD args; - args["MESSAGE"] = (rlv_handler_t::isEnabled() ? LLTrans::getString("RlvDisabled") : LLTrans::getString("RlvEnabled")); + args["MESSAGE"] = + llformat("RestrainedLove Support will be %s after you restart", (rlv_handler_t::isEnabled()) ? "disabled" : "enabled" ); LLNotificationsUtil::add("GenericAlert", args); return true; @@ -624,7 +627,9 @@ bool rlvPredCanRemoveItem(const LLInventoryItem* pItem) RLV_ASSERT(false); } } - return false; + // HACK-RLVa: Until LL supports temporary attachment detection assume that no inventory item means a temporary + // attachment which are always removeable + return true; } // Checked: 2010-03-22 (RLVa-1.2.0c) | Added: RLVa-1.2.0a diff --git a/indra/newview/rlvcommon.h b/indra/newview/rlvcommon.h index 1f65df74b5..5989d1f8f8 100644 --- a/indra/newview/rlvcommon.h +++ b/indra/newview/rlvcommon.h @@ -274,7 +274,7 @@ template struct RlvPredValuesEqual // Checked: 2010-10-31 (RLVa-1.2.2a) | Added: RLVa-1.2.2a inline const std::string& RlvStrings::getAnonym(const LLAvatarName& avName) { - return getAnonym(avName.mDisplayName); + return getAnonym(avName.getDisplayName()); } // Checked: 2010-03-26 (RLVa-1.2.0b) | Modified: RLVa-1.0.2a diff --git a/indra/newview/rlvfloaters.cpp b/indra/newview/rlvfloaters.cpp index da168f5ccb..72470b44fb 100644 --- a/indra/newview/rlvfloaters.cpp +++ b/indra/newview/rlvfloaters.cpp @@ -229,7 +229,7 @@ void RlvFloaterBehaviours::onBtnCopyToClipboard() if (gObjectList.findObject(idOption)) strOption = rlvGetItemNameFromObjID(idOption, true); else if (LLAvatarNameCache::get(idOption, &avName)) - strOption = (!avName.mUsername.empty()) ? avName.mUsername : avName.mDisplayName; + strOption = (!avName.getAccountName().empty()) ? avName.getAccountName() : avName.getDisplayName(); else if (!gCacheName->getGroupName(idOption, strOption)) strOption = itCmd->getOption(); } @@ -307,7 +307,7 @@ void RlvFloaterBehaviours::refreshAll() } else if (LLAvatarNameCache::get(idOption, &avName)) { - strOption = (!avName.mUsername.empty()) ? avName.mUsername : avName.mDisplayName; + strOption = (!avName.getAccountName().empty()) ? avName.getAccountName() : avName.getDisplayName(); } else if (!gCacheName->getGroupName(idOption, strOption)) { diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index fa3cd86bc5..5b9da94844 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -1612,7 +1612,10 @@ ERlvCmdRet RlvHandler::processForceCommand(const RlvCommand& rlvCmd) const F32 nValue = (rlvCmdOption.m_nPelvisToFoot - gAgentAvatarp->getPelvisToFoot()) * rlvCmdOption.m_nPelvisToFootDeltaMult; nValue += rlvCmdOption.m_nPelvisToFootOffset; // FIRE-9759 - Temporarily remove setting AvatarZOffset - //gSavedPerAccountSettings.setF32(RLV_SETTING_AVATAROFFSET_Z, llclamp(nValue, -1.0f, 1.0f)); + //if (!gAgentAvatarp->isUsingServerBakes()) + // gSavedSettings.setF32(RLV_SETTING_AVATAROFFSET_Z, llclamp(nValue, -1.0f, 1.0f)); + //else + // eRet = RLV_RET_FAILED_DISABLED; } } break; diff --git a/indra/newview/rlvhandler.h b/indra/newview/rlvhandler.h index 0c47fd9d90..e496820957 100644 --- a/indra/newview/rlvhandler.h +++ b/indra/newview/rlvhandler.h @@ -95,11 +95,8 @@ public: // Command specific helper functions bool canEdit(const LLViewerObject* pObj) const; // @edit and @editobj - bool canReceiveIM(const LLUUID& idSender) const; // @recvim and @recvimfrom bool canShowHoverText(const LLViewerObject* pObj) const; // @showhovertext* command family - bool canSendIM(const LLUUID& idRecipient) const; // @sendim and @sendimto bool canSit(LLViewerObject* pObj, const LLVector3& posOffset = LLVector3::zero) const; - bool canStartIM(const LLUUID& idRecipient) const; // @startim and @startimto bool canStand() const; bool canTeleportViaLure(const LLUUID& idAgent) const; bool canTouch(const LLViewerObject* pObj, const LLVector3& posOffset = LLVector3::zero) const; // @touch @@ -248,28 +245,6 @@ inline bool RlvHandler::canEdit(const LLViewerObject* pObj) const ((!hasBehaviour(RLV_BHVR_EDITOBJ)) || (!isException(RLV_BHVR_EDITOBJ, pObj->getRootEdit()->getID()))); } -// Checked: 2010-11-30 (RLVa-1.3.0c) | Added: RLVa-1.3.0c -inline bool RlvHandler::canReceiveIM(const LLUUID& idSender) const -{ - // User can receive an IM from "sender" (could be an agent or a group) if: - // - not generally restricted from receiving IMs (or the sender is an exception) - // - not specifically restricted from receiving an IM from the sender - return - ( (!hasBehaviour(RLV_BHVR_RECVIM)) || (isException(RLV_BHVR_RECVIM, idSender)) ) && - ( (!hasBehaviour(RLV_BHVR_RECVIMFROM)) || (!isException(RLV_BHVR_RECVIMFROM, idSender)) ); -} - -// Checked: 2010-11-30 (RLVa-1.3.0c) | Added: RLVa-1.3.0c -inline bool RlvHandler::canSendIM(const LLUUID& idRecipient) const -{ - // User can send an IM to "recipient" (could be an agent or a group) if: - // - not generally restricted from sending IMs (or the recipient is an exception) - // - not specifically restricted from sending an IM to the recipient - return - ( (!hasBehaviour(RLV_BHVR_SENDIM)) || (isException(RLV_BHVR_SENDIM, idRecipient)) ) && - ( (!hasBehaviour(RLV_BHVR_SENDIMTO)) || (!isException(RLV_BHVR_SENDIMTO, idRecipient)) ); -} - // Checked: 2010-03-27 (RLVa-1.4.0a) | Modified: RLVa-1.0.0f inline bool RlvHandler::canShowHoverText(const LLViewerObject *pObj) const { @@ -280,16 +255,6 @@ inline bool RlvHandler::canShowHoverText(const LLViewerObject *pObj) const (isException(RLV_BHVR_SHOWHOVERTEXT, pObj->getID(), RLV_CHECK_PERMISSIVE)) ) ); } -inline bool RlvHandler::canStartIM(const LLUUID& idRecipient) const -{ - // User can start an IM session with "recipient" (could be an agent or a group) if: - // - not generally restricted from starting IM sessions (or the recipient is an exception) - // - not specifically restricted from starting an IM session with the recipient - return - ( (!hasBehaviour(RLV_BHVR_STARTIM)) || (isException(RLV_BHVR_STARTIM, idRecipient)) ) && - ( (!hasBehaviour(RLV_BHVR_STARTIMTO)) || (!isException(RLV_BHVR_STARTIMTO, idRecipient)) ); -} - // Checked: 2010-12-11 (RLVa-1.2.2c) | Added: RLVa-1.2.2c inline bool RlvHandler::canTeleportViaLure(const LLUUID& idAgent) const { diff --git a/indra/newview/rlvui.cpp b/indra/newview/rlvui.cpp index 82c7b8c888..2a66a4baec 100644 --- a/indra/newview/rlvui.cpp +++ b/indra/newview/rlvui.cpp @@ -21,7 +21,7 @@ #include "llavatarnamecache.h" // [FS communication UI] //#include "llcallfloater.h" -#include "fsfloatervoicecontrols.h" +//#include "fsfloatervoicecontrols.h" // removed in CHUI // [FS communication UI] #include "llenvmanager.h" #include "llfloatersidepanelcontainer.h" @@ -76,6 +76,7 @@ RlvUIEnabler::RlvUIEnabler() // onToggleXXX m_Handlers.insert(std::pair(RLV_BHVR_EDIT, boost::bind(&RlvUIEnabler::onToggleEdit, this))); + m_Handlers.insert(std::pair(RLV_BHVR_SENDIM, boost::bind(&RlvUIEnabler::onToggleSendIM, this))); m_Handlers.insert(std::pair(RLV_BHVR_SETDEBUG, boost::bind(&RlvUIEnabler::onToggleSetDebug, this))); m_Handlers.insert(std::pair(RLV_BHVR_SETENV, boost::bind(&RlvUIEnabler::onToggleSetEnv, this))); m_Handlers.insert(std::pair(RLV_BHVR_SHOWINV, boost::bind(&RlvUIEnabler::onToggleShowInv, this, _1))); @@ -156,6 +157,13 @@ void RlvUIEnabler::onToggleMovement() LLFloaterMove::sUpdateMovementStatus(); } +// Checked: 2013-05-11 (RLVa-1.4.9) +void RlvUIEnabler::onToggleSendIM() +{ + bool fEnable = !gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM); + gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->setHiddenFromSettingsEditor(!fEnable); +} + // Checked: 2011-05-28 (RLVa-1.4.0a) | Added: RLVa-1.4.0a void RlvUIEnabler::onToggleSetDebug() { @@ -384,13 +392,13 @@ void RlvUIEnabler::onToggleShowNames(bool fQuitting) pRadar->updateNames(); // [Standalone radar] - // Refresh the speaker list - // [FS communication UI] - //LLCallFloater* pCallFloater = LLFloaterReg::findTypedInstance("voice_controls"); - FSFloaterVoiceControls* pCallFloater = LLFloaterReg::findTypedInstance("fs_voice_controls"); - // [FS communication UI] - if (pCallFloater) - pCallFloater->getAvatarCallerList()->updateAvatarNames(); +// // Refresh the speaker list +// // [FS communication UI] removed in CHUI +// //LLCallFloater* pCallFloater = LLFloaterReg::findTypedInstance("voice_controls"); +// FSFloaterVoiceControls* pCallFloater = LLFloaterReg::findTypedInstance("fs_voice_controls"); +// // [FS communication UI] +// if (pCallFloater) +// pCallFloater->getAvatarCallerList()->updateAvatarNames(); // Force the use of the "display name" cache so we can filter both display and legacy names (or return back to the user's preference) if (!fEnable) diff --git a/indra/newview/rlvui.h b/indra/newview/rlvui.h index 996b993a34..de87465e40 100644 --- a/indra/newview/rlvui.h +++ b/indra/newview/rlvui.h @@ -46,6 +46,7 @@ protected: void onRefreshHoverText(); // showloc, shownames, showhovertext(all|world|hud) void onToggleEdit(); // edit void onToggleMovement(); // fly, alwaysrun and temprun + void onToggleSendIM(); // sendim void onToggleSetDebug(); // setdebug void onToggleSetEnv(); // setenv void onToggleShowInv(bool fQuitting); // showinv diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 26d96f40ea..a99d428f0e 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -19,8 +19,8 @@ - - - - - - - - - + + + + + + - - + - - - - + + + - - + name="DkGray2" + value="0.169 0.169 0.169 1" /> + + + + + - - - - + - - - + + + - - - - - - - + + + + + + @@ -213,8 +216,8 @@ name="NavyBlue_0" value="0 0.12 0.24 0" /> + name="MdBlue" + value=".07 .38 .51 1" /> + Leave this here until all Unused? are removed below, otherwise + the viewer generates many warnings on startup. --> + name="Unused?" + value=".831 1 0 1" /> @@ -344,24 +347,24 @@ - - - + + - - - + + reference="White" /> - - - - - - - - - + + + + + + - + - - + - - - - - - - - - - + + + + + + + + - - + - - + - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - + - - + - - - - - - - - + + + + + + - - - - + + - - - - - - - + + + + + + - - + + - - - - - + - - - + + + - - - + + + - - - - - - - - - - + + + + + + + + - - - - - + + + + - - + - + - - - - + + + - - - - - - + + + + + + - - - - + - - - + - - + - - - - - - - + + + + + - - + + - - + - - - - - - + name="MultiSliderDisabledThumbColor" + reference="Black" /> + + + + + - + - - - - - - - - - - - - + + + - + - - - - - + + + + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1057,38 +1063,38 @@ name="MutedChatColor" value="0.4 0.4 0.4 1" /> - - - + + - - - - + + - - - + - @@ -1106,22 +1112,22 @@ name="GroupHiddenInProfile" reference="EmphasisColorLight" /> - + + name="white" + value="1 1 1 1"/> + name="black" + value="0 0 0 1"/> + name="red" + value="1 0 0 1"/> + name="green" + value="0 1 0 1"/> + name="blue" + value="0 0 1 1"/> + + + + + + + + + + + + + + + + + @@ -398,6 +415,8 @@ with the same filename but different name + + @@ -728,7 +747,6 @@ with the same filename but different name - @@ -741,12 +759,6 @@ with the same filename but different name - - - - - - 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 @@ - - - - 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 @@ - + - - - - - - + top="31" + right="-1"/> 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 @@ + + + + + + + + + \ No newline at end of file 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 @@ -80,54 +80,6 @@ - - - - - - + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + - - + name="nearby_del_btn" + tool_tip="Remove selected person as a friend" + top_delta="0" + width="31"> + + + + + + + + + + - - - - + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + - (Locations, images, web, search history) - + text_color="LtGray_50" + width="300"> + (Locations, images, web, search history) + - - - + width="350" /> - - + width="350" /> + - - Chat Logs: - - - - - - - -