using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; using System.Windows.Forms; namespace CraftSynth.ImageEditor { /// /// List of graphic objects /// [Serializable] public class GraphicsList: IDisposable { private ArrayList graphicsList; private bool _isDirty; public bool Dirty { get { if (_isDirty == false) { foreach (DrawObject o in graphicsList) { if (o.Dirty) { _isDirty = true; break; } } } return _isDirty; } set { foreach (DrawObject o in graphicsList) o.Dirty = false; _isDirty = false; } } /// /// Returns IEnumerable object which may be used for enumeration /// of selected objects. /// /// Note: returning IEnumerable breaks CLS-compliance /// (assembly CLSCompliant = true is removed from AssemblyInfo.cs). /// To make this program CLS-compliant, replace /// IEnumerable with IEnumerable. This requires /// casting to object at runtime. /// /// public IEnumerable Selection { get { foreach (DrawObject o in graphicsList) { if (o.Selected) { yield return o; } } } } private const string entryCount = "ObjectCount"; private const string entryType = "ObjectType"; public GraphicsList() { graphicsList = new ArrayList(); } // Public implementation of Dispose pattern callable by consumers. public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } // Flag: Has Dispose already been called? bool _disposed = false; // Protected implementation of Dispose pattern. protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { // Free any managed objects here. // if (this.graphicsList != null) { for (int i = 0; i < this.graphicsList.Count; i++) { if (this.graphicsList[i] != null) { ((DrawObject) this.graphicsList[i]).Dispose(); } } } } // Free any unmanaged objects here. // this._disposed = true; } } ~GraphicsList() { this.Dispose(false); } /// /// Load the GraphicsList from data pulled from disk /// /// Data from disk /// Layer number to be loaded public void LoadFromStream(SerializationInfo info, int orderNumber) { graphicsList = new ArrayList(); // Get number of DrawObjects in this GraphicsList int numberObjects = info.GetInt32( String.Format(CultureInfo.InvariantCulture, "{0}{1}", entryCount, orderNumber)); for (int i = 0; i < numberObjects; i++) { string typeName; typeName = info.GetString( String.Format(CultureInfo.InvariantCulture, "{0}{1}", entryType, i)); object drawObject; drawObject = Assembly.GetExecutingAssembly().CreateInstance( typeName); // Let the Draw Object load itself ((DrawObject)drawObject).LoadFromStream(info, orderNumber, i); graphicsList.Add(drawObject); } } /// /// Save GraphicsList to the stream /// /// Stream to place the GraphicsList into /// Layer Number the List is on public void SaveToStream(SerializationInfo info, int orderNumber) { // First store the number of DrawObjects in the list info.AddValue( String.Format(CultureInfo.InvariantCulture, "{0}{1}", entryCount, orderNumber), graphicsList.Count); // Next save each individual object int i = 0; foreach (DrawObject o in graphicsList) { info.AddValue( String.Format(CultureInfo.InvariantCulture, "{0}{1}", entryType, i), o.GetType().FullName); // Let each object save itself o.SaveToStream(info, orderNumber, i); i++; } } /// /// Draw all the visible objects in the List /// /// Graphics to draw on public void Draw(Graphics g) { int numberObjects = graphicsList.Count; // Enumerate list in reverse order // to get first object on the top //graphicsList.Sort(); for (int i = numberObjects - 1; i >= 0; i--) { DrawObject o; o = (DrawObject)graphicsList[i]; // Only draw objects that are visible if (o.IntersectsWith(Rectangle.Round(g.ClipBounds))) o.Draw(g); if (o.Selected) o.DrawTracker(g); } } /// /// Clear all objects in the list /// /// /// true if at least one object is deleted /// public bool Clear() { bool result = (graphicsList.Count > 0); if (graphicsList.Count > 0) { for (int i = graphicsList.Count - 1; i >= 0; i--) { if (graphicsList[i] != null) { ((DrawObject) graphicsList[i]).Dispose(); } graphicsList.RemoveAt(i); } } // Set dirty flag based on result. Result is true only if at least one item was cleared and since the list is empty, there can be nothing dirty. if (result) _isDirty = false; return result; } /// /// Count and this [nIndex] allow to read all graphics objects /// from GraphicsList in the loop. /// public int Count { get { return graphicsList.Count; } } /// /// Allow accessing Draw Objects by index /// /// 0-based index to retrieve /// Selected DrawObject public DrawObject this[int index] { get { if (index < 0 || index >= graphicsList.Count) return null; return (DrawObject)graphicsList[index]; } } /// /// SelectedCount and GetSelectedObject allow to read /// selected objects in the loop /// public int SelectionCount { get { int n = 0; foreach (DrawObject o in graphicsList) { if (o.Selected) n++; } return n; } } public DrawObject GetSelectedObject(int index) { int n = -1; foreach (DrawObject o in graphicsList) { if (o.Selected) { n++; if (n == index) return o; } } return null; } public void Add(DrawObject obj) { graphicsList.Sort(); foreach (DrawObject o in graphicsList) o.ZOrder++; graphicsList.Insert(0, obj); } public void AddAsInitialGraphic(DrawObject obj) { graphicsList.Add(obj); } /// /// Thanks to Member 3272353 for this fix to object ordering problem. /// /// public void Append(DrawObject obj) { graphicsList.Add(obj); } public void SelectInRectangle(Rectangle rectangle) { UnselectAll(); foreach (DrawObject o in graphicsList) { if (o.IntersectsWith(rectangle)) o.Selected = true; } } public void UnselectAll() { foreach (DrawObject o in graphicsList) { o.Selected = false; } } public void SelectAll() { foreach (DrawObject o in graphicsList) { o.Selected = true; } } /// /// Delete selected items /// /// /// true if at least one object is deleted /// public bool DeleteSelection() { bool result = false; int n = graphicsList.Count; for (int i = n - 1; i >= 0; i--) { if (((DrawObject)graphicsList[i]).Selected) { graphicsList.RemoveAt(i); result = true; } } if (result) _isDirty = true; return result; } /// /// Delete last added object from the list /// (used for Undo operation). /// public void DeleteLastAddedObject() { if (graphicsList.Count > 0) { graphicsList.RemoveAt(0); } } /// /// Replace object in specified place. /// Used for Undo. /// public void Replace(int index, DrawObject obj) { if (index >= 0 && index < graphicsList.Count) { graphicsList.RemoveAt(index); graphicsList.Insert(index, obj); } } /// /// Remove object by index. /// Used for Undo. /// public void RemoveAt(int index) { graphicsList.RemoveAt(index); } /// /// Move selected items to front (beginning of the list) /// /// /// true if at least one object is moved /// public bool MoveSelectionToFront() { int n; int i; ArrayList tempList; tempList = new ArrayList(); n = graphicsList.Count; // Read source list in reverse order, add every selected item // to temporary list and remove it from source list for (i = n - 1; i >= 0; i--) { if (((DrawObject)graphicsList[i]).Selected) { tempList.Add(graphicsList[i]); graphicsList.RemoveAt(i); } } // Read temporary list in direct order and insert every item // to the beginning of the source list n = tempList.Count; for (i = 0; i < n; i++) { graphicsList.Insert(0, tempList[i]); } if (n > 0) _isDirty = true; return (n > 0); } /// /// Move selected items to back (end of the list) /// /// /// true if at least one object is moved /// public bool MoveSelectionToBack() { int n; int i; ArrayList tempList; tempList = new ArrayList(); n = graphicsList.Count; // Read source list in reverse order, add every selected item // to temporary list and remove it from source list for (i = n - 1; i >= 0; i--) { if (((DrawObject)graphicsList[i]).Selected) { tempList.Add(graphicsList[i]); graphicsList.RemoveAt(i); } } // Read temporary list in reverse order and add every item // to the end of the source list n = tempList.Count; for (i = n - 1; i >= 0; i--) { graphicsList.Add(tempList[i]); } if (n > 0) _isDirty = true; return (n > 0); } /// /// Get properties from selected objects and fill GraphicsProperties instance /// /// private GraphicsProperties GetProperties() { GraphicsProperties properties = new GraphicsProperties(); //int n = SelectionCount; //if (n < 1) // return properties; //DrawObject o = GetSelectedObject(0); //int firstColor = o.Color.ToArgb(); //int firstPenWidth = o.PenWidth; //bool allColorsAreEqual = true; //bool allWidthAreEqual = true; //for (int i = 1; i < n; i++) //{ // if (GetSelectedObject(i).Color.ToArgb() != firstColor) // allColorsAreEqual = false; // if (GetSelectedObject(i).PenWidth != firstPenWidth) // allWidthAreEqual = false; //} //if (allColorsAreEqual) //{ // properties.ColorDefined = true; // properties.Color = Color.FromArgb(firstColor); //} //if (allWidthAreEqual) //{ // properties.PenWidthDefined = true; // properties.PenWidth = firstPenWidth; //} return properties; } /// /// Apply properties for all selected objects /// private void ApplyProperties() { //foreach (DrawObject o in graphicsList) //{ // if (o.Selected) // { // if (properties.ColorDefined) // { // o.Color = properties.Color; // DrawObject.LastUsedColor = properties.Color; // } // if (properties.PenWidthDefined) // { // o.PenWidth = properties.PenWidth; // DrawObject.LastUsedPenWidth = properties.PenWidth; // } // } //} } /// /// Show Properties dialog. Return true if list is changed /// /// /// public bool ShowPropertiesDialog(IWin32Window parent) { if (SelectionCount < 1) return false; GraphicsProperties properties = GetProperties(); PropertiesDialog dlg = new PropertiesDialog(); dlg.Properties = properties; if (dlg.ShowDialog(parent) != DialogResult.OK) return false; ApplyProperties(); return true; } } }