context: #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.Text;
  using System.Windows.Forms;
  using Microsoft.Win32;
  using CodeImp.DoomBuilder.Actions;
  using CodeImp.DoomBuilder.Data;
  using CodeImp.DoomBuilder.Config;
  using CodeImp.DoomBuilder.Map;
  using CodeImp.DoomBuilder.Controls;
  using CodeImp.DoomBuilder.Windows;
  using System.Reflection;
  using System.Globalization;
  using System.Threading;
  using CodeImp.DoomBuilder.Editing;
  using CodeImp.DoomBuilder.Geometry;
  using CodeImp.DoomBuilder.Types;
  
  #endregion
  
  namespace CodeImp.DoomBuilder.CommentsPanel
  {
  	public partial class CommentsDocker : UserControl
  	{
  		#region ================== Variables
  		
  		Dictionary<string, CommentInfo> v_comments = new Dictionary<string, CommentInfo>();
  		Dictionary<string, CommentInfo> l_comments = new Dictionary<string, CommentInfo>();
  		Dictionary<string, CommentInfo> s_comments = new Dictionary<string, CommentInfo>();
  		Dictionary<string, CommentInfo> t_comments = new Dictionary<string, CommentInfo>();
  		bool preventupdate = false;
  		
  		#endregion
  		
  		#region ================== Constructor
  
  		// Constructor
  		public CommentsDocker()
  		{
  			InitializeComponent();
  
  			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()
  		{
  			if(this.ParentForm != null)
  			{
  				this.ParentForm.Activated -= ParentForm_Activated;
  			}
  
  			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<string, CommentInfo> newcomments, Dictionary<string, CommentInfo> comments, Image icon)
  		{
  			// Remove old comments
  			List<CommentInfo> commentslist = new List<CommentInfo>(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<string, CommentInfo> c in newcomments)
  			{
  				DataGridViewRow row;
  				CommentInfo cc = c.Value;
  				
  				if(!comments.ContainsKey(c.Key))
  				{
  					// Create grid row
  					int index = grid.Rows.Add();
  					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.Interval = 100;
  			updatetimer.Start();
  		}
  		
  		// This finds all comments and updates the list
  		public void UpdateList()
  		{
  			//bool firstitem = (grid.Rows.Count == 0);
  
  			if(!preventupdate)
  			{
  				// Update vertices
  				Dictionary<string, CommentInfo> newcomments = new Dictionary<string, CommentInfo>();
  				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 void AddComments(MapElement e, Dictionary<string, CommentInfo> 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 void ViewComment(CommentInfo c)
  		{
  			List<Vector2D> points = new List<Vector2D>();
  			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 = (Vector2D)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
  			{
  
gt: 				float delta = area.Height - area.Width;