11
11
using Microsoft . Msagl . Drawing ;
12
12
using Microsoft . Msagl . WpfGraphControl ;
13
13
using Color = Microsoft . Msagl . Drawing . Color ;
14
+ using Node = Microsoft . Msagl . Drawing . Node ;
14
15
15
16
namespace CSharpCodeAnalyst . GraphArea ;
16
17
@@ -20,12 +21,13 @@ namespace CSharpCodeAnalyst.GraphArea;
20
21
/// Dependencies of the same type (i.e a method Calls another multiple times) are handled
21
22
/// in the parser. In this case the dependency holds all source references.
22
23
/// </summary>
23
- internal class DependencyGraphViewer : IDependencyGraphViewer , IDependencyGraphBinding , INotifyPropertyChanged
24
+ internal partial class DependencyGraphViewer : IDependencyGraphViewer , IDependencyGraphBinding , INotifyPropertyChanged
24
25
{
25
26
private readonly List < IContextCommand > _contextCommands = [ ] ;
26
27
private readonly MsaglBuilder _msaglBuilder ;
27
28
private readonly IPublisher _publisher ;
28
- private readonly LinkedList < CodeGraph > _undoStack = new ( ) ;
29
+
30
+ private readonly LinkedList < UndoState > _undoStack = new ( ) ;
29
31
30
32
private readonly int _undoStackSize = 10 ;
31
33
@@ -45,6 +47,7 @@ internal class DependencyGraphViewer : IDependencyGraphViewer, IDependencyGraphB
45
47
private IViewerEdge ? _lastHighlightedEdge ;
46
48
47
49
private GraphViewer ? _msaglViewer ;
50
+ private PresentationState _presentationState = new ( ) ;
48
51
49
52
private RenderOption _renderOption = new DefaultRenderOptions ( ) ;
50
53
private bool _showFlatGraph ;
@@ -77,20 +80,13 @@ public void ShowFlatGraph(bool value)
77
80
RefreshGraph ( ) ;
78
81
}
79
82
80
- /// <summary>
81
- /// Adding an existing element or dependency is prevented.
82
- /// Note from the originalCodeElement we don't add parent or children.
83
- /// We just use this information to integrate the node into the existing canvas.
84
- /// </summary>
85
- public void AddToGraph ( IEnumerable < CodeElement > originalCodeElements , IEnumerable < Dependency > newDependencies )
83
+ private void AddToGraphInternal ( IEnumerable < CodeElement > originalCodeElements , IEnumerable < Dependency > newDependencies )
86
84
{
87
85
if ( _msaglViewer is null )
88
86
{
89
87
return ;
90
88
}
91
89
92
- PushUndo ( ) ;
93
-
94
90
IntegrateNewFromOriginal ( originalCodeElements ) ;
95
91
96
92
// Add dependencies we explicitly requested.
@@ -104,25 +100,56 @@ public void AddToGraph(IEnumerable<CodeElement> originalCodeElements, IEnumerabl
104
100
RefreshGraph ( ) ;
105
101
}
106
102
103
+ /// <summary>
104
+ /// Adding an existing element or dependency is prevented.
105
+ /// Note from the originalCodeElement we don't add parent or children.
106
+ /// We just use this information to integrate the node into the existing canvas.
107
+ /// </summary>
108
+ public void AddToGraph ( IEnumerable < CodeElement > originalCodeElements , IEnumerable < Dependency > newDependencies )
109
+ {
110
+ if ( _msaglViewer is null )
111
+ {
112
+ return ;
113
+ }
114
+
115
+ PushUndo ( ) ;
116
+ AddToGraphInternal ( originalCodeElements , newDependencies ) ;
117
+ }
118
+
107
119
public void AddContextCommand ( IContextCommand command )
108
120
{
109
121
_contextCommands . Add ( command ) ;
110
122
}
111
123
112
- public void Clear ( )
124
+ private void Clear ( bool withUndoStack )
113
125
{
114
126
if ( _msaglViewer is null )
115
127
{
116
128
return ;
117
129
}
118
130
119
131
_clonedCodeGraph = new CodeGraph ( ) ;
120
- _undoStack . Clear ( ) ;
132
+
133
+ if ( withUndoStack )
134
+ {
135
+ ClearUndo ( ) ;
136
+ }
137
+
138
+ // Nothing collapsed by default
139
+ _presentationState = new PresentationState ( ) ;
121
140
RefreshGraph ( ) ;
122
141
}
142
+ public void Clear ( )
143
+ {
144
+ Clear ( true ) ;
145
+ }
123
146
147
+ private void ClearUndo ( )
148
+ {
149
+ _undoStack . Clear ( ) ;
150
+ }
124
151
125
- public void Reset ( )
152
+ public void Layout ( )
126
153
{
127
154
//_msaglViewer?.SetInitialTransform();
128
155
RefreshGraph ( ) ;
@@ -187,19 +214,36 @@ public void ShowGlobalContextMenu()
187
214
globalContextMenu . IsOpen = true ;
188
215
}
189
216
217
+
218
+
190
219
public bool Undo ( )
191
220
{
192
221
if ( _undoStack . Any ( ) is false )
193
222
{
194
223
return false ;
195
224
}
196
225
197
- _clonedCodeGraph = _undoStack . First ( ) ;
226
+ var state = _undoStack . First ( ) ;
198
227
_undoStack . RemoveFirst ( ) ;
228
+
229
+ _clonedCodeGraph = state . CodeGraph ;
230
+ _presentationState = state . PresentationState ;
231
+
199
232
RefreshGraph ( ) ;
200
233
return true ;
201
234
}
202
235
236
+ public void ImportCycleGroup ( List < CodeElement > codeElements , List < Dependency > dependencies )
237
+ {
238
+ PushUndo ( ) ;
239
+ Clear ( false ) ;
240
+
241
+ // Everything is collapsed by default. This allows to import large graphs.
242
+ var defaultState = codeElements . Where ( c => c . Children . Any ( ) ) . ToDictionary ( c => c . Id , c => true ) ;
243
+ _presentationState = new PresentationState ( defaultState ) ;
244
+ AddToGraphInternal ( codeElements , dependencies ) ;
245
+ }
246
+
203
247
204
248
public event PropertyChangedEventHandler ? PropertyChanged ;
205
249
@@ -211,7 +255,8 @@ private void PushUndo()
211
255
_undoStack . RemoveLast ( ) ;
212
256
}
213
257
214
- _undoStack . AddFirst ( _clonedCodeGraph . Clone ( null , null ) ) ;
258
+ var state = new UndoState ( _clonedCodeGraph . Clone ( null , null ) , _presentationState . Clone ( ) ) ;
259
+ _undoStack . AddFirst ( state ) ;
215
260
}
216
261
217
262
private void ClearEdgeColoring ( )
@@ -264,7 +309,8 @@ private void RefreshGraph()
264
309
{
265
310
if ( _msaglViewer != null )
266
311
{
267
- var graph = _msaglBuilder . CreateGraphFromCodeStructure ( _clonedCodeGraph , _showFlatGraph ) ;
312
+ var graph = _msaglBuilder . CreateGraphFromCodeStructure ( _clonedCodeGraph , _presentationState ,
313
+ _showFlatGraph ) ;
268
314
269
315
_renderOption . Apply ( graph ) ;
270
316
_msaglViewer . Graph = graph ;
@@ -400,18 +446,38 @@ bool IsCtrlPressed()
400
446
var node = clickedObject . Node ;
401
447
var contextMenu = new ContextMenu ( ) ;
402
448
403
- var addParentMenuItem = new MenuItem { Header = "Add parent" } ;
404
- addParentMenuItem . Click += ( _ , _ ) => AddParentRequest ( node ) ;
449
+ MenuItem item = null ;
450
+ if ( node . UserData is CodeElement codeElement )
451
+ {
452
+ if ( _presentationState . IsCollapsed ( codeElement . Id ) &&
453
+ codeElement . Children . Any ( ) )
454
+ {
455
+ item = new MenuItem { Header = "Expand" } ;
456
+ item . Click += ( _ , _ ) => Expand ( codeElement . Id ) ;
457
+ contextMenu . Items . Add ( item ) ;
458
+ }
405
459
406
- var findInTreeMenuItem = new MenuItem { Header = "Find in Tree" } ;
407
- findInTreeMenuItem . Click += ( _ , _ ) => FindInTree ( node ) ;
460
+ if ( ! _presentationState . IsCollapsed ( codeElement . Id ) &&
461
+ codeElement . Children . Any ( ) )
462
+ {
463
+ item = new MenuItem { Header = "Collapse" } ;
464
+ item . Click += ( _ , _ ) => Collapse ( codeElement . Id ) ;
465
+ contextMenu . Items . Add ( item ) ;
466
+ }
467
+ }
468
+
469
+ item = new MenuItem { Header = "Delete Node" } ;
470
+ item . Click += ( _ , _ ) => DeleteNode ( node ) ;
471
+ contextMenu . Items . Add ( item ) ;
472
+
473
+ item = new MenuItem { Header = "Find in Tree" } ;
474
+ item . Click += ( _ , _ ) => FindInTree ( node ) ;
475
+ contextMenu . Items . Add ( item ) ;
408
476
409
- var deleteMenuItem = new MenuItem { Header = "Delete Node" } ;
410
- deleteMenuItem . Click += ( _ , _ ) => DeleteNode ( node ) ;
477
+ item = new MenuItem { Header = "Add parent" } ;
478
+ item . Click += ( _ , _ ) => AddParentRequest ( node ) ;
479
+ contextMenu . Items . Add ( item ) ;
411
480
412
- contextMenu . Items . Add ( deleteMenuItem ) ;
413
- contextMenu . Items . Add ( findInTreeMenuItem ) ;
414
- contextMenu . Items . Add ( addParentMenuItem ) ;
415
481
contextMenu . Items . Add ( new Separator ( ) ) ;
416
482
var lastItemIsSeparator = true ;
417
483
@@ -451,6 +517,21 @@ bool IsCtrlPressed()
451
517
}
452
518
}
453
519
520
+ private void Collapse ( string id )
521
+ {
522
+ PushUndo ( ) ;
523
+ _presentationState . SetCollapsedState ( id , true ) ;
524
+ RefreshGraph ( ) ;
525
+ }
526
+
527
+ private void Expand ( string id )
528
+ {
529
+ PushUndo ( ) ;
530
+ _presentationState . SetCollapsedState ( id , false ) ;
531
+
532
+ RefreshGraph ( ) ;
533
+ }
534
+
454
535
private void DeleteAllMarkedElements ( )
455
536
{
456
537
if ( _msaglViewer is null )
0 commit comments