diff --git a/docs/pages/_en/1.0/reference/release-notes.md b/docs/pages/_en/1.0/reference/release-notes.md index 2e1943591..92e52624a 100644 --- a/docs/pages/_en/1.0/reference/release-notes.md +++ b/docs/pages/_en/1.0/reference/release-notes.md @@ -15,6 +15,8 @@ group: Deployment & Configs ### RC +- Added 'Open View' component. + {% endcapture %} {% include ltr/release_header_next.html title="Upcoming Changes" note=rc_release_notes %} diff --git a/src/RhinoInside.Revit.External/DB/Extensions/Document.cs b/src/RhinoInside.Revit.External/DB/Extensions/Document.cs index 8b48eea27..33eeadf8e 100644 --- a/src/RhinoInside.Revit.External/DB/Extensions/Document.cs +++ b/src/RhinoInside.Revit.External/DB/Extensions/Document.cs @@ -998,12 +998,17 @@ public static View GetActiveGraphicalView(this Document doc) } /// - /// Sets the active Graphical of the provided . + /// Sets the active of the provided . /// /// /// View to be activated - public static bool SetActiveGraphicalView(this Document document, View view) => - SetActiveGraphicalView(document, view, out var _); + public static bool SetActiveView(this Document document, View view) + { + if (view is null) + throw new ArgumentNullException(nameof(view)); + + return SetActiveView(document, view, out var _); + } /// /// Sets the active Graphical of the provided . @@ -1018,6 +1023,16 @@ public static bool SetActiveGraphicalView(this Document document, View view, out if (!view.IsGraphicalView()) throw new ArgumentException("Input view is not a graphical view.", nameof(view)); + return SetActiveView(document, view, out wasOpened); + } + + /// + /// Sets the active Graphical of the provided . + /// + /// + /// View to be activated + private static bool SetActiveView(this Document document, View view, out bool wasOpened) + { if (!document.Equals(view.Document)) throw new ArgumentException("View does not belong to the specified document", nameof(view)); diff --git a/src/RhinoInside.Revit.External/UI/UIHostApplicationUnconstrained.cs b/src/RhinoInside.Revit.External/UI/UIHostApplicationUnconstrained.cs index 1b78d9e0d..ace0a197e 100644 --- a/src/RhinoInside.Revit.External/UI/UIHostApplicationUnconstrained.cs +++ b/src/RhinoInside.Revit.External/UI/UIHostApplicationUnconstrained.cs @@ -46,7 +46,7 @@ public override UIDocument ActiveUIDocument if (value.TryGetActiveGraphicalView(out var uiView)) { HostedApplication.Active.InvokeInHostContext - (() => value.Document.SetActiveGraphicalView(value.Document.GetElement(uiView.ViewId) as View)); + (() => value.Document.SetActiveGraphicalView(value.Document.GetElement(uiView.ViewId) as View, out var _)); } } } diff --git a/src/RhinoInside.Revit.GH/Components/Groups/AddDetailGroup.cs b/src/RhinoInside.Revit.GH/Components/Groups/AddDetailGroup.cs index 62f3d80de..19afee910 100644 --- a/src/RhinoInside.Revit.GH/Components/Groups/AddDetailGroup.cs +++ b/src/RhinoInside.Revit.GH/Components/Groups/AddDetailGroup.cs @@ -111,7 +111,7 @@ protected override void AfterSolveInstance() { base.AfterSolveInstance(); - ActiveView?.Document.SetActiveGraphicalView(ActiveView); + ActiveView?.Document.SetActiveView(ActiveView); ActiveView = default; if (ViewsToClose.Count > 0) diff --git a/src/RhinoInside.Revit.GH/Components/Topology/AddSpace.cs b/src/RhinoInside.Revit.GH/Components/Topology/AddSpace.cs index 1daaa42b4..108a3db28 100644 --- a/src/RhinoInside.Revit.GH/Components/Topology/AddSpace.cs +++ b/src/RhinoInside.Revit.GH/Components/Topology/AddSpace.cs @@ -145,7 +145,7 @@ protected override void AfterSolveInstance() { base.AfterSolveInstance(); - ActiveView?.Document.SetActiveGraphicalView(ActiveView); + ActiveView?.Document.SetActiveView(ActiveView); ActiveView = default; foreach (var view in (ViewsToClose as IEnumerable).Reverse()) diff --git a/src/RhinoInside.Revit.GH/Components/Views/Open.cs b/src/RhinoInside.Revit.GH/Components/Views/Open.cs new file mode 100644 index 000000000..2a81f3c10 --- /dev/null +++ b/src/RhinoInside.Revit.GH/Components/Views/Open.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using Grasshopper.Kernel; +using Grasshopper.Kernel.Data; +using Grasshopper.Kernel.Parameters; +using Grasshopper.Kernel.Types; +using RhinoInside.Revit.External.DB.Extensions; +using ARDB = Autodesk.Revit.DB; +using ARUI = Autodesk.Revit.UI; + +namespace RhinoInside.Revit.GH.Components.Views +{ + [ComponentVersion(introduced: "1.18")] + public class ViewOpen : ZuiComponent + { + public override Guid ComponentGuid => new Guid("E13FC388-D607-4A62-BEBC-498A9445F91A"); + public override GH_Exposure Exposure => GH_Exposure.primary; + protected override string IconTag => string.Empty; + + public ViewOpen() : base + ( + name: "Open View", + nickname: "V-Open", + description: "Open-Close a Revit view", + category: "Revit", + subCategory: "View" + ) + { } + + protected override ParamDefinition[] Inputs => inputs; + static readonly ParamDefinition[] inputs = + { + ParamDefinition.Create("View", "V", string.Empty), + ParamDefinition.Create("Open", "O", string.Empty, optional: true, relevance: ParamRelevance.Primary) + }; + + protected override ParamDefinition[] Outputs => outputs; + static readonly ParamDefinition[] outputs = + { + ParamDefinition.Create("View", "V", string.Empty), + ParamDefinition.Create("Open", "O", string.Empty, optional: true, relevance: ParamRelevance.Primary) + }; + + private readonly Dictionary ViewStates = new Dictionary(); + protected override void BeforeSolveInstance() + { + base.BeforeSolveInstance(); + } + + protected override void TrySolveInstance(IGH_DataAccess DA) + { + if (!Params.TryGetData(DA, "View", out Types.View view, x => x.IsValid)) return; + else Params.TrySetData(DA, "View", () => view); + if (!Params.TryGetData(DA, "Open", out bool? open)) return; + + var isOpen = view.Value.IsOpen(); + if (open is object) + { + if (view.Value.IsTemplate && open is true) + AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Can't open view template '{view.DisplayName}'"); + else if (view.Value.IsCallout && open is true) + AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, $"Can't open callout view'{view.DisplayName}'"); + else + ViewStates[view] = open.Value; + } + + Params.TrySetData(DA, "Open", () => isOpen); + } + + protected override void AfterSolveInstance() + { + if (ViewStates.Count > 0) + { + try + { + Guest.Instance.CommitTransactionGroups(); + var activeView = Revit.ActiveUIDocument.ActiveView; + + try + { + foreach (var group in ViewStates.GroupBy(x => x.Key.Document)) + { + using (var uiDocument = new ARUI.UIDocument(group.Key)) + { + var openViews = uiDocument.GetOpenUIViews(); + if (openViews.Count == 0) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Document {uiDocument.Document.GetTitle()} is not open on UI."); + } + else + { + var openViewIds = new HashSet(openViews.Select(x => x.ViewId)); + var viewsToClose = group.Where(x => x.Value == false).Select(x => x.Key); + var viewsToOpen = group.Where(x => x.Value == true).Select(x => x.Key); + + foreach (var view in viewsToOpen) + { + if (!openViewIds.Contains(view.Id)) + view.Value.Document.SetActiveView(view.Value); + } + + foreach (var view in viewsToClose) + { + if (view.Value.IsEquivalent(activeView)) + { + AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Can't close '{view.DisplayName}' because is the active one."); + } + else + { + try { view.Value.Close(); } + catch (Exception e) { AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message); } + } + } + } + } + } + } + finally + { + activeView.Document.SetActiveView(activeView); + } + + // Reconstruct output 'Open' with final values from 'View'. + var _View_ = Params.IndexOfOutputParam("View"); + var _Open_ = Params.IndexOfOutputParam("Open"); + if (_View_ >= 0 && _Open_ >= 0) + { + var viewParam = Params.Output[_View_]; + var openParam = Params.Output[_Open_]; + var openData = new GH_Structure(); + + var viewData = viewParam.VolatileData; + foreach (var path in viewData.Paths) + { + var open = viewData.get_Branch(path).Cast().Select(x => (x as Types.View)?.Value.IsOpen()); + openData.AppendRange(open.Select(x => x.HasValue ? new GH_Boolean(x.Value) : null), path); + } + + openParam.ClearData(); + openParam.AddVolatileDataTree(openData); + } + } + finally + { + ViewStates.Clear(); + Guest.Instance.StartTransactionGroups(); + } + } + + base.AfterSolveInstance(); + } + + #region UI + protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu) + { + base.AppendAdditionalComponentMenuItems(menu); + + var activeApp = Revit.ActiveUIApplication; +#if REVIT_2019 + menu.AppendPostableCommand(ARUI.PostableCommand.CloseInactiveViews, "Close Inactive Views…"); + menu.AppendPostableCommand(ARUI.PostableCommand.TabViews, "Tab Views…"); + menu.AppendPostableCommand(ARUI.PostableCommand.TileViews, "Tile Views…"); +#endif + } + #endregion + } +} diff --git a/src/RhinoInside.Revit.GH/RhinoInside.Revit.GH.csproj b/src/RhinoInside.Revit.GH/RhinoInside.Revit.GH.csproj index 273d91680..1e6a8ef2a 100644 --- a/src/RhinoInside.Revit.GH/RhinoInside.Revit.GH.csproj +++ b/src/RhinoInside.Revit.GH/RhinoInside.Revit.GH.csproj @@ -301,6 +301,7 @@ +