#region ================== Copyright (c) 2010 Pascal vd Heiden /* * Copyright (c) 2010 Pascal vd Heiden, www.codeimp.com * This program is released under GNU General Public License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #endregion #region ================== Namespaces using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using CodeImp.DoomBuilder.Map; using CodeImp.DoomBuilder.Editing; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Types; #endregion namespace CodeImp.DoomBuilder.CommentsPanel { public partial class CommentsDocker : UserControl { #region ================== Variables private readonly Dictionary v_comments = new Dictionary(StringComparer.Ordinal); private readonly Dictionary l_comments = new Dictionary(StringComparer.Ordinal); private readonly Dictionary s_comments = new Dictionary(StringComparer.Ordinal); private readonly Dictionary t_comments = new Dictionary(StringComparer.Ordinal); private bool preventupdate; #endregion #region ================== Constructor // Constructor public CommentsDocker() { InitializeComponent(); CodeImp.DoomBuilder.General.ApplyDataGridViewFix(grid); filtermode.Checked = General.Settings.ReadPluginSetting("filtermode", false); clickselects.Checked = General.Settings.ReadPluginSetting("clickselects", false); } // Disposer protected override void Dispose(bool disposing) { General.Settings.WritePluginSetting("filtermode", filtermode.Checked); General.Settings.WritePluginSetting("clickselects", clickselects.Checked); if(disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #endregion #region ================== Methods // When attached to the docker public void Setup() { if(this.ParentForm != null) { this.ParentForm.Activated += ParentForm_Activated; } grid.CurrentCell = null; updatetimer.Start(); enabledtimer.Start(); } // Before detached from the docker public void Terminate() { preventupdate = true; //mxd if(this.ParentForm != null) this.ParentForm.Activated -= ParentForm_Activated; updatetimer.Tick -= updatetimer_Tick; //mxd enabledtimer.Tick -= enabledtimer_Tick; //mxd updatetimer.Stop(); enabledtimer.Stop(); } // This sends the focus back to the display and removes grid selection private void LoseFocus() { preventupdate = false; grid.CurrentCell = null; grid.ClearSelection(); General.Interface.FocusDisplay(); } // This updates the list for a specific kind of group private void UpdateGroupList(Dictionary newcomments, Dictionary comments, Image icon) { // Remove old comments List commentslist = new List(comments.Values); foreach(CommentInfo c in commentslist) { if(!newcomments.ContainsKey(c.Comment)) { grid.Rows.Remove(c.Row); comments.Remove(c.Comment); } } // Update the list with comments foreach(KeyValuePair c in newcomments) { CommentInfo cc = c.Value; if(!comments.ContainsKey(c.Key)) { // Create grid row int index = grid.Rows.Add(); DataGridViewRow row = grid.Rows[index]; row.Cells[0].Value = icon; row.Cells[0].Style.Alignment = DataGridViewContentAlignment.TopCenter; row.Cells[0].Style.Padding = new Padding(0, 5, 0, 0); row.Cells[1].Value = cc.Comment; row.Cells[1].Style.WrapMode = DataGridViewTriState.True; row.Cells[1].Tag = cc; // Add to list of comments cc.Row = row; comments.Add(c.Key, cc); } else { cc = comments[c.Key]; //row = cc.Row; cc.ReplaceElements(c.Value); } } } // This sets the timer to update the list very soon public void UpdateListSoon() { updatetimer.Stop(); updatetimer.Start(); } // This finds all comments and updates the list public void UpdateList() { if(!preventupdate && General.Map.Map.IsSafeToAccess) { // Update vertices Dictionary newcomments = new Dictionary(StringComparer.Ordinal); if(!filtermode.Checked || (General.Editing.Mode.GetType().Name == "VerticesMode")) { foreach(Vertex v in General.Map.Map.Vertices) AddComments(v, newcomments); } UpdateGroupList(newcomments, v_comments, Properties.Resources.VerticesMode); // Update linedefs/sidedefs newcomments.Clear(); if(!filtermode.Checked || (General.Editing.Mode.GetType().Name == "LinedefsMode")) { foreach(Linedef l in General.Map.Map.Linedefs) AddComments(l, newcomments); foreach(Sidedef sd in General.Map.Map.Sidedefs) AddComments(sd, newcomments); } UpdateGroupList(newcomments, l_comments, Properties.Resources.LinesMode); // Update sectors newcomments.Clear(); if(!filtermode.Checked || (General.Editing.Mode.GetType().Name == "SectorsMode")) { foreach(Sector s in General.Map.Map.Sectors) AddComments(s, newcomments); } UpdateGroupList(newcomments, s_comments, Properties.Resources.SectorsMode); // Update things newcomments.Clear(); if(!filtermode.Checked || (General.Editing.Mode.GetType().Name == "ThingsMode")) { foreach(Thing t in General.Map.Map.Things) AddComments(t, newcomments); } UpdateGroupList(newcomments, t_comments, Properties.Resources.ThingsMode); // Sort grid grid.Sort(grid.Columns[1], ListSortDirection.Ascending); // Update grid.Update(); grid.CurrentCell = null; grid.ClearSelection(); } } // This adds comments from a MapElement private static void AddComments(MapElement e, Dictionary comments) { if(e.Fields.ContainsKey("comment")) { string c = e.Fields["comment"].Value.ToString(); if(comments.ContainsKey(c)) comments[c].AddElement(e); else comments[c] = new CommentInfo(c, e); } } // This changes the view to see the objects of a comment private static void ViewComment(CommentInfo c) { List points = new List(); RectangleF area = MapSet.CreateEmptyArea(); // Add all points to a list foreach(MapElement obj in c.Elements) { if(obj is Vertex) { points.Add((obj as Vertex).Position); } else if(obj is Linedef) { points.Add((obj as Linedef).Start.Position); points.Add((obj as Linedef).End.Position); } else if(obj is Sidedef) { points.Add((obj as Sidedef).Line.Start.Position); points.Add((obj as Sidedef).Line.End.Position); } else if(obj is Sector) { Sector s = (obj as Sector); foreach(Sidedef sd in s.Sidedefs) { points.Add(sd.Line.Start.Position); points.Add(sd.Line.End.Position); } } else if(obj is Thing) { Thing t = (obj as Thing); Vector2D p = t.Position; points.Add(p); points.Add(p + new Vector2D(t.Size * 2.0f, t.Size * 2.0f)); points.Add(p + new Vector2D(t.Size * 2.0f, -t.Size * 2.0f)); points.Add(p + new Vector2D(-t.Size * 2.0f, t.Size * 2.0f)); points.Add(p + new Vector2D(-t.Size * 2.0f, -t.Size * 2.0f)); } else { General.Fail("Unknown object given to zoom in on."); } } // Make a view area from the points foreach(Vector2D p in points) area = MapSet.IncreaseArea(area, p); // Make the area square, using the largest side if(area.Width > area.Height) { float delta = area.Width - area.Height; area.Y -= delta * 0.5f; area.Height += delta; } else { float delta = area.Height - area.Width; area.X -= delta * 0.5f; area.Width += delta; } // Add padding area.Inflate(100f, 100f); // Zoom to area ClassicMode editmode = (General.Editing.Mode as ClassicMode); editmode.CenterOnArea(area, 0.6f); } // This selects the elements in a comment private static void SelectComment(CommentInfo c, bool clear) { //string editmode = ""; // Leave any volatile mode General.Editing.CancelVolatileMode(); if(clear) { General.Map.Map.ClearAllSelected(); if(c.Elements[0] is Thing) General.Editing.ChangeMode("ThingsMode"); else if(c.Elements[0] is Vertex) General.Editing.ChangeMode("VerticesMode"); else if((c.Elements[0] is Linedef) || (c.Elements[0] is Sidedef)) General.Editing.ChangeMode("LinedefsMode"); else if(c.Elements[0] is Sector) General.Editing.ChangeMode("SectorsMode"); } else { if(!(c.Elements[0] is Thing)) { // Sectors mode is a bitch because it deals with selections somewhat aggressively // so we have to switch to linedefs to make this work right if((General.Editing.Mode.GetType().Name == "VerticesMode") || (General.Editing.Mode.GetType().Name == "SectorsMode") || (General.Editing.Mode.GetType().Name == "MakeSectorMode")) General.Editing.ChangeMode("LinedefsMode"); } } // Select the map elements foreach(MapElement obj in c.Elements) { if(obj is SelectableElement) { (obj as SelectableElement).Selected = true; if(obj is Sector) { foreach(Sidedef sd in (obj as Sector).Sidedefs) sd.Line.Selected = true; } } } General.Editing.Mode.UpdateSelectionInfo(); //mxd General.Interface.RedrawDisplay(); } // This removes the comments from elements in a comment private void RemoveComment(CommentInfo c) { // Create undo if(c.Elements.Count == 1) General.Map.UndoRedo.CreateUndo("Remove comment"); else General.Map.UndoRedo.CreateUndo("Remove " + c.Elements.Count + " comments"); // Erase comment fields foreach(MapElement obj in c.Elements) { obj.Fields.BeforeFieldsChange(); obj.Fields.Remove("comment"); } UpdateList(); General.Interface.RedrawDisplay(); } // This gets the currently selected comment or returns null when none is selected private CommentInfo GetSelectedComment() { if(grid.SelectedCells.Count > 0) { foreach(DataGridViewCell c in grid.SelectedCells) { if((c.ValueType != typeof(Image)) && (c.Tag is CommentInfo)) return (CommentInfo)(c.Tag); } } return null; } #endregion #region ================== Events // This gives a good idea when comments could have been changed // as it is called every time a dialog window closes. private void ParentForm_Activated(object sender, EventArgs e) { UpdateListSoon(); } // Update regulary private void updatetimer_Tick(object sender, EventArgs e) { updatetimer.Stop(); if (!BuilderPlug.Me.IsDockerActive()) return; UpdateList(); } // Mouse pressed private void grid_MouseDown(object sender, MouseEventArgs e) { preventupdate = true; if(e.Button == MouseButtons.Right) { DataGridView.HitTestInfo h = grid.HitTest(e.X, e.Y); if(h.Type == DataGridViewHitTestType.Cell) { grid.ClearSelection(); grid.Rows[h.RowIndex].Selected = true; } } } // Mouse released private void grid_MouseUp(object sender, MouseEventArgs e) { CommentInfo c = GetSelectedComment(); // Show popup menu? if((e.Button == MouseButtons.Right) && (c != null)) { // Determine objects type string objname = ""; if((c.Elements[0] is Vertex) && (c.Elements.Count > 1)) objname = "Vertices"; else if((c.Elements[0] is Vertex) && (c.Elements.Count == 1)) objname = "Vertex"; else if(((c.Elements[0] is Linedef) || (c.Elements[0] is Sidedef)) && (c.Elements.Count > 1)) objname = "Linedefs"; else if(((c.Elements[0] is Linedef) || (c.Elements[0] is Sidedef)) && (c.Elements.Count == 1)) objname = "Linedef"; else if((c.Elements[0] is Sector) && (c.Elements.Count > 1)) objname = "Sectors"; else if((c.Elements[0] is Sector) && (c.Elements.Count == 1)) objname = "Sector"; else if((c.Elements[0] is Thing) && (c.Elements.Count > 1)) objname = "Things"; else if((c.Elements[0] is Thing) && (c.Elements.Count == 1)) objname = "Thing"; editobjectitem.Text = "Edit " + objname + "..."; // Show popup menu Point screenpos = grid.PointToScreen(new Point(e.X, e.Y)); contextmenu.Tag = c; contextmenu.Show(screenpos); } else { // View selected comment if(c != null) { ViewComment(c); if(clickselects.Checked) SelectComment(c, true); } LoseFocus(); } } // Select private void selectitem_Click(object sender, EventArgs e) { // Select comment CommentInfo c = (CommentInfo)contextmenu.Tag; if(c != null) SelectComment(c, true); } // Select additive private void selectadditiveitem_Click(object sender, EventArgs e) { // Select comment CommentInfo c = (CommentInfo)contextmenu.Tag; if(c != null) SelectComment(c, false); } // Remove comments private void removecommentsitem_Click(object sender, EventArgs e) { // Remove comment CommentInfo c = (CommentInfo)contextmenu.Tag; if(c != null) RemoveComment(c); } // Edit objects private void editobjectitem_Click(object sender, EventArgs e) { CommentInfo c = (CommentInfo)contextmenu.Tag; if(c != null) { if(c.Elements[0] is Vertex) { List vertices = new List(); foreach(MapElement m in c.Elements) vertices.Add((Vertex)m); General.Interface.ShowEditVertices(vertices); } else if((c.Elements[0] is Linedef) || (c.Elements[0] is Sidedef)) { List linedefs = new List(); foreach(MapElement m in c.Elements) { if(m is Sidedef) linedefs.Add((m as Sidedef).Line); else linedefs.Add((Linedef)m); } General.Interface.ShowEditLinedefs(linedefs); } else if(c.Elements[0] is Sector) { List sectors = new List(); foreach(MapElement m in c.Elements) sectors.Add((Sector)m); General.Interface.ShowEditSectors(sectors); } else if(c.Elements[0] is Thing) { List things = new List(); foreach(MapElement m in c.Elements) things.Add((Thing)m); General.Interface.ShowEditThings(things); } General.Map.Map.Update(); UpdateList(); LoseFocus(); General.Interface.RedrawDisplay(); } } // Menu closes private void contextmenu_Closed(object sender, ToolStripDropDownClosedEventArgs e) { LoseFocus(); } // Mode filter option changed private void filtermode_CheckedChanged(object sender, EventArgs e) { UpdateList(); LoseFocus(); } // Click selects changes private void clickselects_CheckedChanged(object sender, EventArgs e) { LoseFocus(); } // This sets the comment on the current selection private void addcomment_Click(object sender, EventArgs e) { List elements = new List(); if(General.Editing.Mode.GetType().Name == "VerticesMode") { ICollection vs = General.Map.Map.GetSelectedVertices(true); foreach(Vertex v in vs) elements.Add(v); } if(General.Editing.Mode.GetType().Name == "LinedefsMode") { ICollection ls = General.Map.Map.GetSelectedLinedefs(true); foreach(Linedef l in ls) elements.Add(l); } if(General.Editing.Mode.GetType().Name == "SectorsMode") { ICollection ss = General.Map.Map.GetSelectedSectors(true); foreach(Sector s in ss) elements.Add(s); } if(General.Editing.Mode.GetType().Name == "ThingsMode") { ICollection ts = General.Map.Map.GetSelectedThings(true); foreach(Thing t in ts) elements.Add(t); } if(elements.Count > 0) { // Create undo if(elements.Count == 1) General.Map.UndoRedo.CreateUndo("Add comment"); else General.Map.UndoRedo.CreateUndo("Add " + elements.Count + " comments"); // Set comment on elements foreach(MapElement el in elements) { el.Fields.BeforeFieldsChange(); el.Fields["comment"] = new UniValue((int)UniversalType.String, addcommenttext.Text); } } addcommenttext.Text = ""; UpdateList(); LoseFocus(); General.Interface.RedrawDisplay(); } // Text typed in add comment box private void addcommenttext_KeyDown(object sender, KeyEventArgs e) { if((e.KeyCode == Keys.Enter) && (e.Modifiers == Keys.None) && addcomment.Enabled) { addcomment.PerformClick(); e.Handled = true; e.SuppressKeyPress = true; } } // Check if the add comment box should be enabled private void enabledtimer_Tick(object sender, EventArgs e) { if(General.Editing.Mode == null) return; //mxd switch(General.Editing.Mode.GetType().Name) { case "VerticesMode": addcommentgroup.Enabled = (General.Map.Map.SelectedVerticessCount > 0); break; case "LinedefsMode": addcommentgroup.Enabled = (General.Map.Map.SelectedLinedefsCount > 0); break; case "SectorsMode": addcommentgroup.Enabled = (General.Map.Map.SelectedSectorsCount > 0); break; case "ThingsMode": addcommentgroup.Enabled = (General.Map.Map.SelectedThingsCount > 0); break; } } // Focus lost private void grid_Leave(object sender, EventArgs e) { preventupdate = false; } private void CommentsDocker_VisibleChanged(object sender, EventArgs e) { if (Visible) UpdateList(); } #endregion } }