|
1 |
| -using System.Diagnostics.CodeAnalysis; |
2 | 1 | using System.Windows.Forms;
|
3 |
| -#if DEBUG |
4 |
| -using System.Text; |
5 |
| -#endif |
| 2 | +using Speckle.Connector.Navisworks.Plugin.Tools; |
6 | 3 |
|
7 | 4 | namespace Speckle.Connector.Navisworks.Plugin;
|
8 | 5 |
|
| 6 | +/// <summary> |
| 7 | +/// Handles plugin state and ribbon management for the Speckle V3 and V2 connectors. |
| 8 | +/// </summary> |
9 | 9 | [
|
10 |
| - NAV.Plugins.Plugin("SpeckleNavisworksNextGen", "Speckle", DisplayName = "Speckle (Beta)"), |
11 |
| - NAV.Plugins.Strings("NavisworksRibbon.name"), |
| 10 | + NAV.Plugins.Plugin(SpeckleV3Tool.PLUGIN_ID, SpeckleV3Tool.DEVELOPER_ID, DisplayName = SpeckleV3Tool.DISPLAY_NAME), |
| 11 | + NAV.Plugins.Strings(SpeckleV3Tool.RIBBON_STRINGS), |
12 | 12 | NAV.Plugins.RibbonLayout("NavisworksRibbon.xaml"),
|
13 |
| - NAV.Plugins.RibbonTab("Speckle", DisplayName = "Speckle", LoadForCanExecute = true), |
| 13 | + NAV.Plugins.RibbonTab( |
| 14 | + SpeckleV3Tool.RIBBON_TAB_ID, |
| 15 | + DisplayName = SpeckleV3Tool.RIBBON_TAB_DISPLAY_NAME, |
| 16 | + LoadForCanExecute = true |
| 17 | + ), |
14 | 18 | NAV.Plugins.Command(
|
15 |
| - LaunchSpeckleConnector.COMMAND, |
| 19 | + SpeckleV3Tool.COMMAND, |
16 | 20 | LoadForCanExecute = true,
|
17 |
| - Icon = "Resources/s2logo16.png", |
18 |
| - LargeIcon = "Resources/s2logo32.png", |
19 |
| - ToolTip = "Next Gen Speckle Connector (Beta) for Navisworks", |
20 |
| - DisplayName = "Speckle (Beta)" |
| 21 | + Icon = "Resources/v3_logo16.png", |
| 22 | + LargeIcon = "Resources/v3_logo32.png", |
| 23 | + ToolTip = "Speckle Connector for Navisworks", |
| 24 | + DisplayName = "$Speckle_Launch.DisplayName" |
21 | 25 | ),
|
| 26 | + NAV.Plugins.Command( |
| 27 | + SpeckleV2Tool.COMMAND, |
| 28 | + LoadForCanExecute = true, |
| 29 | + Icon = "Resources/v2_logo16.png", |
| 30 | + LargeIcon = "Resources/v2_logo32.png", |
| 31 | + ToolTip = "Legacy Speckle v2 Connector", |
| 32 | + DisplayName = "$Speckle_Launch_V2.DisplayName" |
| 33 | + ) |
22 | 34 | ]
|
23 |
| -[SuppressMessage( |
24 |
| - "design", |
25 |
| - "CA1812:Avoid uninstantiated internal classes", |
26 |
| - Justification = "Instantiated by Navisworks" |
27 |
| -)] |
28 | 35 | internal sealed class RibbonHandler : NAV.Plugins.CommandHandlerPlugin
|
29 | 36 | {
|
30 |
| - // ReSharper disable once CollectionNeverQueried.Local |
31 |
| - private static readonly Dictionary<NAV.Plugins.Plugin, bool> s_loadedPlugins = []; |
| 37 | + private static bool? s_isV2PluginAvailable; // Nullable to indicate uncached state. |
| 38 | + private static bool s_isV2RibbonHidden; // Tracks if the ribbon tab is already hidden. |
32 | 39 |
|
33 |
| - /// <summary> |
34 |
| - /// Determines the state of a command in Navisworks. |
35 |
| - /// </summary> |
36 |
| - /// <param name="commandId">The ID of the command to check.</param> |
37 |
| - /// <returns>The state of the command.</returns> |
38 |
| - public override NAV.Plugins.CommandState CanExecuteCommand(string commandId) => |
39 |
| - commandId == LaunchSpeckleConnector.COMMAND |
40 |
| - ? new NAV.Plugins.CommandState(true) |
41 |
| - : new NAV.Plugins.CommandState(false); |
| 40 | + static RibbonHandler() |
| 41 | + { |
| 42 | + // Subscribe to the static PluginRecordsChanged event |
| 43 | + NAV.ApplicationParts.ApplicationPlugins.PluginRecordsChanged += OnPluginRecordsChanged; |
| 44 | + } |
| 45 | + |
| 46 | + private static void OnPluginRecordsChanged(object sender, EventArgs e) => s_isV2PluginAvailable = null; |
42 | 47 |
|
43 | 48 | /// <summary>
|
44 |
| - /// Loads a plugin in Navisworks. |
| 49 | + /// Determines whether a command can be executed and manages V2 plugin visibility. |
45 | 50 | /// </summary>
|
46 |
| - /// <param name="plugin">The name of the plugin to load.</param> |
47 |
| - /// <param name="notAutomatedCheck">Optional. Specifies whether to check if the application is automated. Default is true.</param> |
48 |
| - /// <param name="command">Optional. The command associated with the plugin. Default is an empty string.</param> |
49 |
| - private static void LoadPlugin(string plugin, bool notAutomatedCheck = true, string command = "") |
| 51 | + /// <param name="commandId">The command identifier to check.</param> |
| 52 | + /// <returns>A CommandState indicating whether the command can be executed.</returns> |
| 53 | + public override NAV.Plugins.CommandState CanExecuteCommand(string commandId) |
50 | 54 | {
|
51 |
| - if (ShouldSkipLoad(notAutomatedCheck)) |
| 55 | + switch (commandId) |
52 | 56 | {
|
53 |
| - return; |
54 |
| - } |
| 57 | + case SpeckleV3Tool.COMMAND: |
| 58 | + return new NAV.Plugins.CommandState(true); |
| 59 | + case SpeckleV2Tool.COMMAND: |
| 60 | + { |
| 61 | + // Find the v2 plugin |
| 62 | + NAV.Plugins.PluginRecord? v2Plugin = PluginUtilities.FindV2Plugin(); |
| 63 | + s_isV2PluginAvailable = v2Plugin != null; |
55 | 64 |
|
56 |
| - if (ShouldSkipPluginLoad(plugin, command)) |
57 |
| - { |
58 |
| - return; |
59 |
| - } |
| 65 | + // Pass the plugin to the method for managing ribbon visibility |
| 66 | + HideV2RibbonTab(); |
60 | 67 |
|
61 |
| - var pluginRecord = NavisworksApp.Plugins.FindPlugin(plugin + ".Speckle"); |
62 |
| - if (pluginRecord is null) |
63 |
| - { |
64 |
| - return; |
| 68 | + return new NAV.Plugins.CommandState((bool)s_isV2PluginAvailable); |
| 69 | + } |
| 70 | + default: |
| 71 | + return new NAV.Plugins.CommandState(false); |
65 | 72 | }
|
66 |
| - |
67 |
| - var loadedPlugin = pluginRecord.LoadedPlugin ?? pluginRecord.LoadPlugin(); |
68 |
| - |
69 |
| - ActivatePluginPane(pluginRecord, loadedPlugin, command); |
70 | 73 | }
|
71 | 74 |
|
72 |
| - /// <summary> |
73 |
| - /// Checks whether the load should be skipped based on the notAutomatedCheck flag and application automation status. |
74 |
| - /// </summary> |
75 |
| - /// <param name="notAutomatedCheck">The flag indicating whether to check if the application is automated.</param> |
76 |
| - /// <returns>True if the load should be skipped, False otherwise.</returns> |
77 |
| - private static bool ShouldSkipLoad(bool notAutomatedCheck) => notAutomatedCheck && NavisworksApp.IsAutomated; |
78 |
| - |
79 |
| - /// <summary> |
80 |
| - /// Checks whether the plugin load should be skipped based on the plugin and command values. |
81 |
| - /// </summary> |
82 |
| - /// <param name="plugin">The name of the plugin.</param> |
83 |
| - /// <param name="command">The command associated with the plugin.</param> |
84 |
| - /// <returns>True if the plugin load should be skipped, False otherwise.</returns> |
85 |
| - private static bool ShouldSkipPluginLoad(string plugin, string command) => |
86 |
| - string.IsNullOrEmpty(plugin) || string.IsNullOrEmpty(command); |
87 |
| - |
88 |
| - /// <summary> |
89 |
| - /// Activates the plugin's pane if it is of the right type. |
90 |
| - /// </summary> |
91 |
| - /// <param name="pluginRecord">The plugin record.</param> |
92 |
| - /// <param name="loadedPlugin">The loaded plugin instance.</param> |
93 |
| - /// <param name="command">The command associated with the plugin.</param> |
94 |
| - private static void ActivatePluginPane(NAV.Plugins.PluginRecord pluginRecord, object loadedPlugin, string command) |
| 75 | + private static void HideV2RibbonTab() |
95 | 76 | {
|
96 |
| - if (ShouldActivatePluginPane(pluginRecord)) |
| 77 | + if (s_isV2RibbonHidden) |
97 | 78 | {
|
98 |
| - var dockPanePlugin = (NAV.Plugins.DockPanePlugin)loadedPlugin; |
99 |
| - dockPanePlugin.ActivatePane(); |
100 |
| - |
101 |
| - s_loadedPlugins[dockPanePlugin] = true; |
| 79 | + return; // Skip if already hidden. |
102 | 80 | }
|
103 |
| - else |
| 81 | + |
| 82 | + var v2RibbonTab = Autodesk.Windows.ComponentManager.Ribbon.Tabs.FirstOrDefault(tab => |
| 83 | + tab.Id == SpeckleV2Tool.RIBBON_TAB_ID + SpeckleV2Tool.PLUGIN_SUFFIX |
| 84 | + ); |
| 85 | + |
| 86 | + if (v2RibbonTab == null) |
104 | 87 | {
|
105 |
| -#if DEBUG |
106 |
| - ShowPluginInfoMessageBox(); |
107 |
| - ShowPluginNotLoadedMessageBox(command); |
108 |
| -#endif |
| 88 | + return; |
109 | 89 | }
|
| 90 | + |
| 91 | + Autodesk.Windows.ComponentManager.Ribbon.Tabs.Remove(v2RibbonTab); |
| 92 | + s_isV2RibbonHidden = true; // Mark as hidden to avoid redundant calls. |
110 | 93 | }
|
111 | 94 |
|
112 | 95 | /// <summary>
|
113 |
| - /// Checks whether the plugin's pane should be activated based on the plugin record. |
| 96 | + /// Executes the specified command after validating the Navisworks version. |
114 | 97 | /// </summary>
|
115 |
| - /// <param name="pluginRecord">The plugin record.</param> |
116 |
| - /// <returns>True if the plugin's pane should be activated, False otherwise.</returns> |
117 |
| - private static bool ShouldActivatePluginPane(NAV.Plugins.PluginRecord pluginRecord) => |
118 |
| - pluginRecord.IsLoaded && pluginRecord is NAV.Plugins.DockPanePluginRecord && pluginRecord.IsEnabled; |
119 |
| - |
| 98 | + /// <param name="commandId">The command to execute.</param> |
| 99 | + /// <param name="parameters">Additional command parameters.</param> |
| 100 | + /// <returns>0 if successful, non-zero otherwise.</returns> |
120 | 101 | public override int ExecuteCommand(string commandId, params string[] parameters)
|
121 | 102 | {
|
122 |
| - // ReSharper disable once RedundantAssignment |
123 |
| - var buildVersion = string.Empty; |
124 |
| - |
125 |
| -#if NAVIS2020 |
126 |
| - buildVersion = "2020"; |
127 |
| -#endif |
128 |
| -#if NAVIS2021 |
129 |
| - buildVersion = "2021"; |
130 |
| -#endif |
131 |
| -#if NAVIS2022 |
132 |
| - buildVersion = "2022"; |
133 |
| -#endif |
134 |
| -#if NAVIS2023 |
135 |
| - buildVersion = "2023"; |
136 |
| -#endif |
137 |
| -#if NAVIS2024 |
138 |
| - buildVersion = "2024"; |
139 |
| -#endif |
140 |
| -#if NAVIS2025 |
141 |
| - buildVersion = "2025"; |
142 |
| -#endif |
143 |
| - |
144 |
| - // Version |
145 |
| - if (!NavisworksApp.Version.RuntimeProductName.Contains(buildVersion)) |
| 103 | + if (!IsValidVersion()) |
146 | 104 | {
|
147 |
| - MessageBox.Show( |
148 |
| - "This Add-In was built for Navisworks " |
149 |
| - + buildVersion |
150 |
| - + ", please contact jonathon@speckle.systems for assistance...", |
151 |
| - "Cannot Continue!", |
152 |
| - MessageBoxButtons.OK, |
153 |
| - MessageBoxIcon.Error |
154 |
| - ); |
155 | 105 | return 0;
|
156 | 106 | }
|
157 | 107 |
|
158 | 108 | switch (commandId)
|
159 | 109 | {
|
160 |
| - case LaunchSpeckleConnector.COMMAND: |
161 |
| - { |
162 |
| - LoadPlugin(LaunchSpeckleConnector.PLUGIN, command: commandId); |
| 110 | + case SpeckleV3Tool.COMMAND: |
| 111 | + HandleCommand(SpeckleV3Tool.PLUGIN, commandId); |
163 | 112 | break;
|
164 |
| - } |
165 | 113 |
|
| 114 | + case SpeckleV2Tool.COMMAND: |
| 115 | + HandleCommand(SpeckleV2Tool.PLUGIN, $"{SpeckleV2Tool.PLUGIN}.{SpeckleV2Tool.DEVELOPER_ID}"); |
| 116 | + break; |
166 | 117 | default:
|
167 | 118 | {
|
168 |
| - MessageBox.Show("You have clicked on an unexpected command with ID = '" + commandId + "'"); |
| 119 | + MessageBox.Show($"You have clicked on an unexpected command with ID = '{commandId}'"); |
169 | 120 | break;
|
170 | 121 | }
|
171 | 122 | }
|
172 | 123 |
|
173 | 124 | return 0;
|
174 | 125 | }
|
175 | 126 |
|
176 |
| -#if DEBUG |
177 |
| - /// <summary> |
178 |
| - /// Shows a message box displaying plugin information. |
179 |
| - /// </summary> |
180 |
| - private static void ShowPluginInfoMessageBox() |
| 127 | + private static void HandleCommand(string pluginId, string commandId) |
181 | 128 | {
|
182 |
| - var sb = new StringBuilder(); |
183 |
| - foreach (var pr in NavisworksApp.Plugins.PluginRecords) |
| 129 | + if (PluginUtilities.ShouldSkipLoad(pluginId, commandId, true)) |
184 | 130 | {
|
185 |
| - sb.AppendLine(pr.Name + ": " + pr.DisplayName + ", " + pr.Id); |
| 131 | + return; |
186 | 132 | }
|
187 | 133 |
|
188 |
| - MessageBox.Show(sb.ToString()); |
| 134 | + var pluginRecord = NavisworksApp.Plugins.FindPlugin(pluginId + SpeckleV3Tool.PLUGIN_SUFFIX); |
| 135 | + if (pluginRecord == null) |
| 136 | + { |
| 137 | + return; |
| 138 | + } |
| 139 | + |
| 140 | + _ = pluginRecord.LoadedPlugin ?? pluginRecord.LoadPlugin(); |
| 141 | + PluginUtilities.ActivatePluginPane(pluginRecord); |
189 | 142 | }
|
190 | 143 |
|
191 |
| - /// <summary> |
192 |
| - /// Shows a message box indicating that the plugin was not loaded. |
193 |
| - /// </summary> |
194 |
| - /// <param name="command">The command associated with the plugin.</param> |
195 |
| - private static void ShowPluginNotLoadedMessageBox(string command) => MessageBox.Show(command + " Plugin not loaded."); |
196 |
| -#endif |
| 144 | + private static bool IsValidVersion() |
| 145 | + { |
| 146 | + if (NavisworksApp.Version.RuntimeProductName.Contains(SpeckleV3Tool.Version.ToString().Replace("v", ""))) |
| 147 | + { |
| 148 | + return true; |
| 149 | + } |
| 150 | + |
| 151 | + MessageBox.Show( |
| 152 | + $"This Add-In was built for Navisworks {SpeckleV3Tool.Version}, " |
| 153 | + + $"please contact support@speckle.systems for assistance...", |
| 154 | + "Cannot Continue!", |
| 155 | + MessageBoxButtons.OK, |
| 156 | + MessageBoxIcon.Error |
| 157 | + ); |
| 158 | + return false; |
| 159 | + } |
197 | 160 | }
|
0 commit comments