OSDN Git Service

Remove redundant refresh button RAD-471
[radegast/radegast.git] / Radegast / GUI / Consoles / GroupDetails.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31 using System;
32 using System.Collections;
33 using System.Collections.Generic;
34 #if (COGBOT_LIBOMV || USE_STHREADS)
35 using ThreadPoolUtil;
36 using Thread = ThreadPoolUtil.Thread;
37 using ThreadPool = ThreadPoolUtil.ThreadPool;
38 using Monitor = ThreadPoolUtil.Monitor;
39 #endif
40 using System.Threading;
41 using System.Timers;
42 using System.Windows.Forms;
43 using OpenMetaverse;
44 using OpenMetaverse.Packets;
45
46 namespace Radegast
47 {
48     public partial class GroupDetails : UserControl
49     {
50         private RadegastInstance instance;
51         private GridClient client { get { return instance.Client; } }
52         private Group group;
53         private Dictionary<UUID, GroupTitle> titles;
54         private Dictionary<UUID, Group> myGroups { get { return instance.Groups; } }
55         private List<KeyValuePair<UUID, UUID>> roleMembers;
56         private Dictionary<UUID, GroupRole> roles;
57         private bool isMember;
58         private GroupMemberSorter memberSorter = new GroupMemberSorter();
59         private System.Threading.Timer nameUpdateTimer;
60
61         private UUID groupTitlesRequest, groupMembersRequest, groupRolesRequest, groupRolesMembersRequest;
62
63         public GroupDetails(RadegastInstance instance, Group group)
64         {
65             InitializeComponent();
66             Disposed += new EventHandler(GroupDetails_Disposed);
67
68             this.instance = instance;
69             this.group = group;
70
71             if (group.InsigniaID != UUID.Zero)
72             {
73                 SLImageHandler insignia = new SLImageHandler(instance, group.InsigniaID, string.Empty);
74                 insignia.Dock = DockStyle.Fill;
75                 pnlInsignia.Controls.Add(insignia);
76             }
77
78             nameUpdateTimer = new System.Threading.Timer(nameUpdateTimer_Elapsed, this,
79                 System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
80
81             txtGroupID.Text = group.ID.ToString();
82
83             lblGroupName.Text = group.Name;
84
85             isMember = instance.Groups.ContainsKey(group.ID);
86
87             if (!isMember)
88             {
89                 tcGroupDetails.TabPages.Remove(tpMembersRoles);
90                 tcGroupDetails.TabPages.Remove(tpNotices);
91                 tcGroupDetails.TabPages.Remove(tpBanned);
92             }
93             else
94             {
95                 RefreshBans();
96             }
97
98             lvwNoticeArchive.SmallImageList = frmMain.ResourceImages;
99             lvwNoticeArchive.ListViewItemSorter = new GroupNoticeSorter();
100
101             // Callbacks
102             client.Groups.GroupTitlesReply += new EventHandler<GroupTitlesReplyEventArgs>(Groups_GroupTitlesReply);
103             client.Groups.GroupMembersReply += new EventHandler<GroupMembersReplyEventArgs>(Groups_GroupMembersReply);
104             client.Groups.GroupProfile += new EventHandler<GroupProfileEventArgs>(Groups_GroupProfile);
105             client.Groups.CurrentGroups += new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
106             client.Groups.GroupNoticesListReply += new EventHandler<GroupNoticesListReplyEventArgs>(Groups_GroupNoticesListReply);
107             client.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupJoinedReply);
108             client.Groups.GroupLeaveReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupLeaveReply);
109             client.Groups.GroupRoleDataReply += new EventHandler<GroupRolesDataReplyEventArgs>(Groups_GroupRoleDataReply);
110             client.Groups.GroupMemberEjected += new EventHandler<GroupOperationEventArgs>(Groups_GroupMemberEjected);
111             client.Groups.GroupRoleMembersReply += new EventHandler<GroupRolesMembersReplyEventArgs>(Groups_GroupRoleMembersReply);
112             client.Self.IM += new EventHandler<InstantMessageEventArgs>(Self_IM);
113             instance.Names.NameUpdated += new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
114             RefreshControlsAvailability();
115             RefreshGroupInfo();
116         }
117
118         void GroupDetails_Disposed(object sender, EventArgs e)
119         {
120             client.Groups.GroupTitlesReply -= new EventHandler<GroupTitlesReplyEventArgs>(Groups_GroupTitlesReply);
121             client.Groups.GroupMembersReply -= new EventHandler<GroupMembersReplyEventArgs>(Groups_GroupMembersReply);
122             client.Groups.GroupProfile -= new EventHandler<GroupProfileEventArgs>(Groups_GroupProfile);
123             client.Groups.CurrentGroups -= new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
124             client.Groups.GroupNoticesListReply -= new EventHandler<GroupNoticesListReplyEventArgs>(Groups_GroupNoticesListReply);
125             client.Groups.GroupJoinedReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupJoinedReply);
126             client.Groups.GroupLeaveReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupLeaveReply);
127             client.Groups.GroupRoleDataReply -= new EventHandler<GroupRolesDataReplyEventArgs>(Groups_GroupRoleDataReply);
128             client.Groups.GroupRoleMembersReply -= new EventHandler<GroupRolesMembersReplyEventArgs>(Groups_GroupRoleMembersReply);
129             client.Groups.GroupMemberEjected -= new EventHandler<GroupOperationEventArgs>(Groups_GroupMemberEjected);
130             client.Self.IM -= new EventHandler<InstantMessageEventArgs>(Self_IM);
131             if (instance != null && instance.Names != null)
132             {
133                 instance.Names.NameUpdated -= new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
134             }
135             if (nameUpdateTimer != null)
136             {
137                 nameUpdateTimer.Dispose();
138                 nameUpdateTimer = null;
139             }
140         }
141
142         #region Network callbacks
143
144         void Groups_GroupMemberEjected(object sender, GroupOperationEventArgs e)
145         {
146             if (e.GroupID != group.ID) return;
147
148             if (e.Success)
149             {
150                 BeginInvoke(new MethodInvoker(() => { RefreshGroupInfo(); }));
151                 instance.TabConsole.DisplayNotificationInChat("Group member ejected.");
152             }
153             else
154             {
155                 instance.TabConsole.DisplayNotificationInChat("Failed to eject group member.");
156             }
157         }
158
159         void Groups_GroupRoleMembersReply(object sender, GroupRolesMembersReplyEventArgs e)
160         {
161             if (e.GroupID == group.ID && e.RequestID == groupRolesMembersRequest)
162             {
163                 roleMembers = e.RolesMembers;
164                 BeginInvoke(new MethodInvoker(() =>
165                     {
166                         btnInviteNewMember.Enabled = HasPower(GroupPowers.Invite);
167                         btnEjectMember.Enabled = HasPower(GroupPowers.Eject);
168                         lvwMemberDetails_SelectedIndexChanged(null, null);
169                     }
170                 ));
171             }
172         }
173
174         void Groups_GroupRoleDataReply(object sender, GroupRolesDataReplyEventArgs e)
175         {
176             if (e.GroupID == group.ID && e.RequestID == groupRolesRequest)
177             {
178                 groupRolesMembersRequest = client.Groups.RequestGroupRolesMembers(group.ID);
179                 if (roles == null) roles = e.Roles;
180                 else lock (roles) roles = e.Roles;
181                 BeginInvoke(new MethodInvoker(() => DisplayGroupRoles()));
182             }
183         }
184
185         void Groups_GroupLeaveReply(object sender, GroupOperationEventArgs e)
186         {
187             if (e.GroupID == group.ID && e.Success)
188             {
189                 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
190             }
191         }
192
193         void Groups_GroupJoinedReply(object sender, GroupOperationEventArgs e)
194         {
195             if (e.GroupID == group.ID && e.Success)
196             {
197                 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
198             }
199         }
200
201         UUID destinationFolderID;
202
203         void Self_IM(object sender, InstantMessageEventArgs e)
204         {
205             if (e.IM.Dialog != InstantMessageDialog.GroupNoticeRequested) return;
206
207             if (InvokeRequired)
208             {
209                 BeginInvoke(new MethodInvoker(() => Self_IM(sender, e)));
210                 return;
211             }
212
213             InstantMessage msg = e.IM;
214             AssetType type;
215             UUID groupID;
216
217             if (msg.BinaryBucket.Length >= 18)
218             {
219                 groupID = new UUID(msg.BinaryBucket, 2);
220             }
221             else
222             {
223                 groupID = msg.FromAgentID;
224             }
225
226             if (groupID != group.ID) return;
227
228             if (msg.BinaryBucket.Length > 18 && msg.BinaryBucket[0] != 0)
229             {
230                 type = (AssetType)msg.BinaryBucket[1];
231                 destinationFolderID = client.Inventory.FindFolderForType(type);
232                 int icoIndx = InventoryConsole.GetItemImageIndex(type.ToString().ToLower());
233                 if (icoIndx >= 0)
234                 {
235                     icnItem.Image = frmMain.ResourceImages.Images[icoIndx];
236                     icnItem.Visible = true;
237                 }
238                 txtItemName.Text = Utils.BytesToString(msg.BinaryBucket, 18, msg.BinaryBucket.Length - 19);
239                 btnSave.Enabled = true;
240                 btnSave.Visible = icnItem.Visible = txtItemName.Visible = true;
241                 btnSave.Tag = msg;
242             }
243
244             string text = msg.Message.Replace("\n", System.Environment.NewLine);
245             int pos = msg.Message.IndexOf('|');
246             string title = msg.Message.Substring(0, pos);
247             text = text.Remove(0, pos + 1);
248             txtNotice.Text = text;
249         }
250
251         void Groups_GroupNoticesListReply(object sender, GroupNoticesListReplyEventArgs e)
252         {
253             if (e.GroupID != group.ID) return;
254
255             if (InvokeRequired)
256             {
257                 BeginInvoke(new MethodInvoker(() => Groups_GroupNoticesListReply(sender, e)));
258                 return;
259             }
260
261             lvwNoticeArchive.BeginUpdate();
262
263             foreach (GroupNoticesListEntry notice in e.Notices)
264             {
265                 ListViewItem item = new ListViewItem();
266                 item.SubItems.Add(notice.Subject);
267                 item.SubItems.Add(notice.FromName);
268                 string noticeDate = string.Empty;
269                 if (notice.Timestamp != 0)
270                 {
271                     noticeDate = Utils.UnixTimeToDateTime(notice.Timestamp).ToShortDateString();
272                 }
273                 item.SubItems.Add(noticeDate);
274
275                 if (notice.HasAttachment)
276                 {
277                     item.ImageIndex = InventoryConsole.GetItemImageIndex(notice.AssetType.ToString().ToLower());
278                 }
279
280                 item.Tag = notice;
281
282                 lvwNoticeArchive.Items.Add(item);
283             }
284             lvwNoticeArchive.EndUpdate();
285         }
286
287         void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)
288         {
289             BeginInvoke(new MethodInvoker(RefreshControlsAvailability));
290         }
291
292         void Groups_GroupProfile(object sender, GroupProfileEventArgs e)
293         {
294             if (group.ID != e.Group.ID) return;
295
296             if (InvokeRequired)
297             {
298                 BeginInvoke(new MethodInvoker(() => Groups_GroupProfile(sender, e)));
299                 return;
300             }
301
302             group = e.Group;
303             if (group.InsigniaID != UUID.Zero && pnlInsignia.Controls.Count == 0)
304             {
305                 SLImageHandler insignia = new SLImageHandler(instance, group.InsigniaID, string.Empty);
306                 insignia.Dock = DockStyle.Fill;
307                 pnlInsignia.Controls.Add(insignia);
308             }
309
310             lblGroupName.Text = e.Group.Name;
311             tbxCharter.Text = group.Charter.Replace("\n", Environment.NewLine);
312             lblFounded.Text = "Founded by: " + instance.Names.Get(group.FounderID);
313             cbxShowInSearch.Checked = group.ShowInList;
314             cbxOpenEnrollment.Checked = group.OpenEnrollment;
315
316             if (group.MembershipFee > 0)
317             {
318                 cbxEnrollmentFee.Checked = true;
319                 nudEnrollmentFee.Value = group.MembershipFee;
320             }
321             else
322             {
323                 cbxEnrollmentFee.Checked = false;
324                 nudEnrollmentFee.Value = 0;
325             }
326
327             if (group.MaturePublish)
328             {
329                 cbxMaturity.SelectedIndex = 1;
330             }
331             else
332             {
333                 cbxMaturity.SelectedIndex = 0;
334             }
335
336             btnJoin.Enabled = btnJoin.Visible = false;
337
338             if (myGroups.ContainsKey(group.ID)) // I am in this group
339             {
340                 cbxReceiveNotices.Checked = myGroups[group.ID].AcceptNotices;
341                 cbxListInProfile.Checked = myGroups[group.ID].ListInProfile;
342                 cbxReceiveNotices.CheckedChanged += new EventHandler(cbxListInProfile_CheckedChanged);
343                 cbxListInProfile.CheckedChanged += new EventHandler(cbxListInProfile_CheckedChanged);
344             }
345             else if (group.OpenEnrollment) // I am not in this group, but I could join it
346             {
347                 btnJoin.Text = "Join $L" + group.MembershipFee;
348                 btnJoin.Enabled = btnJoin.Visible = true;
349             }
350
351             RefreshControlsAvailability();
352         }
353
354         void Names_NameUpdated(object sender, UUIDNameReplyEventArgs e)
355         {
356             ProcessNameUpdate(e.Names);
357         }
358
359         int lastTick = 0;
360
361         void ProcessNameUpdate(Dictionary<UUID, string> Names)
362         {
363             if (Names.ContainsKey(group.FounderID))
364             {
365                 if (InvokeRequired)
366                 {
367                     BeginInvoke(new MethodInvoker(() => { lblFounded.Text = "Founded by: " + Names[group.FounderID]; }));
368                 }
369                 else
370                 {
371                     lblFounded.Text = "Founded by: " + Names[group.FounderID];
372                 }
373             }
374
375             WorkPool.QueueUserWorkItem(sync =>
376             {
377                 try
378                 {
379                     bool hasUpdates = false;
380
381                     foreach (var name in Names)
382                     {
383                         var member = GroupMembers.Find((m) => m.Base.ID == name.Key);
384                         if (member == null) continue;
385
386                         hasUpdates = true;
387                         member.Name = name.Value;
388                     }
389
390                     if (hasUpdates)
391                     {
392                         int tick = Environment.TickCount;
393                         int elapsed = tick - lastTick;
394                         if (elapsed > 500)
395                         {
396                             lastTick = tick;
397                             nameUpdateTimer_Elapsed(this);
398                         }
399                         nameUpdateTimer.Change(500, System.Threading.Timeout.Infinite);
400                     }
401                 }
402                 catch (Exception ex)
403                 {
404                     Logger.DebugLog("Failed updating group member names: " + ex.ToString());
405                 }
406             });
407         }
408
409         void Groups_GroupTitlesReply(object sender, GroupTitlesReplyEventArgs e)
410         {
411             if (groupTitlesRequest != e.RequestID) return;
412
413             if (InvokeRequired)
414             {
415                 BeginInvoke(new MethodInvoker(() => Groups_GroupTitlesReply(sender, e)));
416                 return;
417             }
418
419             this.titles = e.Titles;
420
421             foreach (GroupTitle title in titles.Values)
422             {
423                 cbxActiveTitle.Items.Add(title);
424                 if (title.Selected)
425                 {
426                     cbxActiveTitle.SelectedItem = title;
427                 }
428             }
429
430             cbxActiveTitle.SelectedIndexChanged += cbxActiveTitle_SelectedIndexChanged;
431         }
432
433         List<EnhancedGroupMember> GroupMembers = new List<EnhancedGroupMember>();
434
435         void Groups_GroupMembersReply(object sender, GroupMembersReplyEventArgs e)
436         {
437             if (InvokeRequired)
438             {
439                 BeginInvoke(new MethodInvoker(() => Groups_GroupMembersReply(sender, e)));
440                 return;
441             }
442
443             lvwGeneralMembers.VirtualListSize = 0;
444             lvwMemberDetails.VirtualListSize = 0;
445
446             var members = new List<EnhancedGroupMember>(e.Members.Count);
447             foreach (var member in e.Members)
448             {
449                 members.Add(new EnhancedGroupMember(instance.Names.Get(member.Key), member.Value));
450             }
451
452             GroupMembers = members;
453             GroupMembers.Sort(memberSorter);
454             lvwGeneralMembers.VirtualListSize = GroupMembers.Count;
455             lvwMemberDetails.VirtualListSize = GroupMembers.Count;
456         }
457
458         void lvwMemberDetails_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
459         {
460             EnhancedGroupMember member = null;
461             try
462             {
463                 member = GroupMembers[e.ItemIndex];
464             }
465             catch
466             {
467                 e.Item = new ListViewItem();
468                 return;
469             }
470
471             ListViewItem item = new ListViewItem(member.Name);
472             item.Tag = member;
473             item.Name = member.Base.ID.ToString();
474             item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Contribution.ToString()));
475             if (member.LastOnline != DateTime.MinValue)
476             {
477                 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
478             }
479
480             e.Item = item;
481         }
482
483         void lvwGeneralMembers_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
484         {
485             EnhancedGroupMember member = null;
486             try
487             {
488                 member = GroupMembers[e.ItemIndex];
489             }
490             catch
491             {
492                 e.Item = new ListViewItem();
493                 return;
494             }
495             ListViewItem item = new ListViewItem(member.Name);
496
497             item.Tag = member;
498             item.Name = member.Base.ID.ToString();
499             item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Title));
500             if (member.LastOnline != DateTime.MinValue)
501             {
502                 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
503             }
504
505             e.Item = item;
506         }
507         #endregion
508
509         #region Privatate methods
510
511         private void nameUpdateTimer_Elapsed(object sync)
512         {
513             if (InvokeRequired)
514             {
515                 BeginInvoke(new MethodInvoker(() => nameUpdateTimer_Elapsed(sync)));
516                 return;
517             }
518
519             GroupMembers.Sort(memberSorter);
520             lvwGeneralMembers.Invalidate();
521             lvwMemberDetails.Invalidate();
522         }
523
524         private void DisplayGroupRoles()
525         {
526             lvwRoles.Items.Clear();
527
528             lock (roles)
529             {
530                 foreach (GroupRole role in roles.Values)
531                 {
532                     ListViewItem item = new ListViewItem();
533                     item.Name = role.ID.ToString();
534                     item.Text = role.Name;
535                     item.SubItems.Add(role.Title);
536                     item.SubItems.Add(role.ID.ToString());
537                     item.Tag = role;
538                     lvwRoles.Items.Add(item);
539                 }
540             }
541
542         }
543
544
545         private bool HasPower(GroupPowers power)
546         {
547             if (!instance.Groups.ContainsKey(group.ID))
548                 return false;
549
550             return (instance.Groups[group.ID].Powers & power) != 0;
551         }
552
553         private void RefreshControlsAvailability()
554         {
555             if (!HasPower(GroupPowers.ChangeOptions))
556             {
557                 nudEnrollmentFee.ReadOnly = true;
558                 cbxEnrollmentFee.Enabled = false;
559                 cbxOpenEnrollment.Enabled = false;
560             }
561
562             if (!HasPower(GroupPowers.ChangeIdentity))
563             {
564                 tbxCharter.ReadOnly = true;
565                 cbxShowInSearch.Enabled = false;
566                 cbxMaturity.Enabled = false;
567             }
568
569             if (!myGroups.ContainsKey(group.ID))
570             {
571                 cbxReceiveNotices.Enabled = false;
572                 cbxListInProfile.Enabled = false;
573             }
574         }
575
576         private void RefreshGroupNotices()
577         {
578             lvwNoticeArchive.Items.Clear();
579             client.Groups.RequestGroupNoticesList(group.ID);
580             btnNewNotice.Enabled = HasPower(GroupPowers.SendNotices);
581         }
582
583         private void RefreshGroupInfo()
584         {
585             lvwGeneralMembers.VirtualListSize = 0;
586             if (isMember) lvwMemberDetails.VirtualListSize = 0;
587
588             cbxActiveTitle.SelectedIndexChanged -= cbxActiveTitle_SelectedIndexChanged;
589             cbxReceiveNotices.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
590             cbxListInProfile.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
591
592             cbxActiveTitle.Items.Clear();
593
594             // Request group info
595             client.Groups.RequestGroupProfile(group.ID);
596             groupTitlesRequest = client.Groups.RequestGroupTitles(group.ID);
597             groupMembersRequest = client.Groups.RequestGroupMembers(group.ID);
598         }
599
600         private void RefreshRoles()
601         {
602             if (!isMember) return;
603
604             lvwRoles.SelectedItems.Clear();
605             lvwRoles.Items.Clear();
606             btnApply.Enabled = false;
607             btnCreateNewRole.Enabled = HasPower(GroupPowers.CreateRole);
608             btnDeleteRole.Enabled = HasPower(GroupPowers.DeleteRole);
609             txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = false;
610             groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
611         }
612
613         private void RefreshMembersRoles()
614         {
615             if (!isMember) return;
616
617             btnApply.Enabled = false;
618             lvwAssignedRoles.Items.Clear();
619             groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
620         }
621         #endregion
622
623         #region Controls change handlers
624         void cbxListInProfile_CheckedChanged(object sender, EventArgs e)
625         {
626             if (myGroups.ContainsKey(group.ID))
627             {
628                 Group g = myGroups[group.ID];
629                 // g.AcceptNotices = cbxReceiveNotices.Checked;
630                 // g.ListInProfile = cbxListInProfile.Checked;
631                 client.Groups.SetGroupAcceptNotices(g.ID, cbxReceiveNotices.Checked, cbxListInProfile.Checked);
632                 client.Groups.RequestCurrentGroups();
633             }
634         }
635
636         private void cbxActiveTitle_SelectedIndexChanged(object sender, EventArgs e)
637         {
638             GroupTitle title = (GroupTitle)cbxActiveTitle.SelectedItem;
639             client.Groups.ActivateTitle(title.GroupID, title.RoleID);
640         }
641
642         private void btnRefresh_Click(object sender, EventArgs e)
643         {
644             switch (tcGroupDetails.SelectedTab.Name)
645             {
646                 case "tpGeneral":
647                     RefreshGroupInfo();
648                     break;
649
650                 case "tpNotices":
651                     RefreshGroupNotices();
652                     break;
653
654                 case "tpMembersRoles":
655                     RefreshMembersRoles();
656                     break;
657
658                 case "tpBanned":
659                     RefreshBans();
660                     break;
661             }
662         }
663         #endregion
664
665         void lvwGeneralMembers_ColumnClick(object sender, ColumnClickEventArgs e)
666         {
667             ListView lb = (ListView)sender;
668             switch (e.Column)
669             {
670                 case 0:
671                     memberSorter.SortBy = GroupMemberSorter.SortByColumn.Name;
672                     break;
673
674                 case 1:
675                     if (lb.Name == "lvwMemberDetails")
676                         memberSorter.SortBy = GroupMemberSorter.SortByColumn.Contribution;
677                     else
678                         memberSorter.SortBy = GroupMemberSorter.SortByColumn.Title;
679                     break;
680
681                 case 2:
682                     memberSorter.SortBy = GroupMemberSorter.SortByColumn.LastOnline;
683                     break;
684             }
685
686             if (memberSorter.CurrentOrder == GroupMemberSorter.SortOrder.Ascending)
687                 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Descending;
688             else
689                 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Ascending;
690
691             GroupMembers.Sort(memberSorter);
692             lb.Invalidate();
693         }
694
695         private void lvwNoticeArchive_ColumnClick(object sender, ColumnClickEventArgs e)
696         {
697
698             GroupNoticeSorter sorter = (GroupNoticeSorter)lvwNoticeArchive.ListViewItemSorter;
699
700             switch (e.Column)
701             {
702                 case 1:
703                     sorter.SortBy = GroupNoticeSorter.SortByColumn.Subject;
704                     break;
705
706                 case 2:
707                     sorter.SortBy = GroupNoticeSorter.SortByColumn.Sender;
708                     break;
709
710                 case 3:
711                     sorter.SortBy = GroupNoticeSorter.SortByColumn.Date;
712                     break;
713             }
714
715             if (sorter.CurrentOrder == GroupNoticeSorter.SortOrder.Ascending)
716                 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Descending;
717             else
718                 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Ascending;
719
720             lvwNoticeArchive.Sort();
721         }
722
723
724         private void btnClose_Click(object sender, EventArgs e)
725         {
726             this.FindForm().Close();
727         }
728
729         private void tcGroupDetails_SelectedIndexChanged(object sender, EventArgs e)
730         {
731             switch (tcGroupDetails.SelectedTab.Name)
732             {
733                 case "tpNotices":
734                     RefreshGroupNotices();
735                     break;
736
737                 case "tpMembersRoles":
738                     RefreshMembersRoles();
739                     break;
740
741                 case "tpBanned":
742                     RefreshBans();
743                     break;
744             }
745         }
746
747         private void lvwNoticeArchive_SelectedIndexChanged(object sender, EventArgs e)
748         {
749             if (lvwNoticeArchive.SelectedItems.Count == 1)
750             {
751                 if (lvwNoticeArchive.SelectedItems[0].Tag is GroupNoticesListEntry)
752                 {
753                     GroupNoticesListEntry notice = (GroupNoticesListEntry)lvwNoticeArchive.SelectedItems[0].Tag;
754                     lblSentBy.Text = "Sent by " + notice.FromName;
755                     lblTitle.Text = notice.Subject;
756                     txtNotice.Text = string.Empty;
757                     btnSave.Enabled = btnSave.Visible = icnItem.Visible = txtItemName.Visible = false;
758                     client.Groups.RequestGroupNotice(notice.NoticeID);
759                     pnlNewNotice.Visible = false;
760                     pnlArchivedNotice.Visible = true;
761                     return;
762                 }
763             }
764             pnlArchivedNotice.Visible = false;
765         }
766
767         private void btnSave_Click(object sender, EventArgs e)
768         {
769             if (btnSave.Tag is InstantMessage)
770             {
771                 InstantMessage msg = (InstantMessage)btnSave.Tag;
772                 client.Self.InstantMessage(client.Self.Name, msg.FromAgentID, string.Empty, msg.IMSessionID, InstantMessageDialog.GroupNoticeInventoryAccepted, InstantMessageOnline.Offline, client.Self.SimPosition, client.Network.CurrentSim.RegionID, destinationFolderID.GetBytes());
773                 btnSave.Enabled = false;
774                 btnClose.Focus();
775             }
776         }
777
778         private void btnJoin_Click(object sender, EventArgs e)
779         {
780             client.Groups.RequestJoinGroup(group.ID);
781         }
782
783         private void lvwGeneralMembers_MouseDoubleClick(object sender, MouseEventArgs e)
784         {
785             ListViewItem item = lvwGeneralMembers.GetItemAt(e.X, e.Y);
786             if (item != null)
787             {
788                 try
789                 {
790                     UUID agentID = new UUID(item.Name);
791                     instance.MainForm.ShowAgentProfile(item.Text, agentID);
792                 }
793                 catch (Exception) { }
794             }
795         }
796
797         private void lvwMemberDetails_MouseDoubleClick(object sender, MouseEventArgs e)
798         {
799             ListViewItem item = lvwMemberDetails.GetItemAt(e.X, e.Y);
800             if (item != null)
801             {
802                 try
803                 {
804                     UUID agentID = new UUID(item.Name);
805                     instance.MainForm.ShowAgentProfile(item.Text, agentID);
806                 }
807                 catch (Exception) { }
808             }
809         }
810
811         private void btnEjectMember_Click(object sender, EventArgs e)
812         {
813             if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
814             EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
815             client.Groups.EjectUser(group.ID, m.Base.ID);
816         }
817
818         private void lvwMemberDetails_SelectedIndexChanged(object sender, EventArgs e)
819         {
820             btnBanMember.Enabled = lvwMemberDetails.SelectedIndices.Count > 0;
821
822             if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
823             EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
824
825             btnApply.Enabled = false;
826
827             lvwAssignedRoles.BeginUpdate();
828             lvwAssignedRoles.ItemChecked -= lvwAssignedRoles_ItemChecked;
829             lvwAssignedRoles.Items.Clear();
830             lvwAssignedRoles.Tag = m;
831
832             ListViewItem defaultItem = new ListViewItem();
833             defaultItem.Name = "Everyone";
834             defaultItem.SubItems.Add(defaultItem.Name);
835             defaultItem.Checked = true;
836             lvwAssignedRoles.Items.Add(defaultItem);
837
838             GroupPowers abilities = GroupPowers.None;
839
840             lock (roles)
841             {
842                 foreach (var r in roles)
843                 {
844                     GroupRole role = r.Value;
845
846                     if (role.ID == UUID.Zero)
847                     {
848                         abilities |= role.Powers;
849                         continue;
850                     }
851
852                     ListViewItem item = new ListViewItem();
853                     item.Name = role.Name;
854                     item.SubItems.Add(new ListViewItem.ListViewSubItem(item, role.Name));
855                     item.Tag = role;
856                     var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
857                     bool hasRole = foundRole.Value == m.Base.ID;
858                     item.Checked = hasRole;
859                     lvwAssignedRoles.Items.Add(item);
860
861                     if (hasRole)
862                         abilities |= role.Powers;
863                 }
864             }
865
866             lvwAssignedRoles.ItemChecked += lvwAssignedRoles_ItemChecked;
867             lvwAssignedRoles.EndUpdate();
868
869             lvwAllowedAbilities.BeginUpdate();
870             lvwAllowedAbilities.Items.Clear();
871
872             foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
873             {
874                 if (p != GroupPowers.None && (abilities & p) == p)
875                 {
876                     lvwAllowedAbilities.Items.Add(p.ToString());
877                 }
878             }
879
880
881             lvwAllowedAbilities.EndUpdate();
882
883         }
884
885         private void UpdateMemberRoles()
886         {
887             EnhancedGroupMember m = (EnhancedGroupMember)lvwAssignedRoles.Tag;
888             GroupRoleChangesPacket p = new GroupRoleChangesPacket();
889             p.AgentData.AgentID = client.Self.AgentID;
890             p.AgentData.SessionID = client.Self.SessionID;
891             p.AgentData.GroupID = group.ID;
892             List<GroupRoleChangesPacket.RoleChangeBlock> changes = new List<GroupRoleChangesPacket.RoleChangeBlock>();
893
894             foreach (ListViewItem item in lvwAssignedRoles.Items)
895             {
896                 if (!(item.Tag is GroupRole))
897                     continue;
898
899                 GroupRole role = (GroupRole)item.Tag;
900                 var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
901                 bool hasRole = foundRole.Value == m.Base.ID;
902
903                 if (item.Checked != hasRole)
904                 {
905                     if (item.Checked)
906                         roleMembers.Add(new KeyValuePair<UUID, UUID>(role.ID, m.Base.ID));
907                     else
908                         roleMembers.Remove(foundRole);
909
910                     var rc = new GroupRoleChangesPacket.RoleChangeBlock();
911                     rc.MemberID = m.Base.ID;
912                     rc.RoleID = role.ID;
913                     rc.Change = item.Checked ? 0u : 1u;
914                     changes.Add(rc);
915                 }
916             }
917
918             if (changes.Count > 0)
919             {
920                 p.RoleChange = changes.ToArray();
921                 client.Network.CurrentSim.SendPacket(p);
922             }
923
924             btnApply.Enabled = false;
925             lvwMemberDetails_SelectedIndexChanged(lvwMemberDetails, EventArgs.Empty);
926         }
927
928
929         private void lvwAssignedRoles_ItemChecked(object sender, ItemCheckedEventArgs e)
930         {
931             if (e.Item.Tag == null) // click on the default role
932             {
933                 if (!e.Item.Checked)
934                     e.Item.Checked = true;
935
936                 return;
937             }
938             if (e.Item.Tag is GroupRole)
939             {
940                 EnhancedGroupMember m = (EnhancedGroupMember)lvwAssignedRoles.Tag;
941                 bool modified = false;
942
943                 foreach (ListViewItem item in lvwAssignedRoles.Items)
944                 {
945                     if (!(item.Tag is GroupRole))
946                         continue;
947
948                     GroupRole role = (GroupRole)item.Tag;
949                     var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
950                     bool hasRole = foundRole.Value == m.Base.ID;
951
952                     if (item.Checked != hasRole)
953                     {
954                         modified = true;
955                     }
956                 }
957
958                 btnApply.Enabled = modified;
959             }
960         }
961
962         private void tbxCharter_TextChanged(object sender, EventArgs e)
963         {
964             btnApply.Enabled = true;
965         }
966
967         private void btnApply_Click(object sender, EventArgs e)
968         {
969             switch (tcGroupDetails.SelectedTab.Name)
970             {
971                 case "tpMembersRoles":
972                     UpdateMemberRoles();
973                     break;
974             }
975         }
976
977         private void btnInviteNewMember_Click(object sender, EventArgs e)
978         {
979             (new GroupInvite(instance, group, roles)).Show();
980         }
981
982         private void lvwAllowedAbilities_SizeChanged(object sender, EventArgs e)
983         {
984             lvwAllowedAbilities.Columns[0].Width = lvwAllowedAbilities.Width - 30;
985         }
986
987         private void tcMembersRoles_SelectedIndexChanged(object sender, EventArgs e)
988         {
989             switch (tcMembersRoles.SelectedTab.Name)
990             {
991                 case "tpMembers":
992                     RefreshMembersRoles();
993                     break;
994
995                 case "tpRoles":
996                     RefreshRoles();
997                     break;
998
999             }
1000
1001         }
1002
1003         private void lvwRoles_SelectedIndexChanged(object sender, EventArgs e)
1004         {
1005             txtRoleDescription.Text = txtRoleName.Text = txtRoleTitle.Text = string.Empty;
1006             txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = false;
1007             lvwAssignedMembers.Items.Clear();
1008             lvwRoleAbilitis.Items.Clear();
1009
1010             if (lvwRoles.SelectedItems.Count != 1) return;
1011
1012             GroupRole role = (GroupRole)lvwRoles.SelectedItems[0].Tag;
1013             txtRoleName.Text = role.Name;
1014             txtRoleTitle.Text = role.Title;
1015             txtRoleDescription.Text = role.Description;
1016
1017             if (HasPower(GroupPowers.RoleProperties))
1018             {
1019                 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1020             }
1021
1022             if (HasPower(GroupPowers.ChangeActions))
1023             {
1024                 lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = true;
1025             }
1026
1027             btnSaveRole.Tag = role;
1028
1029             lvwAssignedMembers.BeginUpdate();
1030             if (role.ID == UUID.Zero)
1031             {
1032                 foreach (var member in GroupMembers)
1033                     lvwAssignedMembers.Items.Add(member.Name);
1034             }
1035             else if (roleMembers != null)
1036             {
1037                 var mmb = roleMembers.FindAll((KeyValuePair<UUID, UUID> kvp) => { return kvp.Key == role.ID; });
1038                 foreach (var m in mmb)
1039                 {
1040                     lvwAssignedMembers.Items.Add(instance.Names.Get(m.Value));
1041                 }
1042             }
1043             lvwAssignedMembers.EndUpdate();
1044
1045             lvwRoleAbilitis.Tag = role;
1046
1047             foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
1048             {
1049                 if (p != GroupPowers.None)
1050                 {
1051                     ListViewItem item = new ListViewItem();
1052                     item.Tag = p;
1053                     item.SubItems.Add(p.ToString());
1054                     item.Checked = (p & role.Powers) != 0;
1055                     lvwRoleAbilitis.Items.Add(item);
1056                 }
1057             }
1058         }
1059
1060         private void btnCreateNewRole_Click(object sender, EventArgs e)
1061         {
1062             lvwRoles.SelectedItems.Clear();
1063             txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1064             btnSaveRole.Tag = null;
1065             txtRoleName.Focus();
1066         }
1067
1068         private void btnSaveRole_Click(object sender, EventArgs e)
1069         {
1070             if (btnSaveRole.Tag == null) // new role
1071             {
1072                 GroupRole role = new GroupRole();
1073                 role.Name = txtRoleName.Text;
1074                 role.Title = txtRoleTitle.Text;
1075                 role.Description = txtRoleDescription.Text;
1076                 client.Groups.CreateRole(group.ID, role);
1077                 System.Threading.Thread.Sleep(100);
1078                 RefreshRoles();
1079             }
1080             else if (btnSaveRole.Tag is GroupRole) // update role
1081             {
1082                 GroupRole role = (GroupRole)btnSaveRole.Tag;
1083
1084                 if (HasPower(GroupPowers.ChangeActions))
1085                 {
1086                     role.Powers = GroupPowers.None;
1087
1088                     foreach (ListViewItem item in lvwRoleAbilitis.Items)
1089                     {
1090                         if (item.Checked)
1091                             role.Powers |= (GroupPowers)item.Tag;
1092                     }
1093                 }
1094
1095                 if (HasPower(GroupPowers.RoleProperties))
1096                 {
1097                     role.Name = txtRoleName.Text;
1098                     role.Title = txtRoleTitle.Text;
1099                     role.Description = txtRoleDescription.Text;
1100                 }
1101
1102                 client.Groups.UpdateRole(role);
1103                 System.Threading.Thread.Sleep(100);
1104                 RefreshRoles();
1105             }
1106         }
1107
1108         private void btnDeleteRole_Click(object sender, EventArgs e)
1109         {
1110             if (lvwRoles.SelectedItems.Count == 1)
1111             {
1112                 client.Groups.DeleteRole(group.ID, ((GroupRole)lvwRoles.SelectedItems[0].Tag).ID);
1113                 System.Threading.Thread.Sleep(100);
1114                 RefreshRoles();
1115             }
1116         }
1117
1118         #region New notice
1119         private void btnNewNotice_Click(object sender, EventArgs e)
1120         {
1121             if (HasPower(GroupPowers.SendNotices))
1122             {
1123                 pnlArchivedNotice.Visible = false;
1124                 pnlNewNotice.Visible = true;
1125                 txtNewNoticeTitle.Focus();
1126             }
1127             else
1128             {
1129                 instance.TabConsole.DisplayNotificationInChat("Don't have permission to send notices in this group", ChatBufferTextStyle.Error);
1130             }
1131         }
1132
1133         private void btnPasteInv_Click(object sender, EventArgs e)
1134         {
1135             if (instance.InventoryClipboard != null && instance.InventoryClipboard.Item is InventoryItem)
1136             {
1137                 InventoryItem inv = instance.InventoryClipboard.Item as InventoryItem;
1138                 txtNewNoteAtt.Text = inv.Name;
1139                 int icoIndx = InventoryConsole.GetItemImageIndex(inv.AssetType.ToString().ToLower());
1140                 if (icoIndx >= 0)
1141                 {
1142                     icnNewNoticeAtt.Image = frmMain.ResourceImages.Images[icoIndx];
1143                     icnNewNoticeAtt.Visible = true;
1144                 }
1145                 txtNewNoteAtt.Tag = inv;
1146                 btnRemoveAttachment.Enabled = true;
1147             }
1148         }
1149
1150         private void btnRemoveAttachment_Click(object sender, EventArgs e)
1151         {
1152             txtNewNoteAtt.Tag = null;
1153             txtNewNoteAtt.Text = string.Empty;
1154             btnRemoveAttachment.Enabled = false;
1155             icnNewNoticeAtt.Visible = false;
1156         }
1157
1158         private void btnSend_Click(object sender, EventArgs e)
1159         {
1160             GroupNotice ntc = new GroupNotice();
1161             ntc.Subject = txtNewNoticeTitle.Text;
1162             ntc.Message = txtNewNoticeBody.Text;
1163             if (txtNewNoteAtt.Tag != null && txtNewNoteAtt.Tag is InventoryItem)
1164             {
1165                 InventoryItem inv = txtNewNoteAtt.Tag as InventoryItem;
1166                 ntc.OwnerID = inv.OwnerID;
1167                 ntc.AttachmentID = inv.UUID;
1168             }
1169             client.Groups.SendGroupNotice(group.ID, ntc);
1170             btnRemoveAttachment.PerformClick();
1171             txtNewNoticeTitle.Text = txtNewNoticeBody.Text = string.Empty;
1172             pnlNewNotice.Visible = false;
1173             btnRefresh.PerformClick();
1174             instance.TabConsole.DisplayNotificationInChat("Notice sent", ChatBufferTextStyle.Invisible);
1175         }
1176         #endregion
1177
1178         private void memberListContextMenuSave_Click(object sender, EventArgs e)
1179         {
1180             SaveFileDialog saveMembers = new SaveFileDialog();
1181             saveMembers.Filter = "CSV|.csv|JSON|.json";
1182             saveMembers.Title = "Save visible group members";
1183             saveMembers.ShowDialog();
1184             if (saveMembers.FileName != string.Empty)
1185             {
1186                 try
1187                 {
1188                     switch (saveMembers.FilterIndex)
1189                     {
1190                         case 1:
1191                             System.IO.FileStream fs = (System.IO.FileStream)saveMembers.OpenFile();
1192                             System.IO.StreamWriter sw = new System.IO.StreamWriter(fs, System.Text.Encoding.UTF8);
1193                             sw.WriteLine("UUID,Name");
1194                             foreach (var item in GroupMembers)
1195                             {
1196                                 sw.WriteLine("{0},{1}", item.Base.ID, item.Name);
1197                             }
1198                             sw.Close();
1199                             break;
1200                         case 2:
1201                             OpenMetaverse.StructuredData.OSDArray members = new OpenMetaverse.StructuredData.OSDArray(GroupMembers.Count);
1202                             foreach (var item in GroupMembers)
1203                             {
1204                                 OpenMetaverse.StructuredData.OSDMap member = new OpenMetaverse.StructuredData.OSDMap(2);
1205                                 member["UUID"] = item.Base.ID;
1206                                 member["Name"] = item.Name;
1207                                 members.Add(member);
1208                             }
1209                             System.IO.File.WriteAllText(saveMembers.FileName, OpenMetaverse.StructuredData.OSDParser.SerializeJsonString(members));
1210                             break;
1211                     }
1212
1213                     instance.TabConsole.DisplayNotificationInChat(string.Format("Saved {0} members to {1}", GroupMembers.Count, saveMembers.FileName));
1214                 }
1215                 catch (Exception ex)
1216                 {
1217                     instance.TabConsole.DisplayNotificationInChat("Failed to save member list: " + ex.Message, ChatBufferTextStyle.Error);
1218                 }
1219             }
1220         }
1221
1222         private void copyRoleIDToolStripMenuItem_Click(object sender, EventArgs e)
1223         {
1224             if (lvwRoles.SelectedItems.Count != 1) return;
1225             if (lvwRoles.SelectedItems[0].Tag is GroupRole)
1226             {
1227                 Clipboard.SetText(((GroupRole)lvwRoles.SelectedItems[0].Tag).ID.ToString());
1228             }
1229         }
1230
1231         private void lvwMemberDetails_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
1232         {
1233             if (e.IsTextSearch)
1234             {
1235                 for (int i = 0; i < GroupMembers.Count; i++)
1236                 {
1237                     if (GroupMembers[i].Name.StartsWith(e.Text, StringComparison.CurrentCultureIgnoreCase))
1238                     {
1239                         e.Index = i;
1240                         break;
1241                     }
1242                 }
1243             }
1244         }
1245
1246         #region Group Bans
1247         public void RefreshBans()
1248         {
1249             client.Groups.RequestBannedAgents(group.ID, (xs, xe) =>
1250             {
1251                 UpdateBannedAgents(xe);
1252             });
1253         }
1254
1255         void UpdateBannedAgents(BannedAgentsEventArgs e)
1256         {
1257             if (!e.Success || e.GroupID != group.ID) return;
1258             if (InvokeRequired)
1259             {
1260                 BeginInvoke(new MethodInvoker(() => UpdateBannedAgents(e)));
1261                 return;
1262             }
1263
1264             lwBannedMembers.BeginUpdate();
1265             lwBannedMembers.Items.Clear();
1266
1267             foreach (var member in e.BannedAgents)
1268             {
1269                 var item = new ListViewItem(instance.Names.Get(member.Key));
1270                 item.Name = member.Key.ToString();
1271                 item.SubItems.Add(member.Value.ToShortDateString());
1272                 lwBannedMembers.Items.Add(item);
1273             }
1274             lwBannedMembers.EndUpdate();
1275         }
1276
1277         private void btnBan_Click(object sender, EventArgs e)
1278         {
1279             (new BanGroupMember(instance, group, this)).Show();
1280         }
1281
1282         private void btnUnban_Click(object sender, EventArgs e)
1283         {
1284             List<UUID> toUnban = new List<UUID>();
1285             for (int i=0; i<lwBannedMembers.SelectedItems.Count; i++)
1286             {
1287                 UUID id;
1288                 if (UUID.TryParse(lwBannedMembers.SelectedItems[i].Name, out id))
1289                 {
1290                     toUnban.Add(id);
1291                 }
1292             }
1293
1294             if (toUnban.Count > 0)
1295             {
1296                 client.Groups.RequestBanAction(group.ID, GroupBanAction.Unban, toUnban.ToArray(), (xs, se) =>
1297                 {
1298                     RefreshBans();
1299                 });
1300             }
1301         }
1302
1303         private void lwBannedMembers_SelectedIndexChanged(object sender, EventArgs e)
1304         {
1305             btnUnban.Enabled = lwBannedMembers.SelectedItems.Count > 0;
1306         }
1307
1308         private void btnBanMember_Click(object sender, EventArgs e)
1309         {
1310             try
1311             {
1312                 List<UUID> toBan = new List<UUID>();
1313                 for (int i = 0; i < lvwMemberDetails.SelectedIndices.Count; i++)
1314                 {
1315                     EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[i]];
1316                     toBan.Add(m.Base.ID);
1317                     client.Groups.EjectUser(group.ID, m.Base.ID);
1318                 }
1319
1320                 if (toBan.Count > 0)
1321                 {
1322                     client.Groups.RequestBanAction(group.ID, GroupBanAction.Ban, toBan.ToArray(), (xs, xe) =>
1323                     {
1324                         RefreshBans();
1325                     });
1326                 }
1327             }
1328             catch { }
1329
1330         }
1331         #endregion Group Bans
1332     }
1333
1334     public class EnhancedGroupMember
1335     {
1336         public GroupMember Base;
1337         public DateTime LastOnline;
1338         public string Name;
1339
1340         public EnhancedGroupMember(string name, GroupMember baseMember)
1341         {
1342             Base = baseMember;
1343             Name = name;
1344
1345             if (baseMember.OnlineStatus == "Online")
1346             {
1347                 LastOnline = DateTime.Now;
1348             }
1349             else if (string.IsNullOrEmpty(baseMember.OnlineStatus) || baseMember.OnlineStatus == "unknown")
1350             {
1351                 LastOnline = DateTime.MinValue;
1352             }
1353             else
1354             {
1355                 try
1356                 {
1357                     LastOnline = Convert.ToDateTime(baseMember.OnlineStatus, Utils.EnUsCulture);
1358                 }
1359                 catch (FormatException)
1360                 {
1361                     LastOnline = DateTime.MaxValue;
1362                 }
1363             }
1364         }
1365     }
1366
1367     #region Sorter classes
1368     public class GroupMemberSorter : IComparer<EnhancedGroupMember>
1369     {
1370         public enum SortByColumn
1371         {
1372             Name,
1373             Title,
1374             LastOnline,
1375             Contribution
1376         }
1377
1378         public enum SortOrder
1379         {
1380             Ascending,
1381             Descending
1382         }
1383
1384         public SortOrder CurrentOrder = SortOrder.Ascending;
1385         public SortByColumn SortBy = SortByColumn.Name;
1386
1387         public int Compare(EnhancedGroupMember member1, EnhancedGroupMember member2)
1388         {
1389             switch (SortBy)
1390             {
1391                 case SortByColumn.Name:
1392                     if (CurrentOrder == SortOrder.Ascending)
1393                         return string.Compare(member1.Name, member2.Name);
1394                     else
1395                         return string.Compare(member2.Name, member1.Name);
1396
1397                 case SortByColumn.Title:
1398                     if (CurrentOrder == SortOrder.Ascending)
1399                         return string.Compare(member1.Base.Title, member2.Base.Title);
1400                     else
1401                         return string.Compare(member2.Base.Title, member1.Base.Title);
1402
1403                 case SortByColumn.LastOnline:
1404                     if (CurrentOrder == SortOrder.Ascending)
1405                         return DateTime.Compare(member1.LastOnline, member2.LastOnline);
1406                     else
1407                         return DateTime.Compare(member2.LastOnline, member1.LastOnline);
1408
1409                 case SortByColumn.Contribution:
1410                     if (member1.Base.Contribution < member2.Base.Contribution)
1411                         return CurrentOrder == SortOrder.Ascending ? -1 : 1;
1412                     else if (member1.Base.Contribution > member2.Base.Contribution)
1413                         return CurrentOrder == SortOrder.Ascending ? 1 : -1;
1414                     else
1415                         return 0;
1416             }
1417
1418             return 0;
1419         }
1420     }
1421
1422
1423     public class GroupNoticeSorter : IComparer
1424     {
1425         public enum SortByColumn
1426         {
1427             Subject,
1428             Sender,
1429             Date
1430         }
1431
1432         public enum SortOrder
1433         {
1434             Ascending,
1435             Descending
1436         }
1437
1438         public SortOrder CurrentOrder = SortOrder.Descending;
1439         public SortByColumn SortBy = SortByColumn.Date;
1440
1441         private int IntCompare(uint x, uint y)
1442         {
1443             if (x < y)
1444             {
1445                 return -1;
1446             }
1447             else if (x > y)
1448             {
1449                 return 1;
1450             }
1451             else
1452             {
1453                 return 0;
1454             }
1455         }
1456
1457         public int Compare(object x, object y)
1458         {
1459             ListViewItem item1 = (ListViewItem)x;
1460             ListViewItem item2 = (ListViewItem)y;
1461             GroupNoticesListEntry member1 = (GroupNoticesListEntry)item1.Tag;
1462             GroupNoticesListEntry member2 = (GroupNoticesListEntry)item2.Tag;
1463
1464             switch (SortBy)
1465             {
1466                 case SortByColumn.Subject:
1467                     if (CurrentOrder == SortOrder.Ascending)
1468                         return string.Compare(member1.Subject, member2.Subject);
1469                     else
1470                         return string.Compare(member2.Subject, member1.Subject);
1471
1472                 case SortByColumn.Sender:
1473                     if (CurrentOrder == SortOrder.Ascending)
1474                         return string.Compare(member1.FromName, member2.FromName);
1475                     else
1476                         return string.Compare(member2.FromName, member1.FromName);
1477
1478                 case SortByColumn.Date:
1479                     if (CurrentOrder == SortOrder.Ascending)
1480                         return IntCompare(member1.Timestamp, member2.Timestamp);
1481                     else
1482                         return IntCompare(member2.Timestamp, member1.Timestamp);
1483             }
1484
1485             return 0;
1486         }
1487     }
1488     #endregion
1489 }