/// <summary> /// Called after the layout has been calculated. /// </summary> /// <remarks> /// Applies the layout in an animation and cleans up. Calls OnDone to raise the Done event after the animation has been completed. /// </remarks> /// <param name="graph">The graph to apply the layout to.</param> /// <param name="moduleContext">The module context.</param> /// <param name="compoundEdit">The undo edit which wraps the layout. It was created in <see cref="StartWithIGraph"/> and will be closed here.</param> protected virtual async Task LayoutDone(IGraph graph, ILookup moduleContext, ICompoundEdit compoundEdit) { if (abortDialog != null) { if (abortDialog.IsVisible) { abortDialog.Close(); } abortDialog = null; } GraphControl view = moduleContext.Lookup <GraphControl>(); if (LayoutMorphingEnabled && view != null) { var morphingAnimation = graph.CreateLayoutAnimation(layoutGraph, TimeSpan.FromSeconds(1)); Rectangle2D box = LayoutGraphUtilities.GetBoundingBox(layoutGraph, layoutGraph.GetNodeCursor(), layoutGraph.GetEdgeCursor()); RectD targetBounds = box.ToRectD(); ViewportAnimation vpAnim = new ViewportAnimation(view, targetBounds, TimeSpan.FromSeconds(1)) { MaximumTargetZoom = 1.0d, TargetViewMargins = view.ContentMargins }; var animations = new List <IAnimation>(); animations.Add(morphingAnimation); animations.Add(CreateTableAnimations()); if (ViewPortMorphingEnabled) { animations.Add(vpAnim); } TableLayoutConfigurator.CleanUp(graph); Animator animator = new Animator(view); await animator.Animate(animations.CreateParallelAnimation().CreateEasedAnimation()); try { compoundEdit.Commit(); view.UpdateContentRect(); } finally { OnDone(new LayoutEventArgs()); } } else { layoutGraph.CommitLayoutToOriginalGraph(); RestoreTableLayout(); compoundEdit.Commit(); if (view != null) { view.UpdateContentRect(); } OnDone(new LayoutEventArgs()); } }
/// <summary> /// Prepares the layout execution. /// </summary> public void InitializeLayout() { // prepare undo/redo oldClearRect = clearRect.ToRectD(); layoutEdit = Graph.BeginEdit("Clear Area", "Clear Area"); resetToOriginalGraphStageData = CreateGivenCoordinateStageData(); executor = CreateDraggingLayoutExecutor(); }
/// <summary> /// Modifies <see cref="InUpdate"/> if applicable. /// </summary> public void EndValueUpdate() { if (currentEdit != null) { currentEdit.Commit(); currentEdit = null; } inUpdateCounter--; }
/// <summary> /// Modifies <see cref="InUpdate"/> if applicable. /// </summary> public virtual void BeginValueUpdate() { inUpdateCounter++; var graph = InnerLookup.Lookup <IGraph>(); if (graph != null && currentEdit == null) { currentEdit = graph.BeginEdit("Property Changed", "PropertyChanged"); } }
/// <summary> /// Initializes the drag. /// </summary> public void InitializeDrag(IInputModeContext context) { var imc = context.Lookup <IModelItemCollector>(); if (imc != null) { imc.Add(node); } rotationCenter = node.Layout.GetCenter(); initialAngle = GetAngle(); var graph = context.Lookup <IGraph>(); if (graph != null) { compoundEdit = graph.BeginEdit("Change Rotation Angle", "Change Rotation Angle"); } portHandles.Clear(); var portContext = new DelegatingContext(context); foreach (var port in node.Ports) { var portHandle = new DummyPortLocationModelParameterHandle(port); portHandle.InitializeDrag(portContext); portHandles.Add(portHandle); } if (reshapeHandler != null) { reshapeHandler.InitializeReshape(context); } // Collect other visible nodes and their angles if (SnapToSameAngleDelta > 0) { var canvas = context.CanvasControl; var rotatedNodes = canvas.GetCanvasObjects() .Select(co => co.UserObject) // only collect nodes .OfType <INode>() // ... that are in the viewport .Where(n => canvas.Viewport.Intersects(n.Layout.ToRectD())) // ... and can be rotated .Where(n => n.Style is RotatableNodeStyleDecorator) // ... and are not *this* node .Where(n => n != node); // Group nodes by identical angles nodeAngles = rotatedNodes.GroupBy(n => ((RotatableNodeStyleDecorator)n.Style).Angle) .Select(nodes => Tuple.Create(nodes.Key, nodes.ToList().AsEnumerable())).ToList(); } }
/// <summary> /// Runs the layout and disposes the module afterwards. /// </summary> /// <remarks> /// This method will be called in a separate thread. If the layout has been called successfully /// the method <see cref="LayoutDone"/> will be called afterwards to apply the layout. /// If the layout has been canceled or an error has happened during layout the <see cref="Done"/> /// will be raised with an instance of <see cref="LayoutEventArgs"/>. /// </remarks> /// <param name="moduleContext">The module context for this operation.</param> /// <param name="graph">The graph to apply the layout to.</param> /// <param name="compoundEdit">The undo edit which wraps the layout. It has been created in <see cref="StartWithIGraph"/> /// and will be closed after a successful layout or canceled otherwise.</param> private async Task RunModuleAsync(ILookup moduleContext, IGraph graph, ICompoundEdit compoundEdit) { GraphControl view = moduleContext.Lookup <GraphControl>(); try { await RunModule(); Dispose(); if (view.Dispatcher.CheckAccess()) { await LayoutDone(graph, moduleContext, compoundEdit); } else { Invoke(view.Dispatcher, new Action(async() => await LayoutDone(graph, moduleContext, compoundEdit))); } } catch (ThreadAbortException tae) { compoundEdit.Cancel(); if (view.Dispatcher.CheckAccess()) { OnDone(new LayoutEventArgs(tae)); } else { await view.Dispatcher.BeginInvoke(new Action(() => OnDone(new LayoutEventArgs(tae)))); } } catch (AlgorithmAbortedException aae) { // layout was canceled. do nothing then. compoundEdit.Cancel(); if (view.Dispatcher.CheckAccess()) { OnDone(new LayoutEventArgs(aae)); } else { Invoke(view.Dispatcher, new Action(() => OnDone(new LayoutEventArgs(aae)))); } } catch (Exception ex) { compoundEdit.Cancel(); if (view.Dispatcher.CheckAccess()) { OnDone(new LayoutEventArgs(ex)); } else { Invoke(view.Dispatcher, new Action(() => OnDone(new LayoutEventArgs(ex)))); } } finally { FreeReentrant(); TableLayoutConfigurator.CleanUp(graph); } }
/// <summary> /// Runs the layout and disposes the module afterwards. /// </summary> /// <remarks> /// This method will be called in a separate thread. If the layout has been called successfully /// the method <see cref="LayoutDone"/> will be called afterwards to apply the layout. /// If the layout has been canceled or an error has happened during layout the <see cref="Done"/> /// will be raised with an instance of <see cref="LayoutEventArgs"/>. /// </remarks> /// <param name="moduleContext">The module context for this operation.</param> /// <param name="graph">The graph to apply the layout to.</param> /// <param name="compoundEdit">The undo edit which wraps the layout. It has been created in <see cref="StartWithIGraph"/> /// and will be closed after a successful layout or canceled otherwise.</param> private async Task RunModuleAsync(ILookup moduleContext, IGraph graph, ICompoundEdit compoundEdit) { GraphControl view = moduleContext.Lookup <GraphControl>(); try { await RunModule(); Dispose(); if (!view.InvokeRequired) { await LayoutDone(graph, moduleContext, compoundEdit); } else { view.Invoke(new LayoutDoneHandler(LayoutDone), graph, moduleContext, compoundEdit); } } catch (ThreadAbortException tae) { compoundEdit.Cancel(); if (!view.InvokeRequired) { OnDone(new LayoutEventArgs(tae)); } else { view.BeginInvoke(new EventHandler(OnDone), view, new LayoutEventArgs(tae)); } } catch (AlgorithmAbortedException aae) { // layout was canceled. do nothing then. compoundEdit.Cancel(); if (!view.InvokeRequired) { OnDone(new LayoutEventArgs(aae)); } else { view.Invoke(new EventHandler(OnDone), view, new LayoutEventArgs(aae)); } } catch (Exception ex) { compoundEdit.Cancel(); if (!view.InvokeRequired) { OnDone(new LayoutEventArgs(ex)); } else { view.Invoke(new EventHandler(OnDone), view, new LayoutEventArgs(ex)); } } finally { FreeReentrant(); TableLayoutConfigurator.CleanUp(graph); } }
protected virtual void Clear(IInputModeContext context) { var snapContext = context.Lookup <GraphSnapContext>(); if (snapContext != null) { snapContext.CollectSnapResults -= CollectSnapResults; } reshapeSnapResultProviders.Clear(); originalNodeLayouts.Clear(); reshapeHandlers.Clear(); orthogonalEdgeDragHandlers.Clear(); compoundEdit = null; }
public virtual void InitializeReshape(IInputModeContext context) { this.OriginalBounds = rectangle.TightRectangle; // register our CollectSnapResults callback var snapContext = context.Lookup <GraphSnapContext>(); if (snapContext != null) { snapContext.CollectSnapResults += CollectSnapResults; } // store original node layouts, reshape handlers and reshape snap result providers foreach (var node in ReshapeNodes) { originalNodeLayouts.Add(node, node.Layout.ToRectD()); // store reshape handler to change the shape of node var reshapeHandler = node.Lookup <IReshapeHandler>(); if (reshapeHandler != null) { reshapeHandler.InitializeReshape(context); reshapeHandlers.Add(node, reshapeHandler); } // store reshape snap result provider to collect snap results where node would snap to snaplines etc. var snapResultProvider = node.Lookup <INodeReshapeSnapResultProvider>(); if (snapContext != null && snapResultProvider != null) { reshapeSnapResultProviders.Add(node, snapResultProvider); } // store orthogonal edge drag handler that keeps edges at node orthogonal var orthogonalEdgeDragHandler = OrthogonalEdgeEditingContext.CreateOrthogonalEdgeDragHandler(context, node, false); if (orthogonalEdgeDragHandler != null) { orthogonalEdgeDragHandlers[node] = orthogonalEdgeDragHandler; } } // update the minimum/maximum size of the handle considering all initial node layouts Handle.MinimumSize = CalculateMinimumSize(); Handle.MaximumSize = CalculateMaximumSize(); // start a compound undo unit this.compoundEdit = context.GetGraph().BeginEdit("Undo Group Resize", "Redo Group Resize"); }
/// <summary> /// Prepares the layout execution. /// </summary> public void InitializeLayout() { layoutEdit = Graph.BeginEdit("Clear Area", "Clear Area"); resetToOriginalGraphStageData = CreateGivenCoordinateStageData(); executor = CreateDraggingLayoutExecutor(); }
/// <summary> /// Executes the module on the given graph using the provided context. /// </summary> /// <remarks> /// The layout will be calculated <see cref="RunInBackground">optionally</see> /// in a separate thread in method <see cref="RunModuleAsync"/>. /// </remarks> /// <param name="graph">The graph to execute on.</param> /// <param name="newContext">The context to use. This method will query a <c>ISelectionModel<IModelItem></c></param> /// for the selected nodes and edges and the <c>GraphControl</c> to morph the layout. protected virtual async Task StartWithIGraph(IGraph graph, ILookup newContext) { this.graph = graph; if (ShouldConfigureTableLayout()) { PrepareTableLayout(); } ISelectionModel <IModelItem> selectionModel = newContext.Lookup <ISelectionModel <IModelItem> >(); LayoutGraphAdapter adapter = new LayoutGraphAdapter(graph, selectionModel); this.layoutGraph = adapter.CreateCopiedLayoutGraph(); ILookup additionalLookup = Lookups.Single(layoutGraph, typeof(LayoutGraph)); ILookup wrappedLookup = Lookups.Wrapped(newContext, additionalLookup); try { ICompoundEdit compoundEdit = graph.BeginEdit("Layout", "Layout"); CheckReentrant(wrappedLookup); ConfigureModule(); if (RunInBackground) { // without the LayoutExecutor helper class on the layout graph side of things, we register the aborthandler // to the layout graph with the utility method provided by AbortHandler var abortHandler = AbortHandler.CreateForGraph(layoutGraph); // now create the dialog that controls the abort handler abortDialog = new AbortDialog { AbortHandler = abortHandler, Owner = Application.Current.MainWindow }; // start the layout in another thread. var layoutThread = new Thread(async() => await RunModuleAsync(wrappedLookup, graph, compoundEdit)); // now if we are not doing a quick layout - and if it takes more than a few seconds, we open the dialog to // enable the user to stop or cancel the execution var showDialogTimer = new DispatcherTimer(DispatcherPriority.Normal, abortDialog.Dispatcher) { Interval = TimeSpan.FromSeconds(2) }; showDialogTimer.Tick += delegate { // it could be that the layout is already done - so check whether we still // need to open the dialog var dialogInstance = abortDialog; if (dialogInstance != null) { // open the abort dialog dialogInstance.Show(); } // we only want to let it go off once - so stop the timer showDialogTimer.Stop(); }; // kick-off the timer and the layout showDialogTimer.Start(); layoutThread.Start(); } else { await RunModuleAsync(wrappedLookup, graph, compoundEdit); } } catch (Exception e) { FreeReentrant(); TableLayoutConfigurator.CleanUp(graph); OnDone(new LayoutEventArgs(e)); //optionally do something here... } }