Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
02/26/10 13:57:47 (15 years ago)
Author:
mkommend
Message:

corrected handling of connections (ticket #867)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Tools/MoveTool.cs

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