diff --git a/docs/Navisworks connector.md b/docs/Navisworks connector.md
new file mode 100644
index 00000000..63bf10fd
--- /dev/null
+++ b/docs/Navisworks connector.md
@@ -0,0 +1,13 @@
+# OpenProject BIM
+
+## Revit <-> Navisworks
+
+
+
+## Future ideas
+
+- [x] Revit
+- [x] Navisworks
+- [ ] Tekla Structures
+
+
\ No newline at end of file
diff --git a/docs/images/demo.gif b/docs/images/demo.gif
new file mode 100644
index 00000000..08950536
Binary files /dev/null and b/docs/images/demo.gif differ
diff --git a/docs/images/roadmap.png b/docs/images/roadmap.png
new file mode 100644
index 00000000..30c0c23f
Binary files /dev/null and b/docs/images/roadmap.png differ
diff --git a/docs/images/roadmap2.png b/docs/images/roadmap2.png
new file mode 100644
index 00000000..30c0c23f
Binary files /dev/null and b/docs/images/roadmap2.png differ
diff --git a/openproject-revit-add-in.sln b/openproject-revit-add-in.sln
index ed447128..5e8e312e 100644
--- a/openproject-revit-add-in.sln
+++ b/openproject-revit-add-in.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30204.135
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33502.453
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C185EC12-235E-4F88-8831-91F677F8DC90}"
ProjectSection(SolutionItems) = preProject
@@ -28,110 +28,314 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A7C3B085-2
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenProject.Tests", "test\OpenProject.Tests\OpenProject.Tests.csproj", "{C08A902A-404B-4CB9-A395-5743A4453733}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenProject.Navisworks", "src\OpenProject.Navisworks\OpenProject.Navisworks.csproj", "{7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
+ Debug-2019|Any CPU = Debug-2019|Any CPU
+ Debug-2019|x64 = Debug-2019|x64
+ Debug-2020|Any CPU = Debug-2020|Any CPU
Debug-2020|x64 = Debug-2020|x64
+ Debug-2021|Any CPU = Debug-2021|Any CPU
Debug-2021|x64 = Debug-2021|x64
+ Debug-2022|Any CPU = Debug-2022|Any CPU
+ Debug-2022|x64 = Debug-2022|x64
+ Debug-2024|Any CPU = Debug-2024|Any CPU
+ Debug-2024|x64 = Debug-2024|x64
+ Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
+ Release-2019|Any CPU = Release-2019|Any CPU
+ Release-2019|x64 = Release-2019|x64
+ Release-2020|Any CPU = Release-2020|Any CPU
Release-2020|x64 = Release-2020|x64
+ Release-2021|Any CPU = Release-2021|Any CPU
Release-2021|x64 = Release-2021|x64
- Debug-2022|x64 = Debug-2022|x64
+ Release-2022|Any CPU = Release-2022|Any CPU
Release-2022|x64 = Release-2022|x64
- Debug-2019|x64 = Debug-2019|x64
- Release-2019|x64 = Release-2019|x64
+ Release-2024|Any CPU = Release-2024|Any CPU
+ Release-2024|x64 = Release-2024|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug|Any CPU.Build.0 = Debug|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2019|Any CPU.ActiveCfg = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2019|Any CPU.Build.0 = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2019|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2020|Any CPU.ActiveCfg = Debug-2020|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2020|Any CPU.Build.0 = Debug-2020|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2020|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2021|Any CPU.ActiveCfg = Debug-2021|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2021|Any CPU.Build.0 = Debug-2021|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2021|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2022|Any CPU.ActiveCfg = Debug-2022|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2022|Any CPU.Build.0 = Debug-2022|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2022|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2024|Any CPU.ActiveCfg = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2024|Any CPU.Build.0 = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2024|x64.ActiveCfg = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2024|x64.Build.0 = Debug-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release|Any CPU.ActiveCfg = Release|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release|Any CPU.Build.0 = Release|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Release|x64.ActiveCfg = Release|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2019|Any CPU.ActiveCfg = Release-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2019|Any CPU.Build.0 = Release-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2019|x64.ActiveCfg = Release|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2020|Any CPU.ActiveCfg = Release-2020|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2020|Any CPU.Build.0 = Release-2020|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2020|x64.ActiveCfg = Release|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2021|Any CPU.ActiveCfg = Release-2021|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2021|Any CPU.Build.0 = Release-2021|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2021|x64.ActiveCfg = Release|x64
- {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2022|x64.ActiveCfg = Debug|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2022|Any CPU.ActiveCfg = Release-2022|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2022|Any CPU.Build.0 = Release-2022|x64
{09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2022|x64.ActiveCfg = Release|x64
- {09319316-391C-4F82-8EFE-82C2CAE539BD}.Debug-2019|x64.ActiveCfg = Debug|x64
- {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2019|x64.ActiveCfg = Release|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2024|Any CPU.ActiveCfg = Release-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2024|Any CPU.Build.0 = Release-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2024|x64.ActiveCfg = Release-2019|x64
+ {09319316-391C-4F82-8EFE-82C2CAE539BD}.Release-2024|x64.Build.0 = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug|Any CPU.Build.0 = Debug|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug|x64.ActiveCfg = Debug|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug|x64.Build.0 = Debug|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|Any CPU.ActiveCfg = Debug-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|Any CPU.Build.0 = Debug-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|x64.ActiveCfg = Debug-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|x64.Build.0 = Debug-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2020|Any CPU.ActiveCfg = Debug-2020|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2020|Any CPU.Build.0 = Debug-2020|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2020|x64.ActiveCfg = Debug-2020|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2020|x64.Build.0 = Debug-2020|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2021|Any CPU.ActiveCfg = Debug-2021|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2021|Any CPU.Build.0 = Debug-2021|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2021|x64.ActiveCfg = Debug-2021|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2021|x64.Build.0 = Debug-2021|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|Any CPU.ActiveCfg = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|Any CPU.Build.0 = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|x64.ActiveCfg = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|x64.Build.0 = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2024|Any CPU.ActiveCfg = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2024|Any CPU.Build.0 = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2024|x64.ActiveCfg = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2024|x64.Build.0 = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release|Any CPU.ActiveCfg = Release|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release|Any CPU.Build.0 = Release|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release|x64.ActiveCfg = Release|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release|x64.Build.0 = Release|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|Any CPU.ActiveCfg = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|Any CPU.Build.0 = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|x64.ActiveCfg = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|x64.Build.0 = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2020|Any CPU.ActiveCfg = Release-2020|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2020|Any CPU.Build.0 = Release-2020|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2020|x64.ActiveCfg = Release-2020|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2020|x64.Build.0 = Release-2020|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2021|Any CPU.ActiveCfg = Release-2021|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2021|Any CPU.Build.0 = Release-2021|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2021|x64.ActiveCfg = Release-2021|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2021|x64.Build.0 = Release-2021|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|x64.ActiveCfg = Debug-2022|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2022|x64.Build.0 = Debug-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2022|Any CPU.ActiveCfg = Release-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2022|Any CPU.Build.0 = Release-2022|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2022|x64.ActiveCfg = Release-2022|x64
{4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2022|x64.Build.0 = Release-2022|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|x64.ActiveCfg = Debug-2019|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Debug-2019|x64.Build.0 = Debug-2019|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|x64.ActiveCfg = Release-2019|x64
- {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2019|x64.Build.0 = Release-2019|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2024|Any CPU.ActiveCfg = Release-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2024|Any CPU.Build.0 = Release-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2024|x64.ActiveCfg = Release-2022|x64
+ {4F1C0A21-9543-49D1-AA7E-F69EEA02F905}.Release-2024|x64.Build.0 = Release-2022|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Debug|x64.ActiveCfg = Debug|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Debug|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|Any CPU.Build.0 = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|x64.ActiveCfg = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2020|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2020|Any CPU.Build.0 = Debug|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Debug-2020|x64.ActiveCfg = Debug|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Debug-2020|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2021|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2021|Any CPU.Build.0 = Debug|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Debug-2021|x64.ActiveCfg = Debug|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Debug-2021|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|Any CPU.Build.0 = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|x64.ActiveCfg = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2024|Any CPU.ActiveCfg = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2024|Any CPU.Build.0 = Debug|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2024|x64.ActiveCfg = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2024|x64.Build.0 = Debug|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release|Any CPU.Build.0 = Release|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Release|x64.ActiveCfg = Release|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Release|x64.Build.0 = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|Any CPU.Build.0 = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|x64.ActiveCfg = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|x64.Build.0 = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2020|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2020|Any CPU.Build.0 = Release|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2020|x64.ActiveCfg = Release|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2020|x64.Build.0 = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2021|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2021|Any CPU.Build.0 = Release|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2021|x64.ActiveCfg = Release|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2021|x64.Build.0 = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2022|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2022|Any CPU.Build.0 = Release|Any CPU
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2022|x64.ActiveCfg = Release|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|x64.ActiveCfg = Debug|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2022|x64.Build.0 = Debug|x64
{171002DA-A573-4549-BBB5-708277683B4D}.Release-2022|x64.Build.0 = Release|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|x64.ActiveCfg = Debug|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Debug-2019|x64.Build.0 = Debug|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|x64.ActiveCfg = Release|x64
- {171002DA-A573-4549-BBB5-708277683B4D}.Release-2019|x64.Build.0 = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2024|Any CPU.ActiveCfg = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2024|Any CPU.Build.0 = Release|Any CPU
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2024|x64.ActiveCfg = Release|x64
+ {171002DA-A573-4549-BBB5-708277683B4D}.Release-2024|x64.Build.0 = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug|Any CPU.Build.0 = Debug|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug|x64.ActiveCfg = Debug|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|Any CPU.ActiveCfg = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|Any CPU.Build.0 = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|x64.ActiveCfg = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2020|Any CPU.ActiveCfg = Debug-2020|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2020|Any CPU.Build.0 = Debug-2020|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2020|x64.ActiveCfg = Debug|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2020|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2021|Any CPU.ActiveCfg = Debug-2021|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2021|Any CPU.Build.0 = Debug-2021|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2021|x64.ActiveCfg = Debug|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2021|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|Any CPU.ActiveCfg = Debug-2022|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|Any CPU.Build.0 = Debug-2022|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|x64.ActiveCfg = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2024|Any CPU.ActiveCfg = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2024|Any CPU.Build.0 = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2024|x64.ActiveCfg = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2024|x64.Build.0 = Debug-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release|Any CPU.ActiveCfg = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release|Any CPU.Build.0 = Release|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release|x64.ActiveCfg = Release|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release|x64.Build.0 = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|Any CPU.ActiveCfg = Release-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|Any CPU.Build.0 = Release-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|x64.ActiveCfg = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|x64.Build.0 = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2020|Any CPU.ActiveCfg = Release-2020|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2020|Any CPU.Build.0 = Release-2020|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2020|x64.ActiveCfg = Release|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2020|x64.Build.0 = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2021|Any CPU.ActiveCfg = Release-2021|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2021|Any CPU.Build.0 = Release-2021|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2021|x64.ActiveCfg = Release|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2021|x64.Build.0 = Release|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|x64.ActiveCfg = Debug|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2022|x64.Build.0 = Debug|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2022|Any CPU.ActiveCfg = Release-2022|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2022|Any CPU.Build.0 = Release-2022|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2022|x64.ActiveCfg = Release|x64
{2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2022|x64.Build.0 = Release|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|x64.ActiveCfg = Debug|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Debug-2019|x64.Build.0 = Debug|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|x64.ActiveCfg = Release|x64
- {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2019|x64.Build.0 = Release|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2024|Any CPU.ActiveCfg = Release-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2024|Any CPU.Build.0 = Release-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2024|x64.ActiveCfg = Release-2019|x64
+ {2388D458-990B-4573-B1EF-E13E27C3A003}.Release-2024|x64.Build.0 = Release-2019|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug|x64.ActiveCfg = Debug|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|Any CPU.Build.0 = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|x64.ActiveCfg = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2020|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2020|Any CPU.Build.0 = Debug|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2020|x64.ActiveCfg = Debug|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2020|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2021|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2021|Any CPU.Build.0 = Debug|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2021|x64.ActiveCfg = Debug|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2021|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|Any CPU.Build.0 = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|x64.ActiveCfg = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2024|Any CPU.ActiveCfg = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2024|Any CPU.Build.0 = Debug|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2024|x64.ActiveCfg = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2024|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release|Any CPU.Build.0 = Release|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Release|x64.ActiveCfg = Release|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Release|x64.Build.0 = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|Any CPU.Build.0 = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|x64.ActiveCfg = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|x64.Build.0 = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2020|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2020|Any CPU.Build.0 = Release|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2020|x64.ActiveCfg = Release|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2020|x64.Build.0 = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2021|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2021|Any CPU.Build.0 = Release|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2021|x64.ActiveCfg = Release|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2021|x64.Build.0 = Release|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|x64.ActiveCfg = Debug|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2022|x64.Build.0 = Debug|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2022|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2022|Any CPU.Build.0 = Release|Any CPU
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2022|x64.ActiveCfg = Release|x64
{C08A902A-404B-4CB9-A395-5743A4453733}.Release-2022|x64.Build.0 = Release|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|x64.ActiveCfg = Debug|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Debug-2019|x64.Build.0 = Debug|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|x64.ActiveCfg = Release|x64
- {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2019|x64.Build.0 = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2024|Any CPU.ActiveCfg = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2024|Any CPU.Build.0 = Release|Any CPU
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2024|x64.ActiveCfg = Release|x64
+ {C08A902A-404B-4CB9-A395-5743A4453733}.Release-2024|x64.Build.0 = Release|x64
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug|Any CPU.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug|Any CPU.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug|x64.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug|x64.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2019|Any CPU.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2019|Any CPU.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2019|x64.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2019|x64.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2020|Any CPU.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2020|Any CPU.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2020|x64.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2020|x64.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2021|Any CPU.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2021|Any CPU.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2021|x64.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2021|x64.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2022|Any CPU.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2022|Any CPU.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2022|x64.ActiveCfg = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2022|x64.Build.0 = Debug-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2024|Any CPU.ActiveCfg = Debug-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2024|Any CPU.Build.0 = Debug-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2024|x64.ActiveCfg = Debug-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Debug-2024|x64.Build.0 = Debug-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release|Any CPU.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release|Any CPU.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release|x64.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release|x64.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2019|Any CPU.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2019|Any CPU.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2019|x64.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2019|x64.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2020|Any CPU.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2020|Any CPU.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2020|x64.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2020|x64.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2021|Any CPU.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2021|Any CPU.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2021|x64.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2021|x64.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2022|Any CPU.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2022|Any CPU.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2022|x64.ActiveCfg = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2022|x64.Build.0 = Release-2022|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2024|Any CPU.ActiveCfg = Release-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2024|Any CPU.Build.0 = Release-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2024|x64.ActiveCfg = Release-2024|Any CPU
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88}.Release-2024|x64.Build.0 = Release-2024|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -141,6 +345,7 @@ Global
{171002DA-A573-4549-BBB5-708277683B4D} = {B6823317-E2AC-47C9-944B-093A084DCB86}
{2388D458-990B-4573-B1EF-E13E27C3A003} = {B6823317-E2AC-47C9-944B-093A084DCB86}
{C08A902A-404B-4CB9-A395-5743A4453733} = {A7C3B085-2345-4D50-8D75-16C4429D18C0}
+ {7B1C7490-A794-4B21-AD7A-1DAF47AB2B88} = {B6823317-E2AC-47C9-944B-093A084DCB86}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {440C743A-A1DD-46EA-A56F-5A92471914C6}
diff --git a/src/OpenProject.Navisworks/Application/App.cs b/src/OpenProject.Navisworks/Application/App.cs
new file mode 100644
index 00000000..27c04bf5
--- /dev/null
+++ b/src/OpenProject.Navisworks/Application/App.cs
@@ -0,0 +1,67 @@
+using Autodesk.Navisworks.Api.Plugins;
+using OpenProjectNavisworks.Command;
+using OpenProjectNavisworks.Properties;
+using Serilog;
+using Test = OpenProjectNavisworks.Command.Test;
+using OpenProject.Shared.Logging;
+using System;
+
+
+namespace OpenProjectNavisworks.Application;
+
+[Plugin("IDP", "PavelNedviga,TachkovMaksim", DisplayName = "IDP-ViewPoint")]
+[RibbonLayout("AddinManagerRibbon.xaml")]
+[RibbonTab("ID_AddinManager_TAB", DisplayName = "IDP")]
+[Command("IDE_ButtonConnectOpenProject",
+ DisplayName = "IDP-ViewPoint",
+ Icon = "Resources\\OpenProjectLogo16.png",
+ LargeIcon = "Resources\\OpenProjectLogo32.png",
+ ToolTip = "Plugin IDP-ViewPoint")]
+
+
+public class App : CommandHandlerPlugin
+{
+ public override int ExecuteCommand(string name, params string[] parameters)
+ {
+
+
+ // Buttons
+ switch (name)
+ {
+ case "ID_ButtonAddinManagerManual":
+ AddInManagerManual addInManagerManual = new AddInManagerManual();
+ addInManagerManual.Execute();
+ break;
+ case "ID_ButtonAddinManagerFaceless":
+ AddInManagerFaceLess addInManagerFaceless = new AddInManagerFaceLess();
+ addInManagerFaceless.Execute();
+ break;
+ case "ID_ButtonDockPanelCommand":
+ DockPanelCommand dockPanelCommand = new DockPanelCommand();
+ dockPanelCommand.Execute();
+ break;
+ case "ID_ButtonTest":
+ Test test = new Test();
+ test.Execute();
+ break;
+ case "IDE_ButtonConnectOpenProject":
+ ConnectOpenProject connOP = new ConnectOpenProject();
+ connOP.Execute();
+ break;
+ case "IDE_ButtonSettingsOpenProject":
+ ConnectSettingsOpenProject connSetOP = new ConnectSettingsOpenProject();
+ connSetOP.Execute();
+ break;
+ case "IDE_ButtonGuide":
+ Guide guide = new Guide();
+ guide.Execute();
+ break;
+ case "IDE_ButtonInfo":
+ Info info = new Info();
+ info.Execute();
+ break;
+ }
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Command/AddInManagerCommand.cs b/src/OpenProject.Navisworks/Command/AddInManagerCommand.cs
new file mode 100644
index 00000000..f8175682
--- /dev/null
+++ b/src/OpenProject.Navisworks/Command/AddInManagerCommand.cs
@@ -0,0 +1,107 @@
+using System.Data.Common;
+using System.Diagnostics;
+using System.IO;
+using System.Windows;
+using Autodesk.Navisworks.Api.Plugins;
+using OpenProjectNavisworks.Model;
+using OpenProjectNavisworks.Entry;
+using MessageBox = System.Windows.MessageBox;
+
+namespace OpenProjectNavisworks.Command;
+
+public abstract class IAddinCommand
+{
+ public abstract int Action(params string[] parameters);
+
+ public void Execute(params string[] parameters)
+ {
+ try
+ {
+ Action(parameters);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.ToString());
+ }
+ }
+}
+///
+/// Executer the command Addin Manager Manual
+///
+public class AddInManagerManual : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ Debug.Listeners.Clear();
+ Trace.Listeners.Clear();
+ CodeListener codeListener = new CodeListener();
+ Debug.Listeners.Add(codeListener);
+ return AddinManagerBase.Instance.ExecuteCommand(false, parameters);
+ }
+}
+
+///
+/// Execute the command Addin Manager Faceless
+///
+public class AddInManagerFaceLess : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ return AddinManagerBase.Instance.ExecuteCommand(true, parameters);
+ }
+}
+
+
+///
+/// Execute the command for connection to project
+///
+public class ConnectOpenProject : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ Model.NavisworksWrapper.Document = Autodesk.Navisworks.Api.Application.ActiveDocument;
+ //Model.NavisworksWrapper.Document Autodesk.Navisworks.Api.Application;
+ return CmdMain.ExecuteCommand(true, parameters);
+ }
+}
+
+
+///
+/// Execute the command for connection settings to project
+///
+public class ConnectSettingsOpenProject : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ Model.NavisworksWrapper.Document = Autodesk.Navisworks.Api.Application.ActiveDocument;
+ return CmdMainSettings.ExecuteCommand(true, parameters);
+ }
+}
+
+
+public class Test : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ MessageBox.Show("Hello World");
+ return 0;
+ }
+}
+
+public class Guide : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ Process.Start("http://books.ide-spb.com/books/autodesk-revit/page/idp-viewpoint");
+ return 0;
+ }
+}
+
+public class Info : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ Process.Start("https://gitlab.ide-spb.com/Nedviga.Pavel/openprojectnavisbcf/-/releases");
+ return 0;
+ }
+}
diff --git a/src/OpenProject.Navisworks/Command/AddinManagerBase.cs b/src/OpenProject.Navisworks/Command/AddinManagerBase.cs
new file mode 100644
index 00000000..ac62d085
--- /dev/null
+++ b/src/OpenProject.Navisworks/Command/AddinManagerBase.cs
@@ -0,0 +1,159 @@
+using System.IO;
+using System.Windows;
+using Autodesk.Navisworks.Api.Plugins;
+using OpenProjectNavisworks.Model;
+using OpenProjectNavisworks.View;
+using OpenProjectNavisworks.ViewModel;
+using MessageBox = System.Windows.MessageBox;
+
+namespace OpenProjectNavisworks.Command;
+
+public sealed class AddinManagerBase
+{
+ public int ExecuteCommand(bool faceless, params string[] parameters)
+ {
+ var vm = new AddInManagerViewModel();
+ if (_activeCmd != null && faceless)
+ {
+ return RunActiveCommand(vm, parameters);
+ }
+ var FrmAddInManager = new FrmAddInManager(vm);
+ FrmAddInManager.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ FrmAddInManager.SetNavisAsWindowOwner();
+ FrmAddInManager.ShowDialog();
+ return 0;
+ }
+
+ public string ActiveTempFolder
+ {
+ get => _activeTempFolder;
+ set => _activeTempFolder = value;
+ }
+
+ public int RunActiveCommand(AddInManagerViewModel vm, params string[] parameters)
+ {
+ var filePath = _activeCmd.FilePath;
+ if (!File.Exists(filePath))
+ {
+ MessageBox.Show("File not found: " + filePath,DefaultSetting.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
+ return 0;
+ }
+ int result;
+ try
+ {
+ vm.AssemLoader.HookAssemblyResolve();
+ var assembly = vm.AssemLoader.LoadAddinsToTempFolder(filePath, false);
+ if (null == assembly)
+ {
+ result = 0;
+ }
+ else
+ {
+ _activeTempFolder = vm.AssemLoader.TempFolder;
+ if (assembly.CreateInstance(_activeCmdItem.FullClassName) is not AddInPlugin AddInPlugin)
+ {
+ result = 0;
+ }
+ else
+ {
+ _activeEc = AddInPlugin;
+ return _activeEc.Execute(parameters);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.ToString());
+ result = 0;
+ }
+ finally
+ {
+ vm.AssemLoader.UnhookAssemblyResolve();
+ vm.AssemLoader.CopyGeneratedFilesBack();
+ }
+
+ return result;
+ }
+
+ public static AddinManagerBase Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+#pragma warning disable RCS1059 // Avoid locking on publicly accessible instance.
+ lock (typeof(AddinManagerBase))
+ {
+ if (_instance == null)
+ {
+ _instance = new AddinManagerBase();
+ }
+ }
+#pragma warning restore RCS1059 // Avoid locking on publicly accessible instance.
+ }
+
+ return _instance;
+ }
+ }
+
+ private AddinManagerBase()
+ {
+ _addinManager = new AddinManager();
+ _activeCmd = null;
+ _activeCmdItem = null;
+ _activeApp = null;
+ _activeAppItem = null;
+ }
+
+ public AddInPlugin ActiveEC
+ {
+ get => _activeEc;
+ set => _activeEc = value;
+ }
+
+ public Addin ActiveCmd
+ {
+ get => _activeCmd;
+ set => _activeCmd = value;
+ }
+
+ public AddinItem ActiveCmdItem
+ {
+ get => _activeCmdItem;
+ set => _activeCmdItem = value;
+ }
+
+ public Addin ActiveApp
+ {
+ get => _activeApp;
+ set => _activeApp = value;
+ }
+
+ public AddinItem ActiveAppItem
+ {
+ get => _activeAppItem;
+ set => _activeAppItem = value;
+ }
+
+ public AddinManager AddinManager
+ {
+ get => _addinManager;
+ set => _addinManager = value;
+ }
+
+ private string _activeTempFolder = string.Empty;
+
+ private static volatile AddinManagerBase _instance;
+
+ private AddInPlugin _activeEc;
+
+ private Addin _activeCmd;
+
+ private AddinItem _activeCmdItem;
+
+ private Addin _activeApp;
+
+ private AddinItem _activeAppItem;
+
+ private AddinManager _addinManager;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Command/DocpanelCommand.cs b/src/OpenProject.Navisworks/Command/DocpanelCommand.cs
new file mode 100644
index 00000000..02fb2180
--- /dev/null
+++ b/src/OpenProject.Navisworks/Command/DocpanelCommand.cs
@@ -0,0 +1,35 @@
+using Autodesk.Navisworks.Api.ApplicationParts;
+using Autodesk.Navisworks.Api.Plugins;
+using OpenProjectNavisworks.Model;
+
+namespace OpenProjectNavisworks.Command;
+
+public class DockPanelCommand : IAddinCommand
+{
+ public override int Action(params string[] parameters)
+ {
+ string PluginId = $"OpenProjectNavisworks.ShowHidePanel.ChuongMep";
+ if (Autodesk.Navisworks.Api.Application.IsAutomated)
+ {
+ throw new InvalidOperationException("Invalid when running using Automation");
+ }
+ ApplicationPlugins applicationPlugins = Autodesk.Navisworks.Api.Application.Plugins;
+ PluginRecord pr = applicationPlugins.FindPlugin(PluginId);
+ if (pr != null && pr is DockPanePluginRecord && pr.IsEnabled)
+ {
+ //check if it needs loading
+ if (pr.LoadedPlugin == null)
+ {
+ pr.LoadPlugin();
+ }
+
+ if (pr.LoadedPlugin is DockPanePlugin dpp)
+ {
+ //switch the Visible flag
+ dpp.Visible = !dpp.Visible;
+ }
+ }
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Data/NavisworksUtils.cs b/src/OpenProject.Navisworks/Data/NavisworksUtils.cs
new file mode 100644
index 00000000..5f00ac85
--- /dev/null
+++ b/src/OpenProject.Navisworks/Data/NavisworksUtils.cs
@@ -0,0 +1,198 @@
+using System;
+//using Autodesk.Revit.DB;
+using Autodesk.Navisworks.Api;
+using OpenProject.Shared.Math3D;
+using decMath = DecimalMath.DecimalEx;
+using OpenProjectNavisworks.Model;
+
+namespace OpenProjectNavisworks.Data
+{
+ public static class NavisworksUtils
+ {
+ public static Dictionary ModelItems { get; set; }
+ public static Dictionary> ModelModelItems { get; set; }
+ public static IEnumerable HiddenModelItems { get; set; }
+
+ ///
+ /// Converts a camera position according to a projects base position.
+ /// It takes a position in coordinates of the project base location and
+ /// transform them to global coordinates or does so in reverse, if the flag is set to true.
+ ///
+ /// The revit project base location
+ /// The source position
+ /// Default is false, if set to true, transformation is done from global coordinates to
+ /// revit project base location coordinates
+ /// The resulting position
+ public static Position TransformCameraPosition(
+ ProjectPositionWrapper projectBase,
+ Position position,
+ bool reverse = false)
+ {
+ var i = reverse ? -1 : 1;
+
+ Vector3 translation = projectBase.GetTranslation() * i;
+ var rotation = i * projectBase.Angle;
+
+ // do translation before rotation if we transform from global to local coordinates
+ Vector3 center = reverse
+ ? new Vector3(
+ position.Center.X + translation.X,
+ position.Center.Y + translation.Y,
+ position.Center.Z + translation.Z)
+ : position.Center;
+
+ // rotation
+ var centerX = center.X * decMath.Cos(rotation) - center.Y * decMath.Sin(rotation);
+ var centerY = center.X * decMath.Sin(rotation) + center.Y * decMath.Cos(rotation);
+
+ // do translation after rotation if we transform from local to global coordinates
+ Vector3 newCenter = reverse
+ ? new Vector3(centerX, centerY, center.Z)
+ : new Vector3(centerX + translation.X, centerY + translation.Y, center.Z + translation.Z);
+
+ var forwardX = position.Forward.X * decMath.Cos(rotation) -
+ position.Forward.Y * decMath.Sin(rotation);
+ var forwardY = position.Forward.X * decMath.Sin(rotation) +
+ position.Forward.Y * decMath.Cos(rotation);
+ var newForward = new Vector3(forwardX, forwardY, position.Forward.Z);
+
+ var upX = position.Up.X * decMath.Cos(rotation) - position.Up.Y * decMath.Sin(rotation);
+ var upY = position.Up.X * decMath.Sin(rotation) + position.Up.Y * decMath.Cos(rotation);
+ var newUp = new Vector3(upX, upY, position.Up.Z);
+
+ return new Position(newCenter, newForward, newUp);
+ }
+
+ ///
+ /// Converts a object into a
+ ///
+ /// The position object
+ /// the converted view orientation object
+ public static Tuple ToViewOrientation3D(this Position position) =>
+ //new(position.Center.ToNavisworksPoint3D(),
+ // position.Up.ToNavisworksPoint3D(),
+ // position.Forward.ToNavisworksPoint3D());
+ new(position.Center.ToNavisworksPoint3DWithoutUnitsConversion(),
+ position.Up.ToNavisworksPoint3DWithoutUnitsConversion(),
+ position.Forward.ToNavisworksPoint3DWithoutUnitsConversion());
+
+ ///
+ /// Converts a object into a
+ ///
+ /// The vector3 object
+ /// The Revit vector object
+ public static Point3D ToNavisworksPoint3D(this Vector3 vec) =>
+ new (
+ ToInternalAppUnit(Convert.ToDouble(vec.X)),
+ ToInternalAppUnit(Convert.ToDouble(vec.Y)),
+ ToInternalAppUnit(Convert.ToDouble(vec.Z)));
+ public static Point3D ToNavisworksPoint3DWithoutUnitsConversion(this Vector3 vec) =>
+ new(
+ Convert.ToDouble(vec.X),
+ Convert.ToDouble(vec.Y),
+ Convert.ToDouble(vec.Z));
+
+ ///
+ /// Converts a object into a
+ ///
+ /// The vector3 object
+ /// The Revit vector object
+ public static Vector3 ToVector3(this Point3D vec) => new(vec.X.ToDecimal(), vec.Y.ToDecimal(), vec.Z.ToDecimal());
+
+ ///
+ /// Converts some basic revit view values to a view box height and a view box width.
+ /// The revit views are defined by coordinates in project space.
+ ///
+ /// The top right corner of the revit view.
+ /// The bottom left corner of the revit view.
+ /// The right direction of the revit view.
+ /// A tuple of the height and the width of the view box.
+ //public static (double viewBoxHeight, double viewBoxWidth) ConvertToViewBoxValues(
+ // Point3D topRight, Point3D bottomLeft, Point3D right)
+ //{
+ // var diagonal = topRight.Subtract(bottomLeft);
+ // var distance = topRight.DistanceTo(bottomLeft);
+ // var angleBetweenBottomAndDiagonal = diagonal.Angle(right.ToVector3D());
+
+ // var height = distance * Math.Sin(angleBetweenBottomAndDiagonal);
+ // var width = distance * Math.Cos(angleBetweenBottomAndDiagonal);
+
+ // return (height, width);
+ //}
+
+ ///
+ /// Converts feet units to meters. Feet are the internal Revit units.
+ ///
+ /// Value in internal Revit units to be converted to meters
+ ///
+ public static double ToMeters(this double internalUnits)
+ {
+ return UnitConversion.ScaleFactor(OpenProjectNavisworks.Model.NavisworksWrapper.Document.Units/*Units.Meters*/, Units.Meters)* internalUnits;
+//#if !(Version2021 || Version2022)
+// return UnitUtils.ConvertFromInternalUnits(internalUnits, DisplayUnitType.DUT_METERS);
+//#else
+// return UnitUtils.ConvertFromInternalUnits(internalUnits, UnitTypeId.Meters);
+//#endif
+ }
+
+ ///
+ /// Converts meters units to feet. Feet are the internal Revit units.
+ ///
+ /// Value in feet to be converted to feet
+ ///
+ public static double ToInternalAppUnit(this double meters)
+ {
+ return meters*UnitConversion.ScaleFactor(Units.Meters, OpenProjectNavisworks.Model.NavisworksWrapper.Document.Units);
+//#if Version2021 || Version2022
+// return UnitUtils.ConvertToInternalUnits(meters, UnitTypeId.Meters);
+//#else
+// return UnitUtils.ConvertToInternalUnits(meters, DisplayUnitType.DUT_METERS);
+//#endif
+ }
+
+ ///
+ /// Converts a vector containing values in feet units to meter. Feet are the internal Revit units.
+ ///
+ /// The vector with values in feet
+ /// The vector with values in meter
+ public static Vector3 ToMeters(this Vector3 vec)
+ {
+ var x = vec.X.IsFinite() ? Convert.ToDouble(vec.X).ToMeters().ToDecimal() : vec.X;
+ var y = vec.Y.IsFinite() ? Convert.ToDouble(vec.Y).ToMeters().ToDecimal() : vec.Y;
+ var z = vec.Z.IsFinite() ? Convert.ToDouble(vec.Z).ToMeters().ToDecimal() : vec.Z;
+
+ return new Vector3(x, y, z);
+ }
+
+ ///
+ /// Converts a vector containing values in meter units to feet. Feet are the internal Revit units.
+ ///
+ /// The vector with values in meter
+ /// The vector with values in feet
+ public static Vector3 ToInternalUnits(this Vector3 vec)
+ {
+ var x = vec.X.IsFinite() ? Convert.ToDouble(vec.X).ToInternalAppUnit().ToDecimal() : vec.X;
+ var y = vec.Y.IsFinite() ? Convert.ToDouble(vec.Y).ToInternalAppUnit().ToDecimal() : vec.Y;
+ var z = vec.Z.IsFinite() ? Convert.ToDouble(vec.Z).ToInternalAppUnit().ToDecimal() : vec.Z;
+
+ return new Vector3(x, y, z);
+ }
+
+ ///
+ /// Converts a position containing values in feet units to meter. Feet are the internal Revit units.
+ ///
+ /// The position with values in feet
+ /// The position with values in meter
+ public static Position ToMeters(this Position pos) =>
+ new(pos.Center.ToMeters(), pos.Forward.ToMeters(), pos.Up.ToMeters());
+
+ ///
+ /// Converts a position containing values in meter units to feet. Feet are the internal Revit units.
+ ///
+ /// The position with values in meter
+ /// The position with values in feet
+ public static Position ToInternalUnits(this Position pos) =>
+ //new(pos.Center.ToInternalUnits(), pos.Forward.ToInternalUnits(), pos.Up.ToInternalUnits());
+ new(pos.Center.ToInternalUnits(), pos.Forward, pos.Up);
+ }
+}
diff --git a/src/OpenProject.Navisworks/Data/ProjectPositionWrapper.cs b/src/OpenProject.Navisworks/Data/ProjectPositionWrapper.cs
new file mode 100644
index 00000000..9a5f3aa4
--- /dev/null
+++ b/src/OpenProject.Navisworks/Data/ProjectPositionWrapper.cs
@@ -0,0 +1,63 @@
+//using Autodesk.Revit.DB;
+using OpenProject.Shared.Math3D;
+
+namespace OpenProjectNavisworks.Data
+{
+ ///
+ /// A immutable wrapper class around the
+ ///
+ public sealed class ProjectPositionWrapper
+ {
+ ///
+ /// The translation of the project into east-west, mostly translated into a translation into x-direction.
+ ///
+ public decimal EastWest { get; }
+
+ ///
+ /// The translation of the project into north-south, mostly translated into a translation into y-direction.
+ ///
+ public decimal NorthSouth { get; }
+
+ ///
+ /// The elevation of the project, mostly translated into a translation into z-direction.
+ ///
+ public decimal Elevation { get; }
+
+ ///
+ /// The angle of the project north to true north.
+ ///
+ public decimal Angle { get; }
+
+ public ProjectPositionWrapper(decimal eastWest, decimal northSouth, decimal elevation, decimal angle)
+ {
+ EastWest = eastWest;
+ NorthSouth = northSouth;
+ Elevation = elevation;
+ Angle = angle;
+ }
+
+ public ProjectPositionWrapper()
+ {
+ EastWest = 0;
+ NorthSouth = 0;
+ Elevation = 0;
+ Angle = 0;
+ }
+
+ //public ProjectPositionWrapper(ProjectPosition projectPosition)
+ //{
+ // EastWest = projectPosition.EastWest.ToDecimal();
+ // NorthSouth = projectPosition.NorthSouth.ToDecimal();
+ // Elevation = projectPosition.Elevation.ToDecimal();
+ // Angle = projectPosition.Angle.ToDecimal();
+ //}
+
+ ///
+ /// Returns the project position's translation as a . The east-west translation is taken as x,
+ /// the north-south as y and the elevation as z. This represents the coordinates of the project as an
+ /// xyz-coordinate system.
+ ///
+ /// The project position's location as a vector.
+ public Vector3 GetTranslation() => new(EastWest, NorthSouth, Elevation);
+ }
+}
diff --git a/src/OpenProject.Navisworks/DockPanel/AddinManagerPane.cs b/src/OpenProject.Navisworks/DockPanel/AddinManagerPane.cs
new file mode 100644
index 00000000..94b59797
--- /dev/null
+++ b/src/OpenProject.Navisworks/DockPanel/AddinManagerPane.cs
@@ -0,0 +1,47 @@
+using System.Windows.Forms;
+using System.Windows.Forms.Integration;
+using Autodesk.Navisworks.Api.Plugins;
+using OpenProjectNavisworks.View.Control;
+
+namespace OpenProjectNavisworks.DockPanel
+{
+ [Plugin($"OpenProjectNavisworks.ShowHidePanel", "ChuongMep", DisplayName = "Trace/Debug Output", ToolTip = "Add-in-Manager")]
+ [DockPanePlugin(350, 600, AutoScroll = true, MinimumHeight = 300)]
+ class AddinManagerPane : DockPaneBase
+ {
+ public override Control CreateControlPane()
+ {
+ //create an ElementHost
+ ElementHost elementHost = new ElementHost
+ {
+ //assign the control
+ AutoSize = true,
+ Child = new LogControl(),
+
+
+ };
+ elementHost.CreateControl();
+ elementHost.ParentChanged += this.ElementHost_ParentChanged;
+ //return the ElementHost
+ return elementHost;
+ }
+
+ public override void DestroyControlPane(System.Windows.Forms.Control pane)
+ {
+ pane.Dispose();
+ }
+
+ ///
+ /// Resizing DockPane implies resizing WPF-based ElementHost
+ ///
+ /// ElementHost
+ ///
+ private void ElementHost_ParentChanged(object sender, EventArgs e)
+ {
+ if (sender is ElementHost elementHost)
+ {
+ elementHost.Dock = DockStyle.Fill;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/DockPanel/DockPaneBase.cs b/src/OpenProject.Navisworks/DockPanel/DockPaneBase.cs
new file mode 100644
index 00000000..a0d30cea
--- /dev/null
+++ b/src/OpenProject.Navisworks/DockPanel/DockPaneBase.cs
@@ -0,0 +1,20 @@
+using System.Windows.Forms;
+using Autodesk.Navisworks.Api.Plugins;
+
+namespace OpenProjectNavisworks.DockPanel
+{
+ public abstract class DockPaneBase : DockPanePlugin
+ {
+ ///
+ /// Add a control in navis
+ ///
+ ///
+ public abstract override Control CreateControlPane();
+
+ ///
+ /// dispose control from created in navis
+ ///
+ ///
+ public abstract override void DestroyControlPane(Control control);
+ }
+}
diff --git a/src/OpenProject.Navisworks/Entry/ActiveViewChangedZoomListener.cs b/src/OpenProject.Navisworks/Entry/ActiveViewChangedZoomListener.cs
new file mode 100644
index 00000000..ef702554
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/ActiveViewChangedZoomListener.cs
@@ -0,0 +1,65 @@
+////using Autodesk.Revit.DB;
+////using Autodesk.Revit.UI;
+////using Autodesk.Revit.UI.Events;
+//using Autodesk.Navisworks.Internal;
+//using Autodesk.Navisworks.Api;
+//using System;
+//using System.Linq;
+//using OpenProjectNavisworks.Data;
+//using OpenProjectNavisworks.Services;
+//using Serilog;
+
+//namespace OpenProjectNavisworks.Entry
+//{
+// ///
+// /// This class collects static functions that registers asynchronous callbacks to the idling state of the
+// /// , which deregister themselves after execution.
+// ///
+// public static class AppIdlingCallbackListener
+// {
+// ///
+// /// Sets a callback that applies a zoom to the current view.
+// ///
+// /// The current UI application.
+// /// The view ID for the view to be zoomed.
+// /// The zoom in decimal precision.
+// public static void SetPendingZoomChangedCallback(UIApplication app, ElementId viewId, decimal zoom)
+// {
+// void Callback(object sender, IdlingEventArgs args)
+// {
+// StatusBarService.SetStatusText("Zooming to scale '" + zoom + "' ...");
+// UIView currentView = app.ActiveUIDocument.GetOpenUIViews().First();
+// if (currentView.ViewId != viewId) return;
+
+// UIDocument uiDoc = app.ActiveUIDocument;
+// View activeView = uiDoc.ActiveView;
+
+// var zoomCorners = currentView.GetZoomCorners();
+// XYZ bottomLeft = zoomCorners[0];
+// XYZ topRight = zoomCorners[1];
+// var (currentHeight, currentWidth) =
+// RevitUtils.ConvertToViewBoxValues(topRight, bottomLeft, activeView.RightDirection);
+
+// var zoomedViewBoxHeight = Convert.ToDouble(zoom).ToInternalRevitUnit();
+// var zoomedViewBoxWidth = zoomedViewBoxHeight * currentWidth / currentHeight;
+
+// XYZ newTopRight = activeView.Origin
+// .Add(activeView.UpDirection.Multiply(zoomedViewBoxHeight / 2))
+// .Add(activeView.RightDirection.Multiply(zoomedViewBoxWidth / 2));
+// XYZ newBottomLeft = activeView.Origin
+// .Subtract(activeView.UpDirection.Multiply(zoomedViewBoxHeight / 2))
+// .Subtract(activeView.RightDirection.Multiply(zoomedViewBoxWidth / 2));
+
+// Log.Information("Zoom to {topRight} | {bottomLeft} ...", newTopRight.ToString(), newBottomLeft.ToString());
+// currentView.ZoomAndCenterRectangle(newTopRight, newBottomLeft);
+
+// StatusBarService.ResetStatusBarText();
+// Log.Information("Finished applying zoom for orthogonal view.");
+// app.Idling -= Callback;
+// }
+
+// Log.Information("Append zoom callback for orthogonal view to idle state of Revit application ...");
+// app.Idling += Callback;
+// }
+// }
+//}
diff --git a/src/OpenProject.Navisworks/Entry/CmdMain.cs b/src/OpenProject.Navisworks/Entry/CmdMain.cs
new file mode 100644
index 00000000..634501b6
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/CmdMain.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using Autodesk.Navisworks.Api.Plugins;
+namespace OpenProjectNavisworks.Entry;
+
+///
+/// Obfuscation Ignore for External Interface
+///
+[Obfuscation(Exclude = true, ApplyToMembers = false)]
+//[Transaction(TransactionMode.Manual)]
+//[Regeneration(RegenerationOption.Manual)]
+public sealed class CmdMain
+{
+ ///
+ /// Main Command Entry Point
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static int ExecuteCommand(bool apply, params string[] parameters)
+ {
+ var message = string.Empty;
+ return RibbonButtonClickHandler.OpenMainPluginWindow(Model.NavisworksWrapper.Document, ref message);
+ }
+}
+
diff --git a/src/OpenProject.Navisworks/Entry/CmdMainSettings.cs b/src/OpenProject.Navisworks/Entry/CmdMainSettings.cs
new file mode 100644
index 00000000..ef79f853
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/CmdMainSettings.cs
@@ -0,0 +1,22 @@
+//using Autodesk.Revit.Attributes;
+//using Autodesk.Revit.DB;
+//using Autodesk.Revit.UI;
+using System.Reflection;
+
+namespace OpenProjectNavisworks.Entry
+{
+ ///
+ /// Obfuscation Ignore for External Interface
+ ///
+ [Obfuscation(Exclude = true, ApplyToMembers = false)]
+ //[Transaction(TransactionMode.Manual)]
+ //[Regeneration(RegenerationOption.Manual)]
+ public sealed class CmdMainSettings
+ {
+ public static int ExecuteCommand(bool apply, params string[] parameters)
+ {
+ var message = string.Empty;
+ return RibbonButtonClickHandler.OpenSettingsPluginWindow(Model.NavisworksWrapper.Document, ref message);
+ }
+ }
+}
diff --git a/src/OpenProject.Navisworks/Entry/IpcHandler.cs b/src/OpenProject.Navisworks/Entry/IpcHandler.cs
new file mode 100644
index 00000000..18cb51d5
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/IpcHandler.cs
@@ -0,0 +1,180 @@
+using Autodesk.Navisworks.Gui;
+using Autodesk.Navisworks.Api;
+using Autodesk.Navisworks.Internal;
+using OpenProject.Shared;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using iabi.BCF.APIObjects.V21;
+using Newtonsoft.Json.Converters;
+using OpenProjectNavisworks.Extensions;
+using OpenProjectNavisworks.Services;
+using OpenProject.Shared.BcfApi;
+using ZetaIpc.Runtime.Client;
+using ZetaIpc.Runtime.Server;
+using ZetaIpc.Runtime.Helper;
+using OpenProjectNavisworks.Data;
+
+namespace OpenProjectNavisworks.Entry;
+public class IpcHandler
+{
+ private readonly Document _uiApp;
+ private Action _sendData;
+ private static readonly object _callbackStackLock = new();
+ private static readonly Stack _callbackStack = new();
+
+ public IpcHandler(Document uiApp)
+ {
+ _uiApp = uiApp ?? throw new ArgumentNullException(nameof(uiApp));
+
+
+ Autodesk.Navisworks.Api.Application.Idle += (_, _) =>
+ {
+ lock (_callbackStackLock)
+ {
+ if (!_callbackStack.Any()) return;
+
+ _callbackStack.Pop().Invoke();
+ }
+ };
+ }
+
+ public int StartLocalServerAndReturnPort()
+ {
+ var freePort = FreePortHelper.GetFreePort();
+ var server = new IpcServer();
+ server.Start(freePort);
+ server.ReceivedRequest += (_, e) =>
+ {
+ var eventArgs = JsonConvert.DeserializeObject(e.Request);
+ if (eventArgs == null) return;
+
+ var localMessageType = eventArgs.MessageType;
+ var localTrackingId = eventArgs.TrackingId;
+ var localMessagePayload = eventArgs.MessagePayload;
+
+ lock (_callbackStackLock)
+ {
+ _callbackStack.Push(() =>
+ {
+ switch (localMessageType)
+ {
+ case MessageTypes.VIEWPOINT_DATA:
+ {
+ try
+ {
+ BcfViewpointWrapper bcfViewpoint = MessageDeserializer.DeserializeBcfViewpoint(
+ new WebUiMessageEventArgs(localMessageType, localTrackingId, localMessagePayload));
+
+ // TODO: (if else) Hiding objects feature
+ //if(NavisworksUtils.ModelItems != null)
+ OpenViewpointEventHandler.ShowBcfViewpoint(bcfViewpoint);
+ //else
+ //{
+ // MessageHandler.ShowWarning(
+ // "Object dictionary is not ready yet!",
+ // "Object dictionary is ready, you can use ViewPoint now",
+ // "Object dictionary is ready");
+ //}
+ }
+ catch (Exception exception)
+ {
+ MessageHandler.ShowError(exception, "Error opening a viewpoint.");
+ }
+
+ break;
+ }
+ case MessageTypes.VIEWPOINT_GENERATION_REQUESTED:
+ try
+ {
+
+ AddViewpoint(localTrackingId);
+
+ }
+ catch (Exception exception)
+ {
+ MessageHandler.ShowError(exception, "Error generating a viewpoint.");
+ }
+
+ break;
+ }
+ });
+ }
+ };
+
+ return freePort;
+ }
+
+ public void StartLocalClient(int ipcWinServerPort)
+ {
+ var client = new IpcClient();
+ client.Initialize(ipcWinServerPort);
+ _sendData = message =>
+ {
+ try
+ {
+ client.Send(message);
+ }
+ catch (System.Net.WebException)
+ {
+ // We can ignore the WebException, it's raised after
+ // the shutdown event due to the other side just closing
+ // the open TCP connection. This is what's expected😊
+ }
+ };
+ }
+
+ public void SendShutdownRequestToDesktopApp()
+ {
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.CLOSE_DESKTOP_APPLICATION, "0", string.Empty);
+ var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
+ _sendData(jsonEventArgs);
+ }
+
+ public void SendOpenSettingsRequestToDesktopApp()
+ {
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.GO_TO_SETTINGS, "0", string.Empty);
+ var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
+ _sendData(jsonEventArgs);
+ }
+
+ public void SendBringBrowserToForegroundRequestToDesktopApp()
+ {
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.SET_BROWSER_TO_FOREGROUND, "0", string.Empty);
+ var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
+ _sendData(jsonEventArgs);
+ }
+
+ ///
+ /// Generates a viewpoint from the active view and sends the data as an event to bridge.
+ ///
+ /// The local message tracking id.
+ private void AddViewpoint(string trackingId)
+ {
+ JObject payload = GenerateJsonViewpoint();
+
+ // TODO: remove hack of snapshot data once #39135 is deployed
+ payload["snapshot"] = payload["snapshot"]?["snapshot_data"];
+
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.VIEWPOINT_GENERATED, trackingId, payload.ToString());
+ var so = JsonConvert.SerializeObject(eventArgs);
+ _sendData(so);
+ }
+
+ private JObject GenerateJsonViewpoint()
+ {
+ var serializerSettings = new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver(),
+ };
+
+ serializerSettings.Converters.Add(new StringEnumConverter(new SnakeCaseNamingStrategy(), false));
+
+ Viewpoint_POST viewpoint = _uiApp.GenerateViewpoint();
+ return JObject.Parse(JsonConvert.SerializeObject(viewpoint, serializerSettings));
+ }
+}
+
diff --git a/src/OpenProject.Navisworks/Entry/OpenViewpointEventHandler.cs b/src/OpenProject.Navisworks/Entry/OpenViewpointEventHandler.cs
new file mode 100644
index 00000000..fb022918
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/OpenViewpointEventHandler.cs
@@ -0,0 +1,729 @@
+//using Autodesk.Revit.DB;
+//using Autodesk.Revit.UI;
+using Autodesk.Navisworks.Api;
+using OpenProjectNavisworks.Data;
+using OpenProjectNavisworks.Extensions;
+using OpenProjectNavisworks.Services;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using OpenProject.Shared.BcfApi;
+using OpenProject.Shared.Math3D;
+using OpenProject.Shared.Math3D.Enumeration;
+using Serilog;
+using System.Windows.Forms;
+using iabi.BCF.APIObjects.V10.Viewpoint.Components;
+using iabi.BCF.APIObjects.V21;
+using System.Xml.Linq;
+using System.Windows.Controls;
+using System.Xml.Linq;
+using OpenProject.Shared;
+using System.Windows;
+using System.IO;
+using Optional.Collections;
+
+namespace OpenProjectNavisworks.Entry;
+
+///
+/// Obfuscation Ignore for External Interface
+///
+public class OpenViewpointEventHandler
+{
+ private const decimal _viewpointAngleThresholdRad = 0.087266462599716m;
+
+ ///
+ public void Execute(Document app)
+ {
+ try
+ {
+ ShowBCfViewpointInternal(app);
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, "Failed to load BCF viewpoint");
+ MessageHandler.ShowError(ex, "exception: " + ex);
+ }
+ }
+
+ ///
+ public string GetName() => nameof(OpenViewpointEventHandler);
+
+ private BcfViewpointWrapper _bcfViewpoint;
+
+ private static OpenViewpointEventHandler _instance;
+
+ private static OpenViewpointEventHandler Instance
+ {
+ get
+ {
+ if (_instance != null) return _instance;
+
+ _instance = new OpenViewpointEventHandler();
+ //ExternalEvent = ExternalEvent.Create(_instance);
+
+ return _instance;
+ }
+ }
+
+ //private static ExternalEvent ExternalEvent { get; set; }
+
+ ///
+ /// Wraps the raising of the external event and thus the execution of the event callback,
+ /// that show given bcf viewpoint.
+ ///
+ ///
+ /// http://help.autodesk.com/view/RVT/2014/ENU/?guid=GUID-0A0D656E-5C44-49E8-A891-6C29F88E35C0
+ /// http://matteocominetti.com/starting-a-transaction-from-an-external-application-running-outside-of-api-context-is-not-allowed/
+ ///
+ /// The bcf viewpoint to be shown in current view.
+ public static void ShowBcfViewpoint(BcfViewpointWrapper bcfViewpoint)
+ {
+ Log.Information("Received 'Opening BCF Viewpoint event'. Attempting to open viewpoint ...");
+ Instance._bcfViewpoint = bcfViewpoint;
+ //ExternalEvent.Raise();
+ Instance.Execute(OpenProjectNavisworks.Model.NavisworksWrapper.Document);
+ }
+
+ private void ShowBCfViewpointInternal(Document document)
+ {
+ var hasCamera = _bcfViewpoint.GetCamera().Match(
+ camera =>
+ {
+ Log.Information("Found camera type {t}, opening related OpenProject view ...", camera.Type.ToString());
+
+ var openProjectView = document.GetOpenProjectView(camera.Type);
+
+ //ResetView(document, openProjectView);
+ //Log.Information("Reset view...");
+
+ var viewPoint = document.CurrentViewpoint.CreateCopy();
+
+ Log.Information("Applied view point clipping planes in '{v}'.", "view");
+ ApplyClippingPlanes(document, ref viewPoint);
+
+ ApplyViewOrientationAndVisibility(document, camera, ref viewPoint);
+ Log.Information("Applied view orientation and visibility in '{v}'.", "view");
+
+
+
+ //if (!document.ActiveView.Id.Equals(openProjectView.Id))
+ //{
+ // Log.Information("Setting view '{t}' as active view ...", openProjectView.Name);
+ // document.ActiveView = openProjectView;
+ //}
+
+ //document.RefreshActiveView();
+ document.CurrentViewpoint.CopyFrom(viewPoint);
+ Log.Information("Refreshed active view.");
+ StatusBarService.ResetStatusBarText();
+
+ // MaksT
+ //SavedViewpoint saveViewPoint = new SavedViewpoint(document.CurrentViewpoint.ToViewpoint());
+ //saveViewPoint.DisplayName = "gettedView";
+ //document.SavedViewpoints.AddCopy(saveViewPoint);
+
+ ZoomIfNeeded(document, camera, ref viewPoint);
+ document.CurrentViewpoint.CopyFrom(viewPoint);
+ Log.Information("Finished loading BCF viewpoint.");
+
+ return true;
+ },
+ () => false);
+
+ if (!hasCamera) Log.Error("BCF viewpoint has no camera information. Aborting ...");
+ }
+
+
+ ///
+ /// Zoom the view to the correct scale, if necessary.
+ ///
+ /// In Revit, orthogonal views do not change their camera positions, when zooming in or out. Hence,
+ /// the values stored in the BCF viewpoint are not sufficient to restore the previously exported viewpoint.
+ /// In order to get correct zooming, the scale value (view box height) is used, to calculate the correct zoom
+ /// corners according to view center.
+ /// See https://thebuildingcoder.typepad.com/blog/2020/10/save-and-restore-3d-view-camera-settings.html
+ ///
+ private static void ZoomIfNeeded(Document app, Camera camera, ref Viewpoint viewPoint)
+ {
+ if (camera.Type != CameraType.Orthogonal || camera is not OrthogonalCamera orthoCam) return;
+
+ Log.Information("Found orthogonal camera, setting zoom callback ...");
+ StatusBarService.SetStatusText("Waiting for view to render to apply zoom ...");
+
+ //AppIdlingCallbackListener.SetPendingZoomChangedCallback(app, viewId, orthoCam.ViewToWorldScale);
+ //var cam1 = _bcfViewpoint.GetCamera().
+
+ //app.CurrentViewpoint.Value.HeightField = (double)orthoCam.ViewToWorldScale;
+ viewPoint.HeightField = NavisworksUtils.ToInternalAppUnit((double)orthoCam.ViewToWorldScale);
+
+
+ }
+
+ private static void ResetView(Document doc, Autodesk.Navisworks.Api.View view)
+ {
+ Log.Information("Removing current selection ...");
+ doc.CurrentSelection.Clear();
+ view.TrySetClippingPlanes("");
+ view.Dispose();
+
+ var models = doc.Models;
+
+ foreach (var model in models)
+ {
+ ModelItem rootItem = model.RootItem;
+ ModelItemEnumerableCollection modelItems = rootItem.DescendantsAndSelf;
+ doc.Models.SetHidden(modelItems, false);
+ Log.Information("Unhide {n} currently hidden elements in model {m}...", modelItems.Count(), model.FileName);
+ }
+ }
+
+ private void ApplyViewOrientationAndVisibility(Document doc, Camera camera, ref Viewpoint viewPoint)
+ {
+ StatusBarService.SetStatusText("Loading view point data ...");
+ Log.Information("Calculating view orientation from camera position ...");
+
+ var viewOrientation3D = NavisworksUtils.TransformCameraPosition(
+ new ProjectPositionWrapper(),
+ camera.Position.ToInternalUnits(),
+ true)
+ .ToViewOrientation3D();
+
+ if (camera.Type == CameraType.Perspective)
+ {
+ Log.Information("Setting active far viewer bound to zero ...");
+ viewPoint.Projection = ViewpointProjection.Perspective;
+ var fov = _bcfViewpoint.Viewpoint.Perspective_camera.Field_of_view;
+ //var fieldOfView = _bcfViewpoint.Viewpoint.Perspective_camera.Field_of_view;
+ viewPoint.HeightField = fov;
+ }
+ else
+ {
+ viewPoint.Projection = ViewpointProjection.Orthographic;
+ }
+
+
+
+ Log.Information("Applying new view orientation ...");
+ viewPoint.Position = camera.Position.Center.ToNavisworksPoint3D();
+ //Rotation3D rt3d = new Rotation3D(viewPoint.Rotation.ToAxisAndAngle().Axis,
+ // new UnitVector3D(
+ // Convert.ToDouble(-camera.Position.Forward.X),
+ // Convert.ToDouble(-camera.Position.Forward.Y),
+ // Convert.ToDouble(-camera.Position.Forward.Z)));
+ //Rotation3D rt3d = new Rotation3D(viewPoint.Rotation.ToAxisAndAngle().Axis,
+ //viewPoint.Rotation = rt3d;
+ //Rotation3D rt3d = new Rotation3D(viewPoint.Rotation.ToAxisAndAngle().Axis, new UnitVector3D(
+ // Convert.ToDouble(-camera.Position.Forward.X),
+ // Convert.ToDouble(-camera.Position.Forward.Y),
+ // Convert.ToDouble(-camera.Position.Forward.Z)));
+ //viewPoint.AlignDirection(new Vector3D(
+ // Convert.ToDouble(camera.Position.Forward.X),
+ // Convert.ToDouble(camera.Position.Forward.Y),
+ // Convert.ToDouble(camera.Position.Forward.Z)));
+ var focusPoint = new Point3D(
+ camera.Position.Center.ToNavisworksPoint3D().X + Convert.ToDouble(camera.Position.Forward.X),
+ camera.Position.Center.ToNavisworksPoint3D().Y + Convert.ToDouble(camera.Position.Forward.Y),
+ camera.Position.Center.ToNavisworksPoint3D().Z + Convert.ToDouble(camera.Position.Forward.Z));
+
+ viewPoint.PointAt(focusPoint);
+
+ Double forwardX = Convert.ToDouble(camera.Position.Forward.X);
+ Double forwardY = Convert.ToDouble(camera.Position.Forward.Y);
+ Double forwardZ = Convert.ToDouble(camera.Position.Forward.Z);
+
+ if (forwardX < 0.000001 && forwardY < 0.000001 && Math.Abs(forwardZ) == 1) // We should align by another vector in case of UP and DOWN view
+ {
+ viewPoint.AlignUp(new Vector3D(0, 1, 0));
+ }
+ else {
+ viewPoint.AlignUp(new Vector3D(0, 0, 1));
+ }
+
+
+
+ //Convert.ToDouble(camera.Position.Up.X),
+ //Convert.ToDouble(camera.Position.Up.Y),
+ //Convert.ToDouble(camera.Position.Up.Z)));
+
+
+
+ Log.Information("Applying element visibility ...");
+
+ doc.Models.ResetAllHidden();
+
+ // --------------------------------------------------------------------
+ // SELECTION
+ // --------------------------------------------------------------------
+ // --------------------------------------------------------------------
+ // METHOD 0. OLDMETHOD BRUTEFORCE
+
+ //var currentlyVisibleElements = doc.GetVisibleElementsOfView();
+ //var map = doc.GetIfcGuidElementIdMap(currentlyVisibleElements);
+ //var exceptionElements = GetViewpointVisibilityExceptions(map);
+ //var myexceptionElements = _bcfViewpoint.GetVisibilityExceptions().ToList();
+
+ //var selectedElements = _bcfViewpoint.GetSelection().Select(x => (doc.Models.RootItemDescendants.WhereInstanceGuid(IfcGuid.FromIfcGUID(x.Ifc_guid))).FirstOrDefault()).Distinct().ToList();
+
+ //var interestedGuids = _bcfViewpoint.GetSelection().Select(x => IfcGuid.FromIfcGUID(x.Ifc_guid));
+ //var selectedElements = doc.Models.RootItemDescendants.Where(elem => interestedGuids.Contains(elem.InstanceGuid)).Distinct().ToList();
+
+
+ //var selectedElements = GetViewpointSelection(map);
+ //if (exceptionElements.Any())
+ //{
+ // //if (_bcfViewpoint.GetVisibilityDefault()) // Сделали всегда True
+ // //{
+ // ////create a store for the visible items
+ // //ModelItemCollection visible = new ModelItemCollection();
+
+ // //doc.CurrentSelection.SelectAll();
+ // ////Add all the items that are visible to the visible collection
+ // //foreach (ModelItem item in Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems)
+ // //{
+ // // if (item.AncestorsAndSelf != null)
+ // // visible.AddRange(item.AncestorsAndSelf);
+ // // if (item.Descendants != null)
+ // // visible.AddRange(item.Descendants);
+ // //}
+ // //view.HideElementsTemporary(exceptionElements);
+ // //doc.Models.SetHidden(visible, false);
+
+ // //doc.Models.SetHidden(exceptionElements, true);
+ // doc.Models.SetHidden(exceptionElements, true);
+ // //selectedElements = selectedElements.Where(id => !exceptionElements.Contains(id)).ToList();
+ // //}
+ // //else
+ // //{
+ // //view.IsolateElementsTemporary(exceptionElements);
+
+ // //var sel = doc.CurrentSelection.SelectedItems;
+ // //sel.Clear();
+ // //sel.AddRange(exceptionElements);
+ // //sel.Invert(doc);
+ // //doc.Models.SetHidden(sel, true);
+ // //selectedElements = selectedElements.Where(id => exceptionElements.Contains(id)).ToList();
+ // // Hello world
+
+ // // doc.CurrentSelection.Clear();
+ // // doc.Models.ResetAllHidden();
+ // // doc.CurrentSelection.AddRange(exceptionElements);
+ // // selectedElements = selectedElements.Where(id => exceptionElements.Contains(id)).ToList();
+
+ // //}
+ // doc.CurrentSelection.Clear();
+ //}
+ ////view.ConvertTemporaryHideIsolateToPermanent();
+
+ // --------------------------------------------------------------------
+ // METHOD 1. BRUTEFORCE WHEREINSTANCE | SPEED 1/5
+ //var selectedElements1 = GetSelectedElementsBrute(doc);
+ // --------------------------------------------------------------------
+ // METHOD 2. BYSEARCH | SPEED 3/5
+ //List selectedElements2 = GetSelectedElementsBySearch(doc);
+ // --------------------------------------------------------------------
+
+ // --------------------------------------------------------------------
+ // METHOD 3.1 BY MODELS BRUTE MINE | SPEED 3/5
+ //List selectedElements2 = GetSelectedElementsByModels(doc);
+ // METHOD 3.2 BY MODELS SEARCH | Не удалось реализовать
+ //List selectedElements3 = GetSelectedElementsByModelsAndSearch(doc);
+ // METHOD 3.3 BY MODELS BRUTE WHERE |
+ List selectedElements3 = GetSelectedElementsByModelsWere(doc);
+
+ // --------------------------------------------------------------------
+ // METHOD 4. DICT | SPEED 5/5
+ //Dictionary dict = NavisworksUtils.ModelItems;
+ ////Dictionary> dict = NavisworksUtils.ModelModelItems;
+ ////List exceptions = NavisworksUtils.HiddenModelItems.ToList();
+ //List selectedElements4 = new List();
+ //if (dict != null)
+ //{
+ // selectedElements4 = GetSelectedElementsByDict(dict);
+ // //selectedElements = GetSelectedElementsByModelDict(dict);
+ // //exceptions = GetExceptionsByDict(dict);
+
+ // if (selectedElements4.Any())
+ // {
+ // Log.Information("Select {n} elements ...", selectedElements4.Count());
+ // //doc.CurrentSelection.SelectedItems.Clear();
+ // //doc.CurrentSelection.SelectedItems.AddRange(selectedElements);
+
+ // doc.CurrentSelection.Clear();
+ // doc.CurrentSelection.AddRange(selectedElements4);
+ // }
+
+ // //if (exceptions != null)
+ // //{
+ // // doc.Models.ResetAllHidden();
+ // // doc.Models.SetHidden(exceptions, true);
+ // //}
+ //}
+ //else
+ //{
+ // MessageHandler.ShowWarning(
+ // "Необходимо подождать",
+ // "Словарь объектов модели еще не сформирован, необходимо подождать. Операция может занимать несколько минут",
+ // "Словарь объектов не сформирован");
+ //}
+ // --------------------------------------------------------------------
+ if (selectedElements3.Any())
+ {
+ //Log.Information("Select {n} elements ...", selectedElements3.Count());
+ //doc.CurrentSelection.SelectedItems.Clear();
+ //doc.CurrentSelection.SelectedItems.AddRange(selectedElements);
+
+ doc.CurrentSelection.Clear();
+ doc.CurrentSelection.AddRange(selectedElements3);
+ }
+
+ var stop = 1;
+ // --------------------------------------------------------------------
+ // SELECTION
+ // --------------------------------------------------------------------
+
+
+
+
+ }
+ private List GetSelectedElementsBrute(Document doc)
+ {
+
+ var selectedElements = _bcfViewpoint.GetSelection().Select(x => (doc.Models.RootItemDescendants.WhereInstanceGuid(IfcGuid.FromIfcGUID(x.Ifc_guid))).FirstOrDefault()).Distinct().ToList();
+
+ return selectedElements;
+ }
+ private List GetSelectedElementsBySearch(Document doc)
+ {
+ List selectedElements = new List();
+ foreach (Component component in _bcfViewpoint.GetSelection())
+ {
+ Guid id = IfcGuid.FromIfcGUID(component.Ifc_guid);
+ string strID = id.ToString();
+
+ Search search = new Search();
+ search.Selection.SelectAll();
+
+ //var modelFileNameBCF = component.Authoring_tool_id;
+ //SearchCondition searchCondition2 = SearchCondition.HasPropertyByName("LcOaNode", "LcOaNodeSourceFile");
+ //searchCondition2 = searchCondition2.EqualValue(VariantData.FromDisplayString(modelFileNameBCF));
+ //search.SearchConditions.Add(searchCondition2);
+ SearchCondition searchCondition = SearchCondition.HasPropertyByName("LcOaNode", "LcOaNodeGuid");
+ searchCondition = searchCondition.EqualValue(VariantData.FromDisplayString(strID));
+ search.SearchConditions.Add(searchCondition);
+
+
+
+ ModelItem item = search.FindFirst(doc, false);
+
+ if (item != null)
+ {
+ selectedElements.Add(item);
+ }
+ }
+ return selectedElements;
+ }
+ private List GetSelectedElementsByModels(Document doc)
+ {
+ List selectedElements = new List();
+
+ var models = doc.Models;
+ foreach (var component in _bcfViewpoint.GetSelection())
+ {
+ var modelFileNameBCF = component.Authoring_tool_id;
+
+ foreach (var model in models)
+ {
+ if (modelFileNameBCF == Path.GetFileName(model.FileName))
+ {
+ var descendantsModelItems = model.RootItem.Descendants;
+
+ foreach (var descendant in descendantsModelItems)
+ {
+ if (descendant.InstanceGuid == IfcGuid.FromIfcGUID(component.Ifc_guid))
+ {
+ selectedElements.Add(descendant);
+ break;
+ }
+
+ }
+
+ break;
+ }
+ }
+ }
+
+ return selectedElements;
+ }
+ private List GetSelectedElementsByModelsWere(Document doc)
+ {
+ List selectedElements = new List();
+
+ var models = RibbonButtonClickHandler.GetItemFromLastModelBranch(doc.Models.ToList());
+ foreach (var component in _bcfViewpoint.GetSelection())
+ {
+ var modelFileNameBCF = component.Authoring_tool_id;
+
+ foreach (var model in models)
+ {
+ if (modelFileNameBCF == Path.GetFileNameWithoutExtension(model.SourceFileName))
+ {
+ //selectedElements = _bcfViewpoint.GetSelection().Select(x => (model.RootItem.Descendants.WhereInstanceGuid(IfcGuid.FromIfcGUID(x.Ifc_guid))).FirstOrDefault()).Distinct().ToList();
+ selectedElements.Add(model.RootItem.Descendants.WhereInstanceGuid(IfcGuid.FromIfcGUID(component.Ifc_guid)).First());
+ break;
+
+ }
+ }
+ }
+
+ return selectedElements;
+ }
+ private List GetSelectedElementsByModelsAndSearch(Document doc)
+ {
+ List selectedElements = new List();
+ Search search = new Search();
+ SearchCondition searchCondition = SearchCondition.HasPropertyByName("LcOaNode", "LcOaNodeGuid");
+
+ var models = doc.Models;
+ foreach (var component in _bcfViewpoint.GetSelection())
+ {
+ search.Clear();
+ Guid id = IfcGuid.FromIfcGUID(component.Ifc_guid);
+ string strID = id.ToString();
+ var modelFileNameBCF = component.Authoring_tool_id;
+ foreach (var model in models)
+ {
+ if (modelFileNameBCF == Path.GetFileName(model.FileName))
+ {
+ var descendantsModelItems = model.RootItem.Descendants;
+ search.Selection.Clear();
+ search.Selection.Dispose();
+ //search.Selection.SelectAll();
+ search.Selection.CopyFrom(descendantsModelItems);
+
+ searchCondition = searchCondition.EqualValue(VariantData.FromDisplayString(strID));
+ search.SearchConditions.Add(searchCondition);
+
+ ModelItem item = search.FindFirst(doc, false);
+ if (item != null)
+ {
+ selectedElements.Add(item);
+ }
+ break;
+ }
+ }
+ }
+
+ return selectedElements;
+ }
+
+ private List GetSelectedElementsByDict(Dictionary dict)
+ {
+ List selectedElements = new List();
+
+ foreach (var component in _bcfViewpoint.GetSelection())
+ {
+ var modelFileNameBCF = component.Authoring_tool_id;
+ var modelGuidBCF = IfcGuid.FromIfcGUID(component.Ifc_guid).ToString();
+
+ if(dict.TryGetValue(modelGuidBCF, out ModelItem foundModelItem))
+ {
+ selectedElements.Add(foundModelItem);
+ }
+ }
+
+ return selectedElements;
+ }
+ private List GetSelectedElementsByModelDict(Dictionary> dict)
+ {
+ List selectedElements = new List();
+
+ foreach (var component in _bcfViewpoint.GetSelection())
+ {
+ var modelFileNameBCF = component.Authoring_tool_id;
+ var modelGuidBCF = IfcGuid.FromIfcGUID(component.Ifc_guid).ToString();
+
+ if (dict.TryGetValue(modelFileNameBCF, out Dictionary foundModelDict))
+ {
+ if (foundModelDict.TryGetValue(modelGuidBCF, out ModelItem foundModelItem))
+ {
+ selectedElements.Add(foundModelItem);
+ }
+
+ }
+ }
+
+ return selectedElements;
+ }
+ private List GetExceptionsByDict(Dictionary dict)
+ {
+ List exceptions = new List();
+
+ foreach (var component in _bcfViewpoint.GetVisibilityExceptions())
+ {
+ var modelFileNameBCF = component.Authoring_tool_id;
+ var modelGuidBCF = IfcGuid.FromIfcGUID(component.Ifc_guid).ToString();
+
+ if (dict.TryGetValue(modelGuidBCF, out ModelItem foundModelItem))
+ {
+ exceptions.Add(foundModelItem);
+ }
+ }
+
+ return exceptions;
+ }
+
+ private void ApplyClippingPlanes(Document doc, ref Viewpoint viewPoint)
+ {
+ //Test
+ //var cvp = doc.CurrentViewpoint;
+ //var vp = cvp.Value;
+ //var cps = vp.InternalClipPlanes;
+ //var mode = cps.GetMode();
+ //BoundingBox3D box = new BoundingBox3D();
+ //Rotation3D rotation = new Rotation3D();
+ //cps.GetOrientedBox(box, rotation);
+ //Vector3D dims = box.Size;
+ //box = box.Extend(new Point3D(-dims.X * 0.25, -dims.Y * 0.25, -dims.Z * 0.25));
+ //cps.SetOrientedBox(box, rotation);
+ //doc.CurrentViewpoint.CopyFrom(vp);
+ //
+
+ //using var trans = new Transaction(doc.Document);
+ //if (trans.Start($"Apply view point clipping planes in '{view.Name}'") != TransactionStatus.Started)
+ // return;
+
+ Log.Information("Retrieving viewpoint clipping planes " +
+ "and converting them into an axis aligned bounding box ...");
+ AxisAlignedBoundingBox boundingBox = GetViewpointClippingBox();
+ IEnumerable clippingPlanes = _bcfViewpoint.GetClippingPlanes();
+ //ClipPlane
+
+
+ if (!boundingBox.Equals(AxisAlignedBoundingBox.Infinite))
+ {
+ Log.Information("Found axis aligned clipping planes. Setting resulting section box ...");
+ string json = doc.ActiveView.GetClippingPlanes();
+
+ string minCorner = string.Format("{0},{1},{2}",
+ Convert.ToDouble(boundingBox.Min.X).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture),
+ Convert.ToDouble(boundingBox.Min.Y).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture),
+ Convert.ToDouble(boundingBox.Min.Z).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture));
+ string maxCorner = string.Format("{0},{1},{2}",
+ Convert.ToDouble(boundingBox.Max.X).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture),
+ Convert.ToDouble(boundingBox.Max.Y).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture),
+ Convert.ToDouble(boundingBox.Max.Z).ToInternalAppUnit().ToString("0.00000", System.Globalization.CultureInfo.InvariantCulture));
+
+ // apply the section box.
+ string ClippingBox =
+ "{\"Type\":\"ClipPlaneSet\",\"Version\":1,\"OrientedBox\":{\"Type\":\"OrientedBox3D\",\"Version\":1,\"Box\":[["
+ + minCorner
+ + "],["
+ + maxCorner
+ + "]],\"Rotation\":[0,0,0]},\"Enabled\":true}";
+
+ //MaksT ClipPlanes
+ string ClippingPlanes =
+ "{\"Type\":\"ClipPlaneSet\",\"Version\":1,\"Planes\":[";
+ foreach (Clipping_plane clippingPlane in clippingPlanes)
+ {
+ decimal x = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Location.X).ToInternalAppUnit());
+ decimal y = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Location.Y).ToInternalAppUnit());
+ decimal z = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Location.Z).ToInternalAppUnit());
+ decimal nX = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Direction.X));
+ decimal nY = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Direction.Y));
+ decimal nZ = Convert.ToDecimal(Convert.ToDouble(clippingPlane.Direction.Z));
+ Vector3 n = new Vector3(nX, nY, nZ);
+
+ decimal distance = -(nX * x + nY * y + nZ * z);
+ double dbDistande = Convert.ToDouble(distance);
+ string strDistance = dbDistande.ToString("0.0000000000", System.Globalization.CultureInfo.InvariantCulture);
+
+
+ ClippingPlanes += "{\"Type\":\"ClipPlane\",\"Version\":1," +
+ "\"Normal\":[" + (-nX).ToString("0.0000000000", System.Globalization.CultureInfo.InvariantCulture)
+ + "," + (-nY).ToString("0.0000000000", System.Globalization.CultureInfo.InvariantCulture)
+ + "," + (-nZ).ToString("0.0000000000", System.Globalization.CultureInfo.InvariantCulture) + "]," +
+ "\"Distance\":" + strDistance + "," +
+ "\"Enabled\":" + "true" + "},"
+ ;
+ }
+ ClippingPlanes = ClippingPlanes.Remove(ClippingPlanes.Length - 1);
+ ClippingPlanes += "],\"Linked\":false, \"Enabled\":true}";
+
+ // apply clipping to current view
+ //doc.ActiveView.SetClippingPlanes(ClippingPlanes);
+ Autodesk.Navisworks.Api.View activeView = doc.ActiveView;
+
+ var clipplBefore = activeView.GetClippingPlanes();
+
+
+ //SavedViewpoint activeViewPointBefore = new SavedViewpoint(doc.CurrentViewpoint.ToViewpoint());
+ //activeViewPointBefore.DisplayName = "bcf-point-before";
+ //doc.SavedViewpoints.AddCopy(activeViewPointBefore);
+
+ activeView.SetClippingPlanes(ClippingPlanes);
+ viewPoint = doc.CurrentViewpoint.CreateCopy();
+
+ //var clipplAfter = activeView.GetClippingPlanes();
+ //SavedViewpoint activeViewPointAfter = new SavedViewpoint(doc.CurrentViewpoint.ToViewpoint());
+ //activeViewPointAfter.DisplayName = "bcf-point-after";
+ //doc.SavedViewpoints.AddCopy(activeViewPointAfter);
+
+ //activeView.LookFromFrontRightTop();
+
+
+
+ // doc.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.All);
+
+
+ //doc.ActiveView.SetClippingPlanes(json);
+ //view.SetSectionBox(ToNavisSectionBox(boundingBox));
+ //view.IsSectionBoxActive = true;
+ }
+
+ //if clippingPlanes.Count() > 0 {
+
+ //}
+ //else
+ //{
+
+ //}
+ }
+
+ private AxisAlignedBoundingBox GetViewpointClippingBox()
+ {
+ var clippingPlanes = _bcfViewpoint.GetClippingPlanes()
+ .Select(p => p.ToAxisAlignedBoundingBox(_viewpointAngleThresholdRad))
+ .Aggregate(AxisAlignedBoundingBox.Infinite, (current, nextBox) => current.MergeReduce(nextBox));
+ return clippingPlanes;
+ }
+
+ private List GetViewpointVisibilityExceptions(IReadOnlyDictionary filterMap)
+ => _bcfViewpoint.GetVisibilityExceptions()
+ .Where(bcfComponentException => filterMap.ContainsKey(bcfComponentException.Ifc_guid))
+ .Select(bcfComponentException => filterMap[bcfComponentException.Ifc_guid])
+ .ToList();
+
+
+ private List GetViewpointSelection(IReadOnlyDictionary filterMap)
+ => _bcfViewpoint.GetSelection()
+ .Where(selectedElement => filterMap.ContainsKey(selectedElement.Ifc_guid))
+ .Select(selectedElement => filterMap[selectedElement.Ifc_guid])
+ .ToList();
+
+
+
+ private static BoundingBox3D ToNavisSectionBox(AxisAlignedBoundingBox box)
+ {
+ var min = new Point3D(
+ box.Min.X == decimal.MinValue ? double.MinValue : ((double)box.Min.X).ToInternalAppUnit(),
+ box.Min.Y == decimal.MinValue ? double.MinValue : ((double)box.Min.Y).ToInternalAppUnit(),
+ box.Min.Z == decimal.MinValue ? double.MinValue : ((double)box.Min.Z).ToInternalAppUnit());
+ var max = new Point3D(
+ box.Max.X == decimal.MaxValue ? double.MaxValue : ((double)box.Max.X).ToInternalAppUnit(),
+ box.Max.Y == decimal.MaxValue ? double.MaxValue : ((double)box.Max.Y).ToInternalAppUnit(),
+ box.Max.Z == decimal.MaxValue ? double.MaxValue : ((double)box.Max.Z).ToInternalAppUnit());
+
+ return new BoundingBox3D ( min, max );
+ }
+}
diff --git a/src/OpenProject.Navisworks/Entry/RibbonButtonClickHandler.cs b/src/OpenProject.Navisworks/Entry/RibbonButtonClickHandler.cs
new file mode 100644
index 00000000..0bb294c2
--- /dev/null
+++ b/src/OpenProject.Navisworks/Entry/RibbonButtonClickHandler.cs
@@ -0,0 +1,286 @@
+//using Autodesk.Revit.UI;
+using Autodesk.Navisworks.Api;
+using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
+using ComBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using OpenProjectNavisworks.Services;
+using OpenProject.Shared;
+using Serilog;
+using ZetaIpc.Runtime.Helper;
+using System.Windows.Controls;
+using OpenProjectNavisworks.Data;
+using System.Windows.Media.Media3D;
+using static Autodesk.Navisworks.Gui.Roamer.CommandLineConfig;
+using OpenProject.Shared.Logging;
+
+
+namespace OpenProjectNavisworks.Entry;
+ public static class RibbonButtonClickHandler
+ {
+#if N2024
+ public const string NavisAPIVersion = "21";
+ public const string NavisVersion = "2024";
+#elif N2023
+ public const string NavisAPIVersion = "20";
+ public const string NavisVersion = "2023";
+#elif N2022
+ public const string NavisAPIVersion = "19";
+ public const string NavisVersion = "2022";
+#elif N2021
+ public const string NavisAPIVersion = "18";
+ public const string NavisVersion = "2021";
+#elif N2020
+ public const string NavisAPIVersion = "17";
+ public const string NavisVersion = "2020";
+#else
+ public const string NavisAPIVersion = "16";
+ public const string NavisVersion = "2019";
+#endif
+
+ private static Process _opBrowserProcess;
+ public static IpcHandler IpcHandler { get; private set; }
+
+ public static int OpenMainPluginWindow(Document commandData, ref string message)
+ {
+ try
+ {
+ Logger.ConfigureLogger("OpenProject.Navisworks.Log..txt");
+ EnsureExternalOpenProjectAppIsRunning(commandData);
+ Log.Information("EnsureExternalOpenProjectAppIsRunning");
+ IpcHandler.SendBringBrowserToForegroundRequestToDesktopApp();
+ Log.Information("SendBringBrowserToForegroundRequestToDesktopApp");
+
+ return 1;
+ }
+ catch (Exception exception)
+ {
+ Logger.ConfigureLogger("OpenProject.Navisworks.Log..txt");
+
+ message = exception.Message;
+ Log.Error(exception, message);
+ return 0;
+ }
+ }
+
+ public static int OpenSettingsPluginWindow(Document commandData, ref string message)
+ {
+ try
+ {
+ EnsureExternalOpenProjectAppIsRunning(commandData);
+ IpcHandler.SendOpenSettingsRequestToDesktopApp();
+ IpcHandler.SendBringBrowserToForegroundRequestToDesktopApp();
+ return 1;
+ }
+ catch (Exception exception)
+ {
+ message = exception.Message;
+ Logger.ConfigureLogger("OpenProject.Navisworks.Log..txt");
+ Log.Error(exception, message);
+ return 0;
+ }
+ }
+
+ private static async void EnsureExternalOpenProjectAppIsRunning(Document commandData)
+ {
+ Logger.ConfigureLogger("OpenProject.Navisworks.Log..txt");
+
+ //Version check
+ if (!Autodesk.Navisworks.Api.Application.Version.ApiMajor.ToString().Contains(NavisAPIVersion))
+ {
+ MessageHandler.ShowWarning(
+ "Unexpected version",
+ "The Navisworks version does not match the expectations.",
+ $"This Add-In was built and tested only for Navisworks {NavisVersion}. Further usage is at your own risk");
+ }
+
+ if (_opBrowserProcess is { HasExited: false })
+ return;
+ else
+ {
+ // Clear dict after closing browser window
+ NavisworksUtils.ModelItems = null;
+ }
+
+
+ IpcHandler = new IpcHandler(Autodesk.Navisworks.Api.Application.ActiveDocument);
+ var revitServerPort = IpcHandler.StartLocalServerAndReturnPort();
+
+ var openProjectBrowserExecutablePath = GetOpenProjectBrowserExecutable();
+ if (!File.Exists(openProjectBrowserExecutablePath))
+ throw new SystemException("Browser executable not found.");
+
+ var opBrowserServerPort = FreePortHelper.GetFreePort();
+ var processArguments = $"ipc {opBrowserServerPort} {revitServerPort}";
+ Environment.CurrentDirectory = System.IO.Path.GetDirectoryName(openProjectBrowserExecutablePath);
+ _opBrowserProcess = Process.Start(openProjectBrowserExecutablePath, processArguments);
+ IpcHandler.StartLocalClient(opBrowserServerPort);
+ Log.Information("IPC bridge started between port {port1} and {port2}.",
+ opBrowserServerPort, revitServerPort);
+
+
+ // Initialize document ModelItems dictionary -- for hiding objects. Disabled, because not optimized.
+ NavisworksUtils.ModelModelItems = null;
+ if (NavisworksUtils.ModelModelItems == null)
+ {
+ //NavisworksUtils.ModelItems = null;
+ //await GenerateModelItemDictAsync(Model.NavisworksWrapper.Document);
+ //MessageHandler.ShowWarning("Need to wait",
+ // "Dictionary of objects is in process, need to wait. This can take several minutes",
+ // "Dictionary of objects is in process, need to wait. This can take several minutes");
+ //NavisworksUtils.ModelModelItems = new Dictionary>();
+ // BuildModelDictionary(Model.NavisworksWrapper.Document).Wait();
+ //BuildModelDictionary(Model.NavisworksWrapper.Document);
+ //NavisworksUtils.ModelItems = await BuildModelDictionaryAsync();
+ //GenerateModelItemDict(Model.NavisworksWrapper.Document);
+ }
+
+ }
+
+ private static string GetOpenProjectBrowserExecutable()
+ {
+ var currentAssemblyPathUri = Assembly.GetExecutingAssembly().CodeBase;
+ var currentAssemblyPath = Uri.UnescapeDataString(new Uri(currentAssemblyPathUri).AbsolutePath).Replace("/", "\\");
+ var currentFolder = Path.GetDirectoryName(currentAssemblyPath) ?? string.Empty;
+
+ return Path.Combine(currentFolder, ConfigurationConstant.OpenProjectBrowserExecutablePath);
+ }
+
+
+
+ // TODO: Next functions are for hiding objects feature. Disabled for now, because not optimized --------------------------------------------------------------------
+ static void GenerateModelItemDict(Document doc)
+ {
+ Dictionary modelItems = new Dictionary();
+ List hiddenModelItems = new List();
+ foreach (ModelItem mi in doc.Models.RootItemDescendantsAndSelf)
+ {
+ var k = mi.InstanceGuid;
+ if (k != null)
+ {
+ var kStr = k.ToString();
+ if (!modelItems.ContainsKey(kStr))
+ modelItems.Add(kStr, mi);
+ // if (mi.IsHidden)
+ // hiddenModelItems.Add(mi);
+ }
+
+ }
+ NavisworksUtils.ModelItems = modelItems;
+ //NavisworksUtils.HiddenModelItems = hiddenModelItems;
+ MessageHandler.ShowWarning(
+ "Object dictionary is not ready yet!",
+ "Object dictionary is ready, you can use ViewPoint now",
+ "Object dictionary is ready");
+ }
+
+ static async Task GenerateModelItemDictAsync(Document doc)
+ {
+ await Task.Run(() => GenerateModelItemDict(doc));
+ }
+
+
+ static async Task BuildModelDictionary(Document doc)
+ {
+
+ // Get all models
+ //var models = GetDocumentModels(doc);
+ var models = GetItemFromLastModelBranch(doc.Models.ToList());
+
+ // Async process each model
+ //foreach (var model in models)
+ //{
+ // Task.Run(() => ProcessModelAsync(model));
+ //}
+ foreach (var md in models)
+ {
+ if( md == null)
+ {
+ var stop =1 ;
+ }
+ // ProcessModelAsync(md);
+ }
+
+ var tasks = models.Where(t => t != null).Select(ProcessModelAsync);
+ await Task.WhenAll(tasks);
+
+ MessageHandler.ShowWarning(
+ "Object dictionary is not ready yet!",
+ "Object dictionary is ready, you can use ViewPoint now",
+ "Object dictionary is ready");
+
+ var dct = NavisworksUtils.ModelModelItems;
+
+ return true;
+ }
+ static void ProcessModel(Autodesk.Navisworks.Api.Model model)
+ {
+ // Go through each ModelItem in model
+ Dictionary modelItems = new Dictionary();
+
+ var rootItem = model.RootItem;
+ var descendants = rootItem.Descendants;
+
+ foreach (ModelItem item in descendants)
+ {
+
+ // Getting GUID attribute for each ModelItem
+ var guid = item.InstanceGuid.ToString();
+
+ if (guid != null)
+ {
+ if (!modelItems.ContainsKey(guid))
+ {
+ modelItems.Add(guid, item);
+ }
+ }
+ }
+ var modelFileName = Path.GetFileName(model.SourceFileName);
+ if (!NavisworksUtils.ModelModelItems.ContainsKey(modelFileName))
+ NavisworksUtils.ModelModelItems.Add(modelFileName, modelItems);
+ }
+
+ static async Task ProcessModelAsync(Autodesk.Navisworks.Api.Model model)
+ {
+ await Task.Run(() => ProcessModel(model));
+ }
+
+ public static List GetDocumentModels(Document doc)
+ {
+ var models = doc.Models;
+
+
+ if (models.Count == 1)
+ {
+ var mainModel = models[0];
+ var mainModelModelItem = mainModel.RootItem;
+ var mainModelChildren = mainModelModelItem.Children;
+ return models[0].RootItem.Children.Select(ch => ch.Model).ToList();
+ }
+ else
+ {
+ return models.ToList();
+ }
+
+ }
+ public static List GetItemFromLastModelBranch(List models)
+ {
+ List modelsResult = new List();
+ foreach (var model in models)
+ {
+ var modelChildren = model.RootItem.Children.ToList();
+ if (modelChildren.Count == 0 || modelChildren[0].Model == null)
+ {
+ modelsResult.Add(model);
+ }
+ else
+ {
+ modelsResult.AddRange(GetItemFromLastModelBranch(modelChildren.Select(t => t.Model).ToList()));
+ }
+ }
+ return modelsResult;
+ }
+}
+
diff --git a/src/OpenProject.Navisworks/Extensions/BcfExtensions.cs b/src/OpenProject.Navisworks/Extensions/BcfExtensions.cs
new file mode 100644
index 00000000..5327438c
--- /dev/null
+++ b/src/OpenProject.Navisworks/Extensions/BcfExtensions.cs
@@ -0,0 +1,354 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Autodesk.Navisworks.Api;
+using iabi.BCF.APIObjects.V21;
+using OpenProjectNavisworks.Data;
+using OpenProject.Shared;
+using OpenProject.Shared.BcfApi;
+using OpenProject.Shared.Math3D;
+using OpenProject.Shared.Math3D.Enumeration;
+using Newtonsoft.Json.Linq;
+using System.Xml.Linq;
+using Newtonsoft.Json;
+using System.Windows.Controls;
+using Autodesk.Navisworks.Gui.Roamer;
+
+namespace OpenProjectNavisworks.Extensions
+{
+ ///
+ /// A namespace for static extensions for Revit document classes with focus on getting BCF data out
+ /// of the current state.
+ ///
+ public static class BcfExtensions
+ {
+ ///
+ /// Generates a model meant to send a viewpoint creation request to the BCF API from a Revit UI document.
+ ///
+ /// The Revit UI document
+ /// A BCF model for creating viewpoints.
+ public static Viewpoint_POST GenerateViewpoint(this Document uiDocument)
+ {
+ (Orthogonal_camera ortho, Perspective_camera perspective) = uiDocument.GetBcfCameraValues();
+
+ return new Viewpoint_POST
+ {
+ Clipping_planes = uiDocument.GetBcfClippingPlanes(),
+ Snapshot = uiDocument.GetBcfSnapshotData(),
+ Components = uiDocument.GetBcfComponents(),
+ Orthogonal_camera = ortho,
+ Perspective_camera = perspective
+ };
+ }
+
+ private static List GetBcfClippingPlanes(this Document uiDocument)
+ {
+ var sectionBox = uiDocument.GetBoundingBox(true);
+ var clippl = uiDocument.ActiveView.GetClippingPlanes();
+ var myCleanJsonObject = JObject.Parse(clippl);
+ var orBox = myCleanJsonObject.GetValue("OrientedBox");
+ var planes = myCleanJsonObject["Planes"];
+ List cp = new List();
+
+ Vector3 minCorner, maxCorner, rotation;
+ // If section was created by box
+ if (orBox != null) {
+ var box = orBox.Value("Box");
+ var minC = box.Value(0);
+ var maxC = box.Value(1);
+ Point3D minPoint = new Point3D(minC.Value(0), minC.Value(1), minC.Value(2));
+ Point3D maxPoint = new Point3D(maxC.Value(0), maxC.Value(1), maxC.Value(2));
+ minCorner = minPoint.ToVector3().ToMeters();
+ maxCorner = maxPoint.ToVector3().ToMeters();
+
+ var rot = orBox.Value("Rotation");
+ var rotX = rot.Value(0);
+ var rotY = rot.Value(1);
+ var rotZ = rot.Value(2);
+
+ Point3D rotPoint = new Point3D(rotX, rotY, rotZ);
+ rotation = rotPoint.ToVector3();
+
+ cp = BcfApiExtensions.ToClippingPlanes(minCorner, maxCorner, rotation);
+ }
+ // If section was created by planes. TODO: not implemented yet, now just takes all objects (sectionBox)
+ else if (planes != null)
+ {
+ minCorner = sectionBox.Min.ToVector3().ToMeters();
+ maxCorner = sectionBox.Max.ToVector3().ToMeters();
+ Point3D rotPoint = new Point3D(0, 0, 0);
+ rotation = rotPoint.ToVector3();
+
+ cp = BcfApiExtensions.ToClippingPlanes(minCorner, maxCorner, rotation);
+ }
+ // If there was no sections
+ else
+ {
+ minCorner = sectionBox.Min.ToVector3().ToMeters();
+ maxCorner = sectionBox.Max.ToVector3().ToMeters();
+ Point3D rotPoint = new Point3D(0, 0, 0);
+ rotation = rotPoint.ToVector3();
+
+ cp = BcfApiExtensions.ToClippingPlanes(minCorner, maxCorner, rotation);
+ }
+
+ // Checking if clipping planes enabled
+ var isSectionEnabled = myCleanJsonObject.Value("Enabled");
+ //if (isSectionEnabled)
+ //{
+ return cp;
+ //}
+ //else
+ //{
+ // return null;
+ //}
+
+
+ }
+
+ private static Snapshot_POST GetBcfSnapshotData(this Document uiDocument)
+ {
+ return new Snapshot_POST
+ {
+ Snapshot_type = Snapshot_type.Png,
+ Snapshot_data = "data:image/png;base64," + uiDocument.GetBase64RevitSnapshot()
+ };
+ }
+
+ private static Components GetBcfComponents(this Document uiDocument)
+ {
+
+ //var selectedComponents2 = uiDocument.CurrentSelection.ToSelection()
+ // .GetSelectedItems() // ModelItemCollection
+ // .SelectMany(x => NavisworksDocumentExtensions.ElementDescendants(x))
+ // .Distinct()
+ // .ToList();
+ //foreach (var item in selectedComponents2)
+ //{
+ // var parModel = item.Model;
+ // if (parModel != null)
+ // { var parModelName = parModel.FileName; }
+
+ //}
+
+
+ // SELECTION
+ var selectedComponents = uiDocument.CurrentSelection.ToSelection()
+ .GetSelectedItems() // ModelItemCollection
+ .SelectMany(x => NavisworksDocumentExtensions.ElementDescendants(x))
+ .Distinct()
+ .Select(uiDocument.ElementIdToComponentSelector())
+ .ToList();
+
+ ////
+ //IEnumerable selectedComponents2 = uiDocument.CurrentSelection.ToSelection().GetSelectedItems();
+ //var enumSel = selectedComponents2.GetEnumerator();
+
+ //List lstMI = new List();
+ //while (enumSel.MoveNext())
+ //{
+ // lstMI.Add(enumSel.Current);
+ // var descItems = enumSel.Current.DescendantsAndSelf;
+ //}
+ ////
+ //var selectedComponents3 = uiDocument.CurrentSelection.ToSelection()
+ // .GetSelectedItems().SelectMany(x => NavisworksDocumentExtensions.ElementDescendants(x)).Distinct().ToList();
+
+
+ //// VISIBILITY -- commeted for better times
+ ////create a store for the visible items
+ //ModelItemCollection visible = new ModelItemCollection();
+
+
+
+ ////uiDocument.CurrentSelection.SelectAll();
+ ////var currentSelection = Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems;
+ ////Add all the items that are visible to the visible collection
+ ////foreach (ModelItem item in Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems)
+ ////{
+ //// if (item.AncestorsAndSelf != null)
+ //// visible.AddRange(item.AncestorsAndSelf);
+ //// if (item.Descendants != null)
+ //// visible.AddRange(item.Descendants);
+ ////}
+ //var hiddenElementsOfView = uiDocument.Models.RootItemDescendantsAndSelf
+ // //.SelectMany(t=>t.DescendantsAndSelf)
+ // .Where(t => t.IsHidden);
+ //var hiddenElementsOfViewCount = hiddenElementsOfView.Count();
+ ////new List();
+
+ //var visibleElementsOfViewCount = uiDocument.Models.RootItemDescendantsAndSelf.Count() - hiddenElementsOfViewCount;
+ ////.SelectMany(t => t.DescendantsAndSelf)
+ ////.Where(t => !t.IsHidden)
+ ////.Count(); //new List();
+ ////foreach (ModelItem item in visible)
+ ////{
+ //// if (item.IsHidden)
+ //// {
+ //// hiddenElementsOfView.Add(item);
+ //// }
+ //// else { visibleElementsOfView.Add(item); }
+ ////}
+
+ //// Optimizing visibility
+ //var defaultVisibility = (hiddenElementsOfViewCount < visibleElementsOfViewCount);
+ //var exceptions = (defaultVisibility ?
+ // hiddenElementsOfView
+ // :
+ // uiDocument.Models.RootItemDescendantsAndSelf
+ // .Except(hiddenElementsOfView));
+ ////.SelectMany(t => t.DescendantsAndSelf)
+ ////.Where(t => !t.IsHidden));
+
+ var exceptions = Enumerable.Empty();
+ //var exceptions = NavisworksUtils.HiddenModelItems;
+ var defaultVisibility = true;
+
+
+
+ var componentVisibility = new Components
+ {
+ Selection = selectedComponents,
+ Visibility = new iabi.BCF.APIObjects.V21.Visibility
+ {
+ Default_visibility = defaultVisibility,
+ Exceptions = exceptions
+ .Select(uiDocument.ElementIdToComponentSelector())
+ .ToList()
+ }
+ };
+
+ // uiDocument.CurrentSelection.Clear();
+
+ return componentVisibility;
+ }
+
+ private static (Orthogonal_camera orthogonalCamera, Perspective_camera perspectiveCamera) GetBcfCameraValues(
+ this Document uiDocument)
+ {
+ var view3D = uiDocument.CurrentViewpoint;
+
+ bool isPerspective = view3D.Value.Projection == ViewpointProjection.Perspective;
+ CameraType cameraType = isPerspective ? CameraType.Perspective : CameraType.Orthogonal;
+ Position cameraPosition = uiDocument.GetCameraPosition(isPerspective);
+ Vector3 center = cameraPosition.Center.ToMeters();
+
+ var cameraViewpoint = new iabi.BCF.APIObjects.V21.Point
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(center.Z)
+ };
+
+ // Getting vector perpendicular to view
+ var nwv3d = cameraPosition.Up.ToNavisworksPoint3D().ToVector3D();
+ var right = cameraPosition.Forward.ToNavisworksPoint3D().ToVector3D().Cross(nwv3d);
+ var upVorView = right.Cross(cameraPosition.Forward.ToNavisworksPoint3D().ToVector3D()).Normalize().Negate();
+
+ var cameraUpVector = new Direction
+ {
+ X = Convert.ToSingle(cameraPosition.Up.X),
+ Y = Convert.ToSingle(cameraPosition.Up.Y),
+ Z = Convert.ToSingle(cameraPosition.Up.Z)
+ };
+
+ var cameraDirection = new Direction
+ {
+ X = Convert.ToSingle(cameraPosition.Forward.X * 1),
+ Y = Convert.ToSingle(cameraPosition.Forward.Y * 1),
+ Z = Convert.ToSingle(cameraPosition.Forward.Z * 1)
+ };
+
+ Orthogonal_camera ortho = null;
+ Perspective_camera perspective = null;
+
+ switch (cameraType)
+ {
+ case CameraType.Perspective:
+ var fov = (float)view3D.ToViewpoint().HeightField;
+ perspective = new Perspective_camera
+ {
+ Field_of_view = fov,
+ Camera_direction = cameraDirection,
+ Camera_up_vector = cameraUpVector,
+ Camera_view_point = cameraViewpoint
+ };
+ break;
+ case CameraType.Orthogonal:
+ ortho = new Orthogonal_camera
+ {
+ View_to_world_scale = uiDocument.GetViewBoxHeight(),
+ Camera_direction = cameraDirection,
+ Camera_up_vector = cameraUpVector,
+ Camera_view_point = cameraViewpoint
+ };
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cameraType), cameraType, "invalid camera type");
+ }
+
+ return (ortho, perspective);
+ }
+
+ private static float GetViewBoxHeight(this Document uiDocument)
+ {
+ var sectionBox = uiDocument.GetBoundingBox(true);
+ var bottomLeft = sectionBox.Min;
+ var topRight = sectionBox.Max;
+ var heightVector = topRight - bottomLeft;
+ var widthVector = new Point3D(topRight.X,topRight.Y,0) - new Point3D(bottomLeft.X,bottomLeft.Y,0);
+
+ var (viewBoxHeight, _) = (heightVector.Length, widthVector.Length);
+ //NavisworksUtils.ConvertToViewBoxValues(topRight, bottomLeft, uiDocument.ActiveView);
+
+ var ViewToWorldScale = uiDocument.CurrentViewpoint.Value.HeightField;
+
+ return Convert.ToSingle(ViewToWorldScale.ToMeters());
+ }
+
+ private static Position GetCameraPosition(this Document uiDocument, bool isPerspective)
+ {
+ var zoomCorners = uiDocument.GetBoundingBox(true);
+ var bottomLeft = zoomCorners.Min;
+ var topRight = zoomCorners.Max;
+ var viewCenter = uiDocument.CurrentViewpoint.Value.Position;
+ if (!isPerspective)
+ viewCenter = zoomCorners.Center;
+ var aa = uiDocument.CurrentViewpoint.Value.Rotation.ToAxisAndAngle();
+ JObject json = JObject.Parse(uiDocument.CurrentViewpoint.Value.GetCamera());
+ var vd = json["ViewDirection"];
+ var ud = json["UpDirection"];
+ var viewDirection = aa.Axis.ToVector3D();
+
+ return NavisworksUtils.TransformCameraPosition(
+ new ProjectPositionWrapper(),
+ new Position(
+ viewCenter.ToVector3(),
+ new Vector3(double.Parse(vd[0].ToString()).ToDecimal(), double.Parse(vd[1].ToString()).ToDecimal(), double.Parse(vd[2].ToString()).ToDecimal()), //viewDirection.Y
+ (new Vector3D(
+ double.Parse(ud[0].ToString()),
+ double.Parse(ud[1].ToString()),
+ double.Parse(ud[2].ToString())))
+ .ToPoint3D().ToVector3())); //uiDocument.CurrentViewpoint.Value.WorldUpVector.ToVector3D()
+ }
+
+ private static string GetBase64RevitSnapshot(this Document uiDocument)
+ {
+#if N2024
+ Bitmap image = uiDocument.ActiveView.GenerateImage(ImageGenerationStyle.Scene, 1200, 900, true);
+#else
+ Bitmap image = uiDocument.ActiveView.GenerateImage(ImageGenerationStyle.Scene, 1200, 900);
+#endif
+
+ byte[] buffer;
+ using (var stream = new MemoryStream())
+ {
+ image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
+ buffer = stream.ToArray();
+ }
+ return Convert.ToBase64String(buffer);
+ }
+
+ }
+}
diff --git a/src/OpenProject.Navisworks/Extensions/NavisWorksDocumentExtensions.cs b/src/OpenProject.Navisworks/Extensions/NavisWorksDocumentExtensions.cs
new file mode 100644
index 00000000..f55b95a1
--- /dev/null
+++ b/src/OpenProject.Navisworks/Extensions/NavisWorksDocumentExtensions.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+//using Autodesk.Revit.DB;
+using OpenProjectNavisworks.Entry;
+using Autodesk.Navisworks.Api;
+using Autodesk.Navisworks.Api.Data;
+using Autodesk.Navisworks.Api.Plugins;
+using iabi.BCF.APIObjects.V21;
+using OpenProject.Shared;
+using OpenProject.Shared.Math3D.Enumeration;
+using Serilog;
+using System.IO;
+
+namespace OpenProjectNavisworks.Extensions
+{
+ ///
+ /// Extensions written for handling of classes of the Revit API.
+ ///
+ public static class NavisworksDocumentExtensions
+ {
+ private const string _openProjectOrthogonalViewName = "OpenProject Orthogonal";
+ private const string _openProjectPerspectiveViewName = "OpenProject Perspective";
+
+ ///
+ /// Creates a map between revit element ids and their IFC GUIDs inside the given document.
+ ///
+ /// A revit document
+ /// A list of element ids
+ /// The map between IFC GUIDs and revit element ids.
+ public static Dictionary GetIfcGuidElementIdMap(this Document doc,
+ List elements)
+ {
+ var map = new Dictionary();
+ foreach (ModelItem element in elements)
+ {
+ var ifcGuid = IfcGuid.ToIfcGuid(element.InstanceGuid);
+ if (!map.ContainsKey(ifcGuid))
+ map.Add(ifcGuid, element);
+ }
+
+ return map;
+ }
+
+ ///
+ ///
+ ///
+ /// Navisworks document
+ ///
+ public static List GetVisibleElementsOfView(this Document doc)
+ {
+ var ids = new HashSet();
+ var modelItems = new List();
+ GetVisibleItems(doc.Models.RootItems, modelItems);
+ modelItems.ForEach(m =>
+ {
+ if (m.InstanceGuid != Guid.Empty)
+ {
+ ids.Add(m); //NWGetter.GetIfcGuid(m)
+ }
+ else
+ {
+ var item = m.Ancestors.First(i => i.InstanceGuid != Guid.Empty);
+ ids.Add(item); //NWGetter.GetIfcGuid(m)
+ }
+ });
+
+ return ids.ToList();
+ }
+
+ private static void GetVisibleItems(ModelItemEnumerableCollection modelItems, List visibileItems)
+ {
+ foreach (var item in modelItems)
+ {
+ if (item.IsHidden == false)
+ {
+ if (item.HasGeometry)
+ visibileItems.Add(item);
+ else
+ GetVisibleItems(item.Children, visibileItems);
+ }
+ }
+ }
+
+ ///
+ /// Gets all visible elements in the given view of the document.
+ ///
+ /// The Revit document
+ /// The Revit view
+ /// A list of element ids of all elements, that are currently visible.
+ //public static List GetVisibleElementsOfView(this Document doc, Autodesk.Navisworks.Api.View view) =>
+ // new FilteredElementCollector(doc, view.Id)
+ // .WhereElementIsNotElementType()
+ // .WhereElementIsViewIndependent()
+ // .Where(element => element.CanBeHidden(view))
+ // .Select(element => element.Id);
+
+ ///
+ /// Gets all invisible elements in the given view of the document.
+ ///
+ /// The Revit document
+ /// The Revit view
+ /// A list of element ids of all elements, that are currently hidden.
+ //public static IEnumerable GetHiddenElementsOfView(this Document doc, View view)
+ //{
+ // bool ElementIsHiddenInView(Element element) =>
+ // element.IsHidden(view) ||
+ // element.Category is { CategoryType: CategoryType.Model } &&
+ // view.GetCategoryHidden(element.Category.Id);
+
+ // return new FilteredElementCollector(doc)
+ // .WhereElementIsNotElementType()
+ // .WhereElementIsViewIndependent()
+ // .Where(ElementIsHiddenInView)
+ // .Select(element => element.Id);
+ //}
+
+ ///
+ /// Gets a selector, that converts Revit element ids into BCF API components.
+ /// This is done in the context of a specific Revit Document.
+ ///
+ /// The Revit document
+ /// A selector converting to .
+ public static Func ElementIdToComponentSelector(this Document doc)
+ {
+
+ return modelItem => new Component
+ {
+ Originating_system = "Navisworks " + OpenProjectNavisworks.Entry.RibbonButtonClickHandler.NavisVersion,
+ Ifc_guid = IfcGuid.ToIfcGuid(modelItem.InstanceGuid), //где-то лежит айдишник из ifc
+ //Authoring_tool_id = modelItem.InstanceGuid.ToString()
+ Authoring_tool_id = GetModelFileName(modelItem)
+ };
+ }
+
+ public static IEnumerable ElementDescendants(ModelItem modelItem)
+ {
+ if(Guid.Empty == modelItem.InstanceGuid)
+ {
+ return modelItem.Descendants.SelectMany(x => ElementDescendants(x)).ToList();
+ }
+ else
+ return new List().Append(modelItem).AsEnumerable();
+ }
+ public static ModelItem ElementParentModel(ModelItem modelItem)
+ {
+ if (modelItem.Parent != null)
+ {
+ if (modelItem.Model != null)
+ {
+ return modelItem;
+ }
+ return ElementParentModel(modelItem.Parent);
+ }
+ else
+ {
+ return modelItem;
+ }
+ }
+ public static String GetModelFileName(ModelItem modelItem)
+ {
+
+ ModelItem parentModel = ElementParentModel(modelItem);
+
+ if (parentModel.Model != null)
+ {
+ return Path.GetFileNameWithoutExtension(parentModel.Model.SourceFileName);
+
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the correct 3D view for displaying OpenProject content. The type of the view is dependent of the requested
+ /// camera type, either orthogonal or perspective. If the view is not yet available, it is created.
+ ///
+ /// The current revit document.
+ /// The camera type for the requested view.
+ /// A with the correct settings to display OpenProject content.
+ /// Throws, if camera type is neither orthogonal nor perspective.
+ public static Autodesk.Navisworks.Api.View GetOpenProjectView(this Document doc, CameraType type)
+ {
+ var viewName = type switch
+ {
+ CameraType.Orthogonal => _openProjectOrthogonalViewName,
+ CameraType.Perspective => _openProjectPerspectiveViewName,
+ _ => throw new ArgumentOutOfRangeException(nameof(type), type, "invalid camera type")
+ };
+
+ //if (openProjectView != null)
+ //{
+ // Log.Information("View '{name}' already existent. Finished getting related view.", viewName);
+ // return openProjectView;
+ //}
+
+ Log.Information("View '{name}' doesn't exist yet. Creating new view ...", viewName);
+ var openProjectView = doc.ActiveView;//.CurrentViewpoint.CreateCopy();
+
+
+
+ //using var trans = new Transaction(doc);
+ //trans.Start("Create open project view");
+
+ //openProjectView = type switch
+ //{
+ // CameraType.Orthogonal => View3D.CreateIsometric(doc, doc.GetFamilyViews().First().Id),
+ // CameraType.Perspective => View3D.CreatePerspective(doc, doc.GetFamilyViews().First().Id),
+ // _ => throw new ArgumentOutOfRangeException(nameof(type), type, "invalid camera type")
+ //};
+
+ //openProjectView.Name = viewName;
+ //openProjectView.CropBoxActive = false;
+ //openProjectView.CropBoxVisible = false;
+ //openProjectView.DetailLevel = ViewDetailLevel.Fine;
+ //openProjectView.DisplayStyle = DisplayStyle.Realistic;
+
+ //foreach (Category category in doc.Settings.Categories)
+ // if (category.CategoryType == CategoryType.Annotation && category.Name == "Levels")
+ // openProjectView.SetCategoryHidden(category.Id, true);
+
+ //trans.Commit();
+
+ Log.Information("View '{name}' created. Finished getting related view.", viewName);
+ return openProjectView;
+ }
+
+ //private static IEnumerable GetFamilyViews(this Document doc)
+ //{
+ // return from elem in new FilteredElementCollector(doc).OfClass(typeof(ViewFamilyType))
+ // let type = elem as ViewFamilyType
+ // where type.ViewFamily == ViewFamily.ThreeDimensional
+ // select type;
+ //}
+
+ //private static IEnumerable Get3DViews(this Document doc)
+ //{
+ // return from elem in new FilteredElementCollector(doc).OfClass(typeof(View3D))
+ // let view = elem as View3D
+ // select view;
+ //}
+ }
+}
diff --git a/src/OpenProject.Navisworks/Model/Addin.cs b/src/OpenProject.Navisworks/Model/Addin.cs
new file mode 100644
index 00000000..1c72858f
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/Addin.cs
@@ -0,0 +1,95 @@
+using OpenProjectNavisworks.Command;
+
+namespace OpenProjectNavisworks.Model;
+
+public class Addin : IAddinNode
+{
+ public List ItemList
+ {
+ get => itemList;
+ set => itemList = value;
+ }
+
+ public string FilePath
+ {
+ get => filePath;
+ set => filePath = value;
+ }
+
+ public bool Save
+ {
+ get => save;
+ set => save = value;
+ }
+
+ public bool Hidden
+ {
+ get => hidden;
+ set => hidden = value;
+ }
+
+ public Addin(string filePath)
+ {
+ itemList = new List();
+ this.filePath = filePath;
+ save = true;
+ }
+
+ public Addin(string filePath, List itemList)
+ {
+ this.itemList = itemList;
+ this.filePath = filePath;
+ SortAddinItem();
+ save = true;
+ }
+
+ public void SortAddinItem()
+ {
+ itemList.Sort(new AddinItemComparer());
+ }
+
+ public void RemoveItem(AddinItem item)
+ {
+ itemList.Remove(item);
+ if (itemList.Count == 0)
+ {
+ AddinManagerBase.Instance.AddinManager.RemoveAddin(this);
+ }
+ }
+
+ public void SaveToLocalIni(IniFile file)
+ {
+ if (itemList == null || itemList.Count == 0)
+ {
+ return;
+ }
+ file.WriteSection("ExternalCommands");
+ file.Write("ExternalCommands", "ECCount", 0);
+ var num = 0;
+ foreach (var addinItem in itemList)
+ {
+ if (addinItem.Save)
+ {
+ WriteExternalCommand(file, addinItem, ++num);
+ }
+ }
+
+ file.Write("ExternalCommands", "ECCount", num);
+ }
+
+ private void WriteExternalCommand(IniFile file, AddinItem item, int number)
+ {
+ file.Write("ExternalCommands", "ECName" + number, item.Name);
+ file.Write("ExternalCommands", "ECClassName" + number, item.FullClassName);
+ file.Write("ExternalCommands", "ECAssembly" + number, item.AssemblyName);
+ file.Write("ExternalCommands", "ECDescription" + number, item.Description);
+ }
+
+ private List itemList;
+
+ private string filePath;
+
+ private bool save;
+
+ private bool hidden;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/AddinItem.cs b/src/OpenProject.Navisworks/Model/AddinItem.cs
new file mode 100644
index 00000000..74c52c1c
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/AddinItem.cs
@@ -0,0 +1,137 @@
+using System.IO;
+
+namespace OpenProjectNavisworks.Model;
+
+public class AddinItem : IAddinNode
+{
+ public AddinItem(AddinType type)
+ {
+ AddinType = type;
+ clientId = Guid.NewGuid();
+ ClientIdString = clientId.ToString();
+ assemblyPath = string.Empty;
+ AssemblyName = string.Empty;
+ FullClassName = string.Empty;
+ name = string.Empty;
+ Save = true;
+ VisibilityMode = VisibilityMode.AlwaysVisible;
+ }
+
+ public AddinItem(string assemblyPath, Guid clientId, string fullClassName, AddinType type)
+ {
+
+ AddinType = type;
+ this.assemblyPath = assemblyPath;
+ AssemblyName = Path.GetFileName(this.assemblyPath);
+ this.clientId = clientId;
+ ClientIdString = clientId.ToString();
+ FullClassName = fullClassName;
+ var num = fullClassName.LastIndexOf(".", StringComparison.Ordinal);
+ name = fullClassName.Substring(num + 1);
+ Save = true;
+ VisibilityMode = VisibilityMode.AlwaysVisible;
+ }
+
+ public void SaveToManifest()
+ {
+ var manifestFile = new ManifestFile(name + DefaultSetting.FormatExAddin);
+ if (AddinType == AddinType.Application)
+ {
+ manifestFile.Applications.Add(this);
+ }
+ else if (AddinType == AddinType.Command)
+ {
+ manifestFile.Commands.Add(this);
+ }
+ manifestFile.Save();
+ }
+
+ public AddinType AddinType { get; set; }
+
+ public string AssemblyPath
+ {
+ get => assemblyPath;
+ set
+ {
+ assemblyPath = value;
+ AssemblyName = Path.GetFileName(assemblyPath);
+ }
+ }
+
+ public string AssemblyName { get; set; }
+
+ public Guid ClientId
+ {
+ get => clientId;
+ set
+ {
+ clientId = value;
+ ClientIdString = clientId.ToString();
+ }
+ }
+
+ protected internal string ClientIdString { get; set; }
+
+ public string FullClassName { get; set; }
+
+ public string Name
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ return "External Tool";
+ }
+ return name;
+ }
+ set
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ name = value;
+ return;
+ }
+ name = "External Tool";
+ }
+ }
+
+ public string Description
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(description))
+ {
+ return string.Empty;
+ }
+ return description;
+ }
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ description = string.Empty;
+ return;
+ }
+ description = value;
+ }
+ }
+
+ public VisibilityMode VisibilityMode { get; set; }
+
+ public bool Save { get; set; }
+
+ public bool Hidden { get; set; }
+
+ public override string ToString()
+ {
+ return name;
+ }
+
+ protected string assemblyPath;
+
+ protected Guid clientId;
+
+ private string name;
+
+ private string description;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/AddinItemComparer.cs b/src/OpenProject.Navisworks/Model/AddinItemComparer.cs
new file mode 100644
index 00000000..cdb51962
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/AddinItemComparer.cs
@@ -0,0 +1,12 @@
+namespace OpenProjectNavisworks.Model;
+
+///
+/// Compare Sort By Full Class Name Method
+///
+public class AddinItemComparer : IComparer
+{
+ public int Compare(AddinItem x, AddinItem y)
+ {
+ return string.Compare(x.FullClassName, y.FullClassName, StringComparison.Ordinal);
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/AddinType.cs b/src/OpenProject.Navisworks/Model/AddinType.cs
new file mode 100644
index 00000000..be7f21c2
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/AddinType.cs
@@ -0,0 +1,10 @@
+namespace OpenProjectNavisworks.Model;
+
+[Flags]
+public enum AddinType
+{
+ Invalid = 0,
+ Command = 1,
+ Application = 2,
+ Mixed = 3
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/Addins.cs b/src/OpenProject.Navisworks/Model/Addins.cs
new file mode 100644
index 00000000..8fe222e8
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/Addins.cs
@@ -0,0 +1,101 @@
+using System.IO;
+using System.Reflection;
+using System.Windows;
+using Autodesk.Navisworks.Api.Plugins;
+
+namespace OpenProjectNavisworks.Model;
+
+public abstract class Addins
+{
+ public SortedDictionary AddinDict
+ {
+ get => addinDict;
+ set => addinDict = value;
+ }
+
+ public int Count => addinDict.Count;
+
+ public Addins()
+ {
+ addinDict = new SortedDictionary();
+ }
+
+ public void SortAddin()
+ {
+ foreach (var addin in addinDict.Values)
+ {
+ addin.SortAddinItem();
+ }
+ }
+
+ public void AddAddIn(Addin addin)
+ {
+ var fileName = Path.GetFileName(addin.FilePath);
+ if (addinDict.ContainsKey(fileName))
+ {
+ addinDict.Remove(fileName);
+ }
+ addinDict[fileName] = addin;
+ }
+
+ public bool RemoveAddIn(Addin addin)
+ {
+ var fileName = Path.GetFileName(addin.FilePath);
+ if (addinDict.ContainsKey(fileName))
+ {
+ addinDict.Remove(fileName);
+ return true;
+ }
+ return false;
+ }
+
+ public void AddItem(AddinItem item)
+ {
+ var assemblyName = item.AssemblyName;
+ if (!addinDict.ContainsKey(assemblyName))
+ {
+ addinDict[assemblyName] = new Addin(item.AssemblyPath);
+ }
+ addinDict[assemblyName].ItemList.Add(item);
+ }
+
+ public List LoadItems(Assembly assembly, string fullName, string originalAssemblyFilePath, AddinType type)
+ {
+ var list = new List();
+ Type[] array;
+ try
+ {
+ array = assembly.GetTypes();
+ }
+ catch (ReflectionTypeLoadException ex)
+ {
+ array = ex.Types;
+ if (array == null)
+ {
+ return list;
+ }
+ }
+ foreach (var type2 in array)
+ {
+ try
+ {
+ if (!(null == type2) && type2.IsSubclassOf(typeof(AddInPlugin)))
+ {
+ var item = new AddinItem(originalAssemblyFilePath, Guid.NewGuid(), type2.FullName, type);
+ list.Add(item);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException(e.ToString());
+ }
+ }
+ return list;
+ }
+
+ protected SortedDictionary addinDict;
+
+ protected int maxCount = 100;
+
+ protected int count;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/AssemLoader.cs b/src/OpenProject.Navisworks/Model/AssemLoader.cs
new file mode 100644
index 00000000..42404b40
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/AssemLoader.cs
@@ -0,0 +1,311 @@
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Windows;
+using OpenProjectNavisworks.View;
+
+namespace OpenProjectNavisworks.Model;
+
+public class AssemLoader
+{
+ public string OriginalFolder
+ {
+ get => originalFolder;
+ set => originalFolder = value;
+ }
+
+ public string TempFolder
+ {
+ get => tempFolder;
+ set => tempFolder = value;
+ }
+
+ public AssemLoader()
+ {
+ tempFolder = string.Empty;
+ refedFolders = new List();
+ copiedFiles = new Dictionary();
+ }
+
+ public void CopyGeneratedFilesBack()
+ {
+ var files = Directory.GetFiles(tempFolder, "*.*", SearchOption.AllDirectories);
+ if(!files.Any()) return;
+ foreach (var text in files)
+ {
+ if (copiedFiles.ContainsKey(text))
+ {
+ var t = copiedFiles[text];
+ var fileInfo = new FileInfo(text);
+ if (fileInfo.LastWriteTime > t)
+ {
+ var str = text.Remove(0, tempFolder.Length);
+ var destinationFilename = originalFolder + str;
+ FileUtils.CopyFile(text, destinationFilename);
+ }
+ }
+ else
+ {
+ var str2 = text.Remove(0, tempFolder.Length);
+ var destinationFilename2 = originalFolder + str2;
+ FileUtils.CopyFile(text, destinationFilename2);
+ }
+ }
+ }
+
+ public void HookAssemblyResolve()
+ {
+ AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
+ }
+
+ public void UnhookAssemblyResolve()
+ {
+ AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve;
+ }
+
+ public Assembly LoadAddinsToTempFolder(string originalFilePath, bool parsingOnly)
+ {
+ if (string.IsNullOrEmpty(originalFilePath) || !File.Exists(originalFilePath))
+ {
+ return null;
+ }
+ this.parsingOnly = parsingOnly;
+ originalFolder = Path.GetDirectoryName(originalFilePath);
+ var stringBuilder = new StringBuilder(Path.GetFileNameWithoutExtension(originalFilePath));
+ if (parsingOnly)
+ {
+ stringBuilder.Append("-Parsing-");
+ }
+ else
+ {
+ stringBuilder.Append("-Executing-");
+ }
+ tempFolder = FileUtils.CreateTempFolder(stringBuilder.ToString());
+ var assembly = CopyAndLoadAddin(originalFilePath, parsingOnly);
+
+ if (null == assembly || !IsAPIReferenced(assembly))
+ {
+ return null;
+ }
+ return assembly;
+ }
+
+ private Assembly CopyAndLoadAddin(string srcFilePath, bool onlyCopyRelated)
+ {
+ var text = string.Empty;
+ if (!FileUtils.FileExistsInFolder(srcFilePath, tempFolder))
+ {
+ var directoryName = Path.GetDirectoryName(srcFilePath);
+ if (!refedFolders.Contains(directoryName))
+ {
+ refedFolders.Add(directoryName);
+ }
+ var list = new List();
+ text = FileUtils.CopyFileToFolder(srcFilePath, tempFolder, onlyCopyRelated, list);
+ if (string.IsNullOrEmpty(text))
+ {
+ return null;
+ }
+ foreach (var fileInfo in list)
+ {
+ copiedFiles.Add(fileInfo.FullName, fileInfo.LastWriteTime);
+ }
+ }
+ return LoadAddin(text);
+ }
+
+ private Assembly LoadAddin(string filePath)
+ {
+ Assembly result = null;
+ try
+ {
+ Monitor.Enter(this);
+ //Agree this error to load depend event assembly, see https://github.com/chuongmep/RevitAddInManager/issues/7
+ result = Assembly.LoadFile(filePath);
+ }
+ finally
+ {
+ Monitor.Exit(this);
+ }
+ return result;
+ }
+
+ private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
+ {
+ Assembly result;
+ new AssemblyName(args.Name);
+ var text = SearchAssemblyFileInTempFolder(args.Name);
+ if (File.Exists(text))
+ {
+ result = LoadAddin(text);
+ }
+ else
+ {
+ text = SearchAssemblyFileInOriginalFolders(args.Name);
+ if (string.IsNullOrEmpty(text))
+ {
+ var array = args.Name.Split(new char[]
+ {
+ ','
+ });
+ var text2 = array[0];
+ if (array.Length > 1)
+ {
+ var text3 = array[2];
+ if (text2.EndsWith(".resources", StringComparison.CurrentCultureIgnoreCase) && !text3.EndsWith("neutral", StringComparison.CurrentCultureIgnoreCase))
+ {
+ text2 = text2.Substring(0, text2.Length - ".resources".Length);
+ }
+ text = SearchAssemblyFileInTempFolder(text2);
+ if (File.Exists(text))
+ {
+ return LoadAddin(text);
+ }
+ text = SearchAssemblyFileInOriginalFolders(text2);
+ }
+ }
+ if (string.IsNullOrEmpty(text))
+ {
+ var loader = new AssemblyLoader(args.Name);
+ loader.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ if (loader.ShowDialog() != true)
+ {
+ return null;
+ }
+ text = loader.resultPath;
+ }
+ result = CopyAndLoadAddin(text, true);
+ }
+
+ return result;
+ }
+
+ private string SearchAssemblyFileInTempFolder(string assemName)
+ {
+ try
+ {
+ var array = new string[] { ".dll", ".exe" };
+ var text = string.Empty;
+ var str = assemName.Substring(0, assemName.IndexOf(','));
+ foreach (var str2 in array)
+ {
+ text = tempFolder + "\\" + str + str2;
+
+ if (File.Exists(text))
+ {
+ return text;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException(e.ToString());
+ }
+ return string.Empty;
+ }
+
+ private string SearchAssemblyFileInOriginalFolders(string assemName)
+ {
+ var array = new string[]
+ {
+ ".dll",
+ ".exe"
+ };
+ string text;
+ var text2 = assemName.Substring(0, assemName.IndexOf(','));
+ foreach (var str in array)
+ {
+ text = dotnetDir + "\\" + text2 + str;
+ if (File.Exists(text))
+ {
+ return text;
+ }
+ }
+ foreach (var str2 in array)
+ {
+ foreach (var str3 in refedFolders)
+ {
+ text = str3 + "\\" + text2 + str2;
+ if (File.Exists(text))
+ {
+ return text;
+ }
+ }
+ }
+ try
+ {
+ var directoryInfo = new DirectoryInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));
+ var path = directoryInfo.Parent?.FullName + "\\Regression\\_RegressionTools\\";
+ if (Directory.Exists(path))
+ {
+ foreach (var text3 in Directory.GetFiles(path, "*.*", SearchOption.AllDirectories))
+ {
+ if (Path.GetFileNameWithoutExtension(text3).Equals(text2, StringComparison.OrdinalIgnoreCase))
+ {
+ return text3;
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException(e.ToString());
+ }
+
+ try
+ {
+ var num = assemName.IndexOf("XMLSerializers", StringComparison.OrdinalIgnoreCase);
+ if (num != -1)
+ {
+ assemName = "System.XML" + assemName.Substring(num + "XMLSerializers".Length);
+ return SearchAssemblyFileInOriginalFolders(assemName);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException(e.ToString());
+ }
+ return null;
+ }
+
+ private bool IsAPIReferenced(Assembly assembly)
+ {
+
+ if (string.IsNullOrEmpty(revitApiAssemblyFullName))
+ {
+ foreach (var assembly2 in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ if (String.Compare(assembly2.GetName().Name, DefaultSetting.AssNavisName, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ revitApiAssemblyFullName = assembly2.GetName().Name;
+ break;
+ }
+ }
+ }
+ foreach (var assemblyName in assembly.GetReferencedAssemblies())
+ {
+ if (revitApiAssemblyFullName == assemblyName.Name)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private readonly List refedFolders;
+
+ private readonly Dictionary copiedFiles;
+
+ private bool parsingOnly;
+
+ private string originalFolder;
+
+ private string tempFolder;
+
+ private static string dotnetDir = Environment.GetEnvironmentVariable("windir") + "\\Microsoft.NET\\Framework\\v2.0.50727";
+
+ public static string ResolvedAssemPath = string.Empty;
+
+ private string revitApiAssemblyFullName;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/BitmapSourceConverter.cs b/src/OpenProject.Navisworks/Model/BitmapSourceConverter.cs
new file mode 100644
index 00000000..48b4b66d
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/BitmapSourceConverter.cs
@@ -0,0 +1,69 @@
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+
+namespace OpenProjectNavisworks.Model;
+
+public static class BitmapSourceConverter
+{
+ public enum ImageType
+ {
+ Small,
+ Large
+ }
+
+ public static ImageSource ToImageSource(Bitmap bitmap, ImageType imageType)
+ {
+ switch (imageType)
+ {
+ case ImageType.Small:
+ return ToImageSource(bitmap).Resize(16);
+
+ case ImageType.Large:
+ return ToImageSource(bitmap).Resize(32);
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(imageType), imageType, null);
+ }
+ }
+
+ public static BitmapImage ToImageSource(Bitmap bitmap)
+ {
+ var ms = new MemoryStream();
+ bitmap.Save(ms, ImageFormat.Png);
+ var image = new BitmapImage();
+ image.BeginInit();
+ ms.Seek(0, SeekOrigin.Begin);
+ image.StreamSource = ms;
+ image.EndInit();
+ return image;
+ }
+
+ ///
+ /// Resize ImageResource
+ ///
+ ///
+ ///
+ ///
+ public static ImageSource Resize(this ImageSource imageSource, int size)
+ {
+ return Thumbnail(imageSource, size);
+ }
+
+ private static ImageSource Thumbnail(ImageSource source, int size)
+ {
+ var rect = new Rect(0, 0, size, size);
+ var drawingVisual = new DrawingVisual();
+ using (var drawingContext = drawingVisual.RenderOpen())
+ {
+ drawingContext.DrawImage(source, rect);
+ }
+ var resizedImage = new RenderTargetBitmap((int)rect.Width, (int)rect.Height, 96, 96, PixelFormats.Default);
+ resizedImage.Render(drawingVisual);
+
+ return resizedImage;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/CodeListener.cs b/src/OpenProject.Navisworks/Model/CodeListener.cs
new file mode 100644
index 00000000..65ed5dc5
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/CodeListener.cs
@@ -0,0 +1,27 @@
+using System.Diagnostics;
+using System.IO;
+
+namespace OpenProjectNavisworks.Model;
+
+public class CodeListener : TraceListener
+{
+ public override void Write(string message)
+ {
+ WriteMessage(message);
+ }
+
+ public override void WriteLine(string message)
+ {
+ WriteMessage(message);
+ }
+
+ void WriteMessage(string message)
+ {
+ using (StreamWriter st = new StreamWriter(DefaultSetting.PathLogFile, true))
+ {
+ string join = string.Join(": ", $"{DateTime.Now}", message);
+ st.WriteLine(join);
+ st.Close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/DefaultSetting.cs b/src/OpenProject.Navisworks/Model/DefaultSetting.cs
new file mode 100644
index 00000000..962a5882
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/DefaultSetting.cs
@@ -0,0 +1,50 @@
+using System.IO;
+
+namespace OpenProjectNavisworks.Model;
+
+///
+/// All setting name default for addin
+///
+public static class DefaultSetting
+{
+ public static string AppName = "Navis Addin Manager";
+ public static string FileName = "ExternalTool";
+ public static string AssNavisName = "Autodesk.Navisworks.Api";
+ public static string FormatExAddin = ".addin";
+ public static string FormatDisable = ".disable";
+
+ public static string IniName = "Navis.ini";
+
+ public static string TempFolderName = "NavisAddins";
+
+ public static string AimInternalName = "AimInternalNavis.ini";
+ public static string DirLogFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ "AppData",
+ "Local",
+ "Temp",TempFolderName);
+
+ private static string pathLogFile;
+ public static string PathLogFile
+ {
+ get
+ {
+
+ bool flag = Directory.Exists(DirLogFile);
+ if (!flag) Directory.CreateDirectory(DirLogFile);
+ DirectoryInfo directoryInfo = new DirectoryInfo(DirLogFile);
+ FileInfo fileInfo = directoryInfo.GetFiles("*.txt",SearchOption.TopDirectoryOnly).OrderBy(x=>x.LastWriteTime).LastOrDefault();
+ if (fileInfo==null)
+ {
+ pathLogFile = Path.Combine(DirLogFile, $"{Guid.NewGuid()}.txt");
+ File.Create(pathLogFile).Close();
+ return pathLogFile;
+ }
+ else
+ {
+ pathLogFile = fileInfo.FullName;
+ }
+ return fileInfo.FullName;
+ }
+ set => pathLogFile = value;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/FileUtils.cs b/src/OpenProject.Navisworks/Model/FileUtils.cs
new file mode 100644
index 00000000..ba5b9dca
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/FileUtils.cs
@@ -0,0 +1,240 @@
+using System.IO;
+using System.Windows;
+using MessageBox = System.Windows.MessageBox;
+
+namespace OpenProjectNavisworks.Model;
+
+public static class FileUtils
+{
+ public static DateTime GetModifyTime(string filePath)
+ {
+ return File.GetLastWriteTime(filePath);
+ }
+
+ public static string CreateTempFolder(string prefix)
+ {
+ var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var tempPath = Path.Combine(folderPath, "Temp");
+ var directoryInfo = new DirectoryInfo(Path.Combine(tempPath, DefaultSetting.TempFolderName));
+ if (!directoryInfo.Exists)
+ {
+ directoryInfo.Create();
+ }
+ foreach (var directoryInfo2 in directoryInfo.GetDirectories())
+ {
+ try
+ {
+ Directory.Delete(directoryInfo2.FullName, true);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ var str = $"{DateTime.Now:yyyyMMdd_HHmmss_ffff}";
+ var path = Path.Combine(directoryInfo.FullName, prefix + str);
+ var directoryInfo3 = new DirectoryInfo(path);
+ directoryInfo3.Create();
+ return directoryInfo3.FullName;
+ }
+
+ public static void SetWriteable(string fileName)
+ {
+ if (File.Exists(fileName))
+ {
+ var fileAttributes = File.GetAttributes(fileName) & ~FileAttributes.ReadOnly;
+ File.SetAttributes(fileName, fileAttributes);
+ }
+ }
+
+ public static bool SameFile(string file1, string file2)
+ {
+ return 0 == String.Compare(file1.Trim(), file2.Trim(), StringComparison.OrdinalIgnoreCase);
+ }
+
+ public static bool CreateFile(string filePath)
+ {
+ if (File.Exists(filePath))
+ {
+ return true;
+ }
+ try
+ {
+ var directoryName = Path.GetDirectoryName(filePath);
+ if (!Directory.Exists(directoryName))
+ {
+ Directory.CreateDirectory(directoryName);
+ }
+ using (new FileInfo(filePath).Create())
+ {
+ SetWriteable(filePath);
+ }
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ return File.Exists(filePath);
+ }
+
+ public static void DeleteFile(string fileName)
+ {
+ if (File.Exists(fileName))
+ {
+ var fileAttributes = File.GetAttributes(fileName) & ~FileAttributes.ReadOnly;
+ File.SetAttributes(fileName, fileAttributes);
+ try
+ {
+ File.Delete(fileName);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.ToString());
+ }
+ }
+ }
+
+ public static bool FileExistsInFolder(string filePath, string destFolder)
+ {
+ var path = Path.Combine(destFolder, Path.GetFileName(filePath));
+ return File.Exists(path);
+ }
+
+ public static string CopyFileToFolder(string sourceFilePath, string destFolder, bool onlyCopyRelated, List allCopiedFiles)
+ {
+ if (!File.Exists(sourceFilePath))
+ {
+ return null;
+ }
+ var directoryName = Path.GetDirectoryName(sourceFilePath);
+ if (onlyCopyRelated)
+ {
+ var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(sourceFilePath);
+ var searchPattern = fileNameWithoutExtension + ".*";
+ var files = Directory.GetFiles(directoryName, searchPattern, SearchOption.TopDirectoryOnly);
+ foreach (var text in files)
+ {
+ var fileName = Path.GetFileName(text);
+ var text2 = Path.Combine(destFolder, fileName);
+ var flag = CopyFile(text, text2);
+ if (flag)
+ {
+ var item = new FileInfo(text2);
+ allCopiedFiles.Add(item);
+ }
+ }
+ }
+ else
+ {
+ var folderSize = GetFolderSize(directoryName);
+ if (folderSize > 50L)
+ {
+ switch (FolderTooBigDialog.Show(directoryName, folderSize))
+ {
+ case MessageBoxResult.Yes:
+ CopyDirectory(directoryName, destFolder, allCopiedFiles);
+ break;
+
+ case MessageBoxResult.No:
+ CopyFileToFolder(sourceFilePath, destFolder, true, allCopiedFiles);
+ break;
+
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ CopyDirectory(directoryName, destFolder, allCopiedFiles);
+ }
+ }
+ var text3 = Path.Combine(destFolder, Path.GetFileName(sourceFilePath));
+ if (File.Exists(text3))
+ {
+ return text3;
+ }
+ return null;
+ }
+
+ public static bool CopyFile(string sourceFilename, string destinationFilename)
+ {
+ if (!File.Exists(sourceFilename))
+ {
+ return false;
+ }
+ var fileAttributes = File.GetAttributes(sourceFilename) & ~FileAttributes.ReadOnly;
+ File.SetAttributes(sourceFilename, fileAttributes);
+ if (File.Exists(destinationFilename))
+ {
+ var fileAttributes2 = File.GetAttributes(destinationFilename) & ~FileAttributes.ReadOnly;
+ File.SetAttributes(destinationFilename, fileAttributes2);
+ File.Delete(destinationFilename);
+ }
+ try
+ {
+ if (!Directory.Exists(Path.GetDirectoryName(destinationFilename)))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(destinationFilename));
+ }
+ File.Copy(sourceFilename, destinationFilename, true);
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ return File.Exists(destinationFilename);
+ }
+
+ public static void CopyDirectory(string sourceDir, string desDir, List allCopiedFiles)
+ {
+ try
+ {
+ var directories = Directory.GetDirectories(sourceDir, "*.*", SearchOption.AllDirectories);
+ foreach (var text in directories)
+ {
+ var str = text.Replace(sourceDir, "");
+ var path = desDir + str;
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+ var files = Directory.GetFiles(sourceDir, "*.*", SearchOption.AllDirectories);
+ foreach (var text2 in files)
+ {
+ var str2 = text2.Replace(sourceDir, "");
+ var text3 = desDir + str2;
+ if (!Directory.Exists(Path.GetDirectoryName(text3)))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(text3));
+ }
+ if (CopyFile(text2, text3))
+ {
+ allCopiedFiles.Add(new FileInfo(text3));
+ }
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ public static long GetFolderSize(string folderPath)
+ {
+ var directoryInfo = new DirectoryInfo(folderPath);
+ var num = 0L;
+ foreach (var fileSystemInfo in directoryInfo.GetFileSystemInfos())
+ {
+ if (fileSystemInfo is FileInfo)
+ {
+ num += ((FileInfo)fileSystemInfo).Length;
+ }
+ else
+ {
+ num += GetFolderSize(fileSystemInfo.FullName);
+ }
+ }
+ return num / 1024L / 1024L;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/FolderTooBigDialog.cs b/src/OpenProject.Navisworks/Model/FolderTooBigDialog.cs
new file mode 100644
index 00000000..6d3fdffa
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/FolderTooBigDialog.cs
@@ -0,0 +1,26 @@
+using System.Text;
+using System.Windows;
+using MessageBox = System.Windows.MessageBox;
+
+namespace OpenProjectNavisworks.Model;
+
+internal static class FolderTooBigDialog
+{
+ ///
+ /// Show a waring if file dll too large
+ ///
+ /// folder contains file resource
+ /// limit of dll size
+ ///
+ public static MessageBoxResult Show(string folderPath, long sizeInMB)
+ {
+ var stringBuilder = new StringBuilder();
+ stringBuilder.AppendLine("Folder [" + folderPath + "]");
+ stringBuilder.AppendLine("is " + sizeInMB + "MB large.");
+ stringBuilder.AppendLine("AddinManager will attempt to copy all the files to temp folder");
+ stringBuilder.AppendLine("Select [Yes] to copy all the files to temp folder");
+ stringBuilder.AppendLine("Select [No] to only copy test script DLL");
+ var text = stringBuilder.ToString();
+ return MessageBox.Show(text, DefaultSetting.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Asterisk);
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/IAddinNode.cs b/src/OpenProject.Navisworks/Model/IAddinNode.cs
new file mode 100644
index 00000000..b884d993
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/IAddinNode.cs
@@ -0,0 +1,8 @@
+namespace OpenProjectNavisworks.Model;
+
+public interface IAddinNode
+{
+ bool Save { get; set; }
+
+ bool Hidden { get; set; }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/IEConverter.cs b/src/OpenProject.Navisworks/Model/IEConverter.cs
new file mode 100644
index 00000000..6f498709
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/IEConverter.cs
@@ -0,0 +1,25 @@
+using System.Collections.ObjectModel;
+
+namespace OpenProjectNavisworks.Model;
+
+///
+/// Quick convert collection
+///
+public static class IEConverter
+{
+ ///
+ /// Convert IEnumerableUtils To ObservableCollection
+ ///
+ ///
+ ///
+ ///
+ public static ObservableCollection ToObservableCollection(this IEnumerable source)
+ {
+ var newSource = new ObservableCollection();
+ foreach (var t in source)
+ {
+ newSource.Add(t);
+ }
+ return newSource;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/IniFile.cs b/src/OpenProject.Navisworks/Model/IniFile.cs
new file mode 100644
index 00000000..a94b262e
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/IniFile.cs
@@ -0,0 +1,56 @@
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace OpenProjectNavisworks.Model;
+
+public class IniFile
+{
+ public string FilePath => filePath;
+
+ public IniFile(string filePath)
+ {
+ this.filePath = filePath;
+ if (!File.Exists(this.filePath))
+ {
+ FileUtils.CreateFile(this.filePath);
+ FileUtils.SetWriteable(this.filePath);
+ }
+ }
+
+ public void WriteSection(string iniSection)
+ {
+ WritePrivateProfileSection(iniSection, null, filePath);
+ }
+
+ public void Write(string iniSection, string iniKey, object iniValue)
+ {
+ WritePrivateProfileString(iniSection, iniKey, iniValue.ToString(), filePath);
+ }
+
+ public string ReadString(string iniSection, string iniKey)
+ {
+ var stringBuilder = new StringBuilder(255);
+ GetPrivateProfileString(iniSection, iniKey, "", stringBuilder, 255, filePath);
+ return stringBuilder.ToString();
+ }
+
+ public int ReadInt(string iniSection, string iniKey)
+ {
+ return GetPrivateProfileInt(iniSection, iniKey, 0, filePath);
+ }
+
+ [DllImport("kernel32.dll")]
+ private static extern int WritePrivateProfileSection(string lpAppName, string lpString, string lpFileName);
+
+ [DllImport("kernel32", CharSet = CharSet.Auto)]
+ private static extern int WritePrivateProfileString(string section, string key, string val, string filePath);
+
+ [DllImport("kernel32")]
+ private static extern int GetPrivateProfileInt(string section, string key, int def, string filePath);
+
+ [DllImport("kernel32", CharSet = CharSet.Auto)]
+ private static extern int GetPrivateProfileString(string section, string key, string defaultValue, StringBuilder retVal, int size, string filePath);
+
+ private readonly string filePath;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/LogMessageString.cs b/src/OpenProject.Navisworks/Model/LogMessageString.cs
new file mode 100644
index 00000000..186557af
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/LogMessageString.cs
@@ -0,0 +1,33 @@
+using System.Windows;
+
+namespace OpenProjectNavisworks.Model;
+
+public class LogMessageString
+{
+ private int DEFAULD_FONT_SIZE = 12;
+ public string Message { get; set; }
+ public System.Windows.Media.Brush MessageColor { get; set; }
+ public FontWeight FontWeight { get; set; }
+ public int FontSize { get; set; }
+ public LogMessageString(string message, System.Windows.Media.Brush colore)
+ {
+ Message = message;
+ MessageColor = colore;
+ FontWeight = FontWeights.Normal;
+ FontSize = DEFAULD_FONT_SIZE;
+ }
+ public LogMessageString(string message, System.Windows.Media.Brush colore, FontWeight fontWeight)
+ {
+ Message = message;
+ MessageColor = colore;
+ FontWeight = fontWeight;
+ FontSize = DEFAULD_FONT_SIZE;
+ }
+ public LogMessageString(string message, System.Windows.Media.Brush colore, FontWeight fontWeight, int fontSize)
+ {
+ Message = message;
+ MessageColor = colore;
+ FontWeight = fontWeight;
+ FontSize = fontSize;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/ManifestFile.cs b/src/OpenProject.Navisworks/Model/ManifestFile.cs
new file mode 100644
index 00000000..df346913
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/ManifestFile.cs
@@ -0,0 +1,422 @@
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System.Xml;
+
+namespace OpenProjectNavisworks.Model;
+
+public class ManifestFile
+{
+ public ManifestFile()
+ {
+ local = false;
+ applications = new List();
+ commands = new List();
+ }
+
+ public ManifestFile(string fileName) : this()
+ {
+ this.fileName = fileName;
+ if (string.IsNullOrEmpty(filePath))
+ {
+ var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AddIn");
+ filePath = Path.Combine(path, this.fileName);
+ }
+ }
+
+ public ManifestFile(bool local) : this()
+ {
+ this.local = local;
+ }
+
+ public void Load()
+ {
+ xmlDoc = new XmlDocument();
+ xmlDoc.Load(filePath);
+ var documentElement = xmlDoc.DocumentElement;
+ if (!documentElement.Name.Equals(ROOT_NODE))
+ {
+ throw new ArgumentException(INCORRECT_NODE);
+ }
+ if (documentElement.ChildNodes.Count == 0)
+ {
+ throw new ArgumentException(EMPTY_ADDIN);
+ }
+ applications.Clear();
+ commands.Clear();
+ foreach (var obj in documentElement.ChildNodes)
+ {
+ var xmlNode = (XmlNode)obj;
+ if (!xmlNode.Name.Equals(ADDIN_NODE) || xmlNode.Attributes.Count != 1)
+ {
+ throw new ArgumentException(INCORRECT_NODE);
+ }
+ var xmlAttribute = xmlNode.Attributes[0];
+ if (xmlAttribute.Value.Equals(APPLICATION_NODE))
+ {
+ ParseExternalApplications(xmlNode);
+ }
+ else
+ {
+ if (!xmlAttribute.Value.Equals(COMMAND_NODE))
+ {
+ throw new ArgumentException(INCORRECT_NODE);
+ }
+ ParseExternalCommands(xmlNode);
+ }
+ }
+ }
+
+ public void Save()
+ {
+ SaveAs(filePath);
+ }
+
+ public void SaveAs(string filePath)
+ {
+ if (string.IsNullOrEmpty(filePath))
+ {
+ throw new ArgumentNullException(FILENAME_NULL_OR_EMPTY);
+ }
+ if (!filePath.ToLower().EndsWith(DefaultSetting.FormatExAddin))
+ {
+ throw new ArgumentException(FILENAME_INCORRECT_WARNING + filePath);
+ }
+ var directoryName = Path.GetDirectoryName(filePath);
+ if (!Directory.Exists(directoryName))
+ {
+ Directory.CreateDirectory(directoryName);
+ }
+ var fileInfo = new FileInfo(filePath);
+ xmlDoc = new XmlDocument();
+ CreateXmlForManifest();
+ if (File.Exists(filePath))
+ {
+ File.SetAttributes(filePath, FileAttributes.Normal);
+ }
+ TextWriter w = new StreamWriter(filePath, false, Encoding.UTF8);
+ var xmlTextWriter = new XmlTextWriter(w);
+ xmlTextWriter.Formatting = Formatting.Indented;
+ xmlDoc.Save(xmlTextWriter);
+ xmlTextWriter.Close();
+ this.filePath = fileInfo.FullName;
+ fileName = Path.GetFileName(fileInfo.FullName);
+ }
+
+ public string FileName
+ {
+ get => fileName;
+ set => fileName = value;
+ }
+
+ public bool Local
+ {
+ get => local;
+ set => local = value;
+ }
+
+ private string _vendorDescription;
+
+ public string VendorDescription
+ {
+ get => _vendorDescription;
+ set => _vendorDescription = value;
+ }
+
+ public string FilePath
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(filePath))
+ {
+ var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "AddIn");
+ filePath = Path.Combine(path, DefaultSetting.AimInternalName);
+ }
+ return filePath;
+ }
+ set => filePath = value;
+ }
+
+ public List Applications
+ {
+ get => applications;
+ set => applications = value;
+ }
+
+ public List Commands
+ {
+ get => commands;
+ set => commands = value;
+ }
+
+ private XmlDocument CreateXmlForManifest()
+ {
+ var xmlNode = xmlDoc.AppendChild(xmlDoc.CreateElement(ROOT_NODE));
+ foreach (var currentApp in applications)
+ {
+ var xmlElement = xmlDoc.CreateElement(ADDIN_NODE);
+ xmlElement.SetAttribute(TYPE_ATTRIBUTE, APPLICATION_NODE);
+ xmlNode.AppendChild(xmlElement);
+ AddApplicationToXmlElement(xmlElement, currentApp);
+ var xmlElement2 = xmlDoc.CreateElement(VENDORID);
+ xmlElement2.InnerText = "ADSK";
+ xmlElement.AppendChild(xmlElement2);
+ xmlElement2 = xmlDoc.CreateElement(VENDORDESCRIPTION);
+ xmlElement2.InnerText = "Autodesk, www.autodesk.com";
+ xmlElement.AppendChild(xmlElement2);
+ }
+ foreach (var command in commands)
+ {
+ var xmlElement3 = xmlDoc.CreateElement(ADDIN_NODE);
+ xmlElement3.SetAttribute(TYPE_ATTRIBUTE, COMMAND_NODE);
+ xmlNode.AppendChild(xmlElement3);
+ AddCommandToXmlElement(xmlElement3, command);
+ var xmlElement4 = xmlDoc.CreateElement(VENDORID);
+ xmlElement4.InnerText = "ADSK";
+ xmlElement3.AppendChild(xmlElement4);
+ xmlElement4 = xmlDoc.CreateElement(VENDORDESCRIPTION);
+ if (VendorDescription == string.Empty) xmlElement4.InnerText = "Autodesk, www.autodesk.com";
+ else xmlElement4.InnerText = VendorDescription;
+ xmlElement3.AppendChild(xmlElement4);
+ }
+ return xmlDoc;
+ }
+
+ private void AddAddInItemToXmlElement(XmlElement xmlEle, AddinItem addinItem)
+ {
+ if (!string.IsNullOrEmpty(addinItem.AssemblyPath))
+ {
+ var xmlElement = xmlDoc.CreateElement(ASSEMBLY);
+ if (local)
+ {
+ xmlElement.InnerText = addinItem.AssemblyName;
+ }
+ else
+ {
+ xmlElement.InnerText = addinItem.AssemblyPath;
+ }
+ xmlEle.AppendChild(xmlElement);
+ }
+ if (!string.IsNullOrEmpty(addinItem.ClientIdString))
+ {
+ var xmlElement2 = xmlDoc.CreateElement(CLIENTID);
+ xmlElement2.InnerText = addinItem.ClientIdString;
+ xmlEle.AppendChild(xmlElement2);
+ }
+ if (!string.IsNullOrEmpty(addinItem.FullClassName))
+ {
+ var xmlElement3 = xmlDoc.CreateElement(FULLCLASSNAME);
+ xmlElement3.InnerText = addinItem.FullClassName;
+ xmlEle.AppendChild(xmlElement3);
+ }
+ }
+
+ private void AddApplicationToXmlElement(XmlElement appEle, AddinItem currentApp)
+ {
+ if (!string.IsNullOrEmpty(currentApp.Name))
+ {
+ var xmlElement = xmlDoc.CreateElement(NAME_NODE);
+ xmlElement.InnerText = currentApp.Name;
+ appEle.AppendChild(xmlElement);
+ }
+ AddAddInItemToXmlElement(appEle, currentApp);
+ }
+
+ private void AddCommandToXmlElement(XmlElement commandEle, AddinItem command)
+ {
+ AddAddInItemToXmlElement(commandEle, command);
+ XmlElement xmlElement;
+ if (!string.IsNullOrEmpty(command.Name))
+ {
+ xmlElement = xmlDoc.CreateElement(TEXT);
+ xmlElement.InnerText = command.Name;
+ commandEle.AppendChild(xmlElement);
+ }
+ if (!string.IsNullOrEmpty(command.Description))
+ {
+ xmlElement = xmlDoc.CreateElement(DESCRIPTION);
+ xmlElement.InnerText = command.Description;
+ commandEle.AppendChild(xmlElement);
+ }
+ var text = command.VisibilityMode.ToString();
+ if (!string.IsNullOrEmpty(text))
+ {
+ text = text.Replace(",", " |");
+ }
+ xmlElement = xmlDoc.CreateElement(VISIBILITYMODE);
+ xmlElement.InnerText = text;
+ commandEle.AppendChild(xmlElement);
+ }
+
+ private void ParseExternalApplications(XmlNode nodeApplication)
+ {
+ var addinItem = new AddinItem(AddinType.Application);
+ ParseApplicationItems(addinItem, nodeApplication);
+ applications.Add(addinItem);
+ }
+
+ private void ParseExternalCommands(XmlNode nodeCommand)
+ {
+ var addinItem = new AddinItem(AddinType.Command);
+ ParseCommandItems(addinItem, nodeCommand);
+ commands.Add(addinItem);
+ }
+
+ private void ParseApplicationItems(AddinItem addinApp, XmlNode nodeAddIn)
+ {
+ ParseAddInItem(addinApp, nodeAddIn);
+ var xmlElement = nodeAddIn[NAME_NODE];
+ if (xmlElement != null && !string.IsNullOrEmpty(xmlElement.InnerText))
+ {
+ addinApp.Name = xmlElement.InnerText;
+ }
+ }
+
+ private void ParseCommandItems(AddinItem command, XmlNode nodeAddIn)
+ {
+ ParseAddInItem(command, nodeAddIn);
+ var xmlElement = nodeAddIn[TEXT];
+ if (xmlElement != null)
+ {
+ command.Name = xmlElement.InnerText;
+ }
+ xmlElement = nodeAddIn[DESCRIPTION];
+ if (xmlElement != null)
+ {
+ command.Description = xmlElement.InnerText;
+ }
+ xmlElement = nodeAddIn[VISIBILITYMODE];
+ if (xmlElement != null && !string.IsNullOrEmpty(xmlElement.InnerText))
+ {
+ command.VisibilityMode = ParseVisibilityMode(xmlElement.InnerText);
+ }
+ }
+
+ private void ParseAddInItem(AddinItem addinItem, XmlNode nodeAddIn)
+ {
+ var xmlElement = nodeAddIn[ASSEMBLY];
+ if (xmlElement != null)
+ {
+ if (local)
+ {
+ addinItem.AssemblyName = xmlElement.InnerText;
+ }
+ else
+ {
+ addinItem.AssemblyPath = xmlElement.InnerText;
+ }
+ }
+ xmlElement = nodeAddIn[CLIENTID];
+ if (xmlElement != null)
+ {
+ try
+ {
+ if (!string.IsNullOrEmpty(xmlElement.InnerText))
+ {
+ addinItem.ClientId = new Guid(xmlElement.InnerText);
+ }
+ else
+ {
+ addinItem.ClientId = Guid.Empty;
+ }
+ }
+ catch (Exception)
+ {
+ addinItem.ClientId = Guid.Empty;
+ addinItem.ClientIdString = xmlElement.InnerText;
+ }
+ }
+ xmlElement = nodeAddIn[FULLCLASSNAME];
+ if (xmlElement != null)
+ {
+ addinItem.FullClassName = xmlElement.InnerText;
+ }
+ }
+
+ private VisibilityMode ParseVisibilityMode(string visibilityModeString)
+ {
+ var visibilityMode = VisibilityMode.AlwaysVisible;
+ VisibilityMode result;
+ try
+ {
+ var text = "|";
+ var separator = text.ToCharArray();
+ var array = visibilityModeString.Replace(" | ", "|").Split(separator);
+ foreach (var value in array)
+ {
+ var visibilityMode2 = (VisibilityMode)Enum.Parse(typeof(VisibilityMode), value);
+ visibilityMode |= visibilityMode2;
+ }
+ result = visibilityMode;
+ }
+ catch (Exception)
+ {
+ throw new ArgumentException(UNKNOW_VISIBILITYMODE);
+ }
+ return result;
+ }
+
+ private string getFullPath(string fileName)
+ {
+ FileInfo fileInfo = null;
+ try
+ {
+ fileInfo = new FileInfo(fileName);
+ }
+ catch (Exception ex)
+ {
+ throw new ArgumentException(fileName + Environment.NewLine + ex.ToString());
+ }
+ return fileInfo.FullName;
+ }
+
+ private string fileName;
+
+ private bool local;
+
+ private string filePath;
+
+ private List applications;
+
+ private List commands;
+
+ private string ROOT_NODE = "RevitAddIns";
+
+ private string ADDIN_NODE = "AddIn";
+
+ private string APPLICATION_NODE = "Application";
+
+ private string COMMAND_NODE = "Command";
+
+ private string TYPE_ATTRIBUTE = "Type";
+
+ private string INCORRECT_NODE = "incorrect node in addin file!";
+
+ private string EMPTY_ADDIN = "empty addin file!";
+
+ private string ASSEMBLY = "Assembly";
+
+ private string CLIENTID = "ClientId";
+
+ private string FULLCLASSNAME = "FullClassName";
+
+ private string NAME_NODE = "Name";
+
+ private string TEXT = "Text";
+
+ private string DESCRIPTION = "Description";
+
+ private string VENDORID = "VendorId";
+
+ private string VENDORDESCRIPTION = "VendorDescription";
+
+ private string VISIBILITYMODE = "VisibilityMode";
+
+ private string UNKNOW_VISIBILITYMODE = "Unrecognizable VisibilityMode!";
+
+ private string FILENAME_INCORRECT_WARNING = "File name is incorrect, not .addin file .";
+
+ private string FILENAME_NULL_OR_EMPTY = "File name for RevitAddInManifest is null or empty";
+
+ private XmlDocument xmlDoc;
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/NavisAddin.cs b/src/OpenProject.Navisworks/Model/NavisAddin.cs
new file mode 100644
index 00000000..12e88a62
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/NavisAddin.cs
@@ -0,0 +1,27 @@
+namespace OpenProjectNavisworks.Model;
+
+public class NavisAddin : ViewModelBase
+{
+ public bool IsReadOnly { get; set; }
+ public VisibleModel State { get; set; }
+ public string FilePath { get; set; }
+ public string Assembly { get; set; }
+ public string ClientId { get; set; }
+ public string Name { get; set; }
+ public string NameNotEx { get; set; }
+ public string FullClassName { get; set; }
+ public string Text { get; set; }
+ public string VisibilityMode { get; set; }
+ public string LanguageType { get; set; }
+ public string VendorId { get; set; }
+ public string VendorDescription { get; set; }
+ public AddinType AddinType { get; set; }
+
+ private bool _IsChecked;
+
+ public bool IsChecked
+ {
+ get => _IsChecked;
+ set => OnPropertyChanged(ref _IsChecked, value);
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/NavisWorksWrapper.cs b/src/OpenProject.Navisworks/Model/NavisWorksWrapper.cs
new file mode 100644
index 00000000..77a0a71a
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/NavisWorksWrapper.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using Autodesk.Navisworks.Api;
+using ComApi = Autodesk.Navisworks.Api.Interop.ComApi;
+using ComBridge = Autodesk.Navisworks.Api.ComApi.ComApiBridge;
+
+
+namespace OpenProjectNavisworks.Model
+{
+ public static class NavisworksWrapper
+ {
+ public static Document Document { get; set; }
+ public static string DocumentGUID { get; set; }
+ public static ComBridge ComBridge { get; set; }
+
+ //public static List GetAllSearchSets()
+ //{
+ // List result = new List();
+ // var selectionSets = Document.SelectionSets;
+ // var savedItemCollection = selectionSets.Value;
+
+ // foreach (var saveItem in savedItemCollection)
+ // {
+ // if (!saveItem.IsGroup)
+ // {
+ // SelectionSet selectionSet = saveItem as SelectionSet;
+
+ // if (selectionSet != null)
+ // {
+ // result.Add(selectionSet);
+ // }
+ // }
+ // }
+
+ // return result;
+ //}
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/ProcessManager.cs b/src/OpenProject.Navisworks/Model/ProcessManager.cs
new file mode 100644
index 00000000..3f799741
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/ProcessManager.cs
@@ -0,0 +1,53 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Interop;
+
+namespace OpenProjectNavisworks.Model
+{
+ public static class ProcessManager
+ {
+
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ public static void SetNavisAsWindowOwner(this Window window)
+ {
+ if (null == window) { return; }
+ window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ WindowInteropHelper helper = new WindowInteropHelper(window);
+ helper.Owner = GetActivateWindow();
+ window.Closing += SetActivateWindow;
+ }
+
+ private static void SetActivateWindow(object sender, CancelEventArgs e)
+ {
+ SetActivateWindow();
+ }
+
+ ///
+ /// Set process revert use revit
+ ///
+ ///
+ public static bool SetActivateWindow()
+ {
+ IntPtr ptr = GetActivateWindow();
+ if (ptr != IntPtr.Zero)
+ {
+ return SetForegroundWindow(ptr);
+ }
+ return false;
+ }
+
+ ///
+ /// return active windows is active
+ ///
+ ///
+ public static IntPtr GetActivateWindow()
+ {
+ return Process.GetCurrentProcess().MainWindowHandle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/StaticUtil.cs b/src/OpenProject.Navisworks/Model/StaticUtil.cs
new file mode 100644
index 00000000..a5d95aa0
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/StaticUtil.cs
@@ -0,0 +1,26 @@
+using System.Windows;
+using Autodesk.Navisworks.Api.Plugins;
+using MessageBox = System.Windows.MessageBox;
+
+namespace OpenProjectNavisworks.Model;
+
+public static class StaticUtil
+{
+ public static void ShowWarning(string msg)
+ {
+ MessageBox.Show(msg, DefaultSetting.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
+ }
+ public static string CommandFullName = typeof(AddInPlugin).FullName;
+
+ ///
+ /// CaseInsensitiveContains
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static bool CaseInsensitiveContains(this string text, string value, StringComparison stringComparison = StringComparison.CurrentCultureIgnoreCase)
+ {
+ return text.IndexOf(value, stringComparison) >= 0;
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/ViewModelBase.cs b/src/OpenProject.Navisworks/Model/ViewModelBase.cs
new file mode 100644
index 00000000..10e7feca
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/ViewModelBase.cs
@@ -0,0 +1,73 @@
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+namespace OpenProjectNavisworks.Model;
+
+public class ViewModelBase : INotifyPropertyChanged
+{
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected virtual void OnPropertyChanged(string propertyName, object oldValue, object newValue)
+ {
+ OnPropertyChanged(propertyName);
+ }
+
+ public void OnPropertyChanged(ref T property, T value, [CallerMemberName] string propertyName = "")
+ {
+ property = value;
+ var handler = PropertyChanged;
+ if (handler != null)
+ {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ ///
+ /// Sets the property value.
+ ///
+ /// The type of the property.
+ /// The field.
+ /// The value.
+ /// Name of the property.
+ ///
+ /// True if the property was set.
+ ///
+ /// This method uses the CallerMemberNameAttribute to determine the property name.
+ protected bool SetValue(ref T field, T value, [CallerMemberName] string propertyName = "")
+ {
+ // ReSharper disable once RedundantNameQualifier
+ if (object.Equals(field, value))
+ {
+ return false;
+ }
+ VerifyProperty(propertyName);
+ //// this.OnPropertyChanging(propertyName, field, value);
+ var oldValue = field;
+ field = value;
+ OnPropertyChanged(propertyName, oldValue, value);
+ return true;
+ }
+
+ ///
+ /// Verifies the property name.
+ ///
+ /// Name of the property.
+ [Conditional("DEBUG")]
+ private void VerifyProperty(string propertyName)
+ {
+ var type = GetType();
+
+ // Look for a public property with the specified name.
+ var propertyInfo = type.GetTypeInfo().GetDeclaredProperty(propertyName);
+
+ Debug.Assert(propertyInfo != null, string.Format(CultureInfo.InvariantCulture, "{0} is not a property of {1}", propertyName, type.FullName));
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Model/VisibilityMode.cs b/src/OpenProject.Navisworks/Model/VisibilityMode.cs
new file mode 100644
index 00000000..6fd75a6d
--- /dev/null
+++ b/src/OpenProject.Navisworks/Model/VisibilityMode.cs
@@ -0,0 +1,22 @@
+namespace OpenProjectNavisworks.Model;
+
+[Flags]
+public enum VisibilityMode
+{
+ AlwaysVisible = 0,
+ NotVisibleInProject = 1,
+ NotVisibleInFamily = 2,
+ NotVisibleWhenNoActiveDocument = 4,
+ NotVisibleInArchitecture = 8,
+ NotVisibleInStructure = 16,
+ NotVisibleInMechanical = 32,
+ NotVisibleInElectrical = 64,
+ NotVisibleInPlumbing = 128,
+ NotVisibleInMEP = 224
+}
+
+public enum VisibleModel
+{
+ Enable,
+ Disable
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/OpenProject.Navisworks.csproj b/src/OpenProject.Navisworks/OpenProject.Navisworks.csproj
new file mode 100644
index 00000000..2b2b93fc
--- /dev/null
+++ b/src/OpenProject.Navisworks/OpenProject.Navisworks.csproj
@@ -0,0 +1,210 @@
+
+
+
+ true
+ latest
+ x64
+ enable
+ true
+ net48
+ Debug-2022;Debug-2024;Release-2022;Release-2024;
+ $(Configurations);Release-2022;;Release-2024;
+ OpenProjectNavisworks
+
+ true
+ true
+
+ true
+
+
+
+
+ true
+ full
+ $(DefineConstants);DEBUG
+
+
+ true
+ none
+ $(DefineConstants);RELEASE
+
+
+
+ 2020
+ $(DefineConstants);N2020
+
+
+ 2021
+ $(DefineConstants);N2021
+
+
+ 2022
+ $(DefineConstants);N2022
+
+
+ 2023
+ $(DefineConstants);N2023
+
+
+ 2024
+ $(DefineConstants);N2024
+
+
+
+ $(NavisVersion)
+ true
+ false
+ A Project Support for developer in Navisworks
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+ Always
+
+
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ True
+ Settings.settings
+
+
+ True
+ True
+ Resource.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ PublicResXFileCodeGenerator
+ Resource.Designer.cs
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ MSBuild:Compile
+
+
+
+
+
+ Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/OpenProject.Navisworks/PackageContents.xml b/src/OpenProject.Navisworks/PackageContents.xml
new file mode 100644
index 00000000..ba19b594
--- /dev/null
+++ b/src/OpenProject.Navisworks/PackageContents.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Properties/Resources.Designer.cs b/src/OpenProject.Navisworks/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..bdd4d8b8
--- /dev/null
+++ b/src/OpenProject.Navisworks/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace OpenProjectNavisworks.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenProjectNavisworks.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/src/OpenProject.Navisworks/Properties/Resources.resx b/src/OpenProject.Navisworks/Properties/Resources.resx
new file mode 100644
index 00000000..4fdb1b6a
--- /dev/null
+++ b/src/OpenProject.Navisworks/Properties/Resources.resx
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Properties/Settings.Designer.cs b/src/OpenProject.Navisworks/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..a10a0443
--- /dev/null
+++ b/src/OpenProject.Navisworks/Properties/Settings.Designer.cs
@@ -0,0 +1,26 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace OpenProjectNavisworks.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.5.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/src/OpenProject.Navisworks/Properties/Settings.settings b/src/OpenProject.Navisworks/Properties/Settings.settings
new file mode 100644
index 00000000..049245f4
--- /dev/null
+++ b/src/OpenProject.Navisworks/Properties/Settings.settings
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/OpenProject.Navisworks/Properties/launchSettings.json b/src/OpenProject.Navisworks/Properties/launchSettings.json
new file mode 100644
index 00000000..c0e19c21
--- /dev/null
+++ b/src/OpenProject.Navisworks/Properties/launchSettings.json
@@ -0,0 +1,18 @@
+{
+ "profiles": {
+ "Navis2022": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2022\\Roamer.exe",
+ "commandLineArgs": "-lang en-US"
+ },
+ "Navis2023": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2023\\Roamer.exe"
+ },
+ "Navis2024": {
+ "commandName": "Executable",
+ "executablePath": "C:\\Program Files\\Autodesk\\Navisworks Manage 2024\\Roamer.exe",
+ "commandLineArgs": "-lang en-US"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Resource.Designer.cs b/src/OpenProject.Navisworks/Resource.Designer.cs
new file mode 100644
index 00000000..d5803134
--- /dev/null
+++ b/src/OpenProject.Navisworks/Resource.Designer.cs
@@ -0,0 +1,165 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace OpenProjectNavisworks {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ public class Resource {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resource() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OpenProjectNavisworks.Resource", typeof(Resource).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Navis Addin Manager.
+ ///
+ public static string AppName {
+ get {
+ return ResourceManager.GetString("AppName", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
+ ///
+ public static System.Drawing.Icon dev {
+ get {
+ object obj = ResourceManager.GetObject("dev", resourceCulture);
+ return ((System.Drawing.Icon)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap dev1 {
+ get {
+ object obj = ResourceManager.GetObject("dev1", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to File Not Exit, Please Check !.
+ ///
+ public static string FileNotExit {
+ get {
+ return ResourceManager.GetString("FileNotExit", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to File Not Found, Please Check File Again.
+ ///
+ public static string FileNotFound {
+ get {
+ return ResourceManager.GetString("FileNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ public static System.Drawing.Bitmap folder {
+ get {
+ object obj = ResourceManager.GetObject("folder", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to File Load Is Cancelld.
+ ///
+ public static string LoadCancelled {
+ get {
+ return ResourceManager.GetString("LoadCancelled", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to File Load Is Invalid.
+ ///
+ public static string LoadInvalid {
+ get {
+ return ResourceManager.GetString("LoadInvalid", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Please Check Again Directory And Open Full Access Admin!.
+ ///
+ public static string NeedAdmin {
+ get {
+ return ResourceManager.GetString("NeedAdmin", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Not Any Item Selected.
+ ///
+ public static string NoItemSelected {
+ get {
+ return ResourceManager.GetString("NoItemSelected", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Successfully.
+ ///
+ public static string Successfully {
+ get {
+ return ResourceManager.GetString("Successfully", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/OpenProject.Navisworks/Resource.resx b/src/OpenProject.Navisworks/Resource.resx
new file mode 100644
index 00000000..7731cd26
--- /dev/null
+++ b/src/OpenProject.Navisworks/Resource.resx
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Navis Addin Manager
+
+
+
+ Resources\dev.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\dev.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ File Not Exit, Please Check !
+
+
+ File Not Found, Please Check File Again
+
+
+ Resources\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ File Load Is Cancelld
+
+
+ File Load Is Invalid
+
+
+ Please Check Again Directory And Open Full Access Admin!
+
+
+ Not Any Item Selected
+
+
+ Successfully
+
+
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/Resources/BCFierIcon16x16.png b/src/OpenProject.Navisworks/Resources/BCFierIcon16x16.png
new file mode 100644
index 00000000..39e117ef
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/BCFierIcon16x16.png differ
diff --git a/src/OpenProject.Navisworks/Resources/BCFierIcon32x32.png b/src/OpenProject.Navisworks/Resources/BCFierIcon32x32.png
new file mode 100644
index 00000000..4b45eddb
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/BCFierIcon32x32.png differ
diff --git a/src/OpenProject.Navisworks/Resources/OpenProjectLogo16.png b/src/OpenProject.Navisworks/Resources/OpenProjectLogo16.png
new file mode 100644
index 00000000..8a08a2cf
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/OpenProjectLogo16.png differ
diff --git a/src/OpenProject.Navisworks/Resources/OpenProjectLogo32.png b/src/OpenProject.Navisworks/Resources/OpenProjectLogo32.png
new file mode 100644
index 00000000..5225c6b9
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/OpenProjectLogo32.png differ
diff --git a/src/OpenProject.Navisworks/Resources/Settings32.png b/src/OpenProject.Navisworks/Resources/Settings32.png
new file mode 100644
index 00000000..03a3113d
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/Settings32.png differ
diff --git a/src/OpenProject.Navisworks/Resources/dev.ico b/src/OpenProject.Navisworks/Resources/dev.ico
new file mode 100644
index 00000000..6bae0f6b
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/dev.ico differ
diff --git a/src/OpenProject.Navisworks/Resources/dev.png b/src/OpenProject.Navisworks/Resources/dev.png
new file mode 100644
index 00000000..d1e67df9
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/dev.png differ
diff --git a/src/OpenProject.Navisworks/Resources/dev16x16.png b/src/OpenProject.Navisworks/Resources/dev16x16.png
new file mode 100644
index 00000000..fb9c51e9
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/dev16x16.png differ
diff --git a/src/OpenProject.Navisworks/Resources/dev32x32.png b/src/OpenProject.Navisworks/Resources/dev32x32.png
new file mode 100644
index 00000000..96eb5ddd
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/dev32x32.png differ
diff --git a/src/OpenProject.Navisworks/Resources/folder.png b/src/OpenProject.Navisworks/Resources/folder.png
new file mode 100644
index 00000000..ba66da71
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/folder.png differ
diff --git a/src/OpenProject.Navisworks/Resources/icon.ico b/src/OpenProject.Navisworks/Resources/icon.ico
new file mode 100644
index 00000000..6a2bf0a3
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/icon.ico differ
diff --git a/src/OpenProject.Navisworks/Resources/lab16x16.png b/src/OpenProject.Navisworks/Resources/lab16x16.png
new file mode 100644
index 00000000..5747dade
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/lab16x16.png differ
diff --git a/src/OpenProject.Navisworks/Resources/lab32x32.png b/src/OpenProject.Navisworks/Resources/lab32x32.png
new file mode 100644
index 00000000..a00be3d3
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/lab32x32.png differ
diff --git a/src/OpenProject.Navisworks/Resources/openproject.ico b/src/OpenProject.Navisworks/Resources/openproject.ico
new file mode 100644
index 00000000..eeaf4d8e
Binary files /dev/null and b/src/OpenProject.Navisworks/Resources/openproject.ico differ
diff --git a/src/OpenProject.Navisworks/Services/MessageHandler.cs b/src/OpenProject.Navisworks/Services/MessageHandler.cs
new file mode 100644
index 00000000..ebc76a2a
--- /dev/null
+++ b/src/OpenProject.Navisworks/Services/MessageHandler.cs
@@ -0,0 +1,36 @@
+using System;
+//using Autodesk.Revit.UI;
+using Autodesk.Navisworks.Gui;
+using Autodesk.Windows;
+using Serilog;
+
+namespace OpenProjectNavisworks.Services
+{
+ public static class MessageHandler
+ {
+ public static void ShowWarning(string title, string instruction, string content)
+ {
+ Log.Warning($"{title}: {content}");
+
+ var dialog = new TaskDialog();
+ dialog.WindowTitle = title;
+ dialog.ContentText = content;
+ dialog.ExpandedText = instruction;
+ dialog.Show();
+ }
+
+ public static void ShowError(Exception exception, string message)
+ {
+ if (exception != null)
+ Log.Error(exception, message);
+ else
+ Log.Error(message);
+
+ var dialog = new TaskDialog();
+ dialog.MainIcon = TaskDialogIcon.Error;
+ dialog.ContentText = message;
+
+ dialog.Show();
+ }
+ }
+}
diff --git a/src/OpenProject.Navisworks/Services/StatusBarService.cs b/src/OpenProject.Navisworks/Services/StatusBarService.cs
new file mode 100644
index 00000000..da20a7f4
--- /dev/null
+++ b/src/OpenProject.Navisworks/Services/StatusBarService.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace OpenProjectNavisworks.Services
+{
+ ///
+ /// This service is based on the idea of Revit developer Jeremy Tammik (https://thebuildingcoder.typepad.com/blog/2011/02/status-bar-text.html)
+ ///
+ public static class StatusBarService
+ {
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+ private static extern int SetWindowText(IntPtr hWnd, string lpString);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass,
+ string lpszWindow);
+
+ ///
+ /// Sets a text to the revit main window status bar.
+ ///
+ /// The text to be displayed in the status bar.
+ public static void SetStatusText(string text)
+ {
+ IntPtr revitHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
+ IntPtr statusBar = FindWindowEx(revitHandle, IntPtr.Zero, "msctls_statusbar32", "");
+
+ if (statusBar != IntPtr.Zero)
+ SetWindowText(statusBar, text);
+ }
+
+ ///
+ /// Resets the status bar text.
+ ///
+ public static void ResetStatusBarText() => SetStatusText("Ready");
+ }
+}
diff --git a/src/OpenProject.Navisworks/View/AssemblyLoader.xaml b/src/OpenProject.Navisworks/View/AssemblyLoader.xaml
new file mode 100644
index 00000000..4db935c8
--- /dev/null
+++ b/src/OpenProject.Navisworks/View/AssemblyLoader.xaml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/View/AssemblyLoader.xaml.cs b/src/OpenProject.Navisworks/View/AssemblyLoader.xaml.cs
new file mode 100644
index 00000000..f935e14a
--- /dev/null
+++ b/src/OpenProject.Navisworks/View/AssemblyLoader.xaml.cs
@@ -0,0 +1,78 @@
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+using System.Windows;
+using Microsoft.Win32;
+using OpenProjectNavisworks.Model;
+using MessageBox = System.Windows.MessageBox;
+using OpenFileDialog = Microsoft.Win32.OpenFileDialog;
+
+namespace OpenProjectNavisworks.View;
+
+///
+/// Interaction logic for AssemblyLoader.xaml
+///
+public partial class AssemblyLoader : Window
+{
+ private string _assemName;
+ public string resultPath;
+ private bool isFound;
+
+ public AssemblyLoader(string assemName)
+ {
+ InitializeComponent();
+ _assemName = assemName;
+ tbxAssembly.Content = assemName;
+ }
+
+ private void ShowWarning()
+ {
+ var text = new StringBuilder("The dependent assembly can't be loaded: \"").Append(_assemName).AppendFormat("\".", new object[0]).ToString();
+ MessageBox.Show(text, "Add-in Manager Internal", MessageBoxButton.OK, MessageBoxImage.Exclamation);
+ }
+
+ private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(_assemName))
+ {
+ MessageBox.Show("Assembly null", DefaultSetting.AppName);
+ return;
+ }
+ OpenFileDialog openFileDialog = new OpenFileDialog();
+ openFileDialog.Filter = "Assembly files (*.dll;*.exe,*.mcl)|*.dll;*.exe;*.mcl|All files|*.*||";
+ var str = _assemName.Substring(0, _assemName.IndexOf(','));
+ openFileDialog.FileName = str + ".*";
+ if (openFileDialog.ShowDialog() == true)
+ {
+ ShowWarning();
+ }
+ TbxAssemPath.Text = openFileDialog.FileName;
+ }
+
+ private void OKButtonClick(object sender, RoutedEventArgs e)
+ {
+ if (File.Exists(TbxAssemPath.Text))
+ {
+ resultPath = TbxAssemPath.Text;
+ isFound = true;
+ }
+ else
+ {
+ ShowWarning();
+ }
+ Close();
+ }
+
+ private void AssemblyLoader_OnClosing(object sender, CancelEventArgs e)
+ {
+ if (!isFound)
+ {
+ ShowWarning();
+ }
+ }
+
+ private void Close_OnClick(object sender, RoutedEventArgs e)
+ {
+ Close();
+ }
+}
\ No newline at end of file
diff --git a/src/OpenProject.Navisworks/View/Control/ExtendedTreeView.cs b/src/OpenProject.Navisworks/View/Control/ExtendedTreeView.cs
new file mode 100644
index 00000000..bb45a19b
--- /dev/null
+++ b/src/OpenProject.Navisworks/View/Control/ExtendedTreeView.cs
@@ -0,0 +1,35 @@
+using System.Windows;
+
+namespace OpenProjectNavisworks.View.Control;
+
+public class ExtendedTreeView : System.Windows.Controls.TreeView
+{
+ public ExtendedTreeView()
+ {
+ SelectedItemChanged += ItemChange;
+ }
+
+ private void ItemChange(object sender, RoutedPropertyChangedEventArgs