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 isMember = instance.Groups.ContainsKey(group.ID);
89 tcGroupDetails.TabPages.Remove(tpMembersRoles);
90 tcGroupDetails.TabPages.Remove(tpNotices);
91 tcGroupDetails.TabPages.Remove(tpBanned);
98 lvwNoticeArchive.SmallImageList = frmMain.ResourceImages;
99 lvwNoticeArchive.ListViewItemSorter = new GroupNoticeSorter();
102 client.Groups.GroupTitlesReply += new EventHandler<GroupTitlesReplyEventArgs>(Groups_GroupTitlesReply);
103 client.Groups.GroupMembersReply += new EventHandler<GroupMembersReplyEventArgs>(Groups_GroupMembersReply);
104 client.Groups.GroupProfile += new EventHandler<GroupProfileEventArgs>(Groups_GroupProfile);
105 client.Groups.CurrentGroups += new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
106 client.Groups.GroupNoticesListReply += new EventHandler<GroupNoticesListReplyEventArgs>(Groups_GroupNoticesListReply);
107 client.Groups.GroupJoinedReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupJoinedReply);
108 client.Groups.GroupLeaveReply += new EventHandler<GroupOperationEventArgs>(Groups_GroupLeaveReply);
109 client.Groups.GroupRoleDataReply += new EventHandler<GroupRolesDataReplyEventArgs>(Groups_GroupRoleDataReply);
110 client.Groups.GroupMemberEjected += new EventHandler<GroupOperationEventArgs>(Groups_GroupMemberEjected);
111 client.Groups.GroupRoleMembersReply += new EventHandler<GroupRolesMembersReplyEventArgs>(Groups_GroupRoleMembersReply);
112 client.Self.IM += new EventHandler<InstantMessageEventArgs>(Self_IM);
113 instance.Names.NameUpdated += new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
114 RefreshControlsAvailability();
118 void GroupDetails_Disposed(object sender, EventArgs e)
120 client.Groups.GroupTitlesReply -= new EventHandler<GroupTitlesReplyEventArgs>(Groups_GroupTitlesReply);
121 client.Groups.GroupMembersReply -= new EventHandler<GroupMembersReplyEventArgs>(Groups_GroupMembersReply);
122 client.Groups.GroupProfile -= new EventHandler<GroupProfileEventArgs>(Groups_GroupProfile);
123 client.Groups.CurrentGroups -= new EventHandler<CurrentGroupsEventArgs>(Groups_CurrentGroups);
124 client.Groups.GroupNoticesListReply -= new EventHandler<GroupNoticesListReplyEventArgs>(Groups_GroupNoticesListReply);
125 client.Groups.GroupJoinedReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupJoinedReply);
126 client.Groups.GroupLeaveReply -= new EventHandler<GroupOperationEventArgs>(Groups_GroupLeaveReply);
127 client.Groups.GroupRoleDataReply -= new EventHandler<GroupRolesDataReplyEventArgs>(Groups_GroupRoleDataReply);
128 client.Groups.GroupRoleMembersReply -= new EventHandler<GroupRolesMembersReplyEventArgs>(Groups_GroupRoleMembersReply);
129 client.Groups.GroupMemberEjected -= new EventHandler<GroupOperationEventArgs>(Groups_GroupMemberEjected);
130 client.Self.IM -= new EventHandler<InstantMessageEventArgs>(Self_IM);
131 if (instance != null && instance.Names != null)
133 instance.Names.NameUpdated -= new EventHandler<UUIDNameReplyEventArgs>(Names_NameUpdated);
135 if (nameUpdateTimer != null)
137 nameUpdateTimer.Dispose();
138 nameUpdateTimer = null;
142 #region Network callbacks
144 void Groups_GroupMemberEjected(object sender, GroupOperationEventArgs e)
146 if (e.GroupID != group.ID) return;
150 BeginInvoke(new MethodInvoker(() => { RefreshGroupInfo(); }));
151 instance.TabConsole.DisplayNotificationInChat("Group member ejected.");
155 instance.TabConsole.DisplayNotificationInChat("Failed to eject group member.");
159 void Groups_GroupRoleMembersReply(object sender, GroupRolesMembersReplyEventArgs e)
161 if (e.GroupID == group.ID && e.RequestID == groupRolesMembersRequest)
163 roleMembers = e.RolesMembers;
164 BeginInvoke(new MethodInvoker(() =>
166 btnInviteNewMember.Enabled = HasPower(GroupPowers.Invite);
167 btnEjectMember.Enabled = HasPower(GroupPowers.Eject);
168 lvwMemberDetails_SelectedIndexChanged(null, null);
174 void Groups_GroupRoleDataReply(object sender, GroupRolesDataReplyEventArgs e)
176 if (e.GroupID == group.ID && e.RequestID == groupRolesRequest)
178 groupRolesMembersRequest = client.Groups.RequestGroupRolesMembers(group.ID);
179 if (roles == null) roles = e.Roles;
180 else lock (roles) roles = e.Roles;
181 BeginInvoke(new MethodInvoker(() => DisplayGroupRoles()));
185 void Groups_GroupLeaveReply(object sender, GroupOperationEventArgs e)
187 if (e.GroupID == group.ID && e.Success)
189 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
193 void Groups_GroupJoinedReply(object sender, GroupOperationEventArgs e)
195 if (e.GroupID == group.ID && e.Success)
197 BeginInvoke(new MethodInvoker(() => RefreshGroupInfo()));
201 UUID destinationFolderID;
203 void Self_IM(object sender, InstantMessageEventArgs e)
205 if (e.IM.Dialog != InstantMessageDialog.GroupNoticeRequested) return;
209 BeginInvoke(new MethodInvoker(() => Self_IM(sender, e)));
213 InstantMessage msg = e.IM;
217 if (msg.BinaryBucket.Length >= 18)
219 groupID = new UUID(msg.BinaryBucket, 2);
223 groupID = msg.FromAgentID;
226 if (groupID != group.ID) return;
228 if (msg.BinaryBucket.Length > 18 && msg.BinaryBucket[0] != 0)
230 type = (AssetType)msg.BinaryBucket[1];
231 destinationFolderID = client.Inventory.FindFolderForType(type);
232 int icoIndx = InventoryConsole.GetItemImageIndex(type.ToString().ToLower());
235 icnItem.Image = frmMain.ResourceImages.Images[icoIndx];
236 icnItem.Visible = true;
238 txtItemName.Text = Utils.BytesToString(msg.BinaryBucket, 18, msg.BinaryBucket.Length - 19);
239 btnSave.Enabled = true;
240 btnSave.Visible = icnItem.Visible = txtItemName.Visible = true;
244 string text = msg.Message.Replace("\n", System.Environment.NewLine);
245 int pos = msg.Message.IndexOf('|');
246 string title = msg.Message.Substring(0, pos);
247 text = text.Remove(0, pos + 1);
248 txtNotice.Text = text;
251 void Groups_GroupNoticesListReply(object sender, GroupNoticesListReplyEventArgs e)
253 if (e.GroupID != group.ID) return;
257 BeginInvoke(new MethodInvoker(() => Groups_GroupNoticesListReply(sender, e)));
261 lvwNoticeArchive.BeginUpdate();
263 foreach (GroupNoticesListEntry notice in e.Notices)
265 ListViewItem item = new ListViewItem();
266 item.SubItems.Add(notice.Subject);
267 item.SubItems.Add(notice.FromName);
268 string noticeDate = string.Empty;
269 if (notice.Timestamp != 0)
271 noticeDate = Utils.UnixTimeToDateTime(notice.Timestamp).ToShortDateString();
273 item.SubItems.Add(noticeDate);
275 if (notice.HasAttachment)
277 item.ImageIndex = InventoryConsole.GetItemImageIndex(notice.AssetType.ToString().ToLower());
282 lvwNoticeArchive.Items.Add(item);
284 lvwNoticeArchive.EndUpdate();
287 void Groups_CurrentGroups(object sender, CurrentGroupsEventArgs e)
289 BeginInvoke(new MethodInvoker(RefreshControlsAvailability));
292 void Groups_GroupProfile(object sender, GroupProfileEventArgs e)
294 if (group.ID != e.Group.ID) return;
298 BeginInvoke(new MethodInvoker(() => Groups_GroupProfile(sender, e)));
303 if (group.InsigniaID != UUID.Zero && pnlInsignia.Controls.Count == 0)
305 SLImageHandler insignia = new SLImageHandler(instance, group.InsigniaID, string.Empty);
306 insignia.Dock = DockStyle.Fill;
307 pnlInsignia.Controls.Add(insignia);
310 lblGroupName.Text = e.Group.Name;
311 tbxCharter.Text = group.Charter.Replace("\n", Environment.NewLine);
312 lblFounded.Text = "Founded by: " + instance.Names.Get(group.FounderID);
313 cbxShowInSearch.Checked = group.ShowInList;
314 cbxOpenEnrollment.Checked = group.OpenEnrollment;
316 if (group.MembershipFee > 0)
318 cbxEnrollmentFee.Checked = true;
319 nudEnrollmentFee.Value = group.MembershipFee;
323 cbxEnrollmentFee.Checked = false;
324 nudEnrollmentFee.Value = 0;
327 if (group.MaturePublish)
329 cbxMaturity.SelectedIndex = 1;
333 cbxMaturity.SelectedIndex = 0;
336 btnJoin.Enabled = btnJoin.Visible = false;
338 if (myGroups.ContainsKey(group.ID)) // I am in this group
340 cbxReceiveNotices.Checked = myGroups[group.ID].AcceptNotices;
341 cbxListInProfile.Checked = myGroups[group.ID].ListInProfile;
342 cbxReceiveNotices.CheckedChanged += new EventHandler(cbxListInProfile_CheckedChanged);
343 cbxListInProfile.CheckedChanged += new EventHandler(cbxListInProfile_CheckedChanged);
345 else if (group.OpenEnrollment) // I am not in this group, but I could join it
347 btnJoin.Text = "Join $L" + group.MembershipFee;
348 btnJoin.Enabled = btnJoin.Visible = true;
351 RefreshControlsAvailability();
354 void Names_NameUpdated(object sender, UUIDNameReplyEventArgs e)
356 ProcessNameUpdate(e.Names);
361 void ProcessNameUpdate(Dictionary<UUID, string> Names)
363 if (Names.ContainsKey(group.FounderID))
367 BeginInvoke(new MethodInvoker(() => { lblFounded.Text = "Founded by: " + Names[group.FounderID]; }));
371 lblFounded.Text = "Founded by: " + Names[group.FounderID];
375 WorkPool.QueueUserWorkItem(sync =>
379 bool hasUpdates = false;
381 foreach (var name in Names)
383 var member = GroupMembers.Find((m) => m.Base.ID == name.Key);
384 if (member == null) continue;
387 member.Name = name.Value;
392 int tick = Environment.TickCount;
393 int elapsed = tick - lastTick;
397 nameUpdateTimer_Elapsed(this);
399 nameUpdateTimer.Change(500, System.Threading.Timeout.Infinite);
404 Logger.DebugLog("Failed updating group member names: " + ex.ToString());
409 void Groups_GroupTitlesReply(object sender, GroupTitlesReplyEventArgs e)
411 if (groupTitlesRequest != e.RequestID) return;
415 BeginInvoke(new MethodInvoker(() => Groups_GroupTitlesReply(sender, e)));
419 this.titles = e.Titles;
421 foreach (GroupTitle title in titles.Values)
423 cbxActiveTitle.Items.Add(title);
426 cbxActiveTitle.SelectedItem = title;
430 cbxActiveTitle.SelectedIndexChanged += cbxActiveTitle_SelectedIndexChanged;
433 List<EnhancedGroupMember> GroupMembers = new List<EnhancedGroupMember>();
435 void Groups_GroupMembersReply(object sender, GroupMembersReplyEventArgs e)
439 BeginInvoke(new MethodInvoker(() => Groups_GroupMembersReply(sender, e)));
443 lvwGeneralMembers.VirtualListSize = 0;
444 lvwMemberDetails.VirtualListSize = 0;
446 var members = new List<EnhancedGroupMember>(e.Members.Count);
447 foreach (var member in e.Members)
449 members.Add(new EnhancedGroupMember(instance.Names.Get(member.Key), member.Value));
452 GroupMembers = members;
453 GroupMembers.Sort(memberSorter);
454 lvwGeneralMembers.VirtualListSize = GroupMembers.Count;
455 lvwMemberDetails.VirtualListSize = GroupMembers.Count;
458 void lvwMemberDetails_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
460 EnhancedGroupMember member = null;
463 member = GroupMembers[e.ItemIndex];
467 e.Item = new ListViewItem();
471 ListViewItem item = new ListViewItem(member.Name);
473 item.Name = member.Base.ID.ToString();
474 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Contribution.ToString()));
475 if (member.LastOnline != DateTime.MinValue)
477 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
483 void lvwGeneralMembers_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
485 EnhancedGroupMember member = null;
488 member = GroupMembers[e.ItemIndex];
492 e.Item = new ListViewItem();
495 ListViewItem item = new ListViewItem(member.Name);
498 item.Name = member.Base.ID.ToString();
499 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Title));
500 if (member.LastOnline != DateTime.MinValue)
502 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
509 #region Privatate methods
511 private void nameUpdateTimer_Elapsed(object sync)
515 BeginInvoke(new MethodInvoker(() => nameUpdateTimer_Elapsed(sync)));
519 GroupMembers.Sort(memberSorter);
520 lvwGeneralMembers.Invalidate();
521 lvwMemberDetails.Invalidate();
524 private void DisplayGroupRoles()
526 lvwRoles.Items.Clear();
530 foreach (GroupRole role in roles.Values)
532 ListViewItem item = new ListViewItem();
533 item.Name = role.ID.ToString();
534 item.Text = role.Name;
535 item.SubItems.Add(role.Title);
536 item.SubItems.Add(role.ID.ToString());
538 lvwRoles.Items.Add(item);
545 private bool HasPower(GroupPowers power)
547 if (!instance.Groups.ContainsKey(group.ID))
550 return (instance.Groups[group.ID].Powers & power) != 0;
553 private void RefreshControlsAvailability()
555 if (!HasPower(GroupPowers.ChangeOptions))
557 nudEnrollmentFee.ReadOnly = true;
558 cbxEnrollmentFee.Enabled = false;
559 cbxOpenEnrollment.Enabled = false;
562 if (!HasPower(GroupPowers.ChangeIdentity))
564 tbxCharter.ReadOnly = true;
565 cbxShowInSearch.Enabled = false;
566 cbxMaturity.Enabled = false;
569 if (!myGroups.ContainsKey(group.ID))
571 cbxReceiveNotices.Enabled = false;
572 cbxListInProfile.Enabled = false;
576 private void RefreshGroupNotices()
578 lvwNoticeArchive.Items.Clear();
579 client.Groups.RequestGroupNoticesList(group.ID);
580 btnNewNotice.Enabled = HasPower(GroupPowers.SendNotices);
583 private void RefreshGroupInfo()
585 lvwGeneralMembers.VirtualListSize = 0;
586 if (isMember) lvwMemberDetails.VirtualListSize = 0;
588 cbxActiveTitle.SelectedIndexChanged -= cbxActiveTitle_SelectedIndexChanged;
589 cbxReceiveNotices.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
590 cbxListInProfile.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
592 cbxActiveTitle.Items.Clear();
594 // Request group info
595 client.Groups.RequestGroupProfile(group.ID);
596 groupTitlesRequest = client.Groups.RequestGroupTitles(group.ID);
597 groupMembersRequest = client.Groups.RequestGroupMembers(group.ID);
600 private void RefreshRoles()
602 if (!isMember) return;
604 lvwRoles.SelectedItems.Clear();
605 lvwRoles.Items.Clear();
606 btnApply.Enabled = false;
607 btnCreateNewRole.Enabled = HasPower(GroupPowers.CreateRole);
608 btnDeleteRole.Enabled = HasPower(GroupPowers.DeleteRole);
609 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = false;
610 groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
613 private void RefreshMembersRoles()
615 if (!isMember) return;
617 btnApply.Enabled = false;
618 lvwAssignedRoles.Items.Clear();
619 groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
623 #region Controls change handlers
624 void cbxListInProfile_CheckedChanged(object sender, EventArgs e)
626 if (myGroups.ContainsKey(group.ID))
628 Group g = myGroups[group.ID];
629 // g.AcceptNotices = cbxReceiveNotices.Checked;
630 // g.ListInProfile = cbxListInProfile.Checked;
631 client.Groups.SetGroupAcceptNotices(g.ID, cbxReceiveNotices.Checked, cbxListInProfile.Checked);
632 client.Groups.RequestCurrentGroups();
636 private void cbxActiveTitle_SelectedIndexChanged(object sender, EventArgs e)
638 GroupTitle title = (GroupTitle)cbxActiveTitle.SelectedItem;
639 client.Groups.ActivateTitle(title.GroupID, title.RoleID);
642 private void btnRefresh_Click(object sender, EventArgs e)
644 switch (tcGroupDetails.SelectedTab.Name)
651 RefreshGroupNotices();
654 case "tpMembersRoles":
655 RefreshMembersRoles();
665 void lvwGeneralMembers_ColumnClick(object sender, ColumnClickEventArgs e)
667 ListView lb = (ListView)sender;
671 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Name;
675 if (lb.Name == "lvwMemberDetails")
676 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Contribution;
678 memberSorter.SortBy = GroupMemberSorter.SortByColumn.Title;
682 memberSorter.SortBy = GroupMemberSorter.SortByColumn.LastOnline;
686 if (memberSorter.CurrentOrder == GroupMemberSorter.SortOrder.Ascending)
687 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Descending;
689 memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Ascending;
691 GroupMembers.Sort(memberSorter);
695 private void lvwNoticeArchive_ColumnClick(object sender, ColumnClickEventArgs e)
698 GroupNoticeSorter sorter = (GroupNoticeSorter)lvwNoticeArchive.ListViewItemSorter;
703 sorter.SortBy = GroupNoticeSorter.SortByColumn.Subject;
707 sorter.SortBy = GroupNoticeSorter.SortByColumn.Sender;
711 sorter.SortBy = GroupNoticeSorter.SortByColumn.Date;
715 if (sorter.CurrentOrder == GroupNoticeSorter.SortOrder.Ascending)
716 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Descending;
718 sorter.CurrentOrder = GroupNoticeSorter.SortOrder.Ascending;
720 lvwNoticeArchive.Sort();
724 private void btnClose_Click(object sender, EventArgs e)
726 this.FindForm().Close();
729 private void tcGroupDetails_SelectedIndexChanged(object sender, EventArgs e)
731 switch (tcGroupDetails.SelectedTab.Name)
734 RefreshGroupNotices();
737 case "tpMembersRoles":
738 RefreshMembersRoles();
747 private void lvwNoticeArchive_SelectedIndexChanged(object sender, EventArgs e)
749 if (lvwNoticeArchive.SelectedItems.Count == 1)
751 if (lvwNoticeArchive.SelectedItems[0].Tag is GroupNoticesListEntry)
753 GroupNoticesListEntry notice = (GroupNoticesListEntry)lvwNoticeArchive.SelectedItems[0].Tag;
754 lblSentBy.Text = "Sent by " + notice.FromName;
755 lblTitle.Text = notice.Subject;
756 txtNotice.Text = string.Empty;
757 btnSave.Enabled = btnSave.Visible = icnItem.Visible = txtItemName.Visible = false;
758 client.Groups.RequestGroupNotice(notice.NoticeID);
759 pnlNewNotice.Visible = false;
760 pnlArchivedNotice.Visible = true;
764 pnlArchivedNotice.Visible = false;
767 private void btnSave_Click(object sender, EventArgs e)
769 if (btnSave.Tag is InstantMessage)
771 InstantMessage msg = (InstantMessage)btnSave.Tag;
772 client.Self.InstantMessage(client.Self.Name, msg.FromAgentID, string.Empty, msg.IMSessionID, InstantMessageDialog.GroupNoticeInventoryAccepted, InstantMessageOnline.Offline, client.Self.SimPosition, client.Network.CurrentSim.RegionID, destinationFolderID.GetBytes());
773 btnSave.Enabled = false;
778 private void btnJoin_Click(object sender, EventArgs e)
780 client.Groups.RequestJoinGroup(group.ID);
783 private void lvwGeneralMembers_MouseDoubleClick(object sender, MouseEventArgs e)
785 ListViewItem item = lvwGeneralMembers.GetItemAt(e.X, e.Y);
790 UUID agentID = new UUID(item.Name);
791 instance.MainForm.ShowAgentProfile(item.Text, agentID);
793 catch (Exception) { }
797 private void lvwMemberDetails_MouseDoubleClick(object sender, MouseEventArgs e)
799 ListViewItem item = lvwMemberDetails.GetItemAt(e.X, e.Y);
804 UUID agentID = new UUID(item.Name);
805 instance.MainForm.ShowAgentProfile(item.Text, agentID);
807 catch (Exception) { }
811 private void btnEjectMember_Click(object sender, EventArgs e)
813 if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
814 EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
815 client.Groups.EjectUser(group.ID, m.Base.ID);
818 private void lvwMemberDetails_SelectedIndexChanged(object sender, EventArgs e)
820 btnBanMember.Enabled = lvwMemberDetails.SelectedIndices.Count > 0;
822 if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
823 EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
825 btnApply.Enabled = false;
827 lvwAssignedRoles.BeginUpdate();
828 lvwAssignedRoles.ItemChecked -= lvwAssignedRoles_ItemChecked;
829 lvwAssignedRoles.Items.Clear();
830 lvwAssignedRoles.Tag = m;
832 ListViewItem defaultItem = new ListViewItem();
833 defaultItem.Name = "Everyone";
834 defaultItem.SubItems.Add(defaultItem.Name);
835 defaultItem.Checked = true;
836 lvwAssignedRoles.Items.Add(defaultItem);
838 GroupPowers abilities = GroupPowers.None;
842 foreach (var r in roles)
844 GroupRole role = r.Value;
846 if (role.ID == UUID.Zero)
848 abilities |= role.Powers;
852 ListViewItem item = new ListViewItem();
853 item.Name = role.Name;
854 item.SubItems.Add(new ListViewItem.ListViewSubItem(item, role.Name));
856 var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
857 bool hasRole = foundRole.Value == m.Base.ID;
858 item.Checked = hasRole;
859 lvwAssignedRoles.Items.Add(item);
862 abilities |= role.Powers;
866 lvwAssignedRoles.ItemChecked += lvwAssignedRoles_ItemChecked;
867 lvwAssignedRoles.EndUpdate();
869 lvwAllowedAbilities.BeginUpdate();
870 lvwAllowedAbilities.Items.Clear();
872 foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
874 if (p != GroupPowers.None && (abilities & p) == p)
876 lvwAllowedAbilities.Items.Add(p.ToString());
881 lvwAllowedAbilities.EndUpdate();
885 private void UpdateMemberRoles()
887 EnhancedGroupMember m = (EnhancedGroupMember)lvwAssignedRoles.Tag;
888 GroupRoleChangesPacket p = new GroupRoleChangesPacket();
889 p.AgentData.AgentID = client.Self.AgentID;
890 p.AgentData.SessionID = client.Self.SessionID;
891 p.AgentData.GroupID = group.ID;
892 List<GroupRoleChangesPacket.RoleChangeBlock> changes = new List<GroupRoleChangesPacket.RoleChangeBlock>();
894 foreach (ListViewItem item in lvwAssignedRoles.Items)
896 if (!(item.Tag is GroupRole))
899 GroupRole role = (GroupRole)item.Tag;
900 var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
901 bool hasRole = foundRole.Value == m.Base.ID;
903 if (item.Checked != hasRole)
906 roleMembers.Add(new KeyValuePair<UUID, UUID>(role.ID, m.Base.ID));
908 roleMembers.Remove(foundRole);
910 var rc = new GroupRoleChangesPacket.RoleChangeBlock();
911 rc.MemberID = m.Base.ID;
913 rc.Change = item.Checked ? 0u : 1u;
918 if (changes.Count > 0)
920 p.RoleChange = changes.ToArray();
921 client.Network.CurrentSim.SendPacket(p);
924 btnApply.Enabled = false;
925 lvwMemberDetails_SelectedIndexChanged(lvwMemberDetails, EventArgs.Empty);
929 private void lvwAssignedRoles_ItemChecked(object sender, ItemCheckedEventArgs e)
931 if (e.Item.Tag == null) // click on the default role
934 e.Item.Checked = true;
938 if (e.Item.Tag is GroupRole)
940 EnhancedGroupMember m = (EnhancedGroupMember)lvwAssignedRoles.Tag;
941 bool modified = false;
943 foreach (ListViewItem item in lvwAssignedRoles.Items)
945 if (!(item.Tag is GroupRole))
948 GroupRole role = (GroupRole)item.Tag;
949 var foundRole = roleMembers.Find((KeyValuePair<UUID, UUID> kvp) => { return kvp.Value == m.Base.ID && kvp.Key == role.ID; });
950 bool hasRole = foundRole.Value == m.Base.ID;
952 if (item.Checked != hasRole)
958 btnApply.Enabled = modified;
962 private void tbxCharter_TextChanged(object sender, EventArgs e)
964 btnApply.Enabled = true;
967 private void btnApply_Click(object sender, EventArgs e)
969 switch (tcGroupDetails.SelectedTab.Name)
971 case "tpMembersRoles":
977 private void btnInviteNewMember_Click(object sender, EventArgs e)
979 (new GroupInvite(instance, group, roles)).Show();
982 private void lvwAllowedAbilities_SizeChanged(object sender, EventArgs e)
984 lvwAllowedAbilities.Columns[0].Width = lvwAllowedAbilities.Width - 30;
987 private void tcMembersRoles_SelectedIndexChanged(object sender, EventArgs e)
989 switch (tcMembersRoles.SelectedTab.Name)
992 RefreshMembersRoles();
1003 private void lvwRoles_SelectedIndexChanged(object sender, EventArgs e)
1005 txtRoleDescription.Text = txtRoleName.Text = txtRoleTitle.Text = string.Empty;
1006 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = false;
1007 lvwAssignedMembers.Items.Clear();
1008 lvwRoleAbilitis.Items.Clear();
1010 if (lvwRoles.SelectedItems.Count != 1) return;
1012 GroupRole role = (GroupRole)lvwRoles.SelectedItems[0].Tag;
1013 txtRoleName.Text = role.Name;
1014 txtRoleTitle.Text = role.Title;
1015 txtRoleDescription.Text = role.Description;
1017 if (HasPower(GroupPowers.RoleProperties))
1019 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1022 if (HasPower(GroupPowers.ChangeActions))
1024 lvwRoleAbilitis.Enabled = btnSaveRole.Enabled = true;
1027 btnSaveRole.Tag = role;
1029 lvwAssignedMembers.BeginUpdate();
1030 if (role.ID == UUID.Zero)
1032 foreach (var member in GroupMembers)
1033 lvwAssignedMembers.Items.Add(member.Name);
1035 else if (roleMembers != null)
1037 var mmb = roleMembers.FindAll((KeyValuePair<UUID, UUID> kvp) => { return kvp.Key == role.ID; });
1038 foreach (var m in mmb)
1040 lvwAssignedMembers.Items.Add(instance.Names.Get(m.Value));
1043 lvwAssignedMembers.EndUpdate();
1045 lvwRoleAbilitis.Tag = role;
1047 foreach (GroupPowers p in Enum.GetValues(typeof(GroupPowers)))
1049 if (p != GroupPowers.None)
1051 ListViewItem item = new ListViewItem();
1053 item.SubItems.Add(p.ToString());
1054 item.Checked = (p & role.Powers) != 0;
1055 lvwRoleAbilitis.Items.Add(item);
1060 private void btnCreateNewRole_Click(object sender, EventArgs e)
1062 lvwRoles.SelectedItems.Clear();
1063 txtRoleDescription.Enabled = txtRoleName.Enabled = txtRoleTitle.Enabled = btnSaveRole.Enabled = true;
1064 btnSaveRole.Tag = null;
1065 txtRoleName.Focus();
1068 private void btnSaveRole_Click(object sender, EventArgs e)
1070 if (btnSaveRole.Tag == null) // new role
1072 GroupRole role = new GroupRole();
1073 role.Name = txtRoleName.Text;
1074 role.Title = txtRoleTitle.Text;
1075 role.Description = txtRoleDescription.Text;
1076 client.Groups.CreateRole(group.ID, role);
1077 System.Threading.Thread.Sleep(100);
1080 else if (btnSaveRole.Tag is GroupRole) // update role
1082 GroupRole role = (GroupRole)btnSaveRole.Tag;
1084 if (HasPower(GroupPowers.ChangeActions))
1086 role.Powers = GroupPowers.None;
1088 foreach (ListViewItem item in lvwRoleAbilitis.Items)
1091 role.Powers |= (GroupPowers)item.Tag;
1095 if (HasPower(GroupPowers.RoleProperties))
1097 role.Name = txtRoleName.Text;
1098 role.Title = txtRoleTitle.Text;
1099 role.Description = txtRoleDescription.Text;
1102 client.Groups.UpdateRole(role);
1103 System.Threading.Thread.Sleep(100);
1108 private void btnDeleteRole_Click(object sender, EventArgs e)
1110 if (lvwRoles.SelectedItems.Count == 1)
1112 client.Groups.DeleteRole(group.ID, ((GroupRole)lvwRoles.SelectedItems[0].Tag).ID);
1113 System.Threading.Thread.Sleep(100);
1119 private void btnNewNotice_Click(object sender, EventArgs e)
1121 if (HasPower(GroupPowers.SendNotices))
1123 pnlArchivedNotice.Visible = false;
1124 pnlNewNotice.Visible = true;
1125 txtNewNoticeTitle.Focus();
1129 instance.TabConsole.DisplayNotificationInChat("Don't have permission to send notices in this group", ChatBufferTextStyle.Error);
1133 private void btnPasteInv_Click(object sender, EventArgs e)
1135 if (instance.InventoryClipboard != null && instance.InventoryClipboard.Item is InventoryItem)
1137 InventoryItem inv = instance.InventoryClipboard.Item as InventoryItem;
1138 txtNewNoteAtt.Text = inv.Name;
1139 int icoIndx = InventoryConsole.GetItemImageIndex(inv.AssetType.ToString().ToLower());
1142 icnNewNoticeAtt.Image = frmMain.ResourceImages.Images[icoIndx];
1143 icnNewNoticeAtt.Visible = true;
1145 txtNewNoteAtt.Tag = inv;
1146 btnRemoveAttachment.Enabled = true;
1150 private void btnRemoveAttachment_Click(object sender, EventArgs e)
1152 txtNewNoteAtt.Tag = null;
1153 txtNewNoteAtt.Text = string.Empty;
1154 btnRemoveAttachment.Enabled = false;
1155 icnNewNoticeAtt.Visible = false;
1158 private void btnSend_Click(object sender, EventArgs e)
1160 GroupNotice ntc = new GroupNotice();
1161 ntc.Subject = txtNewNoticeTitle.Text;
1162 ntc.Message = txtNewNoticeBody.Text;
1163 if (txtNewNoteAtt.Tag != null && txtNewNoteAtt.Tag is InventoryItem)
1165 InventoryItem inv = txtNewNoteAtt.Tag as InventoryItem;
1166 ntc.OwnerID = inv.OwnerID;
1167 ntc.AttachmentID = inv.UUID;
1169 client.Groups.SendGroupNotice(group.ID, ntc);
1170 btnRemoveAttachment.PerformClick();
1171 txtNewNoticeTitle.Text = txtNewNoticeBody.Text = string.Empty;
1172 pnlNewNotice.Visible = false;
1173 btnRefresh.PerformClick();
1174 instance.TabConsole.DisplayNotificationInChat("Notice sent", ChatBufferTextStyle.Invisible);
1178 private void memberListContextMenuSave_Click(object sender, EventArgs e)
1180 SaveFileDialog saveMembers = new SaveFileDialog();
1181 saveMembers.Filter = "CSV|.csv|JSON|.json";
1182 saveMembers.Title = "Save visible group members";
1183 saveMembers.ShowDialog();
1184 if (saveMembers.FileName != string.Empty)
1188 switch (saveMembers.FilterIndex)
1191 System.IO.FileStream fs = (System.IO.FileStream)saveMembers.OpenFile();
1192 System.IO.StreamWriter sw = new System.IO.StreamWriter(fs, System.Text.Encoding.UTF8);
1193 sw.WriteLine("UUID,Name");
1194 foreach (var item in GroupMembers)
1196 sw.WriteLine("{0},{1}", item.Base.ID, item.Name);
1201 OpenMetaverse.StructuredData.OSDArray members = new OpenMetaverse.StructuredData.OSDArray(GroupMembers.Count);
1202 foreach (var item in GroupMembers)
1204 OpenMetaverse.StructuredData.OSDMap member = new OpenMetaverse.StructuredData.OSDMap(2);
1205 member["UUID"] = item.Base.ID;
1206 member["Name"] = item.Name;
1207 members.Add(member);
1209 System.IO.File.WriteAllText(saveMembers.FileName, OpenMetaverse.StructuredData.OSDParser.SerializeJsonString(members));
1213 instance.TabConsole.DisplayNotificationInChat(string.Format("Saved {0} members to {1}", GroupMembers.Count, saveMembers.FileName));
1215 catch (Exception ex)
1217 instance.TabConsole.DisplayNotificationInChat("Failed to save member list: " + ex.Message, ChatBufferTextStyle.Error);
1222 private void copyRoleIDToolStripMenuItem_Click(object sender, EventArgs e)
1224 if (lvwRoles.SelectedItems.Count != 1) return;
1225 if (lvwRoles.SelectedItems[0].Tag is GroupRole)
1227 Clipboard.SetText(((GroupRole)lvwRoles.SelectedItems[0].Tag).ID.ToString());
1231 private void lvwMemberDetails_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e)
1235 for (int i = 0; i < GroupMembers.Count; i++)
1237 if (GroupMembers[i].Name.StartsWith(e.Text, StringComparison.CurrentCultureIgnoreCase))
1247 public void RefreshBans()
1249 client.Groups.RequestBannedAgents(group.ID, (xs, xe) =>
1251 UpdateBannedAgents(xe);
1255 void UpdateBannedAgents(BannedAgentsEventArgs e)
1257 if (!e.Success || e.GroupID != group.ID) return;
1260 BeginInvoke(new MethodInvoker(() => UpdateBannedAgents(e)));
1264 lwBannedMembers.BeginUpdate();
1265 lwBannedMembers.Items.Clear();
1267 foreach (var member in e.BannedAgents)
1269 var item = new ListViewItem(instance.Names.Get(member.Key));
1270 item.Name = member.Key.ToString();
1271 item.SubItems.Add(member.Value.ToShortDateString());
1272 lwBannedMembers.Items.Add(item);
1274 lwBannedMembers.EndUpdate();
1277 private void btnBan_Click(object sender, EventArgs e)
1279 (new BanGroupMember(instance, group, this)).Show();
1282 private void btnUnban_Click(object sender, EventArgs e)
1284 List<UUID> toUnban = new List<UUID>();
1285 for (int i=0; i<lwBannedMembers.SelectedItems.Count; i++)
1288 if (UUID.TryParse(lwBannedMembers.SelectedItems[i].Name, out id))
1294 if (toUnban.Count > 0)
1296 client.Groups.RequestBanAction(group.ID, GroupBanAction.Unban, toUnban.ToArray(), (xs, se) =>
1303 private void lwBannedMembers_SelectedIndexChanged(object sender, EventArgs e)
1305 btnUnban.Enabled = lwBannedMembers.SelectedItems.Count > 0;
1308 private void btnBanMember_Click(object sender, EventArgs e)
1312 List<UUID> toBan = new List<UUID>();
1313 for (int i = 0; i < lvwMemberDetails.SelectedIndices.Count; i++)
1315 EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[i]];
1316 toBan.Add(m.Base.ID);
1317 client.Groups.EjectUser(group.ID, m.Base.ID);
1320 if (toBan.Count > 0)
1322 client.Groups.RequestBanAction(group.ID, GroupBanAction.Ban, toBan.ToArray(), (xs, xe) =>
1331 #endregion Group Bans
1334 public class EnhancedGroupMember
1336 public GroupMember Base;
1337 public DateTime LastOnline;
1340 public EnhancedGroupMember(string name, GroupMember baseMember)
1345 if (baseMember.OnlineStatus == "Online")
1347 LastOnline = DateTime.Now;
1349 else if (string.IsNullOrEmpty(baseMember.OnlineStatus) || baseMember.OnlineStatus == "unknown")
1351 LastOnline = DateTime.MinValue;
1357 LastOnline = Convert.ToDateTime(baseMember.OnlineStatus, Utils.EnUsCulture);
1359 catch (FormatException)
1361 LastOnline = DateTime.MaxValue;
1367 #region Sorter classes
1368 public class GroupMemberSorter : IComparer<EnhancedGroupMember>
1370 public enum SortByColumn
1378 public enum SortOrder
1384 public SortOrder CurrentOrder = SortOrder.Ascending;
1385 public SortByColumn SortBy = SortByColumn.Name;
1387 public int Compare(EnhancedGroupMember member1, EnhancedGroupMember member2)
1391 case SortByColumn.Name:
1392 if (CurrentOrder == SortOrder.Ascending)
1393 return string.Compare(member1.Name, member2.Name);
1395 return string.Compare(member2.Name, member1.Name);
1397 case SortByColumn.Title:
1398 if (CurrentOrder == SortOrder.Ascending)
1399 return string.Compare(member1.Base.Title, member2.Base.Title);
1401 return string.Compare(member2.Base.Title, member1.Base.Title);
1403 case SortByColumn.LastOnline:
1404 if (CurrentOrder == SortOrder.Ascending)
1405 return DateTime.Compare(member1.LastOnline, member2.LastOnline);
1407 return DateTime.Compare(member2.LastOnline, member1.LastOnline);
1409 case SortByColumn.Contribution:
1410 if (member1.Base.Contribution < member2.Base.Contribution)
1411 return CurrentOrder == SortOrder.Ascending ? -1 : 1;
1412 else if (member1.Base.Contribution > member2.Base.Contribution)
1413 return CurrentOrder == SortOrder.Ascending ? 1 : -1;
1423 public class GroupNoticeSorter : IComparer
1425 public enum SortByColumn
1432 public enum SortOrder
1438 public SortOrder CurrentOrder = SortOrder.Descending;
1439 public SortByColumn SortBy = SortByColumn.Date;
1441 private int IntCompare(uint x, uint y)
1457 public int Compare(object x, object y)
1459 ListViewItem item1 = (ListViewItem)x;
1460 ListViewItem item2 = (ListViewItem)y;
1461 GroupNoticesListEntry member1 = (GroupNoticesListEntry)item1.Tag;
1462 GroupNoticesListEntry member2 = (GroupNoticesListEntry)item2.Tag;
1466 case SortByColumn.Subject:
1467 if (CurrentOrder == SortOrder.Ascending)
1468 return string.Compare(member1.Subject, member2.Subject);
1470 return string.Compare(member2.Subject, member1.Subject);
1472 case SortByColumn.Sender:
1473 if (CurrentOrder == SortOrder.Ascending)
1474 return string.Compare(member1.FromName, member2.FromName);
1476 return string.Compare(member2.FromName, member1.FromName);
1478 case SortByColumn.Date:
1479 if (CurrentOrder == SortOrder.Ascending)
1480 return IntCompare(member1.Timestamp, member2.Timestamp);
1482 return IntCompare(member2.Timestamp, member1.Timestamp);