/// <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();
        }
Example #3
0
 /// <summary>
 /// Modifies <see cref="InUpdate"/> if applicable.
 /// </summary>
 public void EndValueUpdate()
 {
     if (currentEdit != null)
     {
         currentEdit.Commit();
         currentEdit = null;
     }
     inUpdateCounter--;
 }
Example #4
0
        /// <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");
            }
        }
Example #5
0
        /// <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&lt;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...
            }
        }