Skip to content

Commit ef82446

Browse files
committed
Added shortest path highlighing, delete with children and focus on elements
1 parent 0ea3171 commit ef82446

14 files changed

+445
-160
lines changed

CSharpCodeAnalyst/App.xaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ protected override void OnStartup(StartupEventArgs e)
3939

4040
mainWindow.SetViewer(explorationGraphViewer);
4141
var viewModel = new MainViewModel(messaging, settings);
42-
var graphViewModel = new GraphViewModel(explorationGraphViewer, explorer);
42+
var graphViewModel = new GraphViewModel(explorationGraphViewer, explorer, settings);
4343
var treeViewModel = new TreeViewModel(messaging);
4444
var cycleViewModel = new CycleSummaryViewModel();
4545
viewModel.GraphViewModel = graphViewModel;

CSharpCodeAnalyst/CSharpCodeAnalyst.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<Resource Include="Resources\document-xml_32.png" />
3737
<Resource Include="Resources\four-arrows.png" />
3838
<Resource Include="Resources\four-arrows_32.png" />
39-
<Resource Include="Resources\gear_32.png" />
39+
<Resource Include="Resources\gear_32.png" />
4040
<Resource Include="Resources\import_solution.png" />
4141
<Resource Include="Resources\info_large.png" />
4242
<Resource Include="Resources\lamp.png" />

CSharpCodeAnalyst/GraphArea/DependencyGraphViewer.cs

Lines changed: 103 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
using System.ComponentModel;
2-
using System.IO;
3-
using System.Runtime.CompilerServices;
4-
using System.Windows.Controls;
5-
using System.Windows.Input;
6-
using CodeParser.Extensions;
1+
using CodeParser.Extensions;
72
using Contracts.Graph;
83
using CSharpCodeAnalyst.Common;
4+
using CSharpCodeAnalyst.GraphArea.Highlighig;
95
using CSharpCodeAnalyst.GraphArea.RenderOptions;
106
using CSharpCodeAnalyst.Help;
117
using Microsoft.Msagl.Drawing;
128
using Microsoft.Msagl.WpfGraphControl;
13-
using Color = Microsoft.Msagl.Drawing.Color;
9+
using System.ComponentModel;
10+
using System.IO;
11+
using System.Runtime.CompilerServices;
12+
using System.Windows.Controls;
13+
using System.Windows.Input;
1414
using Node = Microsoft.Msagl.Drawing.Node;
1515

1616
namespace CSharpCodeAnalyst.GraphArea;
@@ -35,20 +35,10 @@ internal partial class DependencyGraphViewer : IDependencyGraphViewer, IDependen
3535
/// Held to read the help
3636
/// </summary>
3737
private IViewerObject? _clickedObject;
38-
3938
private CodeGraph _clonedCodeGraph = new();
40-
41-
4239
private IQuickInfoFactory? _factory;
43-
44-
private HighlightMode _highlightMode;
45-
private Color _lastHighlightedColor;
46-
47-
private IViewerEdge? _lastHighlightedEdge;
48-
4940
private GraphViewer? _msaglViewer;
5041
private PresentationState _presentationState = new();
51-
5242
private RenderOption _renderOption = new DefaultRenderOptions();
5343
private bool _showFlatGraph;
5444

@@ -64,7 +54,6 @@ public DependencyGraphViewer(IPublisher publisher)
6454
_msaglBuilder = new MsaglBuilder();
6555
SetHighlightMode(HighlightMode.EdgeHovered);
6656
}
67-
6857
public void Bind(Panel graphPanel)
6958
{
7059
_msaglViewer = new GraphViewer();
@@ -96,7 +85,6 @@ private void AddToGraphInternal(IEnumerable<CodeElement> originalCodeElements, I
9685
sourceElement.Dependencies.Add(newDependency);
9786
}
9887

99-
10088
RefreshGraph();
10189
}
10290

@@ -141,9 +129,8 @@ private void Clear(bool withUndoStack)
141129
}
142130
public void Clear()
143131
{
144-
Clear(true);
132+
Clear(true);
145133
}
146-
147134
private void ClearUndo()
148135
{
149136
_undoStack.Clear();
@@ -179,11 +166,24 @@ public void SaveToSvg(FileStream stream)
179166

180167
public void SetHighlightMode(HighlightMode valueMode)
181168
{
182-
_highlightMode = valueMode;
183-
ClearEdgeColoring();
169+
_activeHighlighting?.Clear(_msaglViewer);
170+
switch (valueMode)
171+
{
172+
case HighlightMode.EdgeHovered:
173+
_activeHighlighting = new EdgeHoveredHighlighting();
174+
break;
175+
case HighlightMode.OutgoingEdgesChildrenAndSelf:
176+
_activeHighlighting = new OutgointEdgesOfChildrenAndSelfHighlighting();
177+
break;
178+
case HighlightMode.ShortestNonSelfCircuit:
179+
_activeHighlighting = new HighligtShortestNonSelfCircuit();
180+
break;
181+
default:
182+
_activeHighlighting = new EdgeHoveredHighlighting();
183+
break;
184+
}
184185
}
185186

186-
187187
public void SetQuickInfoFactory(IQuickInfoFactory factory)
188188
{
189189
_factory = factory;
@@ -207,23 +207,91 @@ public void ShowGlobalContextMenu()
207207
globalContextMenu.Items.Add(item);
208208

209209

210-
item = new MenuItem { Header = "Delete all marked elements" };
210+
item = new MenuItem { Header = "Delete marked (with children)" };
211211
item.Click += (_, _) => DeleteAllMarkedElements();
212212
globalContextMenu.Items.Add(item);
213213

214+
215+
item = new MenuItem { Header = "Focus on marked elements" };
216+
item.Click += (_, _) => FocusOnMarkedElements();
217+
globalContextMenu.Items.Add(item);
218+
214219
globalContextMenu.IsOpen = true;
215220
}
216221

222+
private void DeleteAllMarkedElements()
223+
{
224+
if (_msaglViewer is null)
225+
{
226+
return;
227+
}
228+
229+
PushUndo();
230+
231+
var ids = _msaglViewer.Entities.Where(e => e.MarkedForDragging).OfType<IViewerNode>().Select(n => n.Node.Id);
232+
233+
var idsToRemove = ids.ToHashSet();
234+
235+
// All children
236+
foreach (var id in ids)
237+
{
238+
var children = _clonedCodeGraph.Nodes[id].GetChildrenIncludingSelf();
239+
idsToRemove.UnionWith(children);
240+
}
241+
242+
_clonedCodeGraph.RemoveCodeElements(idsToRemove);
243+
_presentationState.RemoveStates(idsToRemove);
244+
245+
RefreshGraph();
246+
}
247+
248+
private void FocusOnMarkedElements()
249+
{
250+
// We want to include all children of the collapsed code elements
251+
// and keep also the presentation state. Just less information
252+
253+
if (_msaglViewer is null)
254+
{
255+
return;
256+
}
257+
258+
var ids = _msaglViewer.Entities
259+
.Where(e => e.MarkedForDragging)
260+
.OfType<IViewerNode>().Select(n => n.Node.Id);
261+
262+
if (ids.Any() is false)
263+
{
264+
return;
265+
}
266+
267+
PushUndo();
268+
var idsToKeep = ids.ToHashSet();
269+
270+
// All children
271+
foreach (var id in ids)
272+
{
273+
var children = _clonedCodeGraph.Nodes[id].GetChildrenIncludingSelf();
274+
idsToKeep.UnionWith(children);
275+
}
276+
277+
var newGraph = _clonedCodeGraph.SubGraphOf(idsToKeep);
278+
279+
// Cleanup unused states
280+
var idsToRemove = _clonedCodeGraph.Nodes.Keys.Except(idsToKeep).ToHashSet();
281+
_presentationState.RemoveStates(idsToRemove);
282+
283+
_clonedCodeGraph = newGraph;
284+
RefreshGraph();
285+
}
217286

218-
219287
public bool Undo()
220288
{
221289
if (_undoStack.Any() is false)
222290
{
223291
return false;
224292
}
225293

226-
var state = _undoStack.First();
294+
var state = _undoStack.First();
227295
_undoStack.RemoveFirst();
228296

229297
_clonedCodeGraph = state.CodeGraph;
@@ -244,7 +312,6 @@ public void ImportCycleGroup(List<CodeElement> codeElements, List<Dependency> de
244312
AddToGraphInternal(codeElements, dependencies);
245313
}
246314

247-
248315
public event PropertyChangedEventHandler? PropertyChanged;
249316

250317
private void PushUndo()
@@ -259,20 +326,6 @@ private void PushUndo()
259326
_undoStack.AddFirst(state);
260327
}
261328

262-
private void ClearEdgeColoring()
263-
{
264-
if (_msaglViewer is null)
265-
{
266-
return;
267-
}
268-
269-
var edges = _msaglViewer.Entities.OfType<IViewerEdge>();
270-
foreach (var edge in edges)
271-
{
272-
edge.Edge.Attr.Color = Color.Black;
273-
}
274-
}
275-
276329
/// <summary>
277330
/// Adds the new nodes, integrating hierarchical relationships from
278331
/// original master nodes. Parent / child connections not present in this graph are discarded.
@@ -288,7 +341,6 @@ private void IntegrateNewFromOriginal(IEnumerable<CodeElement> originalCodeEleme
288341
}
289342
}
290343

291-
292344
private void DeleteNode(Node node)
293345
{
294346
if (_msaglViewer is null)
@@ -317,24 +369,8 @@ private void RefreshGraph()
317369
}
318370
}
319371

320-
private void HighlightEdge(IViewerEdge? newEdge)
321-
{
322-
// Reset last highlighted edge
323-
if (_lastHighlightedEdge != null)
324-
{
325-
_lastHighlightedEdge.Edge.Attr.Color = _lastHighlightedColor;
326-
_msaglViewer?.Invalidate(_lastHighlightedEdge);
327-
}
328372

329-
// Highlight new edge, if any
330-
if (newEdge != null)
331-
{
332-
_lastHighlightedColor = newEdge.Edge.Attr.Color;
333-
_lastHighlightedEdge = newEdge;
334-
newEdge.Edge.Attr.Color = Color.Red;
335-
_msaglViewer?.Invalidate(newEdge);
336-
}
337-
}
373+
IHighlighting _activeHighlighting = new EdgeHoveredHighlighting();
338374

339375
private void ObjectUnderMouseCursorChanged(object? sender, ObjectUnderMouseCursorChangedEventArgs e)
340376
{
@@ -344,45 +380,7 @@ private void ObjectUnderMouseCursorChanged(object? sender, ObjectUnderMouseCurso
344380
UpdateQuickInfoPanel(e.NewObject);
345381
}
346382

347-
if (_highlightMode == HighlightMode.EdgeHovered)
348-
{
349-
HighlightEdge(e.NewObject as IViewerEdge);
350-
}
351-
352-
if (_highlightMode == HighlightMode.OutgoingEdgesChildrenAndSelf)
353-
{
354-
HighlightOutgoingEdgesOfChildrenAndSelf(e.NewObject as IViewerNode);
355-
}
356-
}
357-
358-
private void HighlightOutgoingEdgesOfChildrenAndSelf(IViewerNode? node)
359-
{
360-
if (_msaglViewer is null)
361-
{
362-
return;
363-
}
364-
365-
var ids = new HashSet<string>();
366-
if (node != null)
367-
{
368-
var id = node.Node.Id;
369-
var vertex = _clonedCodeGraph.Nodes[id];
370-
ids = vertex.GetChildrenIncludingSelf();
371-
}
372-
373-
var edges = _msaglViewer.Entities.OfType<IViewerEdge>();
374-
foreach (var edge in edges)
375-
{
376-
var sourceId = edge.Edge.Source;
377-
if (ids.Contains(sourceId))
378-
{
379-
edge.Edge.Attr.Color = Color.Red;
380-
}
381-
else
382-
{
383-
edge.Edge.Attr.Color = Color.Black;
384-
}
385-
}
383+
_activeHighlighting.Highlight(_msaglViewer, e.NewObject, _clonedCodeGraph);
386384
}
387385

388386
private void OnPropertyChanged([CallerMemberName] string? name = null)
@@ -434,7 +432,6 @@ bool IsCtrlPressed()
434432
}
435433
}
436434

437-
438435
if (e.RightButtonIsPressed)
439436
{
440437
if (_msaglViewer?.ObjectUnderMouseCursor is not IViewerNode clickedObject)
@@ -532,33 +529,24 @@ private void Expand(string id)
532529
RefreshGraph();
533530
}
534531

535-
private void DeleteAllMarkedElements()
536-
{
537-
if (_msaglViewer is null)
538-
{
539-
return;
540-
}
541532

542-
var ids = _msaglViewer.Entities.Where(e => e.MarkedForDragging).OfType<IViewerNode>().Select(n => n.Node.Id);
543-
foreach (var id in ids)
544-
{
545-
_clonedCodeGraph.RemoveCodeElement(id);
546-
}
547-
548-
RefreshGraph();
549-
}
550533

551534
private void AddMissingDependencies()
552535
{
536+
PushUndo();
537+
538+
// We do not know the original graph.
553539
_publisher.Publish(new AddMissingDependenciesRequest());
554540
}
555541

556542
private void AddParentRequest(Node node)
557543
{
544+
PushUndo();
545+
546+
// We do not know the original graph.
558547
_publisher.Publish(new AddParentContainerRequest(node.Id));
559548
}
560549

561-
562550
private void FindInTree(Node node)
563551
{
564552
_publisher.Publish(new LocateInTreeRequest(node.Id));

0 commit comments

Comments
 (0)