Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Tools/MoveTool.cs @ 2768

Last change on this file since 2768 was 2768, checked in by mkommend, 14 years ago

added solution folders and sources for the netron library (ticket #867)

File size: 12.9 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Collections;
4using System.Drawing;
5using System.Windows.Forms;
6
7namespace Netron.Diagramming.Core
8{
9    /// <summary>
10    /// This tool implement the action of moving shapes on the canvas.
11    /// <para>Note that this tool is slightly different than other tools since it activates itself unless it has been suspended by another tool. </para>
12    /// </summary>
13    public class MoveTool : AbstractTool, IMouseListener
14    {
15
16        #region Fields
17        /// <summary>
18        /// the location of the mouse when the motion starts
19        /// </summary>
20        private Point initialPoint;
21        /// <summary>
22        /// the intermediate location of the mouse during the motion
23        /// </summary>
24        private Point lastPoint;
25
26        private IConnector hoveredConnector;
27        /// <summary>
28        /// the AsConnectorMover field
29        /// </summary>
30        private bool connectorMove;
31        /// <summary>
32        /// Gets or sets the AsConnectorMover
33        /// </summary>
34        public bool AsConnectorMover
35        {
36            get
37            {
38                return connectorMove;
39            }
40            set
41            {
42                connectorMove = value;
43            }
44        }
45
46        public static bool Blocked = false;
47        #endregion
48
49        #region Constructor
50        /// <summary>
51        /// Initializes a new instance of the <see cref="T:MoveTool"/> class.
52        /// </summary>
53        /// <param name="name">The name of the tool.</param>
54        public MoveTool(string name)
55            : base(name)
56        {
57        }
58        #endregion
59
60        #region Methods
61
62        /// <summary>
63        /// Called when the tool is activated.
64        /// </summary>
65        protected override void OnActivateTool()
66        {
67            Controller.View.CurrentCursor = CursorPalette.Move;
68
69        }
70
71        /// <summary>
72        /// Handles the mouse down event
73        /// </summary>
74        /// <param name="e">The <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
75        public bool MouseDown(MouseEventArgs e)
76        {
77            if(e == null)
78                throw new ArgumentNullException("The argument object is 'null'");
79            if(e.Button == MouseButtons.Left && Enabled && !IsSuspended && !Blocked)
80            {
81                if(Selection.SelectedItems.Count > 0)
82                {
83                    initialPoint = e.Location;
84                    lastPoint = initialPoint;
85                    connectorMove = false;
86                    //while moving the shapes we'll clear the tracker
87                    this.Controller.View.ResetTracker();
88                    //now, go for it
89                    this.ActivateTool();
90
91                    // Even if an entity is hit, I don't think the mouse down
92                    // event should be handled here.  That is, return false so
93                    // other mouse listeners can respond.  This tool really
94                    // does its thing when the mouse is moved.
95                    //return true;
96                }
97                else if(Selection.Connector != null && Selection.Connector.Parent is IConnection)
98                {
99                    if(!typeof(IShape).IsInstanceOfType(Selection.Connector.Parent)) //note that there is a separate tool to move shape-connectors!
100                    {
101                        initialPoint = e.Location;
102                        lastPoint = initialPoint;
103                        connectorMove = true; //keep this for the final packaging into the undo manager
104                        this.ActivateTool();
105                        //return true;
106                    }
107                }
108
109            }
110            return false;
111        }
112
113        /// <summary>
114        /// Handles the mouse move event
115        /// </summary>
116        /// <param name="e">The <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
117        public void MouseMove(MouseEventArgs e)
118        {
119            if(e == null)
120                throw new ArgumentNullException("The argument object is 'null'");
121            Point point = e.Location;
122            if(IsActive)
123            {
124                //can be a connector
125                if(Selection.Connector != null)
126                {
127                    Selection.Connector.MoveBy(new Point(point.X - lastPoint.X, point.Y - lastPoint.Y));
128                    #region Do we hit something meaningful?
129
130                    if (hoveredConnector != null)
131                        hoveredConnector.Hovered = false;                       
132                   
133                    hoveredConnector = Selection.FindConnectorAt(e.Location);
134                    if(hoveredConnector!=null)
135                        hoveredConnector.Hovered = true;
136                    if(hoveredConnector != null && hoveredConnector!=Selection.Connector)
137                    {
138                        Controller.View.CurrentCursor = CursorPalette.Grip;
139                       
140                    }
141                    else
142                        Controller.View.CurrentCursor = CursorPalette.Move;
143                    #endregion
144                }
145                else //can be a selection
146                {
147                    foreach(IDiagramEntity entity in Selection.SelectedItems)
148                    {
149                        if (entity.AllowMove)
150                        {
151                            entity.MoveBy(new Point(point.X - lastPoint.X, point.Y - lastPoint.Y));
152                        }
153                    }
154                }
155                lastPoint = point;
156            }
157        }
158        /// <summary>
159        /// When an entity is moved there are various possibilities:
160        /// <list type="bullet">
161        /// <item>
162        /// <term>a connector is moved</term>
163        /// <description>in this case the moved connector can only be part of a onnection because moving shape-connectors is not allowed unless by means of the <see cref="ConnectorMoverTool"/>.
164        /// If a connector attached to a connection is moved we have the following fork:
165        /// <list type="bullet">
166        /// <item>the connector was attached</item>
167        /// <description>the connector has a parent and needs to be detached before being moved and eventually attached to another connector</description>
168        /// <item>the connector is moved</item>
169        /// <description>this is a standard motion and is similar for any <see cref="IDiagramEntity"/>
170        /// </description>
171        /// <item>the connector ends up somewhere near another connector and will become attached to it</item>
172        /// <description>the connector in the proximity of the moved connector will become the parent of it. Note that we previously detached any binding and that a connector can have only one parent.</description>
173        /// </list>
174        /// </description>
175        /// </item>
176        /// <item>an entity is moved</item>
177        /// <description>the normal <see cref="MoveCommand"/> can be used</description>
178        /// <item>a set of entities is moved</item>
179        /// <description>we need to create a bundle to package the entities and then use the <see cref="MoveCommand"/></description>
180        /// </list>
181        /// Several important remarks are in order here:
182        /// <list type="bullet">
183        /// <item>a connector can have only one parent</item>
184        /// <item>we need to package a move action in a command but this command needs NOT to be performed (i.e. call the Redo() method) because the motion already occured through the MouseMove handler. Because of this situation we cannot perform a Redo() on the full package since it would move the entities twice. Hence, commands are execute just after their creation (except for the move).
185        /// <item>when the stack of actions are undone the stack has to be reverted</item>
186        /// </item>
187        /// <item>whatever the situation is, the most economical way to code the different cases is by means of a <see cref="CompoundCommand"/> object</item>
188        /// </list>
189        /// </summary>
190        /// <param name="e">The <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
191        public void MouseUp(MouseEventArgs e)
192        {
193            if(IsActive)
194            {
195                DeactivateTool();
196                //creation of the total undoredo package
197                CompoundCommand package = new CompoundCommand(this.Controller);
198                string message = string.Empty;
199                //notice that the connector can only be a connection connector because of the MouseDown check above
200                if(connectorMove)
201                {
202                    #region We are moving a connection-connector
203                   
204                    #region Is the connector attached?
205                    //detach only if there is a parent different than a connection; the join of a chained connection is allowed to move
206                    if( Selection.Connector.AttachedTo != null && !typeof(IConnection).IsInstanceOfType(Selection.Connector.AttachedTo))
207                    {
208                        DetachConnectorCommand detach = new DetachConnectorCommand(this.Controller, Selection.Connector.AttachedTo, Selection.Connector);
209                        detach.Redo();
210                        package.Commands.Add(detach);
211
212                    }
213                    #endregion
214                   
215                    #region The moving part
216                    //a bundle might look like overkill here but it makes the coding model uniform
217                    Bundle bundle = new Bundle(Controller.Model);
218                    bundle.Entities.Add(Selection.Connector);
219                    MoveCommand move = new MoveCommand(this.Controller, bundle, new Point(lastPoint.X - initialPoint.X, lastPoint.Y - initialPoint.Y));
220                    //no Redo() necessary here!
221                    package.Commands.Add(move);
222                    #endregion
223
224                    #region The re-attachment near another connector
225                    //let's see if the connection endpoints hit other connectors (different than the selected one!)   
226                    //define a predicate delegate to filter things out otherwise the hit will return the moved connector which would results
227                    //in a stack overflow later on
228                    Predicate<IConnector> predicate =
229                        delegate(IConnector conn)
230                        {
231                            //whatever, except itself and any children of the moved connector
232                            //since this would entail a child becoming a parent!
233                            if(conn.Hit(e.Location) && conn != Selection.Connector && !Selection.Connector.AttachedConnectors.Contains(conn))
234                                return true;
235                            return false;
236                        };
237                    //find it!
238                    IConnector parentConnector = Selection.FindConnector(predicate);
239
240                    if(parentConnector != null) //aha, there's an attachment
241                    {                       
242                        BindConnectorsCommand binder = new BindConnectorsCommand(this.Controller, parentConnector, Selection.Connector);
243                        package.Commands.Add(binder);
244                        binder.Redo(); //this one is necessary since the redo cannot be performed on the whole compound command
245                    }
246                    #endregion
247     
248                    message = "Connector move";
249                    #endregion
250                }
251                else
252                {
253                    #region We are moving entities other than a connector
254                    Bundle bundle = new Bundle(Controller.Model);
255                    bundle.Entities.AddRange(Selection.SelectedItems);
256                    MoveCommand cmd = new MoveCommand(this.Controller, bundle, new Point(lastPoint.X - initialPoint.X, lastPoint.Y - initialPoint.Y));
257                    package.Commands.Add(cmd);
258                    //not necessary to perform the Redo action of the command since the mouse-move already moved the bundle!
259                    #endregion
260
261                    message = "Entities move";
262                }
263                //reset the hovered connector, if any
264                if (hoveredConnector != null)
265                    hoveredConnector.Hovered = false;
266                package.Text = message;
267                //whatever the content of the package we add it to the undo history               
268                this.Controller.UndoManager.AddUndoCommand(package);
269
270                //show the tracker again
271                this.Controller.View.ShowTracker();
272
273            }
274        }
275        #endregion
276    }
277
278}
Note: See TracBrowser for help on using the repository browser.