OSDN Git Service

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