# HG changeset patch # User Ivo Smits # Date 1394556348 -3600 # Node ID c9da306e06c93168f21d3725be57868cb0bcc71f # Parent 4ca44dd25a6a8d16e7223d1ac6c17a43f73ba566 FBGUI: Improved FBGContainerControl thread safety diff -r 4ca44dd25a6a -r c9da306e06c9 FBGUI/FBGUI.cs --- a/FBGUI/FBGUI.cs Sun Feb 23 16:59:47 2014 +0100 +++ b/FBGUI/FBGUI.cs Tue Mar 11 17:45:48 2014 +0100 @@ -8,6 +8,7 @@ using System.Reflection; using System.Threading; using System.Windows.Forms; +using UCIS.Util; using UCIS.VNCServer; using ThreadingTimer = System.Threading.Timer; @@ -93,7 +94,7 @@ } public interface IFBGContainerControl { void AddControl(IFBGControl control); - void RemoveControl(IFBGControl control); + Boolean RemoveControl(IFBGControl control); void HandleMessage(IFBGControl sender, FBGMessage e); Size ClientSize { get; } } @@ -212,8 +213,8 @@ protected void RaiseEvent(EventHandler eh, EventArgs ea) { if (eh != null) eh(this, ea); } protected void RaiseEvent(EventHandler eh) { if (eh != null) eh(this, new EventArgs()); } } - public class FBGContainerControl : FBGControl, IFBGContainerControl { - protected List controls = new List(); + public class FBGContainerControl : FBGControl, IFBGContainerControl, IList { + protected IFBGControl[] controls = new IFBGControl[0]; protected IFBGControl mouseCaptureControl = null; protected IFBGControl keyboardCaptureControl = null; public virtual Rectangle ClientRectangle { get { return new Rectangle(Point.Empty, Bounds.Size); } } @@ -221,18 +222,27 @@ public FBGContainerControl(IFBGContainerControl parent) : base(parent) { } void IFBGContainerControl.AddControl(IFBGControl control) { AddControl(control); } protected virtual void AddControl(IFBGControl control) { - controls.Add(control); + ArrayUtil.Add(ref controls, control); if (control.Visible) Invalidate(control); } - public virtual void RemoveControl(IFBGControl control) { - if (controls.Remove(control)) { - if (control.Visible) Invalidate(control); + public virtual Boolean RemoveControl(IFBGControl control) { + if (!ArrayUtil.Remove(ref controls, control)) return false; + if (control.Visible) Invalidate(control); + HandleMessage(control, new FBGPointingCaptureMessage(control, false)); + HandleMessage(control, new FBGKeyboardCaptureMessage(control, false)); + control.Orphaned(); + return true; + } + public virtual void RemoveAllControls() { + IFBGControl[] c = Interlocked.Exchange(ref controls, new IFBGControl[0]); + foreach (IFBGControl control in c) { HandleMessage(control, new FBGPointingCaptureMessage(control, false)); HandleMessage(control, new FBGKeyboardCaptureMessage(control, false)); control.Orphaned(); } + Invalidate(); } - public IList Controls { get { return controls.AsReadOnly(); } } + public IList Controls { get { return this; } } public virtual Point PointToChild(IFBGControl child, Point point) { return point - (Size)child.Bounds.Location - (Size)ClientRectangle.Location; } @@ -240,10 +250,11 @@ return point + (Size)child.Bounds.Location + (Size)ClientRectangle.Location; } public virtual void BringControlToFront(IFBGControl control) { - if (controls.Count == 0) return; - if (ReferenceEquals(controls[controls.Count - 1], control)) return; - if (!controls.Remove(control)) return; - controls.Add(control); + if (controls.Length == 0) return; + int oldindex = Array.IndexOf(controls, control); + if (oldindex == -1 || oldindex == controls.Length - 1) return; + for (int i = oldindex; i < controls.Length - 1; i++) controls[i] = controls[i + 1]; + controls[controls.Length - 1] = control; if (control.Visible) Invalidate(control); } public virtual void Invalidate(IFBGControl control) { @@ -278,7 +289,7 @@ Rectangle childarea = ClientRectangle; if (!childarea.Contains(p)) return null; p.Offset(-childarea.X, -childarea.Y); - return ((List)controls).FindLast(delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); }); + return Array.FindLast(controls, delegate(IFBGControl control) { return control.Visible && control.Bounds.Contains(p); }); } protected override void HandlePointingEvent(FBGPointingEvent e) { IFBGControl control = mouseCaptureControl != null ? mouseCaptureControl : FindControlAtPosition(e.Position); @@ -328,13 +339,31 @@ base.HandleKeyboardCaptureEvent(e); } protected override void Orphaned() { - base.Orphaned(); - IFBGControl[] c = controls.ToArray(); - controls.Clear(); + IFBGControl[] c = Interlocked.Exchange(ref controls, new IFBGControl[0]); foreach (IFBGControl control in c) control.Orphaned(); mouseCaptureControl = null; keyboardCaptureControl = null; + base.Orphaned(); } + + #region IList Members + int IList.IndexOf(IFBGControl item) { return Array.IndexOf(controls, item); } + void IList.Insert(int index, IFBGControl item) { throw new NotSupportedException(); } + void IList.RemoveAt(int index) { RemoveControl(controls[index]); } + IFBGControl IList.this[int index] { + get { return controls[index]; } + set { throw new NotSupportedException(); } + } + void ICollection.Add(IFBGControl item) { throw new NotSupportedException(); } + void ICollection.Clear() { RemoveAllControls(); } + bool ICollection.Contains(IFBGControl item) { return Array.IndexOf(controls, item) != -1; } + void ICollection.CopyTo(IFBGControl[] array, int arrayIndex) { controls.CopyTo(array, arrayIndex); } + int ICollection.Count { get { return controls.Length; } } + bool ICollection.IsReadOnly { get { return false; } } + bool ICollection.Remove(IFBGControl item) { return RemoveControl(item); } + IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)controls).GetEnumerator(); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return controls.GetEnumerator(); } + #endregion } public class FBGDockContainer : FBGContainerControl { private Dictionary dockStyles = new Dictionary(); @@ -351,9 +380,14 @@ base.BringControlToFront(control); if (dockStyles.ContainsKey(control)) DoLayout(); } - public override void RemoveControl(IFBGControl control) { - base.RemoveControl(control); + public override Boolean RemoveControl(IFBGControl control) { + if (!base.RemoveControl(control)) return false; if (dockStyles.Remove(control)) DoLayout(); + return true; + } + public override void RemoveAllControls() { + base.RemoveAllControls(); + dockStyles.Clear(); } public override Rectangle Bounds { get { return base.Bounds; } @@ -384,7 +418,7 @@ public void SetDockStyle(IFBGControl control, DockStyle style) { if (style == DockStyle.None) { if (dockStyles.Remove(control)) DoLayout(); - } else if (controls.Contains(control)) { + } else if (Array.IndexOf(controls, control) != -1) { anchorStyles.Remove(control); dockStyles[control] = style; DoLayout(); @@ -398,13 +432,13 @@ public void SetAnchorStyle(IFBGControl control, AnchorStyles style) { if (style == (AnchorStyles.Left | AnchorStyles.Top)) { anchorStyles.Remove(control); - } else if (controls.Contains(control)) { + } else if (Array.IndexOf(controls, control) != -1) { dockStyles.Remove(control); anchorStyles[control] = style; } } public void SetAnchor(IFBGControl control, AnchorStyles style, int value) { - if (controls.Contains(control)) { + if (Array.IndexOf(controls, control) != -1) { AnchorStyles oldstyle; if (!anchorStyles.TryGetValue(control, out oldstyle)) oldstyle = AnchorStyles.Left | AnchorStyles.Top; Rectangle b = control.Bounds; @@ -496,13 +530,14 @@ control.Bounds = new Rectangle(Point.Empty, ClientSize); Invalidate(control.Bounds); } - void IFBGContainerControl.RemoveControl(IFBGControl control) { - if (!ReferenceEquals(childControl, control)) return; + Boolean IFBGContainerControl.RemoveControl(IFBGControl control) { + if (!ReferenceEquals(childControl, control)) return false; childControl = null; Invalidate(control.Bounds); if (mouseCaptureControl == control) mouseCaptureControl = null; if (keyboardCaptureControl == control) control = null; control.Orphaned(); + return true; } void IFBGContainerControl.HandleMessage(IFBGControl sender, FBGMessage e) { if (e is FBGInvalidateMessage) { @@ -1039,10 +1074,19 @@ } }; } - public override void RemoveControl(IFBGControl control) { - windowcontainer.RemoveControl(control); + public override Boolean RemoveControl(IFBGControl control) { + if (!windowcontainer.RemoveControl(control)) return false; + FBGWindowState ws = windowstates[control]; + menucontainer.RemoveControl(ws.MenuButton); windowstates.Remove(control); ScaleMenuButtons(); + return true; + } + public override void RemoveAllControls() { + windowcontainer.RemoveAllControls(); + menucontainer.RemoveAllControls(); + windowstates.Clear(); + ScaleMenuButtons(); } private void ScaleMenuButtons() { int bcount = windowstates.Count; diff -r 4ca44dd25a6a -r c9da306e06c9 Util/ArrayUtil.cs --- a/Util/ArrayUtil.cs Sun Feb 23 16:59:47 2014 +0100 +++ b/Util/ArrayUtil.cs Tue Mar 11 17:45:48 2014 +0100 @@ -162,5 +162,15 @@ if (index == -1) index = Add(ref array, item); return index; } + public static Boolean Remove(ref T[] array, T item) { + if (array == null) return false; + int index = Array.IndexOf(array, item); + if (index == -1) return false; + T[] newarray = new T[array.Length - 1]; + if (index > 0) Array.Copy(array, 0, newarray, 0, index); + if (index < array.Length - 1) Array.Copy(array, index + 1, newarray, index, array.Length - index - 1); + array = newarray; + return true; + } } }