/// <summary> /// Writes the table layout information provided through <see cref="TableLayoutConfigurator"/> back to all tables. /// </summary> /// <remarks>This method is only called when the layout is not animated.</remarks> /// <seealso cref="PrepareTableLayout"/> protected virtual void RestoreTableLayout() { if (graph != null) { TableLayoutConfigurator.Restore(graph); } }
/// <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> /// 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); } }
/// <summary> /// Set up <see cref="TableLayoutConfigurator"/> for an actual layout run. /// </summary> /// <remarks>This implementation configures <see cref="yWorks.Layout.TableLayoutConfigurator.HorizontalLayout"/> according to the <see cref="MultiStageLayout.LayoutOrientation"/> and calls /// <see cref="yWorks.Layout.TableLayoutConfigurator.Prepare"/></remarks> protected virtual void PrepareTableLayout() { var cms = layoutAlgorithm as MultiStageLayout; if (cms != null && cms.OrientationLayout is OrientationLayout && cms.OrientationLayoutEnabled) { TableLayoutConfigurator.HorizontalLayout = (((OrientationLayout)cms.OrientationLayout).HorizontalOrientation); } if (graph != null) { TableLayoutConfigurator.Prepare(graph); } }
/// <summary> /// Creates an animation that morphs the layout of all <see cref="ITable"/>s in the graph. /// </summary> /// <seealso cref="TableAnimation"/> /// <seealso cref="ConfigureTableNodeLayout"/> protected virtual IAnimation CreateTableAnimations() { var anims = new List <IAnimation>(); foreach (var node in graph.Nodes) { var table = node.Lookup <ITable>(); if (table != null) { INodeLayout nodeLayout = layoutGraph.GetLayout(layoutGraph.GetCopiedNode(node)); var columnLayout = TableLayoutConfigurator.GetColumnLayout(table, new RectD(nodeLayout.X, nodeLayout.Y, nodeLayout.Width, nodeLayout.Height)); var rowLayout = TableLayoutConfigurator.GetRowLayout(table, new RectD(nodeLayout.X, nodeLayout.Y, nodeLayout.Width, nodeLayout.Height)); anims.Add(new TableAnimation(table, columnLayout, rowLayout)); } } return(anims.CreateParallelAnimation()); }
/// <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... } }