2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
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.
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.
32 using System.Collections;
33 using System.Collections.Generic;
34 #if (COGBOT_LIBOMV || USE_STHREADS)
36 using Thread = ThreadPoolUtil.Thread;
37 using ThreadPool = ThreadPoolUtil.ThreadPool;
38 using Monitor = ThreadPoolUtil.Monitor;
40 using System.Threading;
42 using System.Windows.Forms;
44 using OpenMetaverse.Packets;
48 public partial class GroupDetails : UserControl
50 private RadegastInstance instance;
51 private GridClient client { get { return instance.Client; } }
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;
61 private UUID groupTitlesRequest, groupMembersRequest, groupRolesRequest, groupRolesMembersRequest;
63 public GroupDetails(RadegastInstance instance, Group group)
65 InitializeComponent();
66 Disposed += new EventHandler(GroupDetails_Disposed);
68 this.instance = instance;
71 if (group.InsigniaID != UUID.Zero)
73 SLImageHandler insignia = new SLImageHandler(instance, group.InsigniaID, string.Empty);
74 insignia.Dock = DockStyle.Fill;
75 pnlInsignia.Controls.Add(insignia);
78 nameUpdateTimer = new System.Threading.Timer(nameUpdateTimer_Elapsed, this,
79 System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
81 txtGroupID.Text = group.ID.ToString();
83 lblGroupName.Text = group.Name;
85 bool hasGroupBans = null != client.Groups.GetGroupAPIUri(group.ID);
88 lblGroupBansTitle.Text = "Region does not support group bans";
89 pnlBannedBottom.Enabled = pnlBannedTop.Enabled = lwBannedMembers.Enabled = false;
92 isMember = instance.Groups.ContainsKey(group.ID);
96 tcGroupDetails.TabPages.Remove(tpMembersRoles);
97 tcGroupDetails.TabPages.Remove(tpNotices);
98 tcGroupDetails.TabPages.Remove(tpBanned);
105 lvwNoticeArchive.SmallImageList = frmMain.ResourceImages;
106 lvwNoticeArchive.ListViewItemSorter = new GroupNoticeSorter();
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();
125 void GroupDetails_Disposed(object sender, EventArgs e)
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)
140 instance.Names.NameUpdated -= new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
142 if (nameUpdateTimer != null)
144 nameUpdateTimer.Dispose();
145 nameUpdateTimer = null;
149 #region Network callbacks
151 void Groups_GroupMemberEjected(object sender, GroupOperationEventArgs e)
153 if (e.GroupID != group.ID) return;
157 BeginInvoke(new MethodInvoker(() => { RefreshGroupInfo(); }));
158 instance.TabConsole.DisplayNotificationInChat("Group member ejected.");
162 instance.TabConsole.DisplayNotificationInChat("Failed to eject group member.");
166 void Groups_GroupRoleMembersReply(object sender, GroupRolesMembersReplyEventArgs e)
168 if (e.GroupID == group.ID && e.RequestID == groupRolesMembersRequest)
170 roleMembers = e.RolesMembers;
171 BeginInvoke(new MethodInvoker(() =>
173 btnInviteNewMember.Enabled = HasPower(GroupPowers.Invite);
174 btnEjectMember.Enabled = HasPower(GroupPowers.Eject);
175 lvwMemberDetails_SelectedIndexChanged(null, null);
181 void Groups_GroupRoleDataReply(object sender, GroupRolesDataReplyEventArgs e)
183 if (e.GroupID == group.ID && e.RequestID == groupRolesRequest)
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()));
192 void Groups_GroupLeaveReply(object sender, GroupOperationEventArgs e)
194 if (e.GroupID == group.ID && e.Success)
196 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
200 void Groups_GroupJoinedReply(object sender, GroupOperationEventArgs e)
202 if (e.GroupID == group.ID && e.Success)
204 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
208 UUID destinationFolderID;
210 void Self_IM(object sender, InstantMessageEventArgs e)
212 if (e.IM.Dialog != InstantMessageDialog.GroupNoticeRequested) return;
216 BeginInvoke(new MethodInvoker(() => Self_IM(sender, e)));
220 InstantMessage msg = e.IM;
224 if (msg.BinaryBucket.Length >= 18)
226 groupID = new UUID(msg.BinaryBucket, 2);
230 groupID = msg.FromAgentID;
233 if (groupID != group.ID) return;
235 if (msg.BinaryBucket.Length > 18 && msg.BinaryBucket[0] != 0)
237 type = (AssetType)msg.BinaryBucket[1];
238 destinationFolderID = client.Inventory.FindFolderForType(type);
239 int icoIndx = InventoryConsole.GetItemImageIndex(type.ToString().ToLower());
242 icnItem.Image = frmMain.ResourceImages.Images[icoIndx];
243 icnItem.Visible = true;
245 txtItemName.Text = Utils.BytesToString(msg.BinaryBucket, 18, msg.BinaryBucket.Length - 19);
246 btnSave.Enabled = true;
247 btnSave.Visible = icnItem.Visible = txtItemName.Visible = true;
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;
258 void Groups_GroupNoticesListReply(object sender, GroupNoticesListReplyEventArgs e)
260 if (e.GroupID != group.ID) return;
264 BeginInvoke(new MethodInvoker(() => Groups_GroupNoticesListReply(sender, e)));
268 lvwNoticeArchive.BeginUpdate();
270 foreach (GroupNoticesListEntry notice in e.Notices)
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)
278 noticeDate = Utils.UnixTimeToDateTime(notice.Timestamp).ToShortDateString();
280 item.SubItems.Add(noticeDate);
282 if (notice.HasAttachment)
284 item.ImageIndex = InventoryConsole.GetItemImageIndex(notice.AssetType.ToString().ToLower());
289 lvwNoticeArchive.Items.Add(item);
291 lvwNoticeArchive.EndUpdate();
294 void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)
296 BeginInvoke(new MethodInvoker(RefreshControlsAvailability));
299 void Groups_GroupProfile(object sender, GroupProfileEventArgs e)
301 if (group.ID != e.Group.ID) return;
305 BeginInvoke(new MethodInvoker(() => Groups_GroupProfile(sender, e)));
310 if (group.InsigniaID != UUID.Zero && pnlInsignia.Controls.Count == 0)
312 SLImageHandler insignia = new SLImageHandler(instance, group.InsigniaID, string.Empty);
313 insignia.Dock = DockStyle.Fill;
314 pnlInsignia.Controls.Add(insignia);
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;
323 if (group.MembershipFee > 0)
325 cbxEnrollmentFee.Checked = true;
326 nudEnrollmentFee.Value = group.MembershipFee;
330 cbxEnrollmentFee.Checked = false;
331 nudEnrollmentFee.Value = 0;
334 if (group.MaturePublish)
336 cbxMaturity.SelectedIndex = 1;
340 cbxMaturity.SelectedIndex = 0;
343 btnJoin.Enabled = btnJoin.Visible = false;
345 if (myGroups.ContainsKey(group.ID)) // I am in this group
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);
352 else if (group.OpenEnrollment) // I am not in this group, but I could join it
354 btnJoin.Text = "Join $L" + group.MembershipFee;
355 btnJoin.Enabled = btnJoin.Visible = true;
358 RefreshControlsAvailability();
361 void Names_NameUpdated(object sender, UUIDNameReplyEventArgs e)
363 ProcessNameUpdate(e.Names);
368 void ProcessNameUpdate(Dictionary<UUID, string> Names)
370 if (Names.ContainsKey(group.FounderID))
374 BeginInvoke(new MethodInvoker(() => { lblFounded.Text = "Founded by: " + Names[group.FounderID]; }));
378 lblFounded.Text = "Founded by: " + Names[group.FounderID];
382 WorkPool.QueueUserWorkItem(sync =>
386 bool hasUpdates = false;
388 foreach (var name in Names)
390 var member = GroupMembers.Find((m) => m.Base.ID == name.Key);
391 if (member == null) continue;
394 member.Name = name.Value;
399 int tick = Environment.TickCount;
400 int elapsed = tick - lastTick;
404 nameUpdateTimer_Elapsed(this);
406 nameUpdateTimer.Change(500, System.Threading.Timeout.Infinite);
411 Logger.DebugLog("Failed updating group member names: " + ex.ToString());
416 void Groups_GroupTitlesReply(object sender, GroupTitlesReplyEventArgs e)
418 if (groupTitlesRequest != e.RequestID) return;
422 BeginInvoke(new MethodInvoker(() => Groups_GroupTitlesReply(sender, e)));
426 this.titles = e.Titles;
428 foreach (GroupTitle title in titles.Values)
430 cbxActiveTitle.Items.Add(title);
433 cbxActiveTitle.SelectedItem = title;
437 cbxActiveTitle.SelectedIndexChanged += cbxActiveTitle_SelectedIndexChanged;
440 List<EnhancedGroupMember> GroupMembers = new List<EnhancedGroupMember>();
442 void Groups_GroupMembersReply(object sender, GroupMembersReplyEventArgs e)
446 BeginInvoke(new MethodInvoker(() => Groups_GroupMembersReply(sender, e)));
450 lvwGeneralMembers.VirtualListSize = 0;
451 lvwMemberDetails.VirtualListSize = 0;
453 var members = new List<EnhancedGroupMember>(e.Members.Count);
454 foreach (var member in e.Members)
456 members.Add(new EnhancedGroupMember(instance.Names.Get(member.Key), member.Value));
459 GroupMembers = members;
460 GroupMembers.Sort(memberSorter);
461 lvwGeneralMembers.VirtualListSize = GroupMembers.Count;
462 lvwMemberDetails.VirtualListSize = GroupMembers.Count;
465 void lvwMemberDetails_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
467 EnhancedGroupMember member = null;
470 member = GroupMembers[e.ItemIndex];
474 e.Item = new ListViewItem();
478 ListViewItem item = new ListViewItem(member.Name);
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)
484 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
490 void lvwGeneralMembers_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
492 EnhancedGroupMember member = null;
495 member = GroupMembers[e.ItemIndex];
499 e.Item = new ListViewItem();
502 ListViewItem item = new ListViewItem(member.Name);
505 item.Name = member.Base.ID.ToString();
506 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Title));
507 if (member.LastOnline != DateTime.MinValue)
509 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
516 #region Privatate methods
518 private void nameUpdateTimer_Elapsed(object sync)
522 BeginInvoke(new MethodInvoker(() => nameUpdateTimer_Elapsed(sync)));
526 GroupMembers.Sort(memberSorter);
527 lvwGeneralMembers.Invalidate();
528 lvwMemberDetails.Invalidate();
531 private void DisplayGroupRoles()
533 lvwRoles.Items.Clear();
537 foreach (GroupRole role in roles.Values)
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());
545 lvwRoles.Items.Add(item);
552 private bool HasPower(GroupPowers power)
554 if (!instance.Groups.ContainsKey(group.ID))
557 return (instance.Groups[group.ID].Powers & power) != 0;
560 private void RefreshControlsAvailability()
562 if (!HasPower(GroupPowers.ChangeOptions))
564 nudEnrollmentFee.ReadOnly = true;
565 cbxEnrollmentFee.Enabled = false;
566 cbxOpenEnrollment.Enabled = false;
569 if (!HasPower(GroupPowers.ChangeIdentity))
571 tbxCharter.ReadOnly = true;
572 cbxShowInSearch.Enabled = false;
573 cbxMaturity.Enabled = false;
576 if (!myGroups.ContainsKey(group.ID))
578 cbxReceiveNotices.Enabled = false;
579 cbxListInProfile.Enabled = false;
583 private void RefreshGroupNotices()
585 lvwNoticeArchive.Items.Clear();
586 client.Groups.RequestGroupNoticesList(group.ID);
587 btnNewNotice.Enabled = HasPower(GroupPowers.SendNotices);
590 private void RefreshGroupInfo()
592 lvwGeneralMembers.VirtualListSize = 0;
593 if (isMember) lvwMemberDetails.VirtualListSize = 0;
595 cbxActiveTitle.SelectedIndexChanged -= cbxActiveTitle_SelectedIndexChanged;
596 cbxReceiveNotices.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
597 cbxListInProfile.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
599 cbxActiveTitle.Items.Clear();
601 // Request group info
602 client.Groups.RequestGroupProfile(group.ID);
603 groupTitlesRequest = client.Groups.RequestGroupTitles(group.ID);
604 groupMembersRequest = client.Groups.RequestGroupMembers(group.ID);
607 private void RefreshRoles()
609 if (!isMember) return;
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);
620 private void RefreshMembersRoles()
622 if (!isMember) return;
624 btnApply.Enabled = false;
625 lvwAssignedRoles.Items.Clear();
626 groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
630 #region Controls change handlers
631 void cbxListInProfile_CheckedChanged(object sender, EventArgs e)
633 if (myGroups.ContainsKey(group.ID))
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();
643 private void cbxActiveTitle_SelectedIndexChanged(object sender, EventArgs e)
645 GroupTitle title = (GroupTitle)cbxActiveTitle.SelectedItem;
646 client.Groups.ActivateTitle(title.GroupID, title.RoleID);
649 private void btnRefresh_Click(object sender, EventArgs e)
651 switch (tcGroupDetails.SelectedTab.Name)
658 RefreshGroupNotices();
661 case "tpMembersRoles":
662 RefreshMembersRoles();
672 void lvwGeneralMembers_ColumnClick(object sender, ColumnClickEventArgs e)
674 ListView lb = (ListView)sender;
678 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Name;
682 if (lb.Name == "lvwMemberDetails")
683 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Contribution;
685 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Title;
689 memberSorter.SortBy = GroupMemberSorter.SortByColumn.LastOnline;
693 if (memberSorter.CurrentOrder == GroupMemberSorter.SortOrder.Ascending)
694 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Descending;
696 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Ascending;
698 GroupMembers.Sort(memberSorter);
702 private void lvwNoticeArchive_ColumnClick(object sender, ColumnClickEventArgs e)
705 GroupNoticeSorter sorter = (GroupNoticeSorter)lvwNoticeArchive.ListViewItemSorter;
710 sorter.SortBy = GroupNoticeSorter.SortByColumn.Subject;
714 sorter.SortBy = GroupNoticeSorter.SortByColumn.Sender;
718 sorter.SortBy = GroupNoticeSorter.SortByColumn.Date;
722 if (sorter.CurrentOrder == GroupNoticeSorter.SortOrder.Ascending)
723 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Descending;
725 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Ascending;
727 lvwNoticeArchive.Sort();
731 private void btnClose_Click(object sender, EventArgs e)
733 this.FindForm().Close();
736 private void tcGroupDetails_SelectedIndexChanged(object sender, EventArgs e)
738 switch (tcGroupDetails.SelectedTab.Name)
741 RefreshGroupNotices();
744 case "tpMembersRoles":
745 RefreshMembersRoles();
754 private void lvwNoticeArchive_SelectedIndexChanged(object sender, EventArgs e)
756 if (lvwNoticeArchive.SelectedItems.Count == 1)
758 if (lvwNoticeArchive.SelectedItems[0].Tag is GroupNoticesListEntry)
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;
771 pnlArchivedNotice.Visible = false;
774 private void btnSave_Click(object sender, EventArgs e)
776 if (btnSave.Tag is InstantMessage)
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;
785 private void btnJoin_Click(object sender, EventArgs e)
787 client.Groups.RequestJoinGroup(group.ID);
790 private void lvwGeneralMembers_MouseDoubleClick(object sender, MouseEventArgs e)
792 ListViewItem item = lvwGeneralMembers.GetItemAt(e.X, e.Y);
797 UUID agentID = new UUID(item.Name);
798 instance.MainForm.ShowAgentProfile(item.Text, agentID);
800 catch (Exception) { }
804 private void lvwMemberDetails_MouseDoubleClick(object sender, MouseEventArgs e)
806 ListViewItem item = lvwMemberDetails.GetItemAt(e.X, e.Y);
811 UUID agentID = new UUID(item.Name);
812 instance.MainForm.ShowAgentProfile(item.Text, agentID);
814 catch (Exception) { }
818 private void btnEjectMember_Click(object sender, EventArgs e)
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);
825 private void lvwMemberDetails_SelectedIndexChanged(object sender, EventArgs e)
827 btnBanMember.Enabled = lvwMemberDetails.SelectedIndices.Count > 0;
829 if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
830 EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
832 btnApply.Enabled = false;
834 lvwAssignedRoles.BeginUpdate();
835 lvwAssignedRoles.ItemChecked -= lvwAssignedRoles_ItemChecked;
836 lvwAssignedRoles.Items.Clear();
837 lvwAssignedRoles.Tag = m;
839 ListViewItem defaultItem = new ListViewItem();
840 defaultItem.Name = "Everyone";
841 defaultItem.SubItems.Add(defaultItem.Name);
842 defaultItem.Checked = true;
843 lvwAssignedRoles.Items.Add(defaultItem);
845 GroupPowers abilities = GroupPowers.None;
849 foreach (var r in roles)
851 GroupRole role = r.Value;
853 if (role.ID == UUID.Zero)
855 abilities |= role.Powers;
859 ListViewItem item = new ListViewItem();
860 item.Name = role.Name;
861 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, role.Name));
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);
869 abilities |= role.Powers;
873 lvwAssignedRoles.ItemChecked += lvwAssignedRoles_ItemChecked;
874 lvwAssignedRoles.EndUpdate();
876 lvwAllowedAbilities.BeginUpdate();
877 lvwAllowedAbilities.Items.Clear();
879 foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
881 if (p != GroupPowers.None && (abilities & p) == p)
883 lvwAllowedAbilities.Items.Add(p.ToString());
888 lvwAllowedAbilities.EndUpdate();
892 private void UpdateMemberRoles()
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>();
901 foreach (ListViewItem item in lvwAssignedRoles.Items)
903 if (!(item.Tag is GroupRole))
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;
910 if (item.Checked != hasRole)
913 roleMembers.Add(new KeyValuePair<UUID, UUID>(role.ID, m.Base.ID));
915 roleMembers.Remove(foundRole);
917 var rc = new GroupRoleChangesPacket.RoleChangeBlock();
918 rc.MemberID = m.Base.ID;
920 rc.Change = item.Checked ? 0u : 1u;
925 if (changes.Count > 0)
927 p.RoleChange = changes.ToArray();
928 client.Network.CurrentSim.SendPacket(p);
931 btnApply.Enabled = false;
932 lvwMemberDetails_SelectedIndexChanged(lvwMemberDetails, EventArgs.Empty);
936 private void lvwAssignedRoles_ItemChecked(object sender, ItemCheckedEventArgs e)
938 if (e.Item.Tag == null) // click on the default role
941 e.Item.Checked = true;
945 if (e.Item.Tag is GroupRole)
947 EnhancedGroupMember m = (EnhancedGroupMember)lvwAssignedRoles.Tag;
948 bool modified = false;
950 foreach (ListViewItem item in lvwAssignedRoles.Items)
952 if (!(item.Tag is GroupRole))
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;
959 if (item.Checked != hasRole)
965 btnApply.Enabled = modified;
969 private void tbxCharter_TextChanged(object sender, EventArgs e)
971 btnApply.Enabled = true;
974 private void btnApply_Click(object sender, EventArgs e)
976 switch (tcGroupDetails.SelectedTab.Name)
978 case "tpMembersRoles":
984 private void btnInviteNewMember_Click(object sender, EventArgs e)
986 (new GroupInvite(instance, group, roles)).Show();
989 private void lvwAllowedAbilities_SizeChanged(object sender, EventArgs e)
991 lvwAllowedAbilities.Columns[0].Width = lvwAllowedAbilities.Width - 30;
994 private void tcMembersRoles_SelectedIndexChanged(object sender, EventArgs e)
996 switch (tcMembersRoles.SelectedTab.Name)
999 RefreshMembersRoles();
1010 private void lvwRoles_SelectedIndexChanged(object sender, EventArgs e)
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();
1017 if (lvwRoles.SelectedItems.Count != 1) return;
1019 GroupRole role = (GroupRole)lvwRoles.SelectedItems[0].Tag;
1020 txtRoleName.Text = role.Name;
1021 txtRoleTitle.Text = role.Title;
1022 txtRoleDescription.Text = role.Description;
1024 if (HasPower(GroupPowers.RoleProperties))
1026 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1029 if (HasPower(GroupPowers.ChangeActions))
1031 lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = true;
1034 btnSaveRole.Tag = role;
1036 lvwAssignedMembers.BeginUpdate();
1037 if (role.ID == UUID.Zero)
1039 foreach (var member in GroupMembers)
1040 lvwAssignedMembers.Items.Add(member.Name);
1042 else if (roleMembers != null)
1044 var mmb = roleMembers.FindAll((KeyValuePair<UUID, UUID> kvp) => { return kvp.Key == role.ID; });
1045 foreach (var m in mmb)
1047 lvwAssignedMembers.Items.Add(instance.Names.Get(m.Value));
1050 lvwAssignedMembers.EndUpdate();
1052 lvwRoleAbilitis.Tag = role;
1054 foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
1056 if (p != GroupPowers.None)
1058 ListViewItem item = new ListViewItem();
1060 item.SubItems.Add(p.ToString());
1061 item.Checked = (p & role.Powers) != 0;
1062 lvwRoleAbilitis.Items.Add(item);
1067 private void btnCreateNewRole_Click(object sender, EventArgs e)
1069 lvwRoles.SelectedItems.Clear();
1070 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1071 btnSaveRole.Tag = null;
1072 txtRoleName.Focus();
1075 private void btnSaveRole_Click(object sender, EventArgs e)
1077 if (btnSaveRole.Tag == null) // new role
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);
1087 else if (btnSaveRole.Tag is GroupRole) // update role
1089 GroupRole role = (GroupRole)btnSaveRole.Tag;
1091 if (HasPower(GroupPowers.ChangeActions))
1093 role.Powers = GroupPowers.None;
1095 foreach (ListViewItem item in lvwRoleAbilitis.Items)
1098 role.Powers |= (GroupPowers)item.Tag;
1102 if (HasPower(GroupPowers.RoleProperties))
1104 role.Name = txtRoleName.Text;
1105 role.Title = txtRoleTitle.Text;
1106 role.Description = txtRoleDescription.Text;
1109 client.Groups.UpdateRole(role);
1110 System.Threading.Thread.Sleep(100);
1115 private void btnDeleteRole_Click(object sender, EventArgs e)
1117 if (lvwRoles.SelectedItems.Count == 1)
1119 client.Groups.DeleteRole(group.ID, ((GroupRole)lvwRoles.SelectedItems[0].Tag).ID);
1120 System.Threading.Thread.Sleep(100);
1126 private void btnNewNotice_Click(object sender, EventArgs e)
1128 if (HasPower(GroupPowers.SendNotices))
1130 pnlArchivedNotice.Visible = false;
1131 pnlNewNotice.Visible = true;
1132 txtNewNoticeTitle.Focus();
1136 instance.TabConsole.DisplayNotificationInChat("Don't have permission to send notices in this group", ChatBufferTextStyle.Error);
1140 private void btnPasteInv_Click(object sender, EventArgs e)
1142 if (instance.InventoryClipboard != null && instance.InventoryClipboard.Item is InventoryItem)
1144 InventoryItem inv = instance.InventoryClipboard.Item as InventoryItem;
1145 txtNewNoteAtt.Text = inv.Name;
1146 int icoIndx = InventoryConsole.GetItemImageIndex(inv.AssetType.ToString().ToLower());
1149 icnNewNoticeAtt.Image = frmMain.ResourceImages.Images[icoIndx];
1150 icnNewNoticeAtt.Visible = true;
1152 txtNewNoteAtt.Tag = inv;
1153 btnRemoveAttachment.Enabled = true;
1157 private void btnRemoveAttachment_Click(object sender, EventArgs e)
1159 txtNewNoteAtt.Tag = null;
1160 txtNewNoteAtt.Text = string.Empty;
1161 btnRemoveAttachment.Enabled = false;
1162 icnNewNoticeAtt.Visible = false;
1165 private void btnSend_Click(object sender, EventArgs e)
1167 GroupNotice ntc = new GroupNotice();
1168 ntc.Subject = txtNewNoticeTitle.Text;
1169 ntc.Message = txtNewNoticeBody.Text;
1170 if (txtNewNoteAtt.Tag != null && txtNewNoteAtt.Tag is InventoryItem)
1172 InventoryItem inv = txtNewNoteAtt.Tag as InventoryItem;
1173 ntc.OwnerID = inv.OwnerID;
1174 ntc.AttachmentID = inv.UUID;
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);
1185 private void memberListContextMenuSave_Click(object sender, EventArgs e)
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)
1195 switch (saveMembers.FilterIndex)
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)
1203 sw.WriteLine("{0},{1}", item.Base.ID, item.Name);
1208 OpenMetaverse.StructuredData.OSDArray members = new OpenMetaverse.StructuredData.OSDArray(GroupMembers.Count);
1209 foreach (var item in GroupMembers)
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);
1216 System.IO.File.WriteAllText(saveMembers.FileName, OpenMetaverse.StructuredData.OSDParser.SerializeJsonString(members));
1220 instance.TabConsole.DisplayNotificationInChat(string.Format("Saved {0} members to {1}", GroupMembers.Count, saveMembers.FileName));
1222 catch (Exception ex)
1224 instance.TabConsole.DisplayNotificationInChat("Failed to save member list: " + ex.Message, ChatBufferTextStyle.Error);
1229 private void copyRoleIDToolStripMenuItem_Click(object sender, EventArgs e)
1231 if (lvwRoles.SelectedItems.Count != 1) return;
1232 if (lvwRoles.SelectedItems[0].Tag is GroupRole)
1234 Clipboard.SetText(((GroupRole)lvwRoles.SelectedItems[0].Tag).ID.ToString());
1238 private void lvwMemberDetails_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
1242 for (int i = 0; i < GroupMembers.Count; i++)
1244 if (GroupMembers[i].Name.StartsWith(e.Text, StringComparison.CurrentCultureIgnoreCase))
1254 public void RefreshBans()
1256 client.Groups.RequestBannedAgents(group.ID, (xs, xe) =>
1258 UpdateBannedAgents(xe);
1262 void UpdateBannedAgents(BannedAgentsEventArgs e)
1264 if (!e.Success || e.GroupID != group.ID) return;
1267 BeginInvoke(new MethodInvoker(() => UpdateBannedAgents(e)));
1271 lwBannedMembers.BeginUpdate();
1272 lwBannedMembers.Items.Clear();
1274 foreach (var member in e.BannedAgents)
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);
1281 lwBannedMembers.EndUpdate();
1284 private void btnBan_Click(object sender, EventArgs e)
1286 (new BanGroupMember(instance, group, this)).Show();
1289 private void btnUnban_Click(object sender, EventArgs e)
1291 List<UUID> toUnban = new List<UUID>();
1292 for (int i=0; i<lwBannedMembers.SelectedItems.Count; i++)
1295 if (UUID.TryParse(lwBannedMembers.SelectedItems[i].Name, out id))
1301 if (toUnban.Count > 0)
1303 client.Groups.RequestBanAction(group.ID, GroupBanAction.Unban, toUnban.ToArray(), (xs, se) =>
1310 private void lwBannedMembers_SelectedIndexChanged(object sender, EventArgs e)
1312 btnUnban.Enabled = lwBannedMembers.SelectedItems.Count > 0;
1315 private void btnBanMember_Click(object sender, EventArgs e)
1319 List<UUID> toBan = new List<UUID>();
1320 for (int i = 0; i < lvwMemberDetails.SelectedIndices.Count; i++)
1322 EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[i]];
1323 toBan.Add(m.Base.ID);
1324 client.Groups.EjectUser(group.ID, m.Base.ID);
1327 if (toBan.Count > 0)
1329 client.Groups.RequestBanAction(group.ID, GroupBanAction.Ban, toBan.ToArray(), (xs, xe) =>
1338 #endregion Group Bans
1341 public class EnhancedGroupMember
1343 public GroupMember Base;
1344 public DateTime LastOnline;
1347 public EnhancedGroupMember(string name, GroupMember baseMember)
1352 if (baseMember.OnlineStatus == "Online")
1354 LastOnline = DateTime.Now;
1356 else if (string.IsNullOrEmpty(baseMember.OnlineStatus) || baseMember.OnlineStatus == "unknown")
1358 LastOnline = DateTime.MinValue;
1364 LastOnline = Convert.ToDateTime(baseMember.OnlineStatus, Utils.EnUsCulture);
1366 catch (FormatException)
1368 LastOnline = DateTime.MaxValue;
1374 #region Sorter classes
1375 public class GroupMemberSorter : IComparer<EnhancedGroupMember>
1377 public enum SortByColumn
1385 public enum SortOrder
1391 public SortOrder CurrentOrder = SortOrder.Ascending;
1392 public SortByColumn SortBy = SortByColumn.Name;
1394 public int Compare(EnhancedGroupMember member1, EnhancedGroupMember member2)
1398 case SortByColumn.Name:
1399 if (CurrentOrder == SortOrder.Ascending)
1400 return string.Compare(member1.Name, member2.Name);
1402 return string.Compare(member2.Name, member1.Name);
1404 case SortByColumn.Title:
1405 if (CurrentOrder == SortOrder.Ascending)
1406 return string.Compare(member1.Base.Title, member2.Base.Title);
1408 return string.Compare(member2.Base.Title, member1.Base.Title);
1410 case SortByColumn.LastOnline:
1411 if (CurrentOrder == SortOrder.Ascending)
1412 return DateTime.Compare(member1.LastOnline, member2.LastOnline);
1414 return DateTime.Compare(member2.LastOnline, member1.LastOnline);
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;
1430 public class GroupNoticeSorter : IComparer
1432 public enum SortByColumn
1439 public enum SortOrder
1445 public SortOrder CurrentOrder = SortOrder.Descending;
1446 public SortByColumn SortBy = SortByColumn.Date;
1448 private int IntCompare(uint x, uint y)
1464 public int Compare(object x, object y)
1466 ListViewItem item1 = (ListViewItem)x;
1467 ListViewItem item2 = (ListViewItem)y;
1468 GroupNoticesListEntry member1 = (GroupNoticesListEntry)item1.Tag;
1469 GroupNoticesListEntry member2 = (GroupNoticesListEntry)item2.Tag;
1473 case SortByColumn.Subject:
1474 if (CurrentOrder == SortOrder.Ascending)
1475 return string.Compare(member1.Subject, member2.Subject);
1477 return string.Compare(member2.Subject, member1.Subject);
1479 case SortByColumn.Sender:
1480 if (CurrentOrder == SortOrder.Ascending)
1481 return string.Compare(member1.FromName, member2.FromName);
1483 return string.Compare(member2.FromName, member1.FromName);
1485 case SortByColumn.Date:
1486 if (CurrentOrder == SortOrder.Ascending)
1487 return IntCompare(member1.Timestamp, member2.Timestamp);
1489 return IntCompare(member2.Timestamp, member1.Timestamp);