| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609 | using SmartCoalApplication.Base;using System;using System.Collections.Generic;using System.ComponentModel;using System.Drawing;using System.Globalization;using System.Windows.Forms;namespace SmartCoalApplication{    public sealed class SnapManager    {        private Dictionary<SnapObstacle, SnapDescription> obstacles =            new Dictionary<SnapObstacle, SnapDescription>();        private const string isSnappedValueName = "IsSnapped";        private const string leftValueName = "Left";        private const string topValueName = "Top";        private const string widthValueName = "Width";        private const string heightValueName = "Height";        private const string nullName = "";        private const string snappedToValueName = "SnappedTo";        private const string horizontalEdgeValueName = "HorizontalEdge";        private const string verticalEdgeValueName = "VerticalEdge";        private const string xOffsetValueName = "XOffset";        private const string yOffsetValueName = "YOffset";        public void SaveSnapObstacleData(ISimpleCollection<string, string> saveTo, SnapObstacle so)        {            string prefix = so.Name + ".";            SnapDescription sd = this.obstacles[so];            bool isSnappedValue = (sd != null);            saveTo.Set(prefix + isSnappedValueName, isSnappedValue.ToString(CultureInfo.InvariantCulture));            if (isSnappedValue)            {                saveTo.Set(prefix + snappedToValueName, sd.SnappedTo.Name);                saveTo.Set(prefix + horizontalEdgeValueName, sd.HorizontalEdge.ToString());                saveTo.Set(prefix + verticalEdgeValueName, sd.VerticalEdge.ToString());                saveTo.Set(prefix + xOffsetValueName, sd.XOffset.ToString(CultureInfo.InvariantCulture));                saveTo.Set(prefix + yOffsetValueName, sd.YOffset.ToString(CultureInfo.InvariantCulture));            }            saveTo.Set(prefix + leftValueName, so.Bounds.Left.ToString(CultureInfo.InvariantCulture));            saveTo.Set(prefix + topValueName, so.Bounds.Top.ToString(CultureInfo.InvariantCulture));            saveTo.Set(prefix + widthValueName, so.Bounds.Width.ToString(CultureInfo.InvariantCulture));            saveTo.Set(prefix + heightValueName, so.Bounds.Height.ToString(CultureInfo.InvariantCulture));        }        public void LoadSnapObstacleData(ISimpleCollection<string, string> loadFrom, SnapObstacle so)        {            string prefix = so.Name + ".";            SnapDescription sd;            string isSnappedString = loadFrom.Get(prefix + isSnappedValueName);            bool isSnapped = bool.Parse(isSnappedString);            if (isSnapped)            {                string snappedToString = loadFrom.Get(prefix + snappedToValueName);                SnapObstacle snappedTo = FindObstacle(snappedToString);                string horizontalEdgeString = loadFrom.Get(prefix + horizontalEdgeValueName);                HorizontalSnapEdge horizontalEdge = (HorizontalSnapEdge)Enum.Parse(typeof(HorizontalSnapEdge), horizontalEdgeString, true);                string verticalEdgeString = loadFrom.Get(prefix + verticalEdgeValueName);                VerticalSnapEdge verticalEdge = (VerticalSnapEdge)Enum.Parse(typeof(VerticalSnapEdge), verticalEdgeString, true);                string xOffsetString = loadFrom.Get(prefix + xOffsetValueName);                int xOffset = int.Parse(xOffsetString, CultureInfo.InvariantCulture);                string yOffsetString = loadFrom.Get(prefix + yOffsetValueName);                int yOffset = int.Parse(yOffsetString, CultureInfo.InvariantCulture);                sd = new SnapDescription(snappedTo, horizontalEdge, verticalEdge, xOffset, yOffset);            }            else            {                sd = null;            }            this.obstacles[so] = sd;            string leftString = loadFrom.Get(prefix + leftValueName);            int left = int.Parse(leftString, CultureInfo.InvariantCulture);            string topString = loadFrom.Get(prefix + topValueName);            int top = int.Parse(topString, CultureInfo.InvariantCulture);            string widthString = loadFrom.Get(prefix + widthValueName);            int width = int.Parse(widthString, CultureInfo.InvariantCulture);            string heightString = loadFrom.Get(prefix + heightValueName);            int height = int.Parse(heightString, CultureInfo.InvariantCulture);            Rectangle newBounds = new Rectangle(left, top, width, height);            so.RequestBoundsChange(newBounds);            if (sd != null)            {                ParkObstacle(so, sd);            }        }        // Requires that all SnapObstacles are already placed in this.obstacles        public void Save(ISimpleCollection<string, string> saveTo)        {            foreach (SnapObstacle obstacle in this.obstacles.Keys)            {                if (obstacle.EnableSave)                {                    SaveSnapObstacleData(saveTo, obstacle);                }            }        }        public void Load(ISimpleCollection<string, string> loadFrom)        {            SnapObstacle[] newObstacles = new SnapObstacle[this.obstacles.Count];            this.obstacles.Keys.CopyTo(newObstacles, 0);            foreach (SnapObstacle obstacle in newObstacles)            {                if (obstacle.EnableSave)                {                    LoadSnapObstacleData(loadFrom, obstacle);                }            }        }        public void ParkObstacle(ISnapObstacleHost obstacle, ISnapObstacleHost snappedTo, HorizontalSnapEdge hEdge, VerticalSnapEdge vEdge)        {            ParkObstacle(obstacle.SnapObstacle, snappedTo.SnapObstacle, hEdge, vEdge);        }        public void ParkObstacle(SnapObstacle obstacle, SnapObstacle snappedTo, HorizontalSnapEdge hEdge, VerticalSnapEdge vEdge)        {            SnapDescription sd = new SnapDescription(snappedTo, hEdge, vEdge, obstacle.SnapDistance, obstacle.SnapDistance);            this.obstacles[obstacle] = sd;            ParkObstacle(obstacle, sd);        }        public void ReparkObstacle(ISnapObstacleHost obstacle)        {            ReparkObstacle(obstacle.SnapObstacle);        }        public void ReparkObstacle(SnapObstacle obstacle)        {            if (this.obstacles.ContainsKey(obstacle))            {                SnapDescription sd = this.obstacles[obstacle];                if (sd != null)                {                    ParkObstacle(obstacle, sd);                }            }        }        public void AddSnapObstacle(ISnapObstacleHost snapObstacleHost)        {            AddSnapObstacle(snapObstacleHost.SnapObstacle);        }        public void AddSnapObstacle(SnapObstacle snapObstacle)        {            if (!this.obstacles.ContainsKey(snapObstacle))            {                this.obstacles.Add(snapObstacle, null);                if (snapObstacle.StickyEdges)                {                    snapObstacle.BoundsChanging += SnapObstacle_BoundsChanging;                    snapObstacle.BoundsChanged += SnapObstacle_BoundsChanged;                }            }        }        private void SnapObstacle_BoundsChanging(object sender, EventArgs<Rectangle> e)        {        }        private void SnapObstacle_BoundsChanged(object sender, EventArgs<Rectangle> e)        {            SnapObstacle senderSO = (SnapObstacle)sender;            Rectangle fromRect = e.Data;            Rectangle toRect = senderSO.Bounds;            UpdateDependentObstacles(senderSO, fromRect, toRect);        }        private void UpdateDependentObstacles(SnapObstacle senderSO, Rectangle fromRect, Rectangle toRect)        {            int leftDelta = toRect.Left - fromRect.Left;            int topDelta = toRect.Top - fromRect.Top;            int rightDelta = toRect.Right - fromRect.Right;            int bottomDelta = toRect.Bottom - fromRect.Bottom;            foreach (SnapObstacle obstacle in this.obstacles.Keys)            {                if (!object.ReferenceEquals(senderSO, obstacle))                {                    SnapDescription sd = this.obstacles[obstacle];                    if (sd != null && object.ReferenceEquals(sd.SnappedTo, senderSO))                    {                        int deltaX;                        if (sd.VerticalEdge == VerticalSnapEdge.Right)                        {                            deltaX = rightDelta;                        }                        else                        {                            deltaX = leftDelta;                        }                        int deltaY;                        if (sd.HorizontalEdge == HorizontalSnapEdge.Bottom)                        {                            deltaY = bottomDelta;                        }                        else                        {                            deltaY = topDelta;                        }                        Rectangle oldBounds = obstacle.Bounds;                        Point newLocation1 = new Point(oldBounds.Left + deltaX, oldBounds.Top + deltaY);                        Point newLocation2 = AdjustNewLocation(obstacle, newLocation1, sd);                        Rectangle newBounds = new Rectangle(newLocation2, oldBounds.Size);                        obstacle.RequestBoundsChange(newBounds);                        // Recursively update anything snapped to this obstacle                        UpdateDependentObstacles(obstacle, oldBounds, newBounds);                    }                }            }        }        public void RemoveSnapObstacle(ISnapObstacleHost snapObstacleHost)        {            RemoveSnapObstacle(snapObstacleHost.SnapObstacle);        }        public void RemoveSnapObstacle(SnapObstacle snapObstacle)        {            if (this.obstacles.ContainsKey(snapObstacle))            {                this.obstacles.Remove(snapObstacle);                if (snapObstacle.StickyEdges)                {                    snapObstacle.BoundsChanging -= SnapObstacle_BoundsChanging;                    snapObstacle.BoundsChanged -= SnapObstacle_BoundsChanged;                }            }        }        public bool ContainsSnapObstacle(ISnapObstacleHost snapObstacleHost)        {            return ContainsSnapObstacle(snapObstacleHost.SnapObstacle);        }        public bool ContainsSnapObstacle(SnapObstacle snapObstacle)        {            return this.obstacles.ContainsKey(snapObstacle);        }        private static bool AreEdgesClose(int l1, int r1, int l2, int r2)        {            if (r1 < l2)            {                return false;            }            else if (r2 < l1)            {                return false;            }            else if (l1 <= l2 && l2 <= r1 && r1 <= r2)            {                return true;            }            else if (l2 <= l1 && l1 <= r2 && r2 <= r1)            {                return true;            }            else if (l1 <= l2 && r2 <= r1)            {                return true;            }            else if (l2 <= l1 && l1 <= r2)            {                return true;            }            throw new InvalidOperationException();        }        private SnapDescription DetermineNewSnapDescription(            SnapObstacle avoider,            Point newLocation,            SnapObstacle avoidee,            SnapDescription currentSnapDescription)        {            int ourSnapProximity;            if (currentSnapDescription != null &&                (currentSnapDescription.HorizontalEdge != HorizontalSnapEdge.Neither ||                 currentSnapDescription.VerticalEdge != VerticalSnapEdge.Neither))            {                // the avoider is already snapped to the avoidee -- make it more difficult to un-snap                ourSnapProximity = avoidee.SnapProximity * 2;            }            else            {                ourSnapProximity = avoidee.SnapProximity;            }            Rectangle avoiderRect = avoider.Bounds;            avoiderRect.Location = newLocation;            Rectangle avoideeRect = avoidee.Bounds;            // Are the vertical edges close enough for snapping?            bool vertProximity = AreEdgesClose(avoiderRect.Top, avoiderRect.Bottom, avoideeRect.Top, avoideeRect.Bottom);            // Are the horizontal edges close enough for snapping?            bool horizProximity = AreEdgesClose(avoiderRect.Left, avoiderRect.Right, avoideeRect.Left, avoideeRect.Right);            // Compute distances from pertinent edges            // (e.g. if SnapRegion.Interior, figure out distance from avoider's right edge to avoidee's right edge,            //       if SnapRegion.Exterior, figure out distance from avoider's right edge to avoidee's left edge)            int leftDistance;            int rightDistance;            int topDistance;            int bottomDistance;            switch (avoidee.SnapRegion)            {                case SnapRegion.Interior:                    leftDistance = Math.Abs(avoiderRect.Left - avoideeRect.Left);                    rightDistance = Math.Abs(avoiderRect.Right - avoideeRect.Right);                    topDistance = Math.Abs(avoiderRect.Top - avoideeRect.Top);                    bottomDistance = Math.Abs(avoiderRect.Bottom - avoideeRect.Bottom);                    break;                case SnapRegion.Exterior:                    leftDistance = Math.Abs(avoiderRect.Left - avoideeRect.Right);                    rightDistance = Math.Abs(avoiderRect.Right - avoideeRect.Left);                    topDistance = Math.Abs(avoiderRect.Top - avoideeRect.Bottom);                    bottomDistance = Math.Abs(avoiderRect.Bottom - avoideeRect.Top);                    break;                default:                    throw new InvalidEnumArgumentException("avoidee.SnapRegion");            }            bool leftClose = (leftDistance < ourSnapProximity);            bool rightClose = (rightDistance < ourSnapProximity);            bool topClose = (topDistance < ourSnapProximity);            bool bottomClose = (bottomDistance < ourSnapProximity);            VerticalSnapEdge vEdge = VerticalSnapEdge.Neither;            if (vertProximity)            {                if ((leftClose && avoidee.SnapRegion == SnapRegion.Exterior) ||                    (rightClose && avoidee.SnapRegion == SnapRegion.Interior))                {                    vEdge = VerticalSnapEdge.Right;                }                else if ((rightClose && avoidee.SnapRegion == SnapRegion.Exterior) ||                         (leftClose && avoidee.SnapRegion == SnapRegion.Interior))                {                    vEdge = VerticalSnapEdge.Left;                }            }            HorizontalSnapEdge hEdge = HorizontalSnapEdge.Neither;            if (horizProximity)            {                if ((topClose && avoidee.SnapRegion == SnapRegion.Exterior) ||                    (bottomClose && avoidee.SnapRegion == SnapRegion.Interior))                {                    hEdge = HorizontalSnapEdge.Bottom;                }                else if ((bottomClose && avoidee.SnapRegion == SnapRegion.Exterior) ||                         (topClose && avoidee.SnapRegion == SnapRegion.Interior))                {                    hEdge = HorizontalSnapEdge.Top;                }            }            SnapDescription sd;            if (hEdge != HorizontalSnapEdge.Neither || vEdge != VerticalSnapEdge.Neither)            {                int xOffset = avoider.SnapDistance;                int yOffset = avoider.SnapDistance;                if (hEdge == HorizontalSnapEdge.Neither)                {                    if (avoidee.SnapRegion == SnapRegion.Interior)                    {                        yOffset = avoiderRect.Top - avoideeRect.Top;                        hEdge = HorizontalSnapEdge.Top;                    }                }                if (vEdge == VerticalSnapEdge.Neither)                {                    if (avoidee.SnapRegion == SnapRegion.Interior)                    {                        xOffset = avoiderRect.Left - avoideeRect.Left;                        vEdge = VerticalSnapEdge.Left;                    }                }                sd = new SnapDescription(avoidee, hEdge, vEdge, xOffset, yOffset);            }            else            {                sd = null;            }            return sd;        }        private static void ParkObstacle(SnapObstacle avoider, SnapDescription snapDescription)        {            Point newLocation = avoider.Bounds.Location;            Point adjustedLocation = AdjustNewLocation(avoider, newLocation, snapDescription);            Rectangle newBounds = new Rectangle(adjustedLocation, avoider.Bounds.Size);            avoider.RequestBoundsChange(newBounds);        }        private static Point AdjustNewLocation(SnapObstacle obstacle, Point newLocation, SnapDescription snapDescription)        {            if (snapDescription == null ||                (snapDescription.HorizontalEdge == HorizontalSnapEdge.Neither &&                 snapDescription.VerticalEdge == VerticalSnapEdge.Neither))            {                return obstacle.Bounds.Location;            }            Rectangle obstacleRect = new Rectangle(newLocation, obstacle.Bounds.Size);            Rectangle snappedToRect = snapDescription.SnappedTo.Bounds;            HorizontalSnapEdge hEdge = snapDescription.HorizontalEdge;            VerticalSnapEdge vEdge = snapDescription.VerticalEdge;            SnapRegion region = snapDescription.SnappedTo.SnapRegion;            int deltaY = 0;            if (hEdge == HorizontalSnapEdge.Top && region == SnapRegion.Exterior)            {                int newBottomEdge = snappedToRect.Top - snapDescription.YOffset;                deltaY = obstacleRect.Bottom - newBottomEdge;            }            else if (hEdge == HorizontalSnapEdge.Bottom && region == SnapRegion.Exterior)            {                int newTopEdge = snappedToRect.Bottom + snapDescription.YOffset;                deltaY = obstacleRect.Top - newTopEdge;            }            else if (hEdge == HorizontalSnapEdge.Top && region == SnapRegion.Interior)            {                int newTopEdge = Math.Min(snappedToRect.Bottom, snappedToRect.Top + snapDescription.YOffset);                deltaY = obstacleRect.Top - newTopEdge;            }            else if (hEdge == HorizontalSnapEdge.Bottom && region == SnapRegion.Interior)            {                int newBottomEdge = Math.Max(snappedToRect.Top, snappedToRect.Bottom - snapDescription.YOffset);                deltaY = obstacleRect.Bottom - newBottomEdge;            }            int deltaX = 0;            if (vEdge == VerticalSnapEdge.Left && region == SnapRegion.Exterior)            {                int newRightEdge = snappedToRect.Left - snapDescription.XOffset;                deltaX = obstacleRect.Right - newRightEdge;            }            else if (vEdge == VerticalSnapEdge.Right && region == SnapRegion.Exterior)            {                int newLeftEdge = snappedToRect.Right + snapDescription.XOffset;                deltaX = obstacleRect.Left - newLeftEdge;            }            else if (vEdge == VerticalSnapEdge.Left && region == SnapRegion.Interior)            {                int newLeftEdge = Math.Min(snappedToRect.Right, snappedToRect.Left + snapDescription.XOffset);                deltaX = obstacleRect.Left - newLeftEdge;            }            else if (vEdge == VerticalSnapEdge.Right && region == SnapRegion.Interior)            {                int newRightEdge = Math.Max(snappedToRect.Left, snappedToRect.Right - snapDescription.XOffset);                deltaX = obstacleRect.Right - newRightEdge;            }            Point adjustedLocation = new Point(obstacleRect.Left - deltaX, obstacleRect.Top - deltaY);            return adjustedLocation;        }        /// <summary>        /// Given an obstacle and its attempted destination, determines the correct landing        /// spot for an obstacle.        /// </summary>        /// <param name="movingObstacle">The obstacle that is moving.</param>        /// <param name="newLocation">The upper-left coordinate of the obstacle's original intended destination.</param>        /// <returns>        /// A Point that determines where the obstacle should be placed instead. If there are no adjustments        /// required to the obstacle's desintation, then the return value will be equal to newLocation.        /// </returns>        /// <remarks>        /// movingObstacle's SnapDescription will also be updated. The caller of this method is required        /// to update the SnapObstacle with the new, adjusted location.        /// </remarks>        public Point AdjustObstacleDestination(SnapObstacle movingObstacle, Point newLocation)        {            Point adjusted1 = AdjustObstacleDestination(movingObstacle, newLocation, false);            Point adjusted2 = AdjustObstacleDestination(movingObstacle, adjusted1, true);            return adjusted2;        }        public Point AdjustObstacleDestination(SnapObstacle movingObstacle, Point newLocation, bool considerStickies)        {            Point adjustedLocation = newLocation;            SnapDescription sd = this.obstacles[movingObstacle];            SnapDescription newSD = null;            foreach (SnapObstacle avoidee in this.obstacles.Keys)            {                if (avoidee.StickyEdges != considerStickies)                {                    continue;                }                if (avoidee.Enabled && !object.ReferenceEquals(avoidee, movingObstacle))                {                    SnapDescription newSD2 = DetermineNewSnapDescription(movingObstacle, adjustedLocation, avoidee, newSD);                    if (newSD2 != null)                    {                        Point adjustedLocation2 = AdjustNewLocation(movingObstacle, adjustedLocation, newSD2);                        newSD = newSD2;                        adjustedLocation = adjustedLocation2;                        Rectangle newBounds = new Rectangle(adjustedLocation, movingObstacle.Bounds.Size);                    }                }            }            if (sd == null || !sd.SnappedTo.StickyEdges || newSD == null || newSD.SnappedTo.StickyEdges)            {                this.obstacles[movingObstacle] = newSD;            }            return adjustedLocation;        }        public SnapObstacle FindObstacle(string name)        {            foreach (SnapObstacle so in this.obstacles.Keys)            {                if (string.Compare(so.Name, name, true) == 0)                {                    return so;                }            }            return null;        }        public static SnapManager FindMySnapManager(Control me)        {            if (!(me is ISnapObstacleHost))            {                throw new ArgumentException("must be called with a Control that implements ISnapObstacleHost");            }            ISnapManagerHost ismh;            ismh = me as ISnapManagerHost;            if (ismh == null)            {                ismh = me.FindForm() as ISnapManagerHost;            }            SnapManager sm;            if (ismh != null)            {                sm = ismh.SnapManager;            }            else            {                sm = null;            }            return sm;        }        public SnapManager()        {        }    }}
 |