diff --git a/.idea/SpeckleRevit.iml b/.idea/SpeckleRevit.iml
new file mode 100644
index 0000000..24643cc
--- /dev/null
+++ b/.idea/SpeckleRevit.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..56d9dce
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 0000000..ca3bd8a
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ DEFINITION_ORDER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1526389059417
+
+
+ 1526389059417
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SpeckleCore b/SpeckleCore
index a8e4f98..53332c9 160000
--- a/SpeckleCore
+++ b/SpeckleCore
@@ -1 +1 @@
-Subproject commit a8e4f98984e02c983c954dca0c6a47cc1fc76822
+Subproject commit 53332c9fd2c281a6d16d9e37e7488db8a826c74d
diff --git a/SpeckleRevit.sln.DotSettings.user b/SpeckleRevit.sln.DotSettings.user
new file mode 100644
index 0000000..ef2654a
--- /dev/null
+++ b/SpeckleRevit.sln.DotSettings.user
@@ -0,0 +1,6 @@
+
+ <AssemblyExplorer>
+ <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\cefsharp.wpf\63.0.2\CefSharp\x64\CefSharp.Wpf.dll" />
+ <Assembly Path="C:\Users\konrad.sobon\.nuget\packages\newtonsoft.json\11.0.1\lib\net45\Newtonsoft.Json.dll" />
+ <Assembly Path="C:\Windows\Microsoft.NET\assembly\GAC_MSIL\PresentationFramework\v4.0_4.0.0.0__31bf3856ad364e35\PresentationFramework.dll" />
+</AssemblyExplorer>
\ No newline at end of file
diff --git a/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user b/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user
new file mode 100644
index 0000000..e5e922d
--- /dev/null
+++ b/SpeckleRevitConverter/SpeckleRevitConverter.csproj.user
@@ -0,0 +1,7 @@
+
+
+
+ Program
+ C:\Program Files\Autodesk\Revit 2018\Revit.exe
+
+
\ No newline at end of file
diff --git a/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs
new file mode 100644
index 0000000..b8e154d
--- /dev/null
+++ b/SpeckleRevitPlugin/Classes/ISpeckleRevitClient.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Runtime.Serialization;
+using SpeckleCore;
+
+namespace SpeckleRevitPlugin.Classes
+{
+ ///
+ /// Generalises some methods for both senders and receivers.
+ ///
+ public interface ISpeckleRevitClient : IDisposable, ISerializable
+ {
+ ClientRole GetRole();
+
+ string GetClientId();
+
+ void TogglePaused(bool status);
+
+ void ToggleVisibility(bool status);
+
+ void ToggleLayerVisibility(string layerId, bool status);
+
+ void ToggleLayerHover(string layerId, bool status);
+
+ void Dispose(bool delete = false);
+ }
+}
diff --git a/SpeckleRevitPlugin/Classes/Interop.cs b/SpeckleRevitPlugin/Classes/Interop.cs
new file mode 100644
index 0000000..9f53f10
--- /dev/null
+++ b/SpeckleRevitPlugin/Classes/Interop.cs
@@ -0,0 +1,531 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization.Formatters.Binary;
+using Autodesk.Revit.DB;
+using Autodesk.Revit.DB.Events;
+using CefSharp;
+using CefSharp.Wpf;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using SpeckleCore;
+using SpeckleRevitPlugin.Entry;
+using SpeckleRevitPlugin.Utilities;
+#endregion
+
+namespace SpeckleRevitPlugin.Classes
+{
+ ///
+ /// CEF Bound object.
+ /// If CEF will be removed, porting to url hacks will be necessary,
+ /// so let's keep the methods as simple as possible.
+ ///
+ public class Interop : IDisposable
+ {
+ private List _userAccounts;
+ public List UserClients;
+ public Dictionary SpeckleObjectCache;
+ public ChromiumWebBrowser Browser;
+ public bool SpeckleIsReady;
+ public bool SelectionInfoNeedsToBeSentYeMighty = false; // should be false
+
+ public Interop(ChromiumWebBrowser originalBrowser)
+ {
+ // (Luis) Makes sure we always get some camelCaseLove
+ JsonConvert.DefaultSettings = () => new JsonSerializerSettings()
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ };
+
+ _userAccounts = new List();
+ UserClients = new List();
+ SpeckleObjectCache = new Dictionary();
+ Browser = originalBrowser;
+ ReadUserAccounts();
+
+ AppMain.uiApp.ControlledApplication.DocumentCreated += Revit_DocumentCreated;
+ AppMain.uiApp.ControlledApplication.DocumentOpened += Revit_DocumentOpened;
+ AppMain.uiApp.ControlledApplication.DocumentSaved += Revit_DocumentSaved;
+ AppMain.uiApp.ControlledApplication.DocumentSynchronizedWithCentral += Revit_DocumentSynchronized;
+ SpeckleRequestHandler.OnClientsRetrieved += OnClientsRetrieved;
+ }
+
+ public void Dispose()
+ {
+ AppMain.uiApp.ControlledApplication.DocumentCreated -= Revit_DocumentCreated;
+ AppMain.uiApp.ControlledApplication.DocumentOpened -= Revit_DocumentOpened;
+ AppMain.uiApp.ControlledApplication.DocumentSaved -= Revit_DocumentSaved;
+ AppMain.uiApp.ControlledApplication.DocumentSynchronizedWithCentral -= Revit_DocumentSynchronized;
+ SpeckleRequestHandler.OnClientsRetrieved -= OnClientsRetrieved;
+
+ RemoveAllClients();
+ }
+
+ #region Global Events
+
+ private void Revit_DocumentSaved(object sender, DocumentSavedEventArgs e)
+ {
+ SaveFileClients();
+ }
+
+ private void Revit_DocumentSynchronized(object sender, DocumentSynchronizedWithCentralEventArgs e)
+ {
+ SaveFileClients();
+ }
+
+ private void Revit_DocumentCreated(object sender, DocumentCreatedEventArgs e)
+ {
+ var doc = e.Document;
+ if (doc == null || doc.IsFamilyDocument) return;
+
+ NotifySpeckleFrame("purge-clients", "", "");
+ RemoveAllClients();
+ }
+
+ private void Revit_DocumentOpened(object sender, DocumentOpenedEventArgs e)
+ {
+ var doc = e.Document;
+ if (doc == null || doc.IsFamilyDocument) return;
+
+ NotifySpeckleFrame("purge-clients", "", "");
+ RemoveAllClients();
+ InstantiateFileClients();
+ }
+
+ #endregion
+
+ public void SetBrowser(ChromiumWebBrowser browser)
+ {
+ Browser = browser;
+ }
+
+ public void ShowDev()
+ {
+ Browser.ShowDevTools();
+ }
+
+ public string GetDocumentName()
+ {
+ //TODO: Fix this!
+ return "Revit doc name.";
+ //return Rhino.RhinoDoc.ActiveDoc.Name;
+ }
+
+ public string GetDocumentGuid()
+ {
+ //TODO: Fix this!
+ return "Revit GUID";
+ //return Rhino.RhinoDoc.ActiveDoc.DocumentId.ToString();
+ }
+
+ ///
+ /// Do not call this from the constructor as you'll get confilcts with
+ /// browser load, etc.
+ ///
+ public void AppReady()
+ {
+ SpeckleIsReady = true;
+ InstantiateFileClients();
+ }
+
+ #region Account Management
+
+ public string GetUserAccounts( )
+ {
+ ReadUserAccounts();
+ return JsonConvert.SerializeObject(_userAccounts, new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ });
+ }
+
+ ///
+ ///
+ ///
+ private void ReadUserAccounts( )
+ {
+ _userAccounts = new List();
+ var strPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ strPath = strPath + @"\SpeckleSettings";
+
+ if (!Directory.Exists(strPath) || !Directory.EnumerateFiles(strPath, "*.txt").Any()) return;
+
+ foreach (var file in Directory.EnumerateFiles(strPath, "*.txt"))
+ {
+ var content = File.ReadAllText(file);
+ var pieces = content.TrimEnd('\r', '\n').Split(',');
+ _userAccounts.Add(new SpeckleAccount
+ {
+ email = pieces[0],
+ apiToken = pieces[1],
+ serverName = pieces[2],
+ restApi = pieces[3],
+ rootUrl = pieces[4],
+ fileName = file
+ });
+ }
+ }
+
+ public void AddAccount( string payload )
+ {
+ var pieces = payload.Split(',');
+ var strPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+
+ Directory.CreateDirectory(strPath + @"\SpeckleSettings");
+ strPath = strPath + @"\SpeckleSettings\";
+
+ var fileName = pieces[0] + "." + pieces[2] + ".txt";
+ var file = new StreamWriter(strPath + fileName);
+ file.WriteLine(payload);
+ file.Close();
+ }
+
+ public void RemoveAccount( string payload )
+ {
+ var unused = _userAccounts.RemoveAll(account => account.fileName == payload);
+ if (File.Exists(payload)) File.Delete(payload);
+ }
+
+ #endregion
+
+ #region Client Management
+
+ ///
+ /// Called by SpeckleView when new Receiver was added.
+ ///
+ /// Info needed to create RevitReceiver
+ ///
+ public bool AddReceiverClient( string payload )
+ {
+ var unused = new RevitReceiver(payload, this);
+ return true;
+ }
+
+ ///
+ ///
+ ///
+ public void InstantiateFileClients()
+ {
+ // (Konrad) This is the thread safe way of interacting with Revit.
+ // Also it's possible that the Speckle app initiates before Revit
+ // Document is open/ready making Extensible Storage inaccessible.
+ AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.GetClients);
+ AppMain.SpeckleEvent.Raise();
+ }
+
+ ///
+ /// Handler for an event called by Revit when Clients have been retrived from Schema.
+ ///
+ /// Dictionary of Revit Receivers serialized into string.
+ /// Dictionary of Revit Senders serialized into string.
+ private void OnClientsRetrieved(IDictionary receivers, IDictionary senders)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ foreach (var kv in receivers)
+ {
+ var serialisedClient = Convert.FromBase64String(kv.Value);
+ var client = BinaryFormatterUtilities.Read(serialisedClient, assembly);
+ client.Context = this;
+ }
+
+ //TODO: Let's deal with senders later!
+ //foreach (var kv in senders)
+ //{
+ // var serialisedClient = Convert.FromBase64String(kv.Value);
+ // var client = BinaryFormatterUtilities.Read(serialisedClient, assembly);
+ // if (client.Client == null) return;
+
+ // client.CompleteDeserialisation(this);
+ //}
+ }
+
+ ///
+ /// It's used by Speckle View to trigger Client storage.
+ ///
+ public void SaveFileClients()
+ {
+ var senders = new Dictionary();
+ var receivers = new Dictionary();
+ foreach (var rClient in UserClients)
+ {
+ using (var ms = new MemoryStream())
+ {
+ var formatter = new BinaryFormatter();
+ formatter.Serialize(ms, rClient);
+ var client = Convert.ToBase64String(ms.ToArray());
+ var clientId = rClient.GetClientId();
+
+ if (rClient.GetRole() == ClientRole.Receiver) receivers.Add(clientId, client);
+ else senders.Add(clientId, client);
+ }
+ }
+
+ AppMain.SpeckleHandler.Arg1 = senders;
+ AppMain.SpeckleHandler.Arg2 = receivers;
+ AppMain.SpeckleHandler.Request.Make(SpeckleCommandType.SaveClients);
+ AppMain.SpeckleEvent.Raise();
+ }
+
+ //public bool AddSenderClientFromSelection( string _payload )
+ //{
+ // var mySender = new RhinoSender( _payload, this );
+ // return true;
+ //}
+
+ public bool RemoveClient(string _payload)
+ {
+ var myClient = UserClients.FirstOrDefault(client => client.GetClientId() == _payload);
+ if (myClient == null) return false;
+
+ myClient.Dispose(true);
+ var result = UserClients.Remove(myClient);
+
+ // (Konrad) Update Revit Schema.
+ SaveFileClients();
+
+ return result;
+ }
+
+ public bool RemoveAllClients( )
+ {
+ foreach (var uc in UserClients)
+ {
+ uc.Dispose();
+ }
+ UserClients.RemoveAll(c => true);
+ return true;
+ }
+
+ public string GetAllClients( )
+ {
+ foreach (var client in UserClients)
+ {
+ switch (client)
+ {
+ case RevitSender rvtSender:
+ NotifySpeckleFrame("client-add", rvtSender.StreamId, JsonConvert.SerializeObject(new
+ {
+ stream = rvtSender.Client.Stream,
+ client = rvtSender.Client
+ }));
+ break;
+ case RevitReceiver rvtReceiver:
+ NotifySpeckleFrame("client-add", rvtReceiver.StreamId, JsonConvert.SerializeObject(new
+ {
+ stream = rvtReceiver.Client.Stream,
+ client = rvtReceiver.Client
+ }));
+ break;
+ }
+ }
+
+ return JsonConvert.SerializeObject(UserClients);
+ }
+
+ #endregion
+
+ #region To UI (Generic)
+
+ public void NotifySpeckleFrame( string eventType, string streamId, string eventInfo )
+ {
+ if (!SpeckleIsReady)
+ {
+ Debug.WriteLine("Speckle was not ready, trying to send " + eventType);
+ return;
+ }
+
+ var script = $"window.EventBus.$emit('{eventType}', '{streamId}', '{eventInfo}')";
+
+ try
+ {
+ Browser.GetMainFrame().EvaluateScriptAsync(script);
+ }
+ catch
+ {
+ Debug.WriteLine("For some reason, this browser was not initialised.");
+ }
+ }
+ #endregion
+
+ #region From UI (..)
+
+ public void BakeClient(string clientId)
+ {
+ //TODO: Implement client baking
+ }
+
+ public void BakeLayer(string clientId, string layerGuid)
+ {
+ //TODO: Implement baking
+ }
+
+ public void SetClientPause(string clientId, bool status)
+ {
+ var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId);
+ myClient?.TogglePaused(status);
+ }
+
+ public void SetClientVisibility(string clientId, bool status)
+ {
+ var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId);
+ myClient?.ToggleVisibility(status);
+ }
+
+ public void SetClientHover(string clientId, bool status)
+ {
+ var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId);
+ myClient?.ToggleVisibility(status);
+ }
+
+ public void SetLayerVisibility(string clientId, string layerId, bool status)
+ {
+ //TODO: create geometry previews
+ }
+
+ public void SetLayerHover(string clientId, string layerId, bool status)
+ {
+ //TODO: highlight geometry previews
+ }
+
+ public void SetObjectHover(string clientId, string layerId, bool status)
+ {
+ //TODO: implement object hover
+ }
+
+ //public void AddRemoveObjects( string clientId, string _guids, bool remove )
+ //{
+ // string[ ] guids = JsonConvert.DeserializeObject( _guids );
+
+ // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId );
+ // if ( myClient != null )
+ // try
+ // {
+ // if ( !remove )
+ // ( ( RhinoSender ) myClient ).AddTrackedObjects( guids );
+ // else ( ( RhinoSender ) myClient ).RemoveTrackedObjects( guids );
+
+ // }
+ // catch { throw new Exception( "Force send client was not a sender. whoopsie poopsiee." ); }
+ //}
+
+ public void RefreshClient(string clientId)
+ {
+ var myClient = UserClients.FirstOrDefault(c => c.GetClientId() == clientId);
+ if (myClient == null) return;
+
+ try
+ {
+ ((RevitReceiver) myClient).UpdateGlobal();
+ }
+ catch
+ {
+ throw new Exception("Refresh client was not a receiver. whoopsie poopsiee.");
+ }
+ }
+
+ //public void forceSend( string clientId )
+ //{
+ // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId );
+ // if ( myClient != null )
+ // try
+ // {
+ // ( ( RhinoSender ) myClient ).ForceUpdate();
+ // }
+ // catch { throw new Exception( "Force send client was not a sender. whoopsie poopsiee." ); }
+ //}
+
+ public void OpenUrl(string url)
+ {
+ Process.Start(url);
+ }
+
+ //public void setName( string clientId, string name )
+ //{
+ // var myClient = UserClients.FirstOrDefault( c => c.GetClientId() == clientId );
+ // if ( myClient != null && myClient is RhinoSender )
+ // {
+ // ( ( RhinoSender ) myClient ).Client.Stream.Name = name;
+ // ( ( RhinoSender ) myClient ).Client.BroadcastMessage( new { eventType = "update-name" } );
+ // }
+ //}
+
+ #endregion
+
+ #region Sender Helpers
+
+ //public string getLayersAndObjectsInfo( bool ignoreSelection = false )
+ //{
+ // List SelectedObjects;
+ // List layerInfoList = new List();
+
+ // if ( !ignoreSelection )
+ // {
+ // SelectedObjects = RhinoDoc.ActiveDoc.Objects.GetSelectedObjects( false, false ).ToList();
+ // if ( SelectedObjects.Count == 0 || SelectedObjects[ 0 ] == null )
+ // return JsonConvert.SerializeObject( layerInfoList );
+ // }
+ // else
+ // {
+ // SelectedObjects = RhinoDoc.ActiveDoc.Objects.ToList();
+ // if ( SelectedObjects.Count == 0 || SelectedObjects[ 0 ] == null )
+ // return JsonConvert.SerializeObject( layerInfoList );
+
+ // foreach ( Rhino.DocObjects.Layer ll in RhinoDoc.ActiveDoc.Layers )
+ // {
+ // layerInfoList.Add( new LayerSelection()
+ // {
+ // objectCount = 0,
+ // layerName = ll.FullPath,
+ // color = System.Drawing.ColorTranslator.ToHtml( ll.Color ),
+ // ObjectGuids = new List(),
+ // ObjectTypes = new List()
+ // } );
+ // }
+ // }
+
+ // SelectedObjects = SelectedObjects.OrderBy( o => o.Attributes.LayerIndex ).ToList();
+
+ // foreach ( var obj in SelectedObjects )
+ // {
+ // var layer = RhinoDoc.ActiveDoc.Layers[ obj.Attributes.LayerIndex ];
+ // var myLInfo = layerInfoList.FirstOrDefault( l => l.layerName == layer.FullPath );
+
+ // if ( myLInfo != null )
+ // {
+ // myLInfo.objectCount++;
+ // myLInfo.ObjectGuids.Add( obj.Id.ToString() );
+ // myLInfo.ObjectTypes.Add( obj.Geometry.GetType().ToString() );
+ // }
+ // else
+ // {
+ // var myNewLinfo = new LayerSelection()
+ // {
+ // objectCount = 1,
+ // layerName = layer.FullPath,
+ // color = System.Drawing.ColorTranslator.ToHtml( layer.Color ),
+ // ObjectGuids = new List( new string[ ] { obj.Id.ToString() } ),
+ // ObjectTypes = new List( new string[ ] { obj.Geometry.GetType().ToString() } )
+ // };
+ // layerInfoList.Add( myNewLinfo );
+ // }
+ // }
+
+ // return Convert.ToBase64String( System.Text.Encoding.UTF8.GetBytes( JsonConvert.SerializeObject( layerInfoList ) ) );
+ //}
+ #endregion
+ }
+
+ [Serializable]
+ public class LayerSelection
+ {
+ public string LayerName;
+ public int ObjectCount;
+ public string Color;
+ public List ObjectGuids;
+ public List ObjectTypes;
+ }
+}
diff --git a/SpeckleRevitPlugin/Classes/RevitReceiver.cs b/SpeckleRevitPlugin/Classes/RevitReceiver.cs
new file mode 100644
index 0000000..0d4a8d2
--- /dev/null
+++ b/SpeckleRevitPlugin/Classes/RevitReceiver.cs
@@ -0,0 +1,308 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using SpeckleCore;
+using SpeckleRevitPlugin.Utilities;
+#pragma warning disable 4014
+#endregion
+
+namespace SpeckleRevitPlugin.Classes
+{
+ ///
+ /// Class that holds a rhino receiver client warpped around the SpeckleApiClient.
+ ///
+ [Serializable]
+ public class RevitReceiver : ISpeckleRevitClient
+ {
+ public Interop Context { get; set; }
+ public SpeckleApiClient Client { get; set; }
+ public List Objects { get; set; }
+ public string StreamId { get; private set; }
+ public bool Paused { get; set; }
+ public bool Visible { get; set; } = true;
+
+ public RevitReceiver()
+ {
+ }
+
+ public RevitReceiver(string payload, Interop parent)
+ {
+ Context = parent;
+ dynamic p = JsonConvert.DeserializeObject(payload);
+
+ StreamId = (string) p.streamId;
+ Client = new SpeckleApiClient((string) p.account.restApi, true);
+
+ Client.OnReady += Client_OnReady;
+ Client.OnLogData += Client_OnLogData;
+ Client.OnWsMessage += Client_OnWsMessage;
+ Client.OnError += Client_OnError;
+
+ Client.IntializeReceiver((string) p.streamId,
+ Context.GetDocumentName(),
+ "Revit",
+ Context.GetDocumentGuid(),
+ (string) p.account.apiToken);
+
+ Objects = new List();
+ }
+
+ protected RevitReceiver(SerializationInfo info, StreamingContext context)
+ {
+ JsonConvert.DefaultSettings = () => new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ };
+
+ Objects = new List();
+
+ var serialisedClient = Convert.FromBase64String(info.GetString("client"));
+
+ using (var ms = new MemoryStream())
+ {
+ ms.Write(serialisedClient, 0, serialisedClient.Length);
+ ms.Seek(0, SeekOrigin.Begin);
+ var bf = new BinaryFormatter
+ {
+ Binder = new SearchAssembliesBinder(Assembly.GetExecutingAssembly(), true)
+ };
+ Client = (SpeckleApiClient)bf.Deserialize(ms);
+ StreamId = Client.StreamId;
+ }
+
+ Client.OnReady += Client_OnReady;
+ Client.OnLogData += Client_OnLogData;
+ Client.OnWsMessage += Client_OnWsMessage;
+ Client.OnError += Client_OnError;
+ }
+
+ public string GetClientId()
+ {
+ return Client.ClientId;
+ }
+
+ public ClientRole GetRole()
+ {
+ return ClientRole.Receiver;
+ }
+
+ #region Events
+
+ private void Client_OnError( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame( "client-error", StreamId, JsonConvert.SerializeObject( e.EventData ) );
+ }
+
+ public virtual void Client_OnLogData( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame( "client-log", StreamId, JsonConvert.SerializeObject( e.EventData ) );
+ }
+
+ public virtual void Client_OnReady(object source, SpeckleEventArgs e)
+ {
+ Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new
+ {
+ client = Client,
+ stream = Client.Stream
+ }));
+
+ Context.UserClients.Add(this);
+ UpdateGlobal();
+ }
+
+ public virtual void Client_OnWsMessage(object source, SpeckleEventArgs e)
+ {
+ if (Paused)
+ {
+ Context.NotifySpeckleFrame("client-expired", StreamId, "");
+ return;
+ }
+
+ switch ((string)e.EventObject.args.eventType)
+ {
+ case "update-global":
+ UpdateGlobal();
+ break;
+ case "update-meta":
+ UpdateMeta();
+ break;
+ case "update-name":
+ UpdateName();
+ break;
+ case "update-object":
+ break;
+ case "update-children":
+ UpdateChildren();
+ break;
+ default:
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Unkown event: " + (string)e.EventObject.args.eventType));
+ break;
+ }
+ }
+ #endregion
+
+ #region Updates
+
+ public void UpdateName()
+ {
+ try
+ {
+ var response = Client.StreamGetAsync(StreamId, "fields=name");
+ Client.Stream.Name = response.Result.Resource.Name;
+ Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson());
+ }
+ catch (Exception err)
+ {
+ Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ }
+ }
+
+ public void UpdateMeta()
+ {
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Metadata update received."));
+
+ try
+ {
+ var streamGetResponse = Client.StreamGetAsync(StreamId, null).Result;
+
+ if (streamGetResponse.Success == false)
+ {
+ Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message);
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Failed to retrieve global update."));
+ }
+
+ Client.Stream = streamGetResponse.Resource;
+
+ Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson());
+ }
+ catch (Exception err)
+ {
+ Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ }
+ }
+
+ public void UpdateGlobal()
+ {
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Global update received."));
+
+ try
+ {
+ var streamGetResponse = Client.StreamGetAsync(StreamId, null).Result;
+ if (streamGetResponse.Success == false)
+ {
+ Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message);
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Failed to retrieve global update."));
+ }
+
+ Client.Stream = streamGetResponse.Resource;
+ Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson());
+ Context.NotifySpeckleFrame("client-is-loading", StreamId, "");
+
+ // prepare payload
+ var payload = Client.Stream.Objects.Where(o => !Context.SpeckleObjectCache.ContainsKey(o._id)).Select(obj => obj._id).ToArray();
+ var getObjectsResult = Client.ObjectGetBulkAsync(payload, "omit=displayValue").Result;
+
+ if (getObjectsResult.Success == false)
+ Context.NotifySpeckleFrame("client-error", StreamId, streamGetResponse.Message);
+
+ // add to cache
+ foreach (var obj in getObjectsResult.Resources)
+ {
+ Context.SpeckleObjectCache[obj._id] = obj;
+ }
+
+ // populate real objects
+ Objects.Clear();
+ foreach (var obj in Client.Stream.Objects)
+ {
+ Objects.Add(Context.SpeckleObjectCache[obj._id]);
+ }
+
+ //DisplayContents();
+ Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ }
+ catch (Exception err)
+ {
+ Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ }
+ }
+
+ public void UpdateChildren()
+ {
+ try
+ {
+ var getStream = Client.StreamGetAsync(StreamId, null).Result;
+ Client.Stream = getStream.Resource;
+
+ Context.NotifySpeckleFrame("client-children", StreamId, Client.Stream.ToJson());
+ }
+ catch (Exception err)
+ {
+ Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ }
+ }
+
+ #endregion
+
+ #region Toggles
+
+ public void TogglePaused(bool status)
+ {
+ Paused = status;
+ }
+
+ public void ToggleVisibility(bool status)
+ {
+ //TODO: Implement
+ }
+
+ public void ToggleLayerVisibility(string layerId, bool status)
+ {
+ //TODO: Implement
+ }
+
+ public void ToggleLayerHover(string layerId, bool status)
+ {
+ //TODO: Implement
+ }
+
+ #endregion
+
+ #region Serialisation & Dispose
+
+ public void Dispose()
+ {
+ Client.Dispose();
+ }
+
+ public void Dispose(bool delete = false)
+ {
+ Client.Dispose(delete);
+ }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ using (var ms = new MemoryStream())
+ {
+ var formatter = new BinaryFormatter();
+ formatter.Serialize(ms, Client);
+ info.AddValue("client", Convert.ToBase64String(ms.ToArray()));
+ info.AddValue("paused", Paused);
+ info.AddValue("visible", Visible);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SpeckleRevitPlugin/Classes/RevitSender.cs b/SpeckleRevitPlugin/Classes/RevitSender.cs
new file mode 100644
index 0000000..3245713
--- /dev/null
+++ b/SpeckleRevitPlugin/Classes/RevitSender.cs
@@ -0,0 +1,573 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Dynamic;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Timers;
+using Newtonsoft.Json;
+using SpeckleCore;
+#endregion
+
+namespace SpeckleRevitPlugin.Classes
+{
+ ///
+ /// Rhino Sender Client
+ ///
+ [Serializable]
+ public class RevitSender : ISpeckleRevitClient
+ {
+ public Interop Context { get; set; }
+ public SpeckleApiClient Client { get; private set; }
+ public List Objects { get; set; }
+ //public SpeckleDisplayConduit Display;
+ public string StreamId { get; set; }
+ public bool Paused { get; set; } = false;
+ public bool Visible { get; set; } = true;
+ Timer DataSender, MetadataSender;
+ public string StreamName;
+ public bool IsSendingUpdate = false, Expired = false;
+
+ public RevitSender()
+ {
+ }
+
+ public RevitSender(string _payload, Interop _Context)
+ {
+ Context = _Context;
+
+ dynamic InitPayload = JsonConvert.DeserializeObject(_payload);
+
+ Client = new SpeckleApiClient((string)InitPayload.account.restApi, true);
+
+ StreamName = (string)InitPayload.streamName;
+
+ SetClientEvents();
+ //SetRhinoEvents();
+ SetTimers();
+
+ //Display = new SpeckleDisplayConduit();
+ //Display.Enabled = true;
+
+ Context.NotifySpeckleFrame("set-gl-load", "", "true");
+
+ Client.IntializeSender((string)InitPayload.account.apiToken, Context.GetDocumentName(), "Rhino", Context.GetDocumentGuid())
+ .ContinueWith(res =>
+ {
+ StreamId = Client.Stream.StreamId;
+ Client.Stream.Name = StreamName;
+
+ Context.NotifySpeckleFrame("set-gl-load", "", "false");
+ Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client }));
+ Context.UserClients.Add(this);
+
+ InitTrackedObjects(InitPayload);
+ DataSender.Start();
+ });
+
+ }
+
+ public void InitTrackedObjects(dynamic payload)
+ {
+ //TODO: implement
+ //foreach (string guid in payload.selection)
+ // RhinoDoc.ActiveDoc.Objects.Find(new Guid(guid)).Attributes.SetUserString("spk_" + StreamId, StreamId);
+ }
+
+ //public void AddTrackedObjects( string[ ] guids )
+ //{
+ // foreach ( string guid in guids )
+ // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, StreamId );
+
+ // DataSender.Start();
+ //}
+
+ //public void RemoveTrackedObjects( string[ ] guids )
+ //{
+ // foreach ( string guid in guids )
+ // RhinoDoc.ActiveDoc.Objects.Find( new Guid( guid ) ).Attributes.SetUserString( "spk_" + StreamId, null );
+
+ // DataSender.Start();
+ //}
+
+ //public void SetRhinoEvents( )
+ //{
+ // RhinoDoc.ModifyObjectAttributes += RhinoDoc_ModifyObjectAttributes;
+ // RhinoDoc.DeleteRhinoObject += RhinoDoc_DeleteRhinoObject;
+ // RhinoDoc.AddRhinoObject += RhinoDoc_AddRhinoObject;
+ // RhinoDoc.UndeleteRhinoObject += RhinoDoc_UndeleteRhinoObject;
+ // RhinoDoc.LayerTableEvent += RhinoDoc_LayerTableEvent;
+ //}
+
+ //public void UnsetRhinoEvents( )
+ //{
+ // RhinoDoc.ModifyObjectAttributes -= RhinoDoc_ModifyObjectAttributes;
+ // RhinoDoc.DeleteRhinoObject -= RhinoDoc_DeleteRhinoObject;
+ // RhinoDoc.AddRhinoObject -= RhinoDoc_AddRhinoObject;
+ // RhinoDoc.UndeleteRhinoObject -= RhinoDoc_UndeleteRhinoObject;
+ // RhinoDoc.LayerTableEvent -= RhinoDoc_LayerTableEvent;
+ //}
+
+ //private void RhinoDoc_LayerTableEvent( object sender, Rhino.DocObjects.Tables.LayerTableEventArgs e )
+ //{
+ // DataSender.Start();
+ //}
+
+ //private void RhinoDoc_UndeleteRhinoObject( object sender, RhinoObjectEventArgs e )
+ //{
+ // //Debug.WriteLine("UNDELETE Event");
+ // if ( Paused )
+ // {
+ // Context.NotifySpeckleFrame( "client-expired", StreamId, "" );
+ // return;
+ // }
+ // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId )
+ // {
+ // DataSender.Start();
+ // }
+ //}
+
+ //private void RhinoDoc_AddRhinoObject( object sender, RhinoObjectEventArgs e )
+ //{
+ // //Debug.WriteLine("ADD Event");
+ // if ( Paused )
+ // {
+ // Context.NotifySpeckleFrame( "client-expired", StreamId, "" );
+ // return;
+ // }
+ // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId )
+ // {
+ // DataSender.Start();
+ // }
+ //}
+
+ //private void RhinoDoc_DeleteRhinoObject( object sender, RhinoObjectEventArgs e )
+ //{
+ // if ( Paused )
+ // {
+ // Context.NotifySpeckleFrame( "client-expired", StreamId, "" );
+ // return;
+ // }
+ // if ( e.TheObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId )
+ // {
+ // DataSender.Start();
+ // }
+ //}
+
+ //private void RhinoDoc_ModifyObjectAttributes( object sender, RhinoModifyObjectAttributesEventArgs e )
+ //{
+ // //Debug.WriteLine("MODIFY Event");
+ // //Prevents https://github.com/speckleworks/SpeckleRhino/issues/51 from happening
+ // if ( Converter.getBase64( e.NewAttributes ) == Converter.getBase64( e.OldAttributes ) ) return;
+
+ // if ( Paused )
+ // {
+ // Context.NotifySpeckleFrame( "client-expired", StreamId, "" );
+ // return;
+ // }
+ // if ( e.RhinoObject.Attributes.GetUserString( "spk_" + StreamId ) == StreamId )
+ // {
+ // DataSender.Start();
+ // }
+ //}
+
+ public void SetClientEvents( )
+ {
+ Client.OnError += Client_OnError;
+ Client.OnLogData += Client_OnLogData;
+ Client.OnWsMessage += Client_OnWsMessage;
+ Client.OnReady += Client_OnReady;
+ }
+
+ public void SetTimers()
+ {
+ MetadataSender = new Timer(500) { AutoReset = false, Enabled = false };
+ MetadataSender.Elapsed += MetadataSender_Elapsed;
+
+ DataSender = new Timer(2000) { AutoReset = false, Enabled = false };
+ DataSender.Elapsed += DataSender_Elapsed;
+ }
+
+ private void Client_OnReady( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Ready Event."));
+ }
+
+ private void DataSender_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ Debug.WriteLine("Boing! Boing!");
+ DataSender.Stop();
+ SendStaggeredUpdate();
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Update Sent."));
+ }
+
+ private void MetadataSender_Elapsed( object sender, ElapsedEventArgs e )
+ {
+ Debug.WriteLine("Ping! Ping!");
+ MetadataSender.Stop();
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("Update Sent."));
+ }
+
+ private void Client_OnWsMessage( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject("WS message received and ignored."));
+ }
+
+ private void Client_OnLogData( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame("client-log", StreamId, JsonConvert.SerializeObject(e.EventData));
+ }
+
+ private void Client_OnError( object source, SpeckleEventArgs e )
+ {
+ Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject(e.EventData));
+ }
+
+ public void ForceUpdate()
+ {
+ SendStaggeredUpdate(true);
+ }
+
+ public async void SendStaggeredUpdate(bool force = false)
+ {
+ //TODO: Implement!
+ //if (Paused && !force)
+ //{
+ // Context.NotifySpeckleFrame("client-expired", StreamId, "");
+ // return;
+ //}
+
+ //if (IsSendingUpdate)
+ //{
+ // Expired = true;
+ // return;
+ //}
+
+ //IsSendingUpdate = true;
+
+ //Context.NotifySpeckleFrame("client-is-loading", StreamId, "");
+
+ ////TODO: I am not sure how we are going to send stuff to Speckle just yet.
+ ////TODO: I only know that serializing the whole damn Revit Element is not a good idea.
+ //var objs = new Element[]{};
+ ////var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString("spk_" + this.StreamId, "*", false).OrderBy(obj => obj.Attributes.LayerIndex);
+
+ //Context.NotifySpeckleFrame("client-progress-message", StreamId, "Converting " + objs.Count() + " objects...");
+
+ //List pLayers = new List();
+ //List convertedObjects = new List();
+ //List> objectUpdatePayloads = new List>();
+
+ //long totalBucketSize = 0;
+ //long currentBucketSize = 0;
+ //List currentBucketObjects = new List();
+ //List allObjects = new List();
+
+ //int lindex = -1, count = 0, orderIndex = 0;
+ //foreach (RhinoObject obj in objs)
+ //{
+ // // layer list creation
+ // Rhino.DocObjects.Layer layer = RhinoDoc.ActiveDoc.Layers[obj.Attributes.LayerIndex];
+ // if (lindex != obj.Attributes.LayerIndex)
+ // {
+ // var spkLayer = new SpeckleCore.Layer()
+ // {
+ // Name = layer.FullPath,
+ // Guid = layer.Id.ToString(),
+ // ObjectCount = 1,
+ // StartIndex = count,
+ // OrderIndex = orderIndex++,
+ // Properties = new LayerProperties() { Color = new SpeckleCore.SpeckleBaseColor() { A = 1, Hex = System.Drawing.ColorTranslator.ToHtml(layer.Color) }, }
+ // };
+
+ // pLayers.Add(spkLayer);
+ // lindex = obj.Attributes.LayerIndex;
+ // }
+ // else
+ // {
+ // var spkl = pLayers.FirstOrDefault(pl => pl.Name == layer.FullPath);
+ // spkl.ObjectCount++;
+ // }
+
+ // count++;
+
+ // // object conversion
+ // SpeckleObject convertedObject;
+
+ // convertedObject = Converter.Serialise(obj.Geometry);
+ // convertedObject.ApplicationId = obj.Id.ToString();
+ // allObjects.Add(convertedObject);
+
+ // Context.NotifySpeckleFrame("client-progress-message", StreamId, "Converted " + count + " objects out of " + objs.Count() + ".");
+
+ // // check cache and see what the response from the server is when sending placeholders
+ // // in the ObjectCreateBulkAsyncRoute
+ // if (Context.SpeckleObjectCache.ContainsKey(convertedObject.Hash))
+ // {
+ // convertedObject = new SpecklePlaceholder() { Hash = convertedObject.Hash, _id = Context.SpeckleObjectCache[convertedObject.Hash]._id, ApplicationId = Context.SpeckleObjectCache[convertedObject.Hash].ApplicationId };
+ // }
+
+ // // size checking & bulk object creation payloads creation
+ // long size = Converter.getBytes(convertedObject).Length;
+ // currentBucketSize += size;
+ // totalBucketSize += size;
+ // currentBucketObjects.Add(convertedObject);
+
+ // if (currentBucketSize > 2e6)
+ // {
+ // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL
+ // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This stream contains a super big object. These are not supported yet :("));
+ // Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ // IsSendingUpdate = false;
+ // return;
+ // }
+
+ // if (currentBucketSize > 5e5) // restrict max to ~500kb; should it be user config? anyway these functions should go into core. at one point.
+ // {
+ // Debug.WriteLine("Reached payload limit. Making a new one, current #: " + objectUpdatePayloads.Count);
+ // objectUpdatePayloads.Add(currentBucketObjects);
+ // currentBucketObjects = new List();
+ // currentBucketSize = 0;
+ // }
+
+ // // catch overflows early
+ // if (totalBucketSize >= 50e6)
+ // {
+ // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)"));
+ // IsSendingUpdate = false;
+ // Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ // return;
+ // }
+ //}
+
+ //// last bucket
+ //if (currentBucketObjects.Count > 0)
+ // objectUpdatePayloads.Add(currentBucketObjects);
+
+ //Debug.WriteLine("Finished, payload object update count is: " + objectUpdatePayloads.Count + " total bucket size is (kb) " + totalBucketSize / 1000);
+
+ //if (objectUpdatePayloads.Count > 100 || totalBucketSize >= 50e6)
+ //{
+ // // means we're around fooking bazillion mb of an upload. FAIL FAIL FAIL
+ // Context.NotifySpeckleFrame("client-error", StreamId, JsonConvert.SerializeObject("This is a humongous update, in the range of ~50mb. For now, create more streams instead of just one massive one! Updates will be faster and snappier, and you can combine them back together at the other end easier. " + totalBucketSize / 1000 + "(kb)"));
+ // IsSendingUpdate = false;
+ // Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ // return;
+ //}
+
+ //// create bulk object creation tasks
+ //int k = 0;
+ //List responses = new List();
+ //foreach (var payload in objectUpdatePayloads)
+ //{
+ // Context.NotifySpeckleFrame("client-progress-message", StreamId, String.Format("Sending payload {0} out of {1}", k++, objectUpdatePayloads.Count));
+ // try
+ // {
+ // responses.Add(await Client.ObjectCreateAsync(payload));
+ // }
+ // catch (Exception err)
+ // {
+ // Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ // Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+ // IsSendingUpdate = false;
+ // return;
+ // }
+ //}
+
+ //Context.NotifySpeckleFrame("client-progress-message", StreamId, "Updating stream...");
+
+ //// finalise layer creation
+ //foreach (var layer in pLayers)
+ // layer.Topology = "0-" + layer.ObjectCount + " ";
+
+ //// create placeholders for stream update payload
+ //List placeholders = new List();
+ //int m = 0;
+ //foreach (var myResponse in responses)
+ // foreach (var obj in myResponse.Resources) placeholders.Add(new SpecklePlaceholder() { _id = obj._id, ApplicationId = allObjects[m++].ApplicationId });
+
+ //// create stream update payload
+ //SpeckleStream streamUpdatePayload = new SpeckleStream();
+ //streamUpdatePayload.Layers = pLayers;
+ //streamUpdatePayload.Objects = placeholders;
+ //streamUpdatePayload.Name = Client.Stream.Name;
+
+ //// set some base properties (will be overwritten)
+ //var baseProps = new Dictionary();
+ //baseProps["units"] = RhinoDoc.ActiveDoc.ModelUnitSystem.ToString();
+ //baseProps["tolerance"] = RhinoDoc.ActiveDoc.ModelAbsoluteTolerance;
+ //baseProps["angleTolerance"] = RhinoDoc.ActiveDoc.ModelAngleToleranceRadians;
+ //streamUpdatePayload.BaseProperties = baseProps;
+
+ //// push it to the server yo!
+ //ResponseBase response = null;
+ //try
+ //{
+ // response = await Client.StreamUpdateAsync(Client.Stream.StreamId, streamUpdatePayload);
+ //}
+ //catch (Exception err)
+ //{
+ // Context.NotifySpeckleFrame("client-error", Client.Stream.StreamId, JsonConvert.SerializeObject(err.Message));
+ // IsSendingUpdate = false;
+ // return;
+ //}
+
+ //// put the objects in the cache
+ //int l = 0;
+
+ //foreach (var obj in streamUpdatePayload.Objects)
+ //{
+ // obj._id = placeholders[l]._id;
+ // Context.SpeckleObjectCache[allObjects[l].Hash] = placeholders[l];
+ // l++;
+ //}
+
+ //// emit events, etc.
+ //Client.Stream.Layers = streamUpdatePayload.Layers.ToList();
+ //Client.Stream.Objects = placeholders;
+
+ //Context.NotifySpeckleFrame("client-metadata-update", StreamId, Client.Stream.ToJson());
+ //Context.NotifySpeckleFrame("client-done-loading", StreamId, "");
+
+ //Client.BroadcastMessage(new { eventType = "update-global" });
+
+ //IsSendingUpdate = false;
+ //if (Expired)
+ //{
+ // DataSender.Start();
+ //}
+ //Expired = false;
+ }
+
+ public ClientRole GetRole( )
+ {
+ return ClientRole.Sender;
+ }
+
+ public string GetClientId( )
+ {
+ return Client.ClientId;
+ }
+
+ public void TogglePaused( bool status )
+ {
+ Paused = status;
+ }
+
+ public void ToggleVisibility( bool status )
+ {
+ Visible = status;
+ }
+
+ //public void ToggleLayerHover( string layerId, bool status )
+ //{
+ // Debug.WriteLine( "OHAI: " + layerId + " " + status );
+ // Display.Enabled = true;
+ // Display.Geometry = new List();
+ // if ( !status )
+ // {
+ // Display.HoverRange = new Interval( 0, 0 );
+ // RhinoDoc.ActiveDoc.Views.Redraw();
+ // return;
+ // }
+
+ // int myLIndex = RhinoDoc.ActiveDoc.Layers.Find( new Guid( layerId ), true );
+
+ // var objs1 = RhinoDoc.ActiveDoc.Objects.FindByUserString( "spk_" + this.StreamId, "*", false );
+ // var cop = objs1;
+ // var objs = objs1.OrderBy( obj => obj.Attributes.LayerIndex ).ToList();
+ // var count = objs.Count;
+ // foreach ( var obj in objs )
+ // {
+ // if ( obj.Attributes.LayerIndex == myLIndex )
+ // Display.Geometry.Add( obj.Geometry );
+ // }
+
+ // Display.HoverRange = new Interval( 0, Display.Geometry.Count );
+ // RhinoDoc.ActiveDoc.Views.Redraw();
+
+ //}
+
+ public void ToggleLayerVisibility(string layerId, bool status)
+ {
+ //TODO: Implement
+ }
+
+ public void ToggleLayerHover(string layerId, bool status)
+ {
+ //TODO: Implement
+ }
+
+ public void Dispose(bool delete = false)
+ {
+ if (delete)
+ {
+ //TODO: Fix this!
+ //var objs = RhinoDoc.ActiveDoc.Objects.FindByUserString("spk_" + StreamId, "*", false);
+ //foreach (var o in objs)
+ // o.Attributes.SetUserString("spk_" + StreamId, null);
+ }
+
+ DataSender.Dispose();
+ MetadataSender.Dispose();
+ //UnsetRhinoEvents();
+ Client.Dispose(delete);
+ }
+
+ public void Dispose()
+ {
+ DataSender.Dispose();
+ MetadataSender.Dispose();
+ //TODO: What events do we track if any?
+ //UnsetRhinoEvents();
+ Client.Dispose();
+ }
+
+ public void CompleteDeserialisation(Interop _Context)
+ {
+ Context = _Context;
+
+ Context.NotifySpeckleFrame("client-add", StreamId, JsonConvert.SerializeObject(new { stream = Client.Stream, client = Client }));
+ Context.UserClients.Add(this);
+ }
+
+ //protected RhinoSender( SerializationInfo info, StreamingContext context )
+ //{
+ // JsonConvert.DefaultSettings = ( ) => new JsonSerializerSettings()
+ // {
+ // ContractResolver = new CamelCasePropertyNamesContractResolver()
+ // };
+
+ // byte[ ] serialisedClient = Convert.FromBase64String( ( string ) info.GetString( "client" ) );
+
+ // using ( var ms = new MemoryStream() )
+ // {
+ // ms.Write( serialisedClient, 0, serialisedClient.Length );
+ // ms.Seek( 0, SeekOrigin.Begin );
+ // Client = ( SpeckleApiClient ) new BinaryFormatter().Deserialize( ms );
+ // StreamId = Client.StreamId;
+ // }
+
+ // SetClientEvents();
+ // SetRhinoEvents();
+ // SetTimers();
+
+ // Display = new SpeckleDisplayConduit();
+ // Display.Enabled = true;
+ //}
+
+ public void GetObjectData( SerializationInfo info, StreamingContext context )
+ {
+ using (var ms = new MemoryStream())
+ {
+ var formatter = new BinaryFormatter();
+ formatter.Serialize(ms, Client);
+ info.AddValue("client", Convert.ToBase64String(ms.ToArray()));
+ info.AddValue("paused", Paused);
+ info.AddValue("visible", Visible);
+ }
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/Classes/SettingsHelper.cs b/SpeckleRevitPlugin/Classes/SettingsHelper.cs
index a8fa48e..d8812f6 100644
--- a/SpeckleRevitPlugin/Classes/SettingsHelper.cs
+++ b/SpeckleRevitPlugin/Classes/SettingsHelper.cs
@@ -1,112 +1,127 @@
#region Namespaces
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
-using Autodesk.Revit.UI.Selection;
+using SpeckleRevitPlugin.Entry;
#endregion
-namespace SpeckleRevitPlugin
+namespace SpeckleRevitPlugin.Classes
{
- public class SettingsHelper
- {
- private ExternalCommandData _cmd;
- private Application _app;
- private Document _doc;
-
- ///
- /// Constructor
- ///
- ///
- public SettingsHelper(ExternalCommandData cmd)
- {
- _doc = null;
- _cmd = cmd;
- }
-
- ///
- /// Constructor - from application (events)
- ///
- ///
- ///
- public SettingsHelper(Document d, Application a)
- {
- _app = a;
- _doc = d;
- }
-
- #region Command Data
- ///
- /// Revit UI application
- ///
- public UIApplication UiApp
- {
- get
- {
- try
- {
- return _cmd.Application;
- }
- catch { }
- return null;
- }
- }
-
- ///
- /// Revit Application
- ///
- public Application App
- {
- get
- {
- try
- {
- return _cmd.Application.Application;
- }
- catch { }
- return null;
- }
- }
-
- ///
- /// Revit UI Document
- ///
- public UIDocument UiDoc
- {
- get
- {
- try
- {
- return _cmd.Application.ActiveUIDocument;
- }
- catch { }
- return null;
- }
- }
-
- ///
- /// Revit Document
- ///
- public Document ActiveDoc
- {
- get
- {
- try
- {
- return UiDoc.Document;
- }
- catch { }
- return null;
- }
- }
- #endregion
-
- #region Public Properties - Modeless
- internal EnumCommandType CommandType { get; set; }
- #endregion
- }
-}
\ No newline at end of file
+ //public class SettingsHelper
+ //{
+ // //private readonly ExternalCommandData _cmd;
+ // private Application _app;
+ // private Document _doc;
+
+ // /////
+ // ///// Constructor
+ // /////
+ // /////
+ // //public SettingsHelper(ExternalCommandData cmd)
+ // //{
+ // // _doc = null;
+ // // _cmd = cmd;
+ // //}
+
+ // ///
+ // /// Constructor - from application (events)
+ // ///
+ // ///
+ // ///
+ // public SettingsHelper(Document d, Application a)
+ // {
+ // _app = a;
+ // _doc = d;
+ // }
+
+ // #region Command Data
+
+ // ///
+ // /// Revit UI application
+ // ///
+ // public UIApplication UiApp
+ // {
+ // get
+ // {
+ // try
+ // {
+ // return _doc.Application.;
+ // }
+ // catch
+ // {
+ // // ignored
+ // }
+
+ // return null;
+ // }
+ // }
+
+ // ///
+ // /// Revit Application
+ // ///
+ // public Application App
+ // {
+ // get
+ // {
+ // try
+ // {
+ // return _cmd.Application.Application;
+ // }
+ // catch
+ // {
+ // // ignored
+ // }
+
+ // return null;
+ // }
+ // }
+
+ // ///
+ // /// Revit UI Document
+ // ///
+ // public UIDocument UiDoc
+ // {
+ // get
+ // {
+ // try
+ // {
+ // return _cmd.Application.ActiveUIDocument;
+ // }
+ // catch
+ // {
+ // // ignored
+ // }
+
+ // return null;
+ // }
+ // }
+
+ // ///
+ // /// Revit Document
+ // ///
+ // public Document ActiveDoc
+ // {
+ // get
+ // {
+ // try
+ // {
+ // return UiDoc.Document;
+ // }
+ // catch
+ // {
+ // // ignored
+ // }
+
+ // return null;
+ // }
+ // }
+
+ // #endregion
+
+ // #region Public Properties - Modeless
+
+ // internal EnumCommandType CommandType { get; set; }
+
+ // #endregion
+ //}
+}
diff --git a/SpeckleRevitPlugin/Classes/SpeckleAccount.cs b/SpeckleRevitPlugin/Classes/SpeckleAccount.cs
new file mode 100644
index 0000000..9949c4c
--- /dev/null
+++ b/SpeckleRevitPlugin/Classes/SpeckleAccount.cs
@@ -0,0 +1,12 @@
+namespace SpeckleRevitPlugin.Classes
+{
+ internal class SpeckleAccount
+ {
+ public string email { get; set; }
+ public string apiToken { get; set; }
+ public string serverName { get; set; }
+ public string restApi { get; set; }
+ public string rootUrl { get; set; }
+ public string fileName { get; set; }
+ }
+}
diff --git a/SpeckleRevitPlugin/Entry/AppMain.cs b/SpeckleRevitPlugin/Entry/AppMain.cs
index a959ab2..46251b6 100644
--- a/SpeckleRevitPlugin/Entry/AppMain.cs
+++ b/SpeckleRevitPlugin/Entry/AppMain.cs
@@ -1,65 +1,134 @@
#region Namespaces
-using Microsoft.VisualBasic;
+
using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Data;
using System.Diagnostics;
-using System.Windows.Media.Imaging;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Windows.Media;
-
-using Autodesk.Revit.ApplicationServices;
+using System.Windows.Media.Imaging;
using Autodesk.Revit.Attributes;
+using Autodesk.Revit.DB.Events;
using Autodesk.Revit.UI;
-using Autodesk.Revit.UI.Events;
+using SpeckleRevitPlugin.Classes;
+using SpeckleRevitPlugin.UI;
+
#endregion
-namespace SpeckleRevitPlugin
+namespace SpeckleRevitPlugin.Entry
{
[Transaction(TransactionMode.Manual)]
- class AppMain : IExternalApplication
+ public class AppMain : IExternalApplication
{
- static string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- static UIControlledApplication uiApp;
- static AppMain _thisApp;
-
- internal static form_MainDock MainDock;
+ private static readonly string m_Path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ private static AppMain _thisApp;
internal DockablePaneProviderData DockData;
- internal static SettingsHelper Settings { get; set; }
- #region Setup
+ public static FormMainDock MainDock;
+ public static UIControlledApplication uiApp;
+ public static ExternalEvent SpeckleEvent;
+ public static SpeckleRequestHandler SpeckleHandler = new SpeckleRequestHandler();
+
+ public Result OnStartup(UIControlledApplication a)
+ {
+ try
+ {
+ uiApp = a;
+ _thisApp = this;
+
+ if (MainDock == null)
+ {
+ MainDock = new FormMainDock();
+ DockData = new DockablePaneProviderData
+ {
+ FrameworkElement = MainDock,
+ InitialState = new DockablePaneState
+ {
+ DockPosition = DockPosition.Right
+ }
+ };
+ }
+
+ uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), MainDock);
+
+ // (Konrad) We are going to use this External Event Handler all across Speckle
+ // It's best to keep it on the main app, and keep it public.
+ SpeckleHandler = new SpeckleRequestHandler();
+ SpeckleEvent = ExternalEvent.Create(SpeckleHandler);
+
+ a.ControlledApplication.DocumentCreated += OnDocumentCreated;
+ a.ControlledApplication.DocumentOpened += OnDocumentOpened;
+
+ AddRibbonPanel(a);
+
+ return Result.Succeeded;
+ }
+ catch
+ {
+ return Result.Failed;
+ }
+ }
+
+ private void OnDocumentOpened(object sender, DocumentOpenedEventArgs e)
+ {
+ var doc = e.Document;
+ if (doc == null || doc.IsFamilyDocument)
+ {
+ HideDockablePane();
+ }
+ }
+
+ private void OnDocumentCreated(object sender, DocumentCreatedEventArgs e)
+ {
+ var doc = e.Document;
+ if (doc == null || doc.IsFamilyDocument)
+ {
+ HideDockablePane();
+ }
+ }
+
+ public Result OnShutdown(UIControlledApplication a)
+ {
+ a.ControlledApplication.DocumentCreated -= OnDocumentCreated;
+ a.ControlledApplication.DocumentOpened -= OnDocumentOpened;
+
+ return Result.Succeeded;
+ }
+
+ #region Ribbon Utilities
+
///
/// Load an Image Source from File
///
///
///
///
- private ImageSource LoadPngImgSource(string SourceName)
+ private static ImageSource LoadPngImgSource(string SourceName)
{
try
{
// Assembly
- Assembly assembly = Assembly.GetExecutingAssembly();
+ var assembly = Assembly.GetExecutingAssembly();
// Stream
- Stream icon = assembly.GetManifestResourceStream(SourceName);
+ var icon = assembly.GetManifestResourceStream(SourceName);
// Decoder
- PngBitmapDecoder decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
+ if (icon != null)
+ {
+ var decoder = new PngBitmapDecoder(icon, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
- // Source
- ImageSource m_source = decoder.Frames[0];
- return (m_source);
+ // Source
+ ImageSource m_source = decoder.Frames[0];
+ return (m_source);
+ }
}
catch
{
+ // ignored
}
- // Fail
return null;
-
}
///
@@ -71,7 +140,6 @@ public void AddRibbonPanel(UIControlledApplication a)
{
try
{
- // First Create the Tab
a.CreateRibbonTab("Speckle");
}
catch
@@ -80,152 +148,83 @@ public void AddRibbonPanel(UIControlledApplication a)
}
// Tools
- AddButton("Speckle",
- "Plugin\r\nTest",
- "Plugin\r\nTest",
- "SpeckleRevitPlugin.Resources.Template_16.png",
- "SpeckleRevitPlugin.Resources.Template_32.png",
- (m_Path + "\\SpeckleRevitPlugin.dll"),
- "SpeckleRevitPlugin.ExtCmd",
+ AddButton("Speckle",
+ "Show\r\nSpeckle",
+ "Show\r\nSpeckle",
+ "SpeckleRevitPlugin.Resources.Template_16.png",
+ "SpeckleRevitPlugin.Resources.Template_32.png",
+ (m_Path + "\\SpeckleRevitPlugin.dll"),
+ "SpeckleRevitPlugin.Entry.ExtCmd",
"Speckle connection test for Revit.");
+
+ AddButton("Tools",
+ "Speckle\r\nWall",
+ "Speckle\r\nWall",
+ "SpeckleRevitPlugin.Resources.Template_16.png",
+ "SpeckleRevitPlugin.Resources.Template_32.png",
+ (m_Path + "\\SpeckleRevitPlugin.dll"),
+ "SpeckleRevitPlugin.Tools.WallTool.SpeckleWallCmd",
+ "Make Walls in Revit using Speckle Streams.");
}
///
/// Add a button to a Ribbon Tab
///
- /// The name of the ribbon panel
- /// The Name of the Button
- /// Command Text
- /// Small Image
- /// Large Image
+ /// The name of the ribbon panel
+ /// The Name of the Button
+ /// Command Text
+ /// Small Image
+ /// Large Image
/// Path to the DLL file
/// Full qualified class descriptor
- /// Tooltip to add to the button
+ /// Tooltip to add to the button
///
///
- private bool AddButton(string Rpanel, string ButtonName, string ButtonText, string ImagePath16, string ImagePath32, string dllPath, string dllClass, string Tooltip)
+ private static void AddButton(string panel,
+ string bName, string bText, string iPath16, string iPath32,
+ string dllPath, string dllClass, string tooltip)
{
try
{
- // The Ribbon Panel
- RibbonPanel ribbonPanel = null;
-
- // Find the Panel within the Case Tab
- List rp = new List();
- rp = uiApp.GetRibbonPanels("Speckle");
- foreach (RibbonPanel x in rp)
- {
- if (x.Name.ToUpper() == Rpanel.ToUpper())
- {
- ribbonPanel = x;
- }
- }
-
// Create the Panel if it doesn't Exist
- if (ribbonPanel == null)
- {
- ribbonPanel = uiApp.CreateRibbonPanel("Speckle", Rpanel);
- }
+ var ribbonPanel =
+ uiApp.GetRibbonPanels("Speckle").FirstOrDefault(x =>
+ string.Equals(x.Name, panel, StringComparison.OrdinalIgnoreCase)) ??
+ uiApp.CreateRibbonPanel("Speckle", panel);
// Create the Pushbutton Data
- PushButtonData pushButtonData = new PushButtonData(ButtonName, ButtonText, dllPath, dllClass);
- if (!string.IsNullOrEmpty(ImagePath16))
+ var pushButtonData = new PushButtonData(bName, bText, dllPath, dllClass);
+ if (!string.IsNullOrEmpty(iPath16))
{
try
{
- pushButtonData.Image = LoadPngImgSource(ImagePath16);
+ pushButtonData.Image = LoadPngImgSource(iPath16);
}
catch
{
Debug.WriteLine("Image not found", "SPK");
}
}
- if (!string.IsNullOrEmpty(ImagePath32))
+ if (!string.IsNullOrEmpty(iPath32))
{
try
{
- pushButtonData.LargeImage = LoadPngImgSource(ImagePath32);
+ pushButtonData.LargeImage = LoadPngImgSource(iPath32);
}
catch
{
Debug.WriteLine("Image not found", "SPK");
}
}
- pushButtonData.ToolTip = Tooltip;
+ pushButtonData.ToolTip = tooltip;
// Add the button to the tab
- PushButton pushButtonDataAdd = (PushButton)ribbonPanel.AddItem(pushButtonData);
+ var unused = (PushButton)ribbonPanel.AddItem(pushButtonData);
}
catch
{
- // Quiet Fail
- }
- return true;
- }
- #endregion
-
- #region Startup
- public Result OnStartup(UIControlledApplication a)
- {
- try
- {
- // The Shared uiApp variable
- uiApp = a;
- _thisApp = this;
-
- // Register the dockable pane
- if (MainDock == null)
- {
- MainDock = new form_MainDock();
- DockData = new DockablePaneProviderData
- {
- FrameworkElement = MainDock,
- InitialState = new DockablePaneState
- {
- DockPosition = DockPosition.Right
- }
- };
- }
- uiApp.RegisterDockablePane(GlobalHelper.MainDockablePaneId, GlobalHelper.MainPanelName(), MainDock as IDockablePaneProvider);
-
- // Detect when a new model is in focus
- a.ViewActivated += OnViewActivated;
-
- // Add the Ribbon Panel!!
- AddRibbonPanel(a);
-
- // Return Success
- return Result.Succeeded;
-
- }
- catch { return Result.Failed; }
- }
- #endregion
-
- #region Shutdown
- public Result OnShutdown(UIControlledApplication a)
- {
- return Result.Succeeded;
- }
- #endregion
-
- #region Internal Members - Events
- ///
- /// View change will detect a model change
- ///
- ///
- ///
- void OnViewActivated(object sender, ViewActivatedEventArgs e)
- {
- try
- {
- if (Settings == null) Settings = new SettingsHelper(e.Document, null);
- if (Settings.App == null)
- {
- HideDockablePane();
- }
+ // ignored
}
- catch { }
}
///
@@ -235,11 +234,15 @@ internal void HideDockablePane()
{
try
{
- DockablePane m_dp = uiApp.GetDockablePane(GlobalHelper.MainDockablePaneId);
+ var m_dp = uiApp.GetDockablePane(GlobalHelper.MainDockablePaneId);
m_dp.Hide();
}
- catch { }
+ catch
+ {
+ // ignored
+ }
}
+
#endregion
}
}
diff --git a/SpeckleRevitPlugin/Entry/ExtCmd.cs b/SpeckleRevitPlugin/Entry/ExtCmd.cs
index cfc91c6..4cdc176 100644
--- a/SpeckleRevitPlugin/Entry/ExtCmd.cs
+++ b/SpeckleRevitPlugin/Entry/ExtCmd.cs
@@ -1,18 +1,14 @@
#region Namespaces
-
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
-
#endregion
-namespace SpeckleRevitPlugin
+namespace SpeckleRevitPlugin.Entry
{
[Transaction(TransactionMode.Manual)]
public class ExtCmd : IExternalCommand
{
- //public static ChromiumWebBrowser Browser;
-
public Result Execute(
ExternalCommandData commandData,
ref string message,
@@ -20,11 +16,11 @@ public Result Execute(
{
// Settings Helper Class
// NOTE: You can use the AppMain.Settings throughout project to access active Revit Document, UIDocument, Application, etc.
- AppMain.Settings = new SettingsHelper(commandData);
+ //AppMain.Settings = new SettingsHelper(commandData);
// SHOW DOCKABLE WINDOW
- DockablePaneId m_dpID = GlobalHelper.MainDockablePaneId;
- DockablePane m_dp = commandData.Application.GetDockablePane(m_dpID);
+ var m_dpID = GlobalHelper.MainDockablePaneId;
+ var m_dp = commandData.Application.GetDockablePane(m_dpID);
m_dp.Show();
diff --git a/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs b/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs
deleted file mode 100644
index d31f859..0000000
--- a/SpeckleRevitPlugin/Entry/ExtCmdModeless.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-#region Namespaces
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows;
-
-using Autodesk.Revit.DB;
-using Autodesk.Revit.UI;
-#endregion
-
-namespace SpeckleRevitPlugin
-{
- public enum EnumCommandType : int
- {
- Command1,
- Command2,
- Command3
- }
-
- ///
- /// Model Updates for Modeless Dialog
- ///
- ///
- class ExtCmdModeless : IExternalEventHandler
- {
- ///
- /// Model Updates for Modeless Dialog
- ///
- ///
- public void Execute(UIApplication uiapp)
- {
- switch (AppMain.Settings.CommandType)
- {
- case EnumCommandType.Command1:
- SampleCommand1(uiapp);
- break;
- case EnumCommandType.Command2:
- SampleCommand2(uiapp);
- break;
- case EnumCommandType.Command3:
- SampleCommand3(uiapp);
- break;
- }
- }
-
- ///
- /// Command 1
- ///
- ///
- private void SampleCommand1(UIApplication uiapp)
- {
- try
- {
- using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something"))
- {
- t.Start();
-
- t.Commit();
- }
- }
- catch { }
- }
-
- ///
- /// Command 2
- ///
- ///
- private void SampleCommand2(UIApplication uiapp)
- {
- try
- {
- using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something"))
- {
- t.Start();
-
- t.Commit();
- }
- }
- catch { }
- }
-
- ///
- /// Command 3
- ///
- ///
- private void SampleCommand3(UIApplication uiapp)
- {
- try
- {
- using (Transaction t = new Transaction(AppMain.Settings.ActiveDoc, "Do something"))
- {
- t.Start();
-
- t.Commit();
- }
- }
- catch { }
- }
-
- ///
- /// External event anme
- ///
- ///
- public string GetName() { return "ExtCmdModeless"; }
- }
-}
diff --git a/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs
new file mode 100644
index 0000000..2e27567
--- /dev/null
+++ b/SpeckleRevitPlugin/Entry/SpeckleRequestHandler.cs
@@ -0,0 +1,130 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using Autodesk.Revit.DB;
+using Autodesk.Revit.UI;
+using SpeckleRevitPlugin.Utilities;
+#endregion
+
+namespace SpeckleRevitPlugin.Entry
+{
+ public class SpeckleRequestHandler : IExternalEventHandler
+ {
+ public SpeckleRequest Request { get; set; } = new SpeckleRequest();
+ public object Arg1 { get; set; }
+ public object Arg2 { get; set; }
+
+ public delegate void ClientsRetrieved(IDictionary receivers, IDictionary senders);
+ public static event ClientsRetrieved OnClientsRetrieved;
+
+ public void Execute(UIApplication uiapp)
+ {
+ try
+ {
+ switch (Request.Take())
+ {
+ case SpeckleCommandType.None:
+ return;
+ case SpeckleCommandType.GetClients:
+ GetClients(uiapp);
+ break;
+ case SpeckleCommandType.SaveClients:
+ SaveClients(uiapp);
+ break;
+ case SpeckleCommandType.Command3:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ private static void GetClients(UIApplication app)
+ {
+ if (!SchemaUtilities.SchemaExist(Properties.Resources.SchemaName)) return;
+
+ var doc = app.ActiveUIDocument.Document;
+ var schema = SchemaUtilities.GetSchema(Properties.Resources.SchemaName);
+ var pInfo = SchemaUtilities.GetProjectInfo(doc);
+ var receivers = pInfo.GetEntity(schema).Get>(schema.GetField("receivers"));
+ var senders = pInfo.GetEntity(schema).Get>(schema.GetField("senders"));
+
+ OnClientsRetrieved?.Invoke(receivers, senders);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ private void SaveClients(UIApplication app)
+ {
+ // (Konrad) Extract the variables used by this method
+ var senders = Arg1 as Dictionary;
+ var receivers = Arg2 as Dictionary;
+
+ var doc = app.ActiveUIDocument.Document;
+ var pInfo = SchemaUtilities.GetProjectInfo(doc);
+ var schemaExists = SchemaUtilities.SchemaExist(Properties.Resources.SchemaName);
+ var schema = schemaExists
+ ? SchemaUtilities.GetSchema(Properties.Resources.SchemaName)
+ : SchemaUtilities.CreateSchema();
+
+ using (var trans = new Transaction(doc, "Store Clients"))
+ {
+ trans.Start();
+
+ if (schemaExists)
+ {
+ SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "senders", senders);
+ SchemaUtilities.UpdateSchemaEntity(schema, pInfo, "receivers", receivers);
+ }
+ else
+ {
+ SchemaUtilities.AddSchemaEntity(schema, pInfo, "senders", senders);
+ SchemaUtilities.AddSchemaEntity(schema, pInfo, "receivers", receivers);
+ }
+
+ trans.Commit();
+ }
+ }
+
+ ///
+ /// External event name
+ ///
+ ///
+ public string GetName() { return "SpeckleRequestHandler"; }
+ }
+
+ public class SpeckleRequest
+ {
+ private int _request = (int)SpeckleCommandType.None;
+
+ public SpeckleCommandType Take()
+ {
+ return (SpeckleCommandType)Interlocked.Exchange(ref _request, (int)SpeckleCommandType.None);
+ }
+
+ public void Make(SpeckleCommandType request)
+ {
+ Interlocked.Exchange(ref _request, (int)request);
+ }
+ }
+
+ public enum SpeckleCommandType
+ {
+ None,
+ GetClients,
+ SaveClients,
+ Command3
+ }
+}
diff --git a/SpeckleRevitPlugin/Properties/Resources.Designer.cs b/SpeckleRevitPlugin/Properties/Resources.Designer.cs
index 23f25c5..8eb7081 100644
--- a/SpeckleRevitPlugin/Properties/Resources.Designer.cs
+++ b/SpeckleRevitPlugin/Properties/Resources.Designer.cs
@@ -22,7 +22,7 @@ namespace SpeckleRevitPlugin.Properties {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
+ public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
@@ -36,7 +36,7 @@ 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 {
+ public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SpeckleRevitPlugin.Properties.Resources", typeof(Resources).Assembly);
@@ -51,7 +51,7 @@ internal Resources() {
/// resource lookups using this strongly typed resource class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
+ public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@@ -61,22 +61,20 @@ internal Resources() {
}
///
- /// Looks up a localized resource of type System.Drawing.Bitmap.
+ /// Looks up a localized string similar to a2666a4c-0eed-4f9c-b4da-7a9d97e1ce40.
///
- internal static System.Drawing.Bitmap Template_16 {
+ public static string SchemaId {
get {
- object obj = ResourceManager.GetObject("Template_16", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
+ return ResourceManager.GetString("SchemaId", resourceCulture);
}
}
///
- /// Looks up a localized resource of type System.Drawing.Bitmap.
+ /// Looks up a localized string similar to SpeckleSchema.
///
- internal static System.Drawing.Bitmap Template_32 {
+ public static string SchemaName {
get {
- object obj = ResourceManager.GetObject("Template_32", resourceCulture);
- return ((System.Drawing.Bitmap)(obj));
+ return ResourceManager.GetString("SchemaName", resourceCulture);
}
}
}
diff --git a/SpeckleRevitPlugin/Properties/Resources.resx b/SpeckleRevitPlugin/Properties/Resources.resx
index 1af7de1..573c813 100644
--- a/SpeckleRevitPlugin/Properties/Resources.resx
+++ b/SpeckleRevitPlugin/Properties/Resources.resx
@@ -117,4 +117,10 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ a2666a4c-0eed-4f9c-b4da-7a9d97e1ce40
+
+
+ SpeckleSchema
+
\ No newline at end of file
diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin
index 7d1d9ec..1b05284 100644
--- a/SpeckleRevitPlugin/SpeckleRevitPlugin.addin
+++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.addin
@@ -1,18 +1,9 @@
-
- Command SpeckleRevitPlugin
- Speckle plugin for Revit
- .\Speckle\SpeckleRevitPlugin.dll
- SpeckleRevitPlugin.ExtCmd
- 76028dcb-3f9b-4a5b-9e8b-36b7a5564a56
- Speckle
- http://speckle.works/
-
Application SpeckleRevitPlugin
- .\Speckle\SpeckleRevitPlugin.dll
- SpeckleRevitPlugin.AppMain
+ Speckle\SpeckleRevitPlugin.dll
+ SpeckleRevitPlugin.Entry.AppMain
0a03f52e-0ee0-4c19-82c4-8181626ec816
Speckle
http://speckle.works/
diff --git a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj
index 17d0bbd..23cfe1f 100644
--- a/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj
+++ b/SpeckleRevitPlugin/SpeckleRevitPlugin.csproj
@@ -5,6 +5,7 @@
Debug
AnyCPU
{EA37FC7B-0CD7-407B-A6AD-C594E4BD2043}
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Library
Properties
SpeckleRevitPlugin
@@ -51,17 +52,59 @@
+
+
+
+
+
-
+
+
+ True
+ True
+ Resources.resx
+
+
+
+ BooleanSelector.xaml
+
+
+
+
+
+ ElementSelector.xaml
+
+
+
+
+
+ InputControl.xaml
+
+
+
+
+ SpeckleWallView.xaml
+
+
form_Main.xaml
form_MainDock.xaml
+
+ StreamSelector.xaml
+
+
+
+
+
+
+
+
@@ -69,11 +112,32 @@
Designer
-
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
- 63.0.2
+ 63.0.1
+
+
+ 3.0.40218
+
+
+ 1.2.4
+
+
+ 1.1.3
+
+
+ 2.3.0.823
+
+
+ 0.0.10
+
+
+ 5.3.0
11.0.1
@@ -83,6 +147,22 @@
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
@@ -91,6 +171,16 @@
MSBuild:Compile
Designer
+
+ Designer
+ MSBuild:Compile
+
+
+
+
+ {cfe27d3d-8a1a-43f9-9387-8fd9e119e174}
+ SpeckleCore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs
new file mode 100644
index 0000000..dba78fc
--- /dev/null
+++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallView.xaml.cs
@@ -0,0 +1,29 @@
+using MaterialDesignThemes.Wpf;
+using MaterialDesignColors;
+using MaterialDesignThemes.MahApps;
+using MahApps.Metro;
+using MahApps.Metro.Controls;
+
+namespace SpeckleRevitPlugin.Tools.WallTool
+{
+ ///
+ /// Interaction logic for SpeckleWallView.xaml
+ ///
+ public partial class SpeckleWallView : MetroWindow
+ {
+ public SpeckleWallView()
+ {
+ InitializeComponent();
+
+ //TODO: How do we force these assemblies to be resolved properly and loaded properly?
+ //TODO: We can get an installer and copy these to locations that would enforce this.
+ // (Konrad) We need to make sure that MaterialDesignThemes and MahApps
+ // are actually loaded into the context. These DLLs are missing if they are not explicitly
+ // loaded into the app via using statement.
+ var unused1 = typeof(FlyoutAssist);
+ var unused2 = typeof(ThemeManager);
+ var unused3 = typeof(Hue);
+ var unused4 = typeof(ShadowAssist);
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs
new file mode 100644
index 0000000..a6ff6ed
--- /dev/null
+++ b/SpeckleRevitPlugin/Tools/WallTool/SpeckleWallViewModel.cs
@@ -0,0 +1,117 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Autodesk.Revit.DB;
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Messaging;
+using MahApps.Metro.Controls;
+using SpeckleRevitPlugin.UI;
+
+namespace SpeckleRevitPlugin.Tools.WallTool
+{
+ public class SpeckleWallViewModel : ViewModelBase
+ {
+ #region Parameters
+
+ private SpeckleWallModel Model { get; set; }
+ public RelayCommand CloseFlyout { get; set; }
+ public RelayCommand AddInput { get; set; }
+ public RelayCommand WindowClosed { get; set; }
+
+ private ObservableCollection _inputs;
+ public ObservableCollection Inputs
+ {
+ get { return _inputs; }
+ set { _inputs = value; RaisePropertyChanged(() => Inputs); }
+ }
+
+ private ObservableCollection _availableInputs;
+ public ObservableCollection AvailableInputs
+ {
+ get { return _availableInputs; }
+ set { _availableInputs = value; RaisePropertyChanged(() => AvailableInputs); }
+ }
+
+ private InputWrapper _selectedInput;
+ public InputWrapper SelectedInput
+ {
+ get { return _selectedInput; }
+ set { _selectedInput = value; RaisePropertyChanged(() => SelectedInput); }
+ }
+
+ private bool _addInputChecked;
+ public bool AddInputChecked
+ {
+ get { return _addInputChecked; }
+ set { _addInputChecked = value; RaisePropertyChanged(() => AddInputChecked); }
+ }
+
+ #endregion
+
+ public SpeckleWallViewModel(SpeckleWallModel model)
+ {
+ Model = model;
+ AvailableInputs = Model.GetAllAvailableInputs();
+
+ CloseFlyout = new RelayCommand(OnCloseFlyout);
+ AddInput = new RelayCommand(OnAddInput);
+ WindowClosed = new RelayCommand(OnWindowClosed);
+
+ Inputs = new ObservableCollection
+ {
+ // (Konrad) Forces the Curve input to only use Speckle Stream as source of data.
+ new InputViewModel(new InputModel(), new InputWrapper
+ {
+ Name = "Curve",
+ AcceptsLocalData = false,
+ IsRequired = true
+ }),
+ //new InputViewModel(new InputModel(), new InputWrapper
+ //{
+ // Name = "Level",
+ // AcceptsLocalData = true,
+ // StorageType = LocalDataType.Element
+ //}),
+ //new InputViewModel(new InputModel(), new InputWrapper
+ //{
+ // Name = "Structural",
+ // AcceptsLocalData = true,
+ // StorageType = LocalDataType.Boolean
+ //})
+ };
+
+ Messenger.Default.Register(this, OnInputDeleted);
+ }
+
+ private void OnWindowClosed(MetroWindow obj)
+ {
+ // (Konrad) Removes all Messanger bindings.
+ Cleanup();
+ }
+
+ private void OnInputDeleted(InputDeleted obj)
+ {
+ // (Konrad) Restore the input in the dropdown and cleanup
+ AvailableInputs.Add(obj.InputViewModel.Input);
+ Inputs.Remove(obj.InputViewModel);
+ }
+
+ private void OnAddInput()
+ {
+ if (SelectedInput == null) return;
+
+ OnCloseFlyout();
+
+ Inputs.Add(new InputViewModel(new InputModel(), SelectedInput));
+
+ // (Konrad) Cleanup Available Inputs
+ AvailableInputs.Remove(SelectedInput);
+ SelectedInput = null;
+ }
+
+ private void OnCloseFlyout()
+ {
+ AddInputChecked = false;
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml
new file mode 100644
index 0000000..3096148
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs
new file mode 100644
index 0000000..e0dac50
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelector.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace SpeckleRevitPlugin.UI.BooleanSelector
+{
+ ///
+ /// Interaction logic for BooleanSelector.xaml
+ ///
+ public partial class BooleanSelector : UserControl
+ {
+ public BooleanSelector()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs
new file mode 100644
index 0000000..ec7fbaa
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorModel.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpeckleRevitPlugin.UI.BooleanSelector
+{
+ public class BooleanSelectorModel
+ {
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs
new file mode 100644
index 0000000..4ce7698
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/BooleanSelector/BooleanSelectorViewModel.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GalaSoft.MvvmLight;
+
+namespace SpeckleRevitPlugin.UI.BooleanSelector
+{
+ public class BooleanSelectorViewModel : ViewModelBase
+ {
+ public BooleanSelectorModel Model { get; set; }
+
+ public BooleanSelectorViewModel(BooleanSelectorModel model)
+ {
+ Model = model;
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/Converters.cs b/SpeckleRevitPlugin/UI/Converters.cs
new file mode 100644
index 0000000..5b668da
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/Converters.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using GalaSoft.MvvmLight.Command;
+
+namespace SpeckleRevitPlugin.UI
+{
+ ///
+ ///
+ ///
+ public class SelectionChangedToSpeckleStreamConverter : IEventArgsConverter
+ {
+ public object Convert(object value, object parameter)
+ {
+ var items = ((SelectionChangedEventArgs) value).AddedItems;
+ return items.Count > 0 ? items[0] : null;
+ }
+ }
+
+ ///
+ /// Inverse of the BooleanToVisibilityConverter. Hides elements when Boolean is True.
+ ///
+ public class BooleanToVisibilityInverseConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value != null && (bool)value ? Visibility.Collapsed : Visibility.Visible;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ ///
+ ///
+ ///
+ public class BooleanInverterConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value == null || !(bool) value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml
new file mode 100644
index 0000000..e7e4881
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs
new file mode 100644
index 0000000..aa4055c
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelector.xaml.cs
@@ -0,0 +1,13 @@
+namespace SpeckleRevitPlugin.UI.ElementSelector
+{
+ ///
+ /// Interaction logic for ElementSelector.xaml
+ ///
+ public partial class ElementSelector
+ {
+ public ElementSelector()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs
new file mode 100644
index 0000000..005e8c3
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorModel.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SpeckleRevitPlugin.UI.ElementSelector
+{
+ public class ElementSelectorModel
+ {
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs
new file mode 100644
index 0000000..d4c81ba
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/ElementSelector/ElementSelectorViewModel.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using GalaSoft.MvvmLight;
+
+namespace SpeckleRevitPlugin.UI.ElementSelector
+{
+ public class ElementSelectorViewModel : ViewModelBase
+ {
+
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/InputControl.xaml b/SpeckleRevitPlugin/UI/InputControl.xaml
new file mode 100644
index 0000000..a938514
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/InputControl.xaml
@@ -0,0 +1,140 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpeckleRevitPlugin/UI/InputControl.xaml.cs b/SpeckleRevitPlugin/UI/InputControl.xaml.cs
new file mode 100644
index 0000000..f250dd2
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/InputControl.xaml.cs
@@ -0,0 +1,13 @@
+namespace SpeckleRevitPlugin.UI
+{
+ ///
+ /// Interaction logic for StreamSelectorView.xaml
+ ///
+ public partial class InputControl
+ {
+ public InputControl()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/InputModel.cs b/SpeckleRevitPlugin/UI/InputModel.cs
new file mode 100644
index 0000000..7855b95
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/InputModel.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+using System.Linq;
+using SpeckleCore;
+using SpeckleRevitPlugin.Classes;
+
+namespace SpeckleRevitPlugin.UI
+{
+ public class InputModel
+ {
+
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/InputViewModel.cs b/SpeckleRevitPlugin/UI/InputViewModel.cs
new file mode 100644
index 0000000..e785d7d
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/InputViewModel.cs
@@ -0,0 +1,126 @@
+#region Namespaces
+
+using System;
+using Autodesk.Revit.DB;
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using GalaSoft.MvvmLight.Messaging;
+using SpeckleRevitPlugin.Tools.WallTool;
+using SpeckleRevitPlugin.UI.BooleanSelector;
+using SpeckleRevitPlugin.UI.ElementSelector;
+using SpeckleRevitPlugin.UI.StreamSelector;
+
+#endregion
+
+namespace SpeckleRevitPlugin.UI
+{
+ public class InputViewModel : ViewModelBase
+ {
+ #region Properties
+
+ public InputModel Model { get; set; }
+ public RelayCommand ToggleMenu { get; set; }
+ public RelayCommand Delete { get; set; }
+ public RelayCommand ToggleSpeckleInput { get; set; }
+
+ private InputWrapper _input;
+ public InputWrapper Input
+ {
+ get { return _input; }
+ set { _input = value; RaisePropertyChanged(() => Input); }
+ }
+
+ private bool _showMenu;
+ public bool ShowMenu
+ {
+ get { return _showMenu; }
+ set { _showMenu = value; RaisePropertyChanged(() => ShowMenu); }
+ }
+
+ private bool _showInputs;
+ public bool ShowInputs
+ {
+ get { return _showInputs; }
+ set { _showInputs = value; RaisePropertyChanged(() => ShowInputs); }
+ }
+
+ private bool _isSpeckleInput;
+ public bool IsSpeckleInput
+ {
+ get { return _isSpeckleInput; }
+ set { _isSpeckleInput = value; RaisePropertyChanged(() => IsSpeckleInput); }
+ }
+
+ private ViewModelBase _selectedDataInput;
+ public ViewModelBase SelectedDataInput
+ {
+ get { return _selectedDataInput; }
+ set { _selectedDataInput = value; RaisePropertyChanged(() => SelectedDataInput); }
+ }
+
+ #endregion
+
+ public InputViewModel(InputModel model, InputWrapper input)
+ {
+ Model = model;
+ Input = input;
+
+ ToggleMenu = new RelayCommand(OnToggleMenu);
+ Delete = new RelayCommand(OnDelete);
+ ToggleSpeckleInput = new RelayCommand(OnToggleSpeckleInput);
+
+ // (Konrad) Set defaults. In case that input doesn't accept
+ // locally defined data we need to ensure that Speckle Stream
+ // input is shown at all times.
+ if (!Input.AcceptsLocalData) IsSpeckleInput = true;
+ OnToggleSpeckleInput(IsSpeckleInput);
+ }
+
+ private void OnDelete()
+ {
+ Messenger.Default.Send(new InputDeleted { InputViewModel = this });
+ ShowMenu = false;
+ }
+
+ private void OnToggleMenu()
+ {
+ ShowMenu = !ShowMenu;
+ }
+
+ #region Events
+
+ private void OnToggleSpeckleInput(bool isChecked)
+ {
+ if (isChecked)
+ {
+ // toggle speckle stream on
+ SelectedDataInput = new StreamSelectorViewModel(new StreamSelectorModel());
+ }
+ else
+ {
+ // toggle local data input
+ switch (Input.DataType)
+ {
+ case LocalDataType.None:
+ break;
+ case LocalDataType.Element:
+ SelectedDataInput = new ElementSelectorViewModel();
+ break;
+ case LocalDataType.Integer:
+ break;
+ case LocalDataType.Boolean:
+ SelectedDataInput = new BooleanSelectorViewModel(new BooleanSelectorModel());
+ break;
+ case LocalDataType.String:
+ break;
+ case LocalDataType.Double:
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/InputWrapper.cs b/SpeckleRevitPlugin/UI/InputWrapper.cs
new file mode 100644
index 0000000..64631e7
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/InputWrapper.cs
@@ -0,0 +1,90 @@
+using System;
+using System.ComponentModel;
+using Autodesk.Revit.DB;
+
+namespace SpeckleRevitPlugin.UI
+{
+ public sealed class InputWrapper : INotifyPropertyChanged
+ {
+ public string Name { get; set; }
+ public int Id { get; set; }
+ public bool AcceptsLocalData { get; set; }
+ public LocalDataType DataType { get; set; }
+ public bool IsInstance { get; set; }
+ public bool IsRequired { get; set; }
+
+ public InputWrapper()
+ {
+ }
+
+ public InputWrapper(Parameter p, bool isInstance = true, bool isRequired = false)
+ {
+ Name = p.Definition.Name;
+ Id = p.Id.IntegerValue;
+ IsInstance = isInstance;
+ IsRequired = isRequired;
+ DataType = StorageTypeToDataType(p);
+
+ // (Konrad) We would make an assumption that a parameter input
+ // always accepts a local input: int, bool, element etc.
+ AcceptsLocalData = true;
+ }
+
+ public override bool Equals(object obj)
+ {
+ var item = obj as InputWrapper;
+ return item != null && Id.Equals(item.Id);
+ }
+
+ public override int GetHashCode()
+ {
+ return Id.GetHashCode();
+ }
+
+ #region Methods
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ internal static LocalDataType StorageTypeToDataType(Parameter p)
+ {
+ switch (p.StorageType)
+ {
+ case StorageType.None:
+ return LocalDataType.None;
+ case StorageType.Integer:
+ return p.Definition.ParameterType == ParameterType.YesNo
+ ? LocalDataType.Boolean
+ : LocalDataType.Integer;
+ case StorageType.Double:
+ return LocalDataType.Double;
+ case StorageType.String:
+ return LocalDataType.String;
+ case StorageType.ElementId:
+ return LocalDataType.Element;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(p.StorageType), p.StorageType, null);
+ }
+ }
+
+ #endregion
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ private void RaisePropertyChanged(string propname)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
+ }
+ }
+
+ public enum LocalDataType
+ {
+ None,
+ Element,
+ Integer,
+ Boolean,
+ String,
+ Double
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml
new file mode 100644
index 0000000..e8a0150
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs
new file mode 100644
index 0000000..8191c93
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelector.xaml.cs
@@ -0,0 +1,13 @@
+namespace SpeckleRevitPlugin.UI.StreamSelector
+{
+ ///
+ /// Interaction logic for StreamSelector.xaml
+ ///
+ public partial class StreamSelector
+ {
+ public StreamSelector()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs
new file mode 100644
index 0000000..bd6d74d
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorModel.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using SpeckleCore;
+using SpeckleRevitPlugin.Classes;
+
+namespace SpeckleRevitPlugin.UI.StreamSelector
+{
+ public class StreamSelectorModel
+ {
+ ///
+ ///
+ ///
+ ///
+ public List GetStreams()
+ {
+ var streams = FormMainDock.Store.UserClients.Where(x => x.GetRole() == ClientRole.Receiver)
+ .Cast()
+ .Select(x => x.Client.Stream)
+ .OrderBy(x => x.Name)
+ .ToList();
+ return streams.Any()
+ ? streams
+ : new List();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public List GetLayers(SpeckleStream stream)
+ {
+ return stream.Layers;
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs
new file mode 100644
index 0000000..e81b9ff
--- /dev/null
+++ b/SpeckleRevitPlugin/UI/StreamSelector/StreamSelectorViewModel.cs
@@ -0,0 +1,72 @@
+#region Namespaces
+
+using System.Collections.Generic;
+using GalaSoft.MvvmLight;
+using GalaSoft.MvvmLight.Command;
+using SpeckleCore;
+
+#endregion
+
+namespace SpeckleRevitPlugin.UI.StreamSelector
+{
+ public class StreamSelectorViewModel : ViewModelBase
+ {
+ #region Properties
+
+ public StreamSelectorModel Model { get; set; }
+ public RelayCommand StreamSelected { get; set; }
+
+ private List _streams = new List();
+ public List Streams
+ {
+ get { return _streams; }
+ set { _streams = value; RaisePropertyChanged(() => Streams); }
+ }
+
+ private SpeckleStream _selectedStream;
+ public SpeckleStream SelectedStream
+ {
+ get { return _selectedStream; }
+ set { _selectedStream = value; RaisePropertyChanged(() => SelectedStream); }
+ }
+
+ private List _layers = new List();
+ public List Layers
+ {
+ get { return _layers; }
+ set { _layers = value; RaisePropertyChanged(() => Layers); }
+ }
+
+ private Layer _selectedLayer;
+ public Layer SelectedLayer
+ {
+ get { return _selectedLayer; }
+ set { _selectedLayer = value; RaisePropertyChanged(() => SelectedLayer); }
+ }
+
+ #endregion
+
+ public StreamSelectorViewModel(StreamSelectorModel model)
+ {
+ Model = model;
+ Streams = Model.GetStreams();
+ StreamSelected = new RelayCommand(OnStreamSelected);
+ }
+
+ #region Event Handlers
+
+ ///
+ ///
+ ///
+ ///
+ private void OnStreamSelected(SpeckleStream stream)
+ {
+ // (Konrad) It's possible for this to be null since we can hide the comboboxes
+ if (stream == null) return;
+
+ Layers = Model.GetLayers(stream);
+ }
+
+ #endregion
+ }
+}
diff --git a/SpeckleRevitPlugin/UI/form_Main.xaml.cs b/SpeckleRevitPlugin/UI/form_Main.xaml.cs
index 92647aa..55da6c4 100644
--- a/SpeckleRevitPlugin/UI/form_Main.xaml.cs
+++ b/SpeckleRevitPlugin/UI/form_Main.xaml.cs
@@ -1,18 +1,6 @@
#region Namespaces
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
#endregion
namespace SpeckleRevitPlugin
@@ -20,17 +8,17 @@ namespace SpeckleRevitPlugin
///
/// Interaction logic for form_Main.xaml
///
- public partial class form_Main : Window
+ public partial class form_Main
{
- private SettingsHelper _s;
+ //private SettingsHelper _s;
- public form_Main(SettingsHelper s)
+ public form_Main()
{
InitializeComponent();
Label_Version.Content = "v." + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
//widen scope
- _s = s;
+ //_s = s;
}
#region Form Events
@@ -41,7 +29,7 @@ public form_Main(SettingsHelper s)
///
private void Button_Close_Click(object sender, RoutedEventArgs e)
{
- this.Close();
+ Close();
}
///
@@ -61,8 +49,7 @@ private void Button_Run_Click(object sender, RoutedEventArgs e)
///
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
- if (e.ChangedButton == MouseButton.Left)
- this.DragMove();
+ if (e.ChangedButton == MouseButton.Left) DragMove();
}
#endregion
}
diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml b/SpeckleRevitPlugin/UI/form_MainDock.xaml
index f60d2b1..b24bb8e 100644
--- a/SpeckleRevitPlugin/UI/form_MainDock.xaml
+++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml
@@ -1,15 +1,12 @@
-
-
diff --git a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs
index 6c32071..302808e 100644
--- a/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs
+++ b/SpeckleRevitPlugin/UI/form_MainDock.xaml.cs
@@ -1,79 +1,71 @@
#region Namespaces
using System;
-using System.Collections.Generic;
-using System.Linq;
+using System.Diagnostics;
+using System.IO;
using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
-
-using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
-using Autodesk.Revit.UI.Events;
using CefSharp;
-using Visibility = System.Windows.Visibility;
-using Path = System.IO.Path;
-using System.IO;
-using System.Diagnostics;
+using SpeckleRevitPlugin.Classes;
+using SpeckleRevitPlugin.Entry;
+
#endregion
-namespace SpeckleRevitPlugin
+namespace SpeckleRevitPlugin.UI
{
///
/// Interaction logic for form_MainDock.xaml
///
- public partial class form_MainDock : Page, IDockablePaneProvider
+ public partial class FormMainDock : IDockablePaneProvider
{
- private ExternalEvent _extEvent;
- private ExtCmdModeless _handler = new ExtCmdModeless();
+ public static Interop Store;
///
/// Main Dockable window
///
- public form_MainDock()
+ public FormMainDock()
{
InitializeCef();
InitializeComponent();
InitializeChromium();
- _extEvent = ExternalEvent.Create(_handler);
- }
+ // initialise one store
+ Store = new Interop(Browser);
- void InitializeCef()
- {
+ // make them talk together
+ Browser.RegisterAsyncJsObject("Interop", Store);
+
+ //_extEvent = ExternalEvent.Create(_handler);
+ }
+ private static void InitializeCef()
+ {
Cef.EnableHighDPISupport();
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var assemblyPath = Path.GetDirectoryName(assemblyLocation);
+ if (assemblyPath == null) return;
+
var pathSubprocess = Path.Combine(assemblyPath, "CefSharp.BrowserSubprocess.exe");
CefSharpSettings.LegacyJavascriptBindingEnabled = true;
var settings = new CefSettings
{
LogSeverity = LogSeverity.Verbose,
LogFile = "ceflog.txt",
- BrowserSubprocessPath = pathSubprocess
- };
-
- // Initialize cef with the provided settings
+ BrowserSubprocessPath = pathSubprocess,
+ RemoteDebuggingPort = 8088
+ };
+ // Initialize cef with the provided settings
+ settings.CefCommandLineArgs.Add("allow-file-access-from-files", "1");
+ settings.CefCommandLineArgs.Add("disable-web-security", "1");
Cef.Initialize(settings);
-
}
public void InitializeChromium()
{
#if DEBUG
-
- Browser.Address = @"http://localhost:9090/";
+ Browser.Address = @"http://localhost:2020/";
#else
var path = Directory.GetParent(Assembly.GetExecutingAssembly().Location);
@@ -94,33 +86,48 @@ public void InitializeChromium()
FileAccessFromFileUrls = CefState.Enabled,
UniversalAccessFromFileUrls = CefState.Enabled
};
-
-
- //Browser.Dock = DockStyle.Fill;
}
#region Form Events
+
///
- /// Safely raise an external command transaction event (for modeless)
+ /// Can't show the Dev Tools panel until window finishes initialization.
///
///
///
- private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs e)
+ private void Form_MainDock_OnLoaded(object sender, RoutedEventArgs e)
+ {
+#if DEBUG
+ //Browser.ShowDevTools();
+#endif
+ }
+
+ ///
+ /// Safely raise an external command transaction event (for modeless)
+ ///
+ ///
+ ///
+ private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs args)
{
try
{
//textBox_Comments.Text;
- AppMain.Settings.CommandType = EnumCommandType.Command1;
+ //AppMain.Settings.CommandType = EnumCommandType.Command1;
// Safe Update
- _extEvent.Raise();
+ //_extEvent.Raise();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
}
- catch { }
}
+
#endregion
#region Dockable Pane
+
///
/// IDockablePaneProvider Implementation
///
@@ -128,10 +135,13 @@ private void Button_ExampleTransaction_Click(object sender, RoutedEventArgs e)
public void SetupDockablePane(DockablePaneProviderData data)
{
data.FrameworkElement = this;
- DockablePaneProviderData d = new DockablePaneProviderData();
- data.InitialState = new DockablePaneState();
- data.InitialState.DockPosition = DockPosition.Right;
+ var unused = new DockablePaneProviderData();
+ data.InitialState = new DockablePaneState
+ {
+ DockPosition = DockPosition.Right
+ };
}
+
#endregion
}
}
diff --git a/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs
new file mode 100644
index 0000000..959089d
--- /dev/null
+++ b/SpeckleRevitPlugin/Utilities/BinaryFormatterUtilities.cs
@@ -0,0 +1,96 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+#endregion
+
+namespace SpeckleRevitPlugin.Utilities
+{
+ public static class BinaryFormatterUtilities
+ {
+ public static T Read(byte[] content, Assembly currentAssembly) where T : new()
+ {
+ var result = default(T);
+
+ try
+ {
+ using (var ms = new MemoryStream())
+ {
+ ms.Write(content, 0, content.Length);
+ ms.Seek(0, SeekOrigin.Begin);
+ var bf = new BinaryFormatter
+ {
+ Binder = new SearchAssembliesBinder(currentAssembly, true)
+ };
+ result = (T)bf.Deserialize(ms);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+
+ return result;
+ }
+
+ //public static void Write(T obj, string filename)
+ //{
+ // FileStream fileStream = new FileStream(filename, FileMode.Create);
+ // BinaryFormatter formatter = new BinaryFormatter();
+ // try
+ // {
+ // formatter.Serialize(fileStream, obj);
+ // }
+ // finally
+ // {
+ // fileStream.Close();
+ // }
+ //}
+ }
+
+ internal sealed class SearchAssembliesBinder : SerializationBinder
+ {
+ private readonly bool _searchInDlls;
+ private readonly Assembly _currentAssembly;
+
+ public SearchAssembliesBinder(Assembly currentAssembly, bool searchInDlls)
+ {
+ _currentAssembly = currentAssembly;
+ _searchInDlls = searchInDlls;
+ }
+
+ public override Type BindToType(string assemblyName, string typeName)
+ {
+ var assemblyNames = new List
+ {
+ _currentAssembly.GetName()
+ };
+
+ if (_searchInDlls)
+ {
+ assemblyNames.AddRange(_currentAssembly.GetReferencedAssemblies());
+ }
+
+ foreach (var an in assemblyNames)
+ {
+ var typeToDeserialize = GetTypeToDeserialize(typeName, an);
+ if (typeToDeserialize != null)
+ {
+ return typeToDeserialize; // found
+ }
+ }
+
+ return null; // not found
+ }
+
+ private static Type GetTypeToDeserialize(string typeName, AssemblyName an)
+ {
+ var fullTypeName = $"{typeName}, {an.FullName}";
+ var typeToDeserialize = Type.GetType(fullTypeName);
+ return typeToDeserialize;
+ }
+ }
+}
diff --git a/SpeckleRevitPlugin/Utilities/JsonUtilities.cs b/SpeckleRevitPlugin/Utilities/JsonUtilities.cs
new file mode 100644
index 0000000..68be954
--- /dev/null
+++ b/SpeckleRevitPlugin/Utilities/JsonUtilities.cs
@@ -0,0 +1,48 @@
+using System;
+using Newtonsoft.Json;
+
+namespace SpeckleRevitPlugin.Utilities
+{
+ public static class JsonUtilities
+ {
+ public static JsonSerializerSettings Settings { get; set; }
+
+ static JsonUtilities()
+ {
+ Settings = new JsonSerializerSettings
+ {
+ NullValueHandling = NullValueHandling.Ignore,
+ MissingMemberHandling = MissingMemberHandling.Ignore,
+ CheckAdditionalContent = true,
+ Formatting = Formatting.Indented
+ };
+ }
+
+ public static T Deserialize(string json) where T : new()
+ {
+ try
+ {
+ var obj = JsonConvert.DeserializeObject(json, Settings);
+ return obj;
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Failed deserializing Json: " + e.Message);
+ }
+ }
+
+ public static string Serialize(object obj)
+ {
+ try
+ {
+ var json = JsonConvert.SerializeObject(obj, Settings);
+ return json;
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Failed serializing Json: " + e.Message);
+ }
+ }
+
+ }
+}
diff --git a/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs
new file mode 100644
index 0000000..9725e9d
--- /dev/null
+++ b/SpeckleRevitPlugin/Utilities/SchemaUtilities.cs
@@ -0,0 +1,146 @@
+#region Namespaces
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Autodesk.Revit.DB;
+using Autodesk.Revit.DB.ExtensibleStorage;
+#endregion
+
+namespace SpeckleRevitPlugin.Utilities
+{
+ public static class SchemaUtilities
+ {
+ ///
+ /// Retrieves Schema by its name.
+ ///
+ /// Schema name.
+ ///
+ public static Schema GetSchema(string schemaName)
+ {
+ var schemas = Schema.ListSchemas();
+ if (schemas == null || !schemas.Any()) return null;
+ return schemas.FirstOrDefault(s => s.SchemaName == schemaName);
+ }
+
+ ///
+ /// Checks if Schema exists in Document.
+ ///
+ /// Schema name.
+ ///
+ public static bool SchemaExist(string schemaName)
+ {
+ return GetSchema(schemaName) != null;
+ }
+
+ ///
+ /// Creates Speckle Schema.
+ ///
+ ///
+ public static Schema CreateSchema()
+ {
+ var schemaGuid = new Guid(Properties.Resources.SchemaId);
+ var schemaBuilder = new SchemaBuilder(schemaGuid);
+ schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
+ schemaBuilder.SetWriteAccessLevel(AccessLevel.Application);
+
+ // (Konrad) These values are set in addin manifest.
+ schemaBuilder.SetApplicationGUID(new Guid("0a03f52e-0ee0-4c19-82c4-8181626ec816"));
+ schemaBuilder.SetVendorId("Speckle");
+
+ schemaBuilder.SetSchemaName(Properties.Resources.SchemaName);
+ schemaBuilder.SetDocumentation("Speckle schema.");
+ schemaBuilder.AddMapField("senders", typeof(string), typeof(string));
+ schemaBuilder.AddMapField("receivers", typeof(string), typeof(string));
+ var schema = schemaBuilder.Finish();
+
+ return schema;
+ }
+
+ ///
+ /// Adds new Speckle Schema Entity to Element.
+ ///
+ /// Schema
+ /// Element
+ /// Filed name
+ /// Field value
+ public static void AddSchemaEntity(Schema s, Element e, string fName, Dictionary fValue)
+ {
+ if (s == null)
+ {
+ throw new NullReferenceException("schema");
+ }
+ if (e == null)
+ {
+ throw new NullReferenceException("element");
+ }
+ if (string.IsNullOrEmpty(fName))
+ {
+ throw new NullReferenceException("fieldName");
+ }
+
+ try
+ {
+ var entity = new Entity(s);
+ var settingsField = s.GetField(fName);
+ entity.Set>(settingsField, fValue);
+
+ e.SetEntity(entity);
+ }
+ catch (Exception ex)
+ {
+ Debug.Write(ex.Message);
+ }
+ }
+
+ ///
+ /// Speckle Schema will always be stored on this Element,
+ /// so now we have a utility to get to it faster.
+ ///
+ /// Revit Document
+ ///
+ public static Element GetProjectInfo(Document doc)
+ {
+ var pInfo = new FilteredElementCollector(doc)
+ .OfClass(typeof(ProjectInfo))
+ .FirstElement();
+ return pInfo;
+ }
+
+ ///
+ /// Updates existing Speckle Schema Entity on given Element.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void UpdateSchemaEntity(Schema s, Element e, string fName, Dictionary fValue)
+ {
+ if (s == null)
+ {
+ throw new NullReferenceException("schema");
+ }
+ if (e == null)
+ {
+ throw new NullReferenceException("element");
+ }
+ if (string.IsNullOrEmpty(fName))
+ {
+ throw new NullReferenceException("fieldName");
+ }
+
+ try
+ {
+ var entity = e.GetEntity(s);
+ var field = s.GetField(fName);
+ entity.Set>(field, fValue);
+
+ e.SetEntity(entity);
+ }
+ catch(Exception ex)
+ {
+ Debug.Write(ex.Message);
+ }
+ }
+ }
+}
diff --git a/SpeckleView b/SpeckleView
index 8f4850c..29f4379 160000
--- a/SpeckleView
+++ b/SpeckleView
@@ -1 +1 @@
-Subproject commit 8f4850c297f79ea4ad4165568d760b145c810c32
+Subproject commit 29f4379c354b8718edc4a8f91de96247d4259e87
diff --git a/UpgradeLog.htm b/UpgradeLog.htm
new file mode 100644
index 0000000..3d964ba
Binary files /dev/null and b/UpgradeLog.htm differ