79f00625ea98d2c607da4c5113356bb08c1e1b89
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / FreeFormEditing / ConnectorEditor.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Activities.Presentation.FreeFormEditing
6 {
7     using System;
8     using System.Activities.Presentation;
9     using System.Collections.Generic;
10     using System.Diagnostics;
11     using System.Windows;
12     using System.Windows.Controls;
13     using System.Windows.Documents;
14     using System.Windows.Input;
15     using System.Runtime;
16     using System.Diagnostics.CodeAnalysis;
17     using System.Windows.Media;
18     using System.Activities.Presentation.Internal.PropertyEditing;
19
20     internal class ConnectorEditor
21     {
22         const double EditPointRadius = 4;
23         const double EditPointHitTestRadius = 9;
24         const int minLengthForSegmentEditPoint = 10;
25         EditPoint activeEditPoint;
26         AdornerLayer adornerLayer;
27         Connector editedConnector;
28         List<EditPoint> editPoints;
29         FreeFormPanel parentPanel;
30
31         public ConnectorEditor(FreeFormPanel panel, Connector connector)
32         {
33             if (panel == null)
34             {
35                 throw FxTrace.Exception.AsError(new ArgumentNullException("panel"));
36             }
37             if (connector == null)
38             {
39                 throw FxTrace.Exception.AsError(new ArgumentNullException("connector"));
40             }
41             this.editPoints = new List<EditPoint>();
42             this.parentPanel = panel;
43             this.editedConnector = connector;
44             this.activeEditPoint = null;
45             connector.IsSelected = true;
46             // When the ConnectorEditor is active, we allow reconnecting the start point of the Connector instead
47             // of creating a new transition that shares the same trigger. So we need to disable tooltips and 
48             // highlighting effects for all overlapping start dots.
49             this.SetIsHitTestVisibleForOverlappingStartDots(false);
50             DisplayEditPoints();
51         }
52         public bool BeingEdited
53         {
54             get
55             {
56                 return (this.activeEditPoint != null);
57             }
58         }
59
60         public bool IsConnectorStartBeingMoved
61         {
62             get
63             {
64                 return (this.BeingEdited && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint
65                     && this.editedConnector.Points[0] != this.EditPoints[0].Location);
66             }
67         }
68
69         public bool IsConnectorEndBeingMoved
70         {
71             get
72             {
73                 return (this.BeingEdited && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint
74                     && this.editedConnector.Points[this.editedConnector.Points.Count - 1] != this.EditPoints[this.EditPoints.Count - 1].Location);
75             }
76         }
77
78         public Connector Connector
79         {
80             get
81             {
82                 return this.editedConnector;
83             }
84
85             set
86             {
87                 this.editedConnector = value;
88             }
89         }
90
91         List<EditPoint> EditPoints
92         {
93             get
94             {
95                 return this.editPoints;
96             }
97         }
98
99         public List<Point> ConnectorEditorLocation
100         {
101             get
102             {
103                 return this.GetPointsFromEditPoints();
104             }
105         }
106
107         void SetIsHitTestVisibleForOverlappingStartDots(bool hitTestVisible)
108         {
109             ConnectionPoint srcConnectionPoint = FreeFormPanel.GetSourceConnectionPoint(this.Connector);
110             foreach (Connector overlappingConnector in srcConnectionPoint.AttachedConnectors)
111             {
112                 if (overlappingConnector.StartDot != null)
113                 {
114                     overlappingConnector.StartDot.IsHitTestVisible = hitTestVisible;
115                 }
116             }
117         }
118
119         //If the result is true this method also sets the currently active edit point.
120         public bool EditPointsHitTest(Point pt)
121         {
122             if (this.EditPoints.Count > 0)
123             {
124                 foreach (EditPoint editPoint in this.EditPoints)
125                 {
126                     if (DesignerGeometryHelper.DistanceBetweenPoints(pt, editPoint.Location) <= EditPointHitTestRadius)
127                     {
128                         this.activeEditPoint = editPoint;
129                         return true;
130                     }
131                 }
132             }
133             return false;
134         }
135
136         //Connector editing is completed. This function saves the state of the connectorEditor into the corresponding connector.
137         //Returns whether the Editor was persisted or not. It might not be persisted if Connector end points do not lie on a designer.
138         public bool Persist(Point finalSnappedPoint)
139         {
140             List<Point> segments = new List<Point>();
141             this.Update(finalSnappedPoint);
142             if (this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
143             {
144                 return false;
145             }
146             segments = this.GetPointsFromEditPoints();
147             this.parentPanel.UpdateConnectorPoints(Connector, segments);
148             this.activeEditPoint = null;
149             RemoveAdorners();
150             DisplayEditPoints();
151             return true;
152         }
153
154         //The Connector editor is to be destroyed. Remove the adorners on the editor. activeEditPoint=null sets BeingEdited property to false.
155         public void Remove()
156         {
157             this.activeEditPoint = null;
158             RemoveAdorners();
159             this.EditPoints.Clear();
160             this.Connector.IsSelected = false;
161             // Restore the IsHitTestVisible property
162             this.SetIsHitTestVisibleForOverlappingStartDots(true);
163             this.Connector = null;
164             this.parentPanel = null;
165         }
166
167         //This method removes the existing adorner on the edited connector, updates the active edit points and creates new adorners.
168         public void Update(Point newPoint)
169         {
170             RemoveAdorners();
171             UpdateEditPoints(newPoint);
172             Fx.Assert(this.activeEditPoint != null, "activeEditPoint is null");
173             adornerLayer.Add(new EditPointAdorner(this, editedConnector, true));
174         }
175
176         //Add edit points of specified type
177         void AddEditPoints(EditPoint.EditPointTypes editPointType)
178         {
179             if (editPointType == EditPoint.EditPointTypes.ConnectionEditPoint)
180             {
181                 if (this.editPoints.Count == 0 || !this.editPoints[0].Location.Equals(editedConnector.Points[0]))
182                 {
183                     this.editPoints.Insert(0, new EditPoint(EditPoint.EditPointTypes.ConnectionEditPoint, editedConnector.Points[0]));
184                 }
185
186                 if (this.editPoints.Count < 2 || !this.editPoints[this.editPoints.Count - 1].Equals(editedConnector.Points[editedConnector.Points.Count - 1]))
187                 {
188                     editPoints.Add(new EditPoint(EditPoint.EditPointTypes.ConnectionEditPoint, editedConnector.Points[editedConnector.Points.Count - 1]));
189                 }
190             }
191             else if (editPointType == EditPoint.EditPointTypes.MultiSegmentEditPoint)
192             {
193                 if (this.editPoints.Count == 2)
194                 {
195                     List<Point> segments = new List<Point>(this.editedConnector.Points);
196                     if (segments.Count > 0)
197                     {
198                         segments.RemoveAt(0);
199                         segments.RemoveAt(segments.Count - 1);
200                     }
201
202                     for (int i = 0; i < segments.Count; i++)
203                     {
204                         this.editPoints.Insert(this.editPoints.Count - 1, new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, segments[i]));
205                     }
206                 }
207                 else
208                 {
209                     Fx.Assert(false, "EditPoints.Count is not 2.");
210                 }
211             }
212         }
213
214         void CreateEditPoints()
215         {
216             this.editPoints.Clear();
217
218             AddEditPoints(EditPoint.EditPointTypes.ConnectionEditPoint);
219             AddEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
220
221             bool validEditPoints = ValidateEditPoints();
222             Fx.Assert(validEditPoints, "Validating EditPoints failed.");
223         }
224
225         void DisplayEditPoints()
226         {
227             CreateEditPoints();
228             adornerLayer = AdornerLayer.GetAdornerLayer(editedConnector);
229             if (adornerLayer != null)
230             {
231                 adornerLayer.Add(new EditPointAdorner(this, editedConnector, false));
232             }
233         }
234
235         List<Point> GetPointsFromEditPoints()
236         {
237             List<Point> segments = new List<Point>();
238             //Connection end points will never be moved/removed in following two function calls. Hence passing null as pointsToRetain.
239             RemoveEditPointSegmentsWithinTolerance(null);
240             RemoveCoincidingEditPoints(null);
241             for (int i = 0; i < this.EditPoints.Count; i++)
242             {
243                 segments.Add(this.EditPoints[i].Location);
244             }
245             return segments;
246         }
247
248         void RemoveAdorners()
249         {
250             if (adornerLayer != null && editedConnector != null)
251             {
252                 Adorner[] adorners = adornerLayer.GetAdorners(editedConnector);
253                 if (adorners != null)
254                 {
255                     foreach (Adorner adorner in adorners)
256                     {
257                         adornerLayer.Remove(adorner);
258                     }
259                 }
260             }
261         }
262
263         //Remove points with the same slope
264         void RemoveCoincidingEditPoints()
265         {
266             if (this.editPoints.Count < 2 ||
267                 this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
268                 this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
269                 (this.activeEditPoint != null && this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint))
270             {
271                 return;
272             }
273
274             //Create list of points to retain
275             List<EditPoint> editPointsToRetain = new List<EditPoint>(this.editPoints.Count);
276             for (int i = 0; i < this.editPoints.Count; i++)
277             {
278                 if (this.editPoints[i].Type != EditPoint.EditPointTypes.MultiSegmentEditPoint ||
279                     this.editPoints[i] == this.activeEditPoint)
280                 {
281                     editPointsToRetain.Add(this.editPoints[i]);
282                 }
283             }
284
285             //Step1: Get rid of all the line segments which are within tolerance range
286             RemoveEditPointSegmentsWithinTolerance(editPointsToRetain);
287
288             //Step2: We should make sure that the active edit point is always retained but those points which are coincidental are always removed
289             RemoveCoincidingEditPoints(editPointsToRetain);
290
291             //Step3: Go through each segment and ensure that all the segments are either vertical or horizontal
292             for (int i = 0; i < this.editPoints.Count - 1; i++)
293             {
294                 EditPoint current = this.editPoints[i];
295                 EditPoint next = this.editPoints[i + 1];
296
297                 double slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
298                 if (slope != 0 && slope != double.MaxValue)
299                 {
300                     Point location = (slope < 1) ? new Point(next.Location.X, current.Location.Y) : new Point(current.Location.X, next.Location.Y);
301                     this.editPoints.Insert(i + 1, new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, location));
302                 }
303             }
304         }
305
306         void RemoveEditPointSegmentsWithinTolerance(List<EditPoint> pointsToRetain)
307         {
308             for (int i = 1; i < this.editPoints.Count - 1; i++)
309             {
310                 EditPoint previous = this.editPoints[i - 1];
311                 EditPoint current = this.editPoints[i];
312                 EditPoint next = this.editPoints[i + 1];
313
314                 if (pointsToRetain == null || !pointsToRetain.Contains(current))
315                 {
316                     double distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { previous.Location, current.Location });
317                     if (distance < ConnectorEditor.EditPointRadius && next.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
318                     {
319                         double slope = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
320                         next.Location = (slope < 1) ? new Point(next.Location.X, previous.Location.Y) : new Point(previous.Location.X, next.Location.Y);
321                         this.editPoints.Remove(current);
322                         i -= 1;
323                     }
324                     else
325                     {
326                         distance = DesignerGeometryHelper.DistanceOfLineSegments(new Point[] { current.Location, next.Location });
327                         if (distance < ConnectorEditor.EditPointRadius && previous.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
328                         {
329                             double slope = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location);
330                             previous.Location = (slope < 1) ? new Point(previous.Location.X, next.Location.Y) : new Point(next.Location.X, previous.Location.Y);
331                             this.editPoints.Remove(current);
332                             i--;
333                         }
334                     }
335                 }
336             }
337
338         }
339
340         void RemoveCoincidingEditPoints(List<EditPoint> pointsToRetain)
341         {
342             for (int i = 1; i < this.EditPoints.Count - 1; i++)
343             {
344                 EditPoint current = this.EditPoints[i];
345                 if (pointsToRetain == null || !pointsToRetain.Contains(current))
346                 {
347                     EditPoint previous = this.EditPoints[i - 1];
348                     EditPoint next = this.EditPoints[i + 1];
349                     double slope1 = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, current.Location);
350                     double slope2 = DesignerGeometryHelper.SlopeOfLineSegment(current.Location, next.Location);
351                     if (Math.Abs(slope1) == Math.Abs(slope2))
352                     {
353                         this.EditPoints.Remove(current);
354                         i -= 1;
355                     }
356                 }
357             }
358         }
359
360         //Remove edit points of specified type
361         //This method does not remove this.activeEditPoint.
362         void RemoveEditPoints(EditPoint.EditPointTypes editPointType)
363         {
364             List<EditPoint> editPointsToRemove = new List<EditPoint>();
365             for (int i = 0; i < this.editPoints.Count; i++)
366             {
367                 EditPoint editPoint = this.editPoints[i];
368                 if (editPoint.Type == editPointType)
369                 {
370                     editPointsToRemove.Add(editPoint);
371                 }
372             }
373
374             for (int i = 0; i < editPointsToRemove.Count; i++)
375             {
376                 EditPoint editPoint = editPointsToRemove[i];
377                 if (editPoint != this.activeEditPoint)
378                 {
379                     this.editPoints.Remove(editPoint);
380                 }
381             }
382         }
383
384         [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "This is a legacy algorithm.")]
385         void UpdateEditPoints(Point newPoint)
386         {
387             if (this.editPoints.Count < 2 ||
388                 this.editPoints[0].Type != EditPoint.EditPointTypes.ConnectionEditPoint ||
389                 this.editPoints[this.editPoints.Count - 1].Type != EditPoint.EditPointTypes.ConnectionEditPoint)
390             {
391                 Fx.Assert(false, "EditPoints are invalid");
392                 return;
393             }
394
395             if (this.activeEditPoint != null)
396             {
397                 int activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
398                 EditPoint previous = (activeEditPointIndex > 0) ? this.editPoints[activeEditPointIndex - 1] : null;
399                 EditPoint next = (activeEditPointIndex < this.editPoints.Count - 1) ? this.editPoints[activeEditPointIndex + 1] : null;
400
401                 //Note that extra edit points are only added if we are connected to connection point
402                 if (previous != null && previous.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
403                 {
404                     double slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location);
405                     Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
406
407                     int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints(previous.Location, (next != null) ? next.Location : this.activeEditPoint.Location)) / 4;
408                     if (orientation == Orientation.Horizontal)
409                     {
410                         editPointOffset *= (previous.Location.X < this.activeEditPoint.Location.X) ? 1 : -1;
411                     }
412                     else
413                     {
414                         editPointOffset *= (previous.Location.Y < this.activeEditPoint.Location.Y) ? 1 : -1;
415                     }
416
417                     activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
418                     Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X + editPointOffset, previous.Location.Y) : new Point(previous.Location.X, previous.Location.Y + editPointOffset);
419                     previous = new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
420                     this.editPoints.InsertRange(activeEditPointIndex, new EditPoint[] { new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation), previous });
421                 }
422
423                 if (next != null && next.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
424                 {
425                     double slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
426                     Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
427
428                     int editPointOffset = Convert.ToInt32(DesignerGeometryHelper.DistanceBetweenPoints((previous != null) ? previous.Location : this.activeEditPoint.Location, next.Location)) / 4;
429                     if (orientation == Orientation.Horizontal)
430                     {
431                         editPointOffset *= (this.activeEditPoint.Location.X < next.Location.X) ? -1 : 1;
432                     }
433                     else
434                     {
435                         editPointOffset *= (this.activeEditPoint.Location.Y < next.Location.Y) ? -1 : 1;
436                     }
437
438                     activeEditPointIndex = this.editPoints.IndexOf(this.activeEditPoint);
439                     Point editPointLocation = (orientation == Orientation.Horizontal) ? new Point(next.Location.X + editPointOffset, next.Location.Y) : new Point(next.Location.X, next.Location.Y + editPointOffset);
440                     next = new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation);
441                     this.editPoints.InsertRange(activeEditPointIndex + 1, new EditPoint[] { next, new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, editPointLocation) });
442                 }
443
444                 if (this.activeEditPoint.Type == EditPoint.EditPointTypes.ConnectionEditPoint)
445                 {
446                     Fx.Assert(this.editPoints[0].Type == EditPoint.EditPointTypes.ConnectionEditPoint, "EditPoint type is wrong.");
447                     Fx.Assert(this.editPoints[editPoints.Count - 1].Type == EditPoint.EditPointTypes.ConnectionEditPoint, "EditPoint type is wrong.");
448                     this.activeEditPoint.Location = newPoint;
449
450                     Fx.Assert(this.editPoints.Count > 0, "Some edit point should exist");
451                     ConnectionPoint targetConnPt = null;
452                     Point[] points = null;
453                     Point begin = this.editPoints[0].Location;
454                     Point end = this.editPoints[this.editPoints.Count - 1].Location;
455
456                     if (typeof(ConnectionPointsAdorner).IsAssignableFrom(Mouse.DirectlyOver.GetType()))
457                     {
458                         ConnectionPointsAdorner connPtsAdorner = Mouse.DirectlyOver as ConnectionPointsAdorner;
459                         targetConnPt = FreeFormPanel.ConnectionPointHitTest(newPoint, connPtsAdorner);
460                     }
461
462                     if (activeEditPointIndex == 0)
463                     {
464                         // We are dragging the source point of a connector.
465                         ConnectionPoint destConnPt = FreeFormPanel.GetDestinationConnectionPoint(this.editedConnector);
466                         if (targetConnPt != null)
467                         {
468                             points = ConnectorRouter.Route(parentPanel, targetConnPt, destConnPt);
469                             this.activeEditPoint.Location = targetConnPt.Location;
470                         }
471                         else
472                         {
473                             points = ConnectorRouter.Route(parentPanel, begin, destConnPt);
474                         }
475                     }
476                     else
477                     {
478                         // We are dragging the destination point of a connector.
479                         ConnectionPoint srcConnPt = FreeFormPanel.GetSourceConnectionPoint(this.editedConnector);
480                         if (targetConnPt != null)
481                         {
482                             points = ConnectorRouter.Route(parentPanel, srcConnPt, targetConnPt);
483                             this.activeEditPoint.Location = targetConnPt.Location;
484                         }
485                         else
486                         {
487                             points = ConnectorRouter.Route(parentPanel, srcConnPt, end);
488                         }
489                     }
490
491                     //When we start editing the end point we need to clear the slate and start over
492                     List<EditPoint> newEditPoints = new List<EditPoint>();
493                     if (points != null && points.Length > 1)
494                     {
495                         RemoveEditPoints(EditPoint.EditPointTypes.MultiSegmentEditPoint);
496                         for (int i = 1; i < points.Length - 1; ++i)
497                         {
498                             newEditPoints.Add(new EditPoint(EditPoint.EditPointTypes.MultiSegmentEditPoint, points[i]));
499                         }
500                         this.editPoints.InsertRange(1, newEditPoints.ToArray());
501                     }
502                 }
503                 else if (this.activeEditPoint.Type == EditPoint.EditPointTypes.MultiSegmentEditPoint)
504                 {
505                     if (previous != null && previous.Type != EditPoint.EditPointTypes.ConnectionEditPoint && next != null && next.Type != EditPoint.EditPointTypes.ConnectionEditPoint)
506                     {
507                         //Update the previous point
508                         double slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(previous.Location, this.activeEditPoint.Location);
509                         Orientation orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
510                         previous.Location = (orientation == Orientation.Horizontal) ? new Point(previous.Location.X, newPoint.Y) : new Point(newPoint.X, previous.Location.Y);
511
512                         //Update the next point
513                         slopeOfLine = DesignerGeometryHelper.SlopeOfLineSegment(this.activeEditPoint.Location, next.Location);
514                         orientation = (Math.Abs(slopeOfLine) < 1) ? Orientation.Horizontal : Orientation.Vertical;
515                         next.Location = (orientation == Orientation.Horizontal) ? new Point(next.Location.X, newPoint.Y) : new Point(newPoint.X, next.Location.Y);
516
517                         //Update the current point
518                         this.activeEditPoint.Location = newPoint;
519                     }
520                     else
521                     {
522                         Fx.Assert(false, "Should not be here. UpdateEditPoints failed.");
523                     }
524                 }
525             }
526
527             // Remove all the redundant edit points
528             RemoveCoincidingEditPoints();
529
530             bool validEditPoints = ValidateEditPoints();
531             Fx.Assert(validEditPoints, "Validating EditPoints failed.");
532         }
533
534         bool ValidateEditPoints()
535         {
536             if (this.editPoints.Count < 2)
537             {
538                 return false;
539             }
540
541             return true;
542         }
543
544         class EditPoint
545         {
546             EditPointTypes editPointType;
547             Point point;
548
549             public EditPoint(EditPointTypes editPointType, Point point)
550             {
551                 this.editPointType = editPointType;
552                 this.point = point;
553             }
554
555             public Point Location
556             {
557                 get
558                 {
559                     return this.point;
560                 }
561
562                 set
563                 {
564                     this.point = value;
565                 }
566             }
567
568             public EditPointTypes Type
569             {
570                 get
571                 {
572                     return this.editPointType;
573                 }
574             }
575
576             public enum EditPointTypes
577             {
578                 ConnectionEditPoint = 1, MultiSegmentEditPoint
579             }
580         }
581
582         sealed class EditPointAdorner : Adorner
583         {
584             ConnectorEditor adornedEditor;
585             bool drawLines;
586
587             public EditPointAdorner(ConnectorEditor cEditor, UIElement adornedElement, bool shouldDrawLines)
588                 : base(adornedElement)
589             {
590                 Fx.Assert(adornedElement != null, "Adorned element is null.");
591                 adornedEditor = cEditor;
592                 this.IsHitTestVisible = false;
593                 this.drawLines = shouldDrawLines;
594             }
595
596             protected override void OnRender(DrawingContext drawingContext)
597             {
598                 if (drawingContext != null)
599                 {
600                     int i = 0;
601                     SolidColorBrush renderBrush = new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementSelectedBackgroundColor);
602                     renderBrush.Opacity = FreeFormPanel.ConnectorEditorOpacity;
603                     Pen renderPen = new Pen(new SolidColorBrush(WorkflowDesignerColors.WorkflowViewElementSelectedBorderColor), FreeFormPanel.ConnectorEditorThickness);
604                     double renderRadius = ConnectorEditor.EditPointRadius;
605                     for (i = 0; i < adornedEditor.EditPoints.Count - 1; i++)
606                     {
607                         drawingContext.DrawEllipse(renderBrush, renderPen, adornedEditor.EditPoints[i].Location, renderRadius, renderRadius);
608                         if (drawLines)
609                         {
610                             drawingContext.DrawLine(renderPen, adornedEditor.EditPoints[i].Location, adornedEditor.EditPoints[i + 1].Location);
611                         }
612                     }
613                     drawingContext.DrawEllipse(renderBrush, renderPen, adornedEditor.EditPoints[i].Location, renderRadius, renderRadius);
614                 }
615                 base.OnRender(drawingContext);
616                 Keyboard.Focus(adornedEditor.Connector);
617             }
618         }
619     }
620 }