From 71e51ea95a1adb3bad8d0f441bade0f1e71f5f42 Mon Sep 17 00:00:00 2001 From: Andrew Le Bihan Date: Fri, 2 Aug 2019 20:14:17 +0300 Subject: [PATCH] First commit! --- .gitattributes | 27 + .gitignore | 175 ++ ILBPRhWrapper.h | 41 + IRhRdkRegisteredProperty.h | 313 +++ IRhRdkRegisteredPropertyManager.h | 83 + LBPBase64.cpp | 170 ++ LBPBase64.h | 28 + LBPBitmapPreview.cpp | 279 +++ LBPBitmapPreview.h | 61 + LBPBuffer.cpp | 289 +++ LBPBuffer.h | 104 + LBPColor.cpp | 120 + LBPColor.h | 39 + LBPException.h | 34 + LBPFileMgr2.cpp | 47 + LBPFileMgr2.h | 19 + LBPLibUtilities.cpp | 314 +++ LBPLibUtilities.h | 44 + LBPPidlMgr.cpp | 244 ++ LBPPidlMgr.h | 44 + LBPPreviewingFileDialog.cpp | 389 +++ LBPPreviewingFileDialog.h | 72 + LBPRhException.h | 27 + LBPRhObjectSelection.cpp | 301 +++ LBPRhObjectSelection.h | 63 + LBPRhObjectWrapper.h | 1060 ++++++++ LBPRh_XMLUserData.cpp | 109 + LBPRh_XMLUserData.h | 212 ++ LBPSimpleByteArray.h | 83 + LBPString.cpp | 2636 ++++++++++++++++++++ LBPString.h | 590 +++++ LBPTime.cpp | 80 + LBPTime.h | 37 + LBPUnicodeTextFile.cpp | 243 ++ LBPUnicodeTextFile.h | 55 + LBP_CRC32.cpp | 137 + LBP_CRC32.h | 51 + LBP_UTF.cpp | 577 +++++ LBP_UTF.h | 70 + LBP_UUID.cpp | 330 +++ LBP_UUID.h | 60 + LBP_XML.cpp | 3462 ++++++++++++++++++++++++++ LBP_XML.h | 649 +++++ ON_SimpleMap.h | 284 +++ RPC.def | 6 + RPC.rc | 162 ++ RPC.vcxproj | 226 ++ RPC.vcxproj.filters | 343 +++ RPCApp.cpp | 72 + RPCApp.h | 24 + RPCPlugIn.cpp | 210 ++ RPCPlugIn.h | 81 + Resource.h | 26 + RhRdkRegisteredPropertiesEventSink.h | 65 + RpcAddCmd.cpp | 116 + RpcAddCmd.h | 16 + RpcAnimationFrameRrp.cpp | 43 + RpcAnimationFrameRrp.h | 49 + RpcClient.cpp | 304 +++ RpcClient.h | 48 + RpcCommand.cpp | 9 + RpcCommand.h | 25 + RpcCrmProvider.cpp | 102 + RpcCrmProvider.h | 33 + RpcDefinitions.h | 7 + RpcDocument.cpp | 93 + RpcDocument.h | 25 + RpcDragDropHandler.cpp | 111 + RpcDragDropHandler.h | 17 + RpcEditCmd.cpp | 110 + RpcEditCmd.h | 15 + RpcEditDlg.cpp | 57 + RpcEditDlg.h | 22 + RpcEventMachine.cpp | 73 + RpcEventMachine.h | 33 + RpcEventSink.cpp | 27 + RpcEventSink.h | 20 + RpcEventWatcher.cpp | 60 + RpcEventWatcher.h | 26 + RpcFileDlg.cpp | 109 + RpcFileDlg.h | 43 + RpcInstance.cpp | 409 +++ RpcInstance.h | 70 + RpcMains.cpp | 204 ++ RpcMains.h | 48 + RpcObject.cpp | 118 + RpcObject.h | 40 + RpcObjectStorageStrings.h | 12 + RpcObjectUserData.cpp | 156 ++ RpcObjectUserData.h | 36 + RpcObjectUserDataWrapper.cpp | 24 + RpcObjectUserDataWrapper.h | 16 + RpcPropertiesDlg.cpp | 283 +++ RpcPropertiesDlg.h | 55 + RpcRdkPlugIn.cpp | 59 + RpcRdkPlugIn.h | 20 + RpcRenderMeshBuilder.cpp | 448 ++++ RpcRenderMeshBuilder.h | 27 + RpcSelectDlg.cpp | 57 + RpcSelectDlg.h | 24 + RpcSetAnimationFrameCmd.cpp | 41 + RpcSetAnimationFrameCmd.h | 13 + RpcUtilities.cpp | 288 +++ RpcUtilities.h | 20 + SDK/inc/RPCapi.h | 3177 +++++++++++++++++++++++ SDK/inc/RPCapiEx.h | 203 ++ SDK/inc/RPCapiML.h | 80 + SDK/inc/RPCapiMem.h | 29 + SDK/inc/RPCapiSI.h | 244 ++ SDK/inc/RPCcontrol.h | 373 +++ SDK/lib/Debug/RPCJPeg.dll | 3 + SDK/lib/Debug/RPCJPeg.pdb | 3 + SDK/lib/Debug/RPCPopulate.pdb | 3 + SDK/lib/Debug/RPCapi.dll | 3 + SDK/lib/Debug/RPCapi.pdb | 3 + SDK/lib/Debug/RPCcls.dll | 3 + SDK/lib/Debug/RPCcls.pdb | 3 + SDK/lib/Debug/RPCpopulate.dll | 3 + SDK/lib/Release/RPCJPeg.dll | 3 + SDK/lib/Release/RPCJPeg.pdb | 3 + SDK/lib/Release/RPCPopulate.pdb | 3 + SDK/lib/Release/RPCapi.dll | 3 + SDK/lib/Release/RPCapi.pdb | 3 + SDK/lib/Release/RPCcls.dll | 3 + SDK/lib/Release/RPCcls.pdb | 3 + SDK/lib/Release/RPCpopulate.dll | 3 + res/RPC.ico | Bin 0 -> 34293 bytes res/RPC.rc2 | 13 + res/rpc_acm.ico | Bin 0 -> 26448 bytes stdafx.cpp | 7 + stdafx.h | 117 + targetver.h | 8 + 132 files changed, 23627 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 ILBPRhWrapper.h create mode 100644 IRhRdkRegisteredProperty.h create mode 100644 IRhRdkRegisteredPropertyManager.h create mode 100644 LBPBase64.cpp create mode 100644 LBPBase64.h create mode 100644 LBPBitmapPreview.cpp create mode 100644 LBPBitmapPreview.h create mode 100644 LBPBuffer.cpp create mode 100644 LBPBuffer.h create mode 100644 LBPColor.cpp create mode 100644 LBPColor.h create mode 100644 LBPException.h create mode 100644 LBPFileMgr2.cpp create mode 100644 LBPFileMgr2.h create mode 100644 LBPLibUtilities.cpp create mode 100644 LBPLibUtilities.h create mode 100644 LBPPidlMgr.cpp create mode 100644 LBPPidlMgr.h create mode 100644 LBPPreviewingFileDialog.cpp create mode 100644 LBPPreviewingFileDialog.h create mode 100644 LBPRhException.h create mode 100644 LBPRhObjectSelection.cpp create mode 100644 LBPRhObjectSelection.h create mode 100644 LBPRhObjectWrapper.h create mode 100644 LBPRh_XMLUserData.cpp create mode 100644 LBPRh_XMLUserData.h create mode 100644 LBPSimpleByteArray.h create mode 100644 LBPString.cpp create mode 100644 LBPString.h create mode 100644 LBPTime.cpp create mode 100644 LBPTime.h create mode 100644 LBPUnicodeTextFile.cpp create mode 100644 LBPUnicodeTextFile.h create mode 100644 LBP_CRC32.cpp create mode 100644 LBP_CRC32.h create mode 100644 LBP_UTF.cpp create mode 100644 LBP_UTF.h create mode 100644 LBP_UUID.cpp create mode 100644 LBP_UUID.h create mode 100644 LBP_XML.cpp create mode 100644 LBP_XML.h create mode 100644 ON_SimpleMap.h create mode 100644 RPC.def create mode 100644 RPC.rc create mode 100644 RPC.vcxproj create mode 100644 RPC.vcxproj.filters create mode 100644 RPCApp.cpp create mode 100644 RPCApp.h create mode 100644 RPCPlugIn.cpp create mode 100644 RPCPlugIn.h create mode 100644 Resource.h create mode 100644 RhRdkRegisteredPropertiesEventSink.h create mode 100644 RpcAddCmd.cpp create mode 100644 RpcAddCmd.h create mode 100644 RpcAnimationFrameRrp.cpp create mode 100644 RpcAnimationFrameRrp.h create mode 100644 RpcClient.cpp create mode 100644 RpcClient.h create mode 100644 RpcCommand.cpp create mode 100644 RpcCommand.h create mode 100644 RpcCrmProvider.cpp create mode 100644 RpcCrmProvider.h create mode 100644 RpcDefinitions.h create mode 100644 RpcDocument.cpp create mode 100644 RpcDocument.h create mode 100644 RpcDragDropHandler.cpp create mode 100644 RpcDragDropHandler.h create mode 100644 RpcEditCmd.cpp create mode 100644 RpcEditCmd.h create mode 100644 RpcEditDlg.cpp create mode 100644 RpcEditDlg.h create mode 100644 RpcEventMachine.cpp create mode 100644 RpcEventMachine.h create mode 100644 RpcEventSink.cpp create mode 100644 RpcEventSink.h create mode 100644 RpcEventWatcher.cpp create mode 100644 RpcEventWatcher.h create mode 100644 RpcFileDlg.cpp create mode 100644 RpcFileDlg.h create mode 100644 RpcInstance.cpp create mode 100644 RpcInstance.h create mode 100644 RpcMains.cpp create mode 100644 RpcMains.h create mode 100644 RpcObject.cpp create mode 100644 RpcObject.h create mode 100644 RpcObjectStorageStrings.h create mode 100644 RpcObjectUserData.cpp create mode 100644 RpcObjectUserData.h create mode 100644 RpcObjectUserDataWrapper.cpp create mode 100644 RpcObjectUserDataWrapper.h create mode 100644 RpcPropertiesDlg.cpp create mode 100644 RpcPropertiesDlg.h create mode 100644 RpcRdkPlugIn.cpp create mode 100644 RpcRdkPlugIn.h create mode 100644 RpcRenderMeshBuilder.cpp create mode 100644 RpcRenderMeshBuilder.h create mode 100644 RpcSelectDlg.cpp create mode 100644 RpcSelectDlg.h create mode 100644 RpcSetAnimationFrameCmd.cpp create mode 100644 RpcSetAnimationFrameCmd.h create mode 100644 RpcUtilities.cpp create mode 100644 RpcUtilities.h create mode 100644 SDK/inc/RPCapi.h create mode 100644 SDK/inc/RPCapiEx.h create mode 100644 SDK/inc/RPCapiML.h create mode 100644 SDK/inc/RPCapiMem.h create mode 100644 SDK/inc/RPCapiSI.h create mode 100644 SDK/inc/RPCcontrol.h create mode 100644 SDK/lib/Debug/RPCJPeg.dll create mode 100644 SDK/lib/Debug/RPCJPeg.pdb create mode 100644 SDK/lib/Debug/RPCPopulate.pdb create mode 100644 SDK/lib/Debug/RPCapi.dll create mode 100644 SDK/lib/Debug/RPCapi.pdb create mode 100644 SDK/lib/Debug/RPCcls.dll create mode 100644 SDK/lib/Debug/RPCcls.pdb create mode 100644 SDK/lib/Debug/RPCpopulate.dll create mode 100644 SDK/lib/Release/RPCJPeg.dll create mode 100644 SDK/lib/Release/RPCJPeg.pdb create mode 100644 SDK/lib/Release/RPCPopulate.pdb create mode 100644 SDK/lib/Release/RPCapi.dll create mode 100644 SDK/lib/Release/RPCapi.pdb create mode 100644 SDK/lib/Release/RPCcls.dll create mode 100644 SDK/lib/Release/RPCcls.pdb create mode 100644 SDK/lib/Release/RPCpopulate.dll create mode 100644 res/RPC.ico create mode 100644 res/RPC.rc2 create mode 100644 res/rpc_acm.ico create mode 100644 stdafx.cpp create mode 100644 stdafx.h create mode 100644 targetver.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..92ea2f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,27 @@ +* text=auto +*.m text +*.mm text +*.sh eol=lf +*.sln eol=crlf +*.cmd eol=crlf +*.bat eol=crlf +*.Targets diff +*.dll filter=lfs diff=lfs merge=lfs -text +*.exe filter=lfs diff=lfs merge=lfs -text +*.lib filter=lfs diff=lfs merge=lfs -text +*.Lib filter=lfs diff=lfs merge=lfs -text +*.pdb filter=lfs diff=lfs merge=lfs -text +*.dylib filter=lfs diff=lfs merge=lfs -text +*.rhp filter=lfs diff=lfs merge=lfs -text +*.cubin filter=lfs diff=lfs merge=lfs -text +*.chm filter=lfs diff=lfs merge=lfs -text +*.ai filter=lfs diff=lfs merge=lfs -text +*.psd filter=lfs diff=lfs merge=lfs -text +*.doxdb filter=lfs diff=lfs merge=lfs -text +*.msm filter=lfs diff=lfs merge=lfs -text +*.aqt filter=lfs diff=lfs merge=lfs -text +*.DLL filter=lfs diff=lfs merge=lfs -text +*.tlb filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.a filter=lfs diff=lfs merge=lfs -text + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e388db --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj +*.msm +*.aps + +# Precompiled Headers +*.gch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll +*.rhp +*.pdb +*.pdb.bak + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Visual Studio temp files +*.suo +*.sdf +*.userprefs +*.exe.config +*.exe.manifest +*.opendb +*.opensdf +*.VC.db +*.DotSettings.user + +# Visual Studio performance session files +*.psess +*.vspx + +# Xcode temp files and folders +*.xcscmblueprint +*.xccheckout +xcuserdata/ + +# NUGET folders +packages/ + +# Output folders +bin/ +Debug*/ +Release*/ +x64/ +obj/ +ipch/ + +# Subversion +.svn/ +boost_1_55_0/ + +# MSBuild cruft +*.metaproj +*.metaproj.tmp +*.opensdf +*.*proj.user +*.teamcity* +BuildWarningReport/ +*.tlog + +# Intermediate C/H files for COM +*_h.h +*_i.h +*_i.c +*_p.c + +# Intermediate autogenerated files from WPF +*.g.cs + +# Rhino generated backups +*.rui_bak + +# Specific files +src4/version.h +src4/version.cs +src4/version.vb +src4/version.wxi +src4/Builder/ +src4/DotNetSDK/rhinocommon/dotnet/AutoNativeEnums.cs +src4/DotNetSDK/rhinocommon/dotnet/AutoNativeEnumsRdk.cs +src4/DotNetSDK/rhinocommon/dotnet/AutoNativeMethods.cs +src4/DotNetSDK/rhinocommon/dotnet/AutoNativeMethodsRdk.cs +src4/DotNetSDK/rhinocommon/MethodGen.exe.config +src4/DotNetSDK/rhinocommon/dotnet/rhino3dm_version.cs +src4/Zoo/crypto/doc/crypto.xml +src4/rhino4/Plug-ins/import_E57/Xerces/src/xercesc/util/Xerces_autoconf_config.hpp +src4/rhino4/Plug-ins/RhinoHandlers/dlldata.c +src4/rhino4/Plug-ins/RDK/ShellExtension/Win32/ +src4/rhino4/Plug-ins/RDK/RPC/copylog.txt +src4/rhino4/Plug-ins/Toolbars/Resources/default.rui +src4/rhino4/assets/localized.rui +/build/obj +/build/logs +/register_plugins +src4/boostbuild/stagerelease +src4/boostbuild/stagedebug +src4/boostbuild/buildrelease +src4/boostbuild/builddebug +src4/boostbuild/build.log +src4/boostbuild/x64 +src4/boostbuild/override.platform.props +src4/BuildSolutions/RhinoMacBuild.userprefs +/src4/tools/EvaluationDebugging/TimeCount +src4/rhino4/Plug-ins/PlugInTest/ShaderBuildErrorSourceCode.txt + +# Localized Resources that get copied into this location - these are in Localization +src4/rhino4/Plug-ins/RhinoXXXXRender/Resources + +# Generated in Debug mode if a shader fails to compile +src4/rhino4/ShaderBuildErrorSourceCode.txt + +# These resources are copied from Localization at build time so can be ignored +src4/rhino4/MacOS/en.lproj/Environment Maps/ +*.bak +/src4/BuildSolutions/UpgradeLog.htm +/src4/BuildSolutions/UpgradeLog2.htm +/src4/BuildSolutions/UpgradeLog3.htm +/src4/BuildSolutions/UpgradeLog32.htm +/src4/BuildSolutions/UpgradeLog4.htm +/src4/BuildSolutions/UpgradeLog5.htm +/src4/BuildSolutions/UpgradeLog6.htm +/installer/.vs +/src4/ProductLicenseSystem/ProductLicenseCtl/Encode +.vs +/src4/Zoo/CloudZooClient/*.DS_Store +/src4/rhino4/MacOS/.DS_Store +/src4/rhino4/MacOS/._.DS_Store +/src4/rhino4/MacOS/._.DS_Store +/src4/rhino4/.DS_Store +*.DS_Store + +# NUnit generated files +$RANDOM_SEED$ +nunit_random_seed.tmp + +# Visual Studio Code files +.vscode/ + +# compiled python scripts +*.pyc +/reporting/cache +/reporting/reports + +#Rhino.Python +/src4/rhino4/Plug-ins/ironpython_osx/plugin/rhinoscriptsyntax/ +/src4/rhino4/Plug-ins/ironpython_osx/plugin/Lib/ +/src4/rhino4/Plug-ins/export_IGES/AutoNativeMethods.cs + +# Sandcastle SDK Help +src4/DotNetSDK/rhinocommon/dotnet/Help +src4/rhino4/Plug-ins/Grasshopper/docs/en/SDK Help/Help + +# Doxygen +src4/SdkDocumentation/Rhino/html +doxygen_entrydb*.tmp +doxygen_objdb*.tmp +/src4/rhino4/RhinoApplication/ShaderBuildErrorSourceCode.txt +ResourceIds.cs diff --git a/ILBPRhWrapper.h b/ILBPRhWrapper.h new file mode 100644 index 0000000..f7a6e41 --- /dev/null +++ b/ILBPRhWrapper.h @@ -0,0 +1,41 @@ +#pragma once + +enum eLBPRhWrapperType +{ + lbprh_wt_none, + lbprh_wt_object, + lbprh_wt_view, + lbprh_wt_material, + lbprh_wt_layer, + lbprh_wt_document, + lbprh_wt_light, + lbprh_wt_rdk_content, +}; + + +class IWrapperBase +{ +public: + virtual ON_Object* GetTarget(void) const = 0; + virtual UUID OriginalUuid(void) const = 0; + virtual const ON_Object* OriginalObject(void) const = 0; + virtual bool RemoveData(void) = 0; + virtual void UndoModifications(void) = 0; + virtual bool CommitChanges(void) = 0; + virtual void RecaptureObject(void) = 0; + virtual void Modify(void) = 0; + virtual bool UserDataPresent(void) const = 0; + virtual bool ModificationsPending(void) const = 0; + virtual bool LastReference(void) const = 0; + virtual eLBPRhWrapperType WrapperType(void) const = 0; + virtual void OnModification(void) const = 0; +}; + +enum eLBPRhWrapper_DefaultUse { lbprh_use_defaults, lbprh_dont_use_defaults }; + +template class ILBPRhWrapper : public IWrapperBase +{ +public: + virtual const T* UserData(eLBPRhWrapper_DefaultUse du = lbprh_dont_use_defaults) const = 0; + virtual T* UserDataToModify(void) = 0; +}; diff --git a/IRhRdkRegisteredProperty.h b/IRhRdkRegisteredProperty.h new file mode 100644 index 0000000..d6ffafa --- /dev/null +++ b/IRhRdkRegisteredProperty.h @@ -0,0 +1,313 @@ + +#pragma once + +class CRhRdkVariant; + +class RHRDK_SDK IRhRdkRegisteredProperty +{ +public: + enum units + { + no_units = 0, + + // SI units. + meters=1, square_meters=2, cubic_meters=3, kilograms=4, seconds=5, radians=6, + + // Rhino units. + rhino_units=7, square_rhino_units=8, cubic_rhino_units=9, + + // Others. + percent=10, // The property will be of type vtDouble in the range 0..1 for 0..100% + + force32bit_units=0xFFFFFFFF + }; + + class ConduitData + { + public: + ConduitData(); + + CRhinoDisplayConduit* m_pConduit; + CRhinoView* m_pView; + CRhinoViewport* m_pVP; + CChannelAttributes* m_pChannelAttrs; + CDisplayPipelineAttributes* m_pDisplayAttrs; + UINT m_iChannel; + CRhinoDisplayPipeline* m_dp; + void* m_pReserved; + }; + + // Unnamed enum - these can be combined for the iterator. + enum + { + object = 1, document = 2, view = 4, application = 8, layer = 16, rdk_content = 32, light = 64, + all = object | document | view | application | layer | rdk_content | light, + force32bit_pt = 0xFFFFFFFF + }; + typedef DWORD prop_type; + +public: + /** Display pipeline channels requested for dynamic display support - see subclasses SetDisplayToValue for more details */ + virtual CSupportChannels RequestedDisplayChannels(void) const = 0; + + /** You must implement this method as \code delete this; \endcode */ + virtual void DeleteThis(void) = 0; + + // Property Identification. + + virtual ON_wString LocalPropertyName(void) const = 0; // Was previously virtual ON_wString PropertyName(void) const = 0; + virtual ON_wString EnglishPropertyName(void) const = 0; + virtual ON_wString PropertyCategory(void) const = 0; + virtual ON_wString LocalPropertyDescription(void) const = 0; // Was previously virtual ON_wString PropertyDescription(void) const = 0; + + virtual UUID PropertyUuid(void) const = 0; + virtual prop_type PropertyType(void) const = 0; + + // Plugin identification. + virtual ON_wString PlugInName(void) const = 0; + virtual ON_wString PlugInDescription(void) const = 0; + virtual UUID PlugInId(void) const = 0; + +public: + // For all types. + virtual CRhRdkVariant DefaultValue(void) const = 0; + virtual bool IsAnimatable(void) const = 0; + virtual bool IsReadOnly(void) const = 0; + virtual bool IsEnabled(void) const = 0; + + virtual CRhRdkVariant::VariantType ValueType(void) const = 0; + + // For integer and real types. + virtual CRhRdkVariant MaxValue(void) const = 0; + virtual CRhRdkVariant MinValue(void) const = 0; + virtual units Units(void) const = 0; + + // For enumeration types. + virtual int NumberOfValidValues(void) const = 0; + virtual CRhRdkVariant GetValidValue(int iIndex) const = 0; + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t*, void*) = 0; + //Defined EVF calls: L"fixed-decimal-places", pvData: int*. Return NULL - use defaults. + +protected: + virtual ~IRhRdkRegisteredProperty() { } +}; + +// Specialized interface classes and sub-classes for the different property types. + +// Object Properties + +class RHRDK_SDK IRhRdkRegisteredObjectProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const UUID& objectId, ON_COMPONENT_INDEX ci, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, const UUID& objectId, ON_COMPONENT_INDEX ci, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForObject() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc, const UUID& objectID, ON_COMPONENT_INDEX ci) const = 0; + + virtual bool IsEnabledForObject(const CRhinoDoc& doc, const UUID& objectId, ON_COMPONENT_INDEX ci) const = 0; + + /** Should determine whether the input class is supported by this property. + Return \e true if you want to decide on a per-object basis. Otherwise implement like this: + \code return id.IsDerivedFrom(ON_Object::ClassId()); \endcode */ + virtual bool IsEnabledForObjectClass(const ON_ClassId& id) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredObjectProperty : public IRhRdkRegisteredObjectProperty +{ +public: + virtual void DeleteThis(void) override { delete this; } + virtual prop_type PropertyType(void) const override { return object; } + virtual CRhRdkVariant::VariantType ValueType(void) const override { return DefaultValue().Type(); } + virtual void* EVF(const wchar_t*, void*) override { return nullptr; } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const UUID& objectId, ON_COMPONENT_INDEX ci, const CRhRdkVariant& valueOld) const; +}; + +// View Properties + +class RHRDK_SDK IRhRdkRegisteredViewProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const UUID& viewId, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, const UUID& viewId, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForView() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc, const UUID& viewId) const = 0; + + virtual bool IsEnabledForView(const CRhinoDoc& doc, const UUID& viewId) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredViewProperty : public IRhRdkRegisteredViewProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return view; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const UUID& viewId, const CRhRdkVariant& valueOld) const; +}; + +// Application Properties + +class RHRDK_SDK IRhRdkRegisteredAppProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. */ + virtual CRhRdkVariant GetValue() const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredAppProperty : public IRhRdkRegisteredAppProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return application; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhRdkVariant& valueOld) const; +}; + +// Document Properties + +class RHRDK_SDK IRhRdkRegisteredDocProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForDoc() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc) const = 0; + + virtual bool IsEnabledForDoc(const CRhinoDoc& doc) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredDocProperty : public IRhRdkRegisteredDocProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return document; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const CRhRdkVariant& valueOld) const; + + /** Implemented inline for Rhino without multiple document support. */ + virtual bool IsEnabledForDoc(const CRhinoDoc&) const { return IsEnabled(); } + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t* wszFunc, void* pvData); +}; + +// Layer Properties + +class RHRDK_SDK IRhRdkRegisteredLayerProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const UUID& layer_id, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, const UUID& layer_id, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForDoc() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc, const UUID& layer_id) const = 0; + + virtual bool IsEnabledForLayer(const CRhinoDoc& doc, const UUID& layer_id) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredLayerProperty : public IRhRdkRegisteredLayerProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return layer; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const UUID& layer_id, const CRhRdkVariant& valueOld) const; + + /** Implemented inline for Rhino without multiple document support. */ + virtual bool IsEnabledForLayer(const CRhinoDoc&, const UUID& /*layer_id*/) const { return IsEnabled(); } + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t* wszFunc, void* pvData); +}; + +// RDK Content Properties + +class RHRDK_SDK IRhRdkRegisteredRdkContentProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const UUID& instance_id, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, const UUID& instance_id, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForDoc() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc, const UUID& instance_id) const = 0; + + virtual bool IsEnabledForContent(const CRhinoDoc& doc, const UUID& instance_id) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredRdkContentProperty : public IRhRdkRegisteredRdkContentProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return rdk_content; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const UUID& instance_id, const CRhRdkVariant& valueOld) const; + + /** Implemented inline for Rhino without multiple document support. */ + virtual bool IsEnabledForContent(const CRhinoDoc&, const UUID& /*instance_id*/) const { return IsEnabled(); } + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t* wszFunc, void* pvData); +}; + +// Light Properties + +class RHRDK_SDK IRhRdkRegisteredLightProperty : public IRhRdkRegisteredProperty +{ +public: + virtual bool SetValue(CRhinoDoc& doc, const UUID& light_id, const CRhRdkVariant& value) = 0; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, const UUID& light_id, ConduitData& conduit, const CRhRdkVariant& value) const = 0; + + /** Should return CRhRdkVariant with type vtNull if the call fails. + If IsEnabledForLight() returns \e true, this should return a value. */ + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc, const UUID& light_id) const = 0; + + virtual bool IsEnabledForLight(const CRhinoDoc& doc, const UUID& light_id) const = 0; +}; + +class RHRDK_SDK CRhRdkRegisteredLightProperty : public IRhRdkRegisteredLightProperty +{ +public: + virtual void DeleteThis(void) { delete this; } + virtual prop_type PropertyType(void) const { return light; } + virtual CRhRdkVariant::VariantType ValueType(void) const { return DefaultValue().Type(); } + + /** Call this function after changing a value on this property. */ + virtual void CallPropertyChangedEvent(const CRhinoDoc& doc, const UUID& light_id, const CRhRdkVariant& valueOld) const; + + /** Implemented inline for Rhino without multiple document support. */ + virtual bool IsEnabledForLight(const CRhinoDoc&, const UUID& /*light_id*/) const { return IsEnabled(); } + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t* wszFunc, void* pvData); +}; diff --git a/IRhRdkRegisteredPropertyManager.h b/IRhRdkRegisteredPropertyManager.h new file mode 100644 index 0000000..09cac80 --- /dev/null +++ b/IRhRdkRegisteredPropertyManager.h @@ -0,0 +1,83 @@ + +#pragma once + +//#ifdef RHRDK_REGPROPS + +#include "IRhRdkRegisteredProperty.h" + +class RHRDK_SDK IRhRdkRegisteredPropertyManager +{ +public: + virtual ~IRhRdkRegisteredPropertyManager() { } + +public: + /** Add a registered property. When you add a property object, you are transferring + ownership to the property manager, so simply call with \code new CMyProperty \endcode + as a parameter. */ + virtual bool Add(IRhRdkRegisteredProperty*) = 0; + +public: + // Property Iteration and location. + class Iterator + { + public: + virtual ~Iterator() { } + + public: + virtual void Reset(void) = 0; + virtual IRhRdkRegisteredProperty* GetNextProperty(void) = 0; + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t*, void*) = 0; + }; + + /** Get an iterator for iterating over properties. The caller shall delete the iterator. */ + virtual Iterator* NewRegisteredPropertyIterator(IRhRdkRegisteredProperty::prop_type t) const = 0; + + /** Find a registered property by its UUID. */ + virtual IRhRdkRegisteredProperty* FindProperty(UUID uuid) const = 0; + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t*, void*) = 0; +}; + +namespace RhRcmInternalPropertyIds +{ + RHRDK_SDK const UUID& ObjectColor(void); + RHRDK_SDK const UUID& ObjectName(void); + RHRDK_SDK const UUID& ObjectVisibility(void); + RHRDK_SDK const UUID& ObjectMaterialDiffuseColor(void); + RHRDK_SDK const UUID& ObjectMaterialGlossyColor(void); + RHRDK_SDK const UUID& ObjectMaterialTransparency(void); + RHRDK_SDK const UUID& ObjectMaterialGlossFinish(void); + + + //RDK sun properties + RHRDK_SDK const UUID& RdkSunEnableOn(void); + RHRDK_SDK const UUID& RdkSunLongitude(void); + RHRDK_SDK const UUID& RdkSunLatitude(void); + RHRDK_SDK const UUID& RdkSunTimezone(void); + RHRDK_SDK const UUID& RdkSunDaylightSavingOn(void); + RHRDK_SDK const UUID& RdkSunDaylightSavingMinutes(void); + RHRDK_SDK const UUID& RdkSunLocalDateTime(void); + + //Layer properties + RHRDK_SDK const UUID& LayerVisible(void); + + //Light properties + RHRDK_SDK const UUID& LightOn(void); + RHRDK_SDK const UUID& LightColor(void); + RHRDK_SDK const UUID& LightShadowIntensity(void); + RHRDK_SDK const UUID& LightIntensity(void); + RHRDK_SDK const UUID& LightSpotlightHardness(void); + + //Content properties + RHRDK_SDK const UUID& RdkTextureRepeat(void); + RHRDK_SDK const UUID& RdkTextureOffset(void); + RHRDK_SDK const UUID& RdkTextureRotation(void); + +} + + + +//#endif diff --git a/LBPBase64.cpp b/LBPBase64.cpp new file mode 100644 index 0000000..d577386 --- /dev/null +++ b/LBPBase64.cpp @@ -0,0 +1,170 @@ + +#include "stdafx.h" +#include "LBPBase64.h" +#include "LBPString.h" + +static const int DecodeTab[128] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, +}; + +CLBPBase64::CLBPBase64() +{ +} + +CLBPBase64::~CLBPBase64() +{ +} + +static bool ReadEncodedByte(int& pos, const WCHAR* wsz, BYTE& byteOut) +{ + wchar_t w = wsz[pos]; + if (w >= 128) + w = 128; // Will fail validation. + + byteOut = (BYTE)w; + + while (byteOut != 0) + { + pos++; + + // Check that the byte is a valid Base64 character. + // The Base64 specification requires garbled data to be ignored. + if ((byteOut < 128) && (DecodeTab[byteOut] >= 0)) + return true; + + // Move on to the next byte and keep trying. + w = wsz[pos]; + if (w >= 128) + w = 128; // Will fail validation. + + byteOut = (BYTE)w; + } + + return false; // End of data. +} + +static size_t InternalDecode(BYTE * pBufferOut, const CLBPString& sBase64In, size_t maxLength) +{ + UINT uBytesWritten = 0; + + int iResultSize = 3; + BYTE a, b, c, d, aResult[3]; + + const WCHAR* wsz = sBase64In.AsWideString(); + + int pos = 0; + while (ReadEncodedByte(pos, wsz, a)) + { + ReadEncodedByte(pos, wsz, b); + ReadEncodedByte(pos, wsz, c); + ReadEncodedByte(pos, wsz, d); + + if ('=' == d) // Handle padding character(s). + { + iResultSize = ('=' == c) ? 1 : 2; + } + + if (pBufferOut != NULL) + { + const ULONG val = (DecodeTab[a] << 18) | (DecodeTab[b] << 12) | + (DecodeTab[c] << 6) | DecodeTab[d]; + + aResult[0] = (BYTE)((val & 0xFF0000) >> 16); + aResult[1] = (BYTE)((val & 0x00FF00) >> 8); + aResult[2] = (BYTE)((val)); + + memcpy(pBufferOut, aResult, iResultSize); + + pBufferOut += iResultSize; + + uBytesWritten += iResultSize; + + if (size_t(uBytesWritten) >= maxLength) + break; + } + else + { + uBytesWritten += iResultSize; + } + } + + return size_t(uBytesWritten); +} + +size_t CLBPBase64::Decode(BYTE* pBufferOut, const CLBPString& sBase64In) const +{ + return InternalDecode(pBufferOut, sBase64In, UINT_MAX); +} + +size_t CLBPBase64::Decode(BYTE* pBufferOut, const CLBPString& sBase64In, size_t maxLength) const +{ + return InternalDecode(pBufferOut, sBase64In, maxLength); +} + +static const WCHAR* EncodeTab = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +bool CLBPBase64::Encode(const BYTE* pBufferIn, size_t bufNumBytes, CLBPString& sBase64Out, bool bAppend) const +{ + // Base64 is about 133% of the data size. Use 150% plus 4 for padding. + const size_t bigBufNumBytes = MulDiv((int)bufNumBytes, 150, 100) + 4; + + const int iExistingLen = bAppend ? sBase64Out.GetLength() : 0; + const size_t outNumBytes = iExistingLen + bigBufNumBytes; + WCHAR* out = sBase64Out.GetWideBufferSetLength((int)outNumBytes); + if (NULL == out) + return false; + + out += iExistingLen; + + const BYTE * p = pBufferIn; + const BYTE * pEnd = p + bufNumBytes; + + BYTE a, b, c; + while (p < pEnd) + { + out[2] = out[3] = L'='; + + a = *p++; + + out[0] = EncodeTab[a >> 2]; + + a = (BYTE)((a & 0x03) << 4); + if (p < pEnd) + { + b = *p++; + out[1] = EncodeTab[a | (b >> 4)]; + + b = (BYTE)((b & 0x0F) << 2); + if (p < pEnd) + { + c = *p++; + out[2] = EncodeTab[b | (c >> 6)]; + out[3] = EncodeTab[c & 0x3F]; + } + else + { + out[2] = EncodeTab[b]; + } + } + else + { + out[1] = EncodeTab[a]; + } + + out += 4; + } + + out[0] = 0; + + sBase64Out.ReleaseWideBuffer(); + + return true; +} diff --git a/LBPBase64.h b/LBPBase64.h new file mode 100644 index 0000000..e7061c4 --- /dev/null +++ b/LBPBase64.h @@ -0,0 +1,28 @@ + +#pragma once + +class CLBPString; + +class CLBPBase64 +{ +public: + CLBPBase64(); + virtual ~CLBPBase64(); + + // Decode a base64 string. + // pBufferOut must be large enough to accomodate the decoded data. It is safe to use the + // length of sBase64in because this Base64 string will always be about 33% bigger than the + // data it was created from. Returns the number of bytes written to pBufferOut. + // If pBufferOut is NULL, the function simply calculates the exact required buffer size. + size_t Decode(BYTE* pBufferOut, const CLBPString& sBase64in) const; + + // The same as Decode() above, but stops when uMaxLength bytes have been decoded. + // Added 13.04.2010 to allow reading of data that was saved with garbage at the end + // due to a bug in the base 64 cache. + size_t Decode(BYTE* pBufferOut, const CLBPString& sBase64in, size_t maxLength) const; + + // Encode data to a base64 string. If bAppend is true, the encoded string is appended to sBase64out. + // Otherwise sBase64out is replaced with the encoded string. + // Returns true if ok, false if unable to allocate the output buffer. + bool Encode(const BYTE* pBufferIn, size_t dwBufNumBytes, CLBPString& sBase64out, bool bAppend) const; +}; diff --git a/LBPBitmapPreview.cpp b/LBPBitmapPreview.cpp new file mode 100644 index 0000000..0613f2c --- /dev/null +++ b/LBPBitmapPreview.cpp @@ -0,0 +1,279 @@ + +#include "stdafx.h" +#include "LBPBitmapPreview.h" + +#if !defined LBPLIB_DISABLE_MFC_CONTROLS + +CLBPBitmapPreview::CLBPBitmapPreview() +{ + m_hBitmap = NULL; + + m_iBorderType = LBPBP_SUNKEN; + + m_ToolTip.m_hWnd = NULL; + + m_dwToolTipStyle = TTS_ALWAYSTIP; +} + +CLBPBitmapPreview::~CLBPBitmapPreview() +{ + if (NULL != m_hBitmap) + { + ::DeleteObject(m_hBitmap); + } +} + +BEGIN_MESSAGE_MAP(CLBPBitmapPreview, CWnd) + ON_WM_ERASEBKGND() + ON_WM_PAINT() + ON_WM_LBUTTONUP() + ON_WM_RBUTTONUP() + ON_WM_MOUSEMOVE() +END_MESSAGE_MAP() + +bool CLBPBitmapPreview::HasBitmap(void) const +{ + return NULL != m_hBitmap; +} + +BOOL CLBPBitmapPreview::OnEraseBkgnd(CDC* pDC) +{ + if (nullptr == pDC) + return false; + + CDC& dc = *pDC; + + EnableToolTips(TRUE); + + CRect rc; + GetClientRect(rc); + + if (m_iBorderType == LBPBP_RAISED) + { + dc.DrawEdge(rc, EDGE_RAISED, BF_RECT | BF_ADJUST | BF_SOFT | BF_MIDDLE); + } + else + if (m_iBorderType == LBPBP_SUNKEN) + { + dc.DrawEdge(rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST | BF_SOFT | BF_MIDDLE); + } + else + { + dc.DrawEdge(rc, EDGE_RAISED, BF_RECT | BF_ADJUST | BF_FLAT | BF_MIDDLE); + } + + if (OnDrawImage(&dc, rc)) + return FALSE; + + CBitmap* pBmp = CBitmap::FromHandle(m_hBitmap); + if (NULL == pBmp) + { + DrawEmpty(&dc, rc); + return FALSE; + } + + BITMAP bm = { 0 }; + if (pBmp->GetBitmap(&bm)) + { + CDC dcBmp; + dcBmp.CreateCompatibleDC(&dc); + CBitmap* pBmpOld = dcBmp.SelectObject(pBmp); + + dc.IntersectClipRect(rc); + + const int x = rc.left + (rc.Width() - bm.bmWidth) / 2; + const int y = rc.top + (rc.Height() - bm.bmHeight) / 2; + dc.BitBlt(x, y, bm.bmWidth, bm.bmHeight, &dcBmp, 0, 0, SRCCOPY); + + dcBmp.SelectObject(pBmpOld); + } + + return FALSE; +} + +BOOL CLBPBitmapPreview::OnDrawImage(CDC* pDC, const RECT* rc) +{ + return FALSE; +} + +void CLBPBitmapPreview::OnPaint() +{ + // Nothing to paint - all done in OnEraseBackground. + CPaintDC dc(this); +} + +void CLBPBitmapPreview::SafeRedraw(void) +{ + if (IsWindow(GetSafeHwnd())) + { + RedrawWindow(); + } +} + +void CLBPBitmapPreview::SetBitmap(HBITMAP hBitmap) +{ + if (NULL != m_hBitmap) + { + ::DeleteObject(m_hBitmap); + m_hBitmap = NULL; + } + + if (NULL == hBitmap) + { + ActivateTooltip(FALSE); + m_sDescription.Empty(); + } + else + { + m_hBitmap = hBitmap; +// SetTooltipText(m_sToolTip); + } + + SafeRedraw(); +} + +void CLBPBitmapPreview::SetSunken(void) +{ + m_iBorderType = LBPBP_SUNKEN; + SafeRedraw(); +} + +void CLBPBitmapPreview::SetRaised(void) +{ + m_iBorderType = LBPBP_RAISED; + SafeRedraw(); +} + +void CLBPBitmapPreview::SetOutlined(void) +{ + m_iBorderType = LBPBP_OUTLINED; + SafeRedraw(); +} + +bool CLBPBitmapPreview::CreateWnd(const RECT& rc, CWnd *pParent, UINT id) +{ + CString s = AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS, + ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)::GetStockObject(LTGRAY_BRUSH), NULL); + +// EnableToolTips(TRUE); + InitToolTip(); + + return CWnd::Create(s, _T("LBPBitmapPreview"), WS_CHILD | WS_VISIBLE, rc, pParent, id) ? true : false; +} + +void CLBPBitmapPreview::OnLButtonUp(UINT nFlags, CPoint point) +{ + GetParent()->SendMessage(WM_LBPBP_LBUTTONUP, GetDlgCtrlID(), NULL); + + CWnd::OnLButtonUp(nFlags, point); +} + +void CLBPBitmapPreview::OnRButtonUp(UINT nFlags, CPoint point) +{ + GetParent()->SendMessage(WM_LBPBP_RBUTTONUP, GetDlgCtrlID(), NULL); + + CWnd::OnRButtonUp(nFlags, point); +} + +BOOL CLBPBitmapPreview::PreTranslateMessage(MSG* pMsg) +{ + InitToolTip(); +// m_ToolTip.RelayEvent(pMsg); + + if (pMsg->message == WM_LBUTTONDBLCLK) + pMsg->message = WM_LBUTTONDOWN; + + return CWnd::PreTranslateMessage(pMsg); +} + +void CLBPBitmapPreview::OnMouseMove(UINT nFlags, CPoint point) +{ + InitToolTip(); + +/* if (m_ToolTip.m_hWnd) + { + UINT_PTR uId; + + TOOLINFO ti; + ti.cbSize = sizeof(TOOLINFO); + ti.hwnd = GetSafeHwnd(); + uId = GetDlgCtrlID(); + ti.uId = uId; + ti.rect.left = point.x; + ti.rect.right = point.x+1; + ti.rect.top = point.y; + ti.rect.bottom = point.y+1; + ti.uFlags = TTF_SUBCLASS; + m_ToolTip.SendMessage(TTM_NEWTOOLRECT, 0, (LPARAM)&ti); + }*/ + + MSG msg; + msg.hwnd = GetSafeHwnd(); + msg.time = 0; + msg.message = WM_MOUSEMOVE; + msg.wParam = nFlags; + msg.lParam = MAKELPARAM(point.x, point.y); + m_ToolTip.RelayEvent(&msg); + + CWnd::OnMouseMove(nFlags, point); +} + +void CLBPBitmapPreview::InitToolTip(void) +{ + if (m_ToolTip.m_hWnd == NULL) + { + if (m_ToolTip.Create(this, m_dwToolTipStyle)) + { + m_ToolTip.Activate(FALSE); + m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, 400); +// m_ToolTip.SetWindowPos(&(CWnd::wndTopMost),0,0,0,0, SWP_NOMOVE | SWP_NOSIZE); + } + } +} + +void CLBPBitmapPreview::SetTooltipText(LPCTSTR lpszText, BOOL bActivate) +{ + if (lpszText == NULL) + return; + + InitToolTip(); + + if (!m_ToolTip.GetSafeHwnd()) + return; + + // If there is no tooltip defined then add it. + if (m_ToolTip.GetToolCount() == 0) + { + CRect rectBtn; + GetClientRect(rectBtn); + m_ToolTip.AddTool(this, lpszText, rectBtn, 1); + } + + // Set text for tooltip. + m_ToolTip.UpdateTipText(lpszText, this, 1); + m_ToolTip.Activate(bActivate); +} + +void CLBPBitmapPreview::ActivateTooltip(BOOL bActivate) +{ + if (m_ToolTip.GetToolCount() == 0) + return; + + // Activate tooltip. + m_ToolTip.Activate(bActivate); +} + +void CLBPBitmapPreview::DrawEmpty(CDC* pDC, const RECT* pRC) +{ +/* pDC->FillSolidRect(pRC, RGB(255, 255, 255)); + +// pDC->Rectangle(pRC); + pDC->MoveTo(pRC->top, pRC->left); + pDC->LineTo(pRC->bottom, pRC->right); + + pDC->MoveTo(pRC->bottom, pRC->left); + pDC->LineTo(pRC->top, pRC->right); +*/ +} + +#endif diff --git a/LBPBitmapPreview.h b/LBPBitmapPreview.h new file mode 100644 index 0000000..5d34878 --- /dev/null +++ b/LBPBitmapPreview.h @@ -0,0 +1,61 @@ + +#pragma once + +#define LBPBP_SUNKEN 0 +#define LBPBP_RAISED 1 +#define LBPBP_OUTLINED 2 + +#define WM_LBPBP_LBUTTONUP WM_USER+0x400 +#define WM_LBPBP_RBUTTONUP WM_USER+0x401 + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +class LBPEXPORT CLBPBitmapPreview : public CWnd +{ +public: + CLBPBitmapPreview(); + virtual ~CLBPBitmapPreview(); + +public: + bool CreateWnd(const RECT& rc, CWnd* pParent, UINT id); + + // Takes ownership of the HBITMAP. + void SetBitmap(HBITMAP hBitmap); + + bool HasBitmap(void) const; + + void SetOutlined(void); + void SetRaised(void); + void SetSunken(void); + + void SetTooltipText(LPCTSTR lpszText, BOOL bActivate = TRUE); + void ActivateTooltip(BOOL bEnable = TRUE); + void InitToolTip(void); + void DrawEmpty(CDC* pDC, const RECT*); + + virtual BOOL PreTranslateMessage(MSG* pMsg); + +protected: + void SafeRedraw(void); + virtual BOOL OnDrawImage(CDC* pDC, const RECT*); // Called from drawing code - return TRUE if overridden. + +protected: + afx_msg BOOL OnEraseBkgnd(CDC* pDC); + afx_msg void OnPaint(); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnRButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + DECLARE_MESSAGE_MAP() + +protected: + HBITMAP m_hBitmap; + int m_iBorderType; + CToolTipCtrl m_ToolTip; + DWORD m_dwToolTipStyle; + +public: + CString m_sDescription; + CString m_sToolTip; +}; diff --git a/LBPBuffer.cpp b/LBPBuffer.cpp new file mode 100644 index 0000000..8dfd925 --- /dev/null +++ b/LBPBuffer.cpp @@ -0,0 +1,289 @@ + +#include "stdafx.h" +#include "LBPBuffer.h" +#include "LBPString.h" +#include "LBPBase64.h" +#include "LBP_CRC32.h" + +static const WCHAR* wszBase64Prefix = L"base64:"; + +const WCHAR* CLBPBuffer::Base64Prefix() +{ + return wszBase64Prefix; +} + +CLBPInternalBuffer::CLBPInternalBuffer() +{ + m_pBytes = NULL; + m_NumBytes = 0; + m_iRefCount = 1; +} + +CLBPInternalBuffer::CLBPInternalBuffer(const void* pBuffer, size_t size) +{ + m_pBytes = NULL; + m_NumBytes = 0; + m_iRefCount = 1; + + Set(pBuffer, size); +} + +CLBPInternalBuffer::CLBPInternalBuffer(size_t size) +{ + m_pBytes = NULL; + m_NumBytes = 0; + m_iRefCount = 1; + + Set(size); +} + +CLBPInternalBuffer::CLBPInternalBuffer(const CLBPString& sBase64) +{ + m_pBytes = NULL; + m_NumBytes = 0; + m_iRefCount = 1; + + *this = sBase64; +} + +CLBPInternalBuffer::~CLBPInternalBuffer() +{ + delete[] m_pBytes; +} + +int CLBPInternalBuffer::Release() +{ + const int iRefCount = ::InterlockedDecrement(&m_iRefCount); + + if (0 == iRefCount) + delete this; + + return iRefCount; +} + +void CLBPInternalBuffer::Set(const void* pBuffer, size_t size) +{ + Set(size); + + if (NULL != m_pBytes) + { + ::CopyMemory(m_pBytes, pBuffer, size); + } +} + +void CLBPInternalBuffer::Set(size_t size) +{ + m_bBase64Dirty = true; + m_bCRCDirty = true; + + if (m_NumBytes != size) + { + delete [] m_pBytes; + m_NumBytes = size; + m_pBytes = (size > 0) ? new BYTE[size] : NULL; + } +} + + +int CLBPInternalBuffer::Compare(const CLBPInternalBuffer& ib) const +{ + if (m_NumBytes != ib.m_NumBytes) + return (int)(m_NumBytes - ib.m_NumBytes); + + if (NULL == m_pBytes) + { + return (NULL == ib.m_pBytes) ? 0 : -1; + } + + if (NULL == ib.m_pBytes) + return +1; + + return memcmp(m_pBytes, ib.m_pBytes, m_NumBytes); +} + +const CLBPInternalBuffer& CLBPInternalBuffer::operator = (const CLBPInternalBuffer& ib) +{ + Set(ib.m_pBytes, ib.m_NumBytes); + return *this; +} + +const CLBPInternalBuffer& CLBPInternalBuffer::operator = (const CLBPString& s) +{ + if (s.StartsWithNoCase(CLBPBuffer::Base64Prefix()) && s != CLBPBuffer::Base64Prefix()) + { + const int iPrefixLen = CLBPString(CLBPBuffer::Base64Prefix()).GetLength(); + + CLBPBase64 b; + const int iSize = s.GetLength() - iPrefixLen; // Base64 is about 33% bigger than the resulting data. + Set(iSize); + m_NumBytes = b.Decode(m_pBytes, s.Mid(iPrefixLen)); + } + else + { + Clear(); + } + + return *this; +} + +const CLBPString& CLBPInternalBuffer::Base64() const +{ + if (m_bBase64Dirty) + { + Base64(m_sBase64); + } + + return m_sBase64; +} + +void CLBPInternalBuffer::Base64(CLBPString& s) const +{ + if (m_bBase64Dirty) + { + m_bBase64Dirty = false; + + s = CLBPBuffer::Base64Prefix(); + + if (NULL != m_pBytes) + { + CLBPBase64 b; + b.Encode(m_pBytes, m_NumBytes, s, true); + } + + m_sBase64 = s; + } + else + { + s = m_sBase64; + } +} + +DWORD CLBPInternalBuffer::CRC(void) const +{ + if (m_bCRCDirty) + { + m_dwCRC = CLBP_CRC32::Calculate(m_pBytes, m_NumBytes); + m_bCRCDirty = false; + } + + return m_dwCRC; +} + + + + +CLBPBuffer::CLBPBuffer() +{ + m_pBuffer = new CLBPInternalBuffer(); +} + +CLBPBuffer::CLBPBuffer(const void* pBuffer, size_t size) +{ + m_pBuffer = new CLBPInternalBuffer(pBuffer, size); +} + +CLBPBuffer::CLBPBuffer(size_t size) +{ + m_pBuffer = new CLBPInternalBuffer(size); +} + +CLBPBuffer::CLBPBuffer(const CLBPBuffer& src) +{ + src.m_pBuffer->AddRef(); + m_pBuffer = src.m_pBuffer; +} + +CLBPBuffer::CLBPBuffer(const CLBPString& sBase64) +{ + m_pBuffer = new CLBPInternalBuffer(sBase64); +} + +const CLBPBuffer& CLBPBuffer::operator=(const CLBPBuffer& src) +{ + if (this == &src) + return *this; + + src.m_pBuffer->AddRef(); + + ASSERT(m_pBuffer); + m_pBuffer->Release(); + + m_pBuffer = src.m_pBuffer; + + return *this; +} + +const CLBPBuffer& CLBPBuffer::operator = (const CLBPString& sBase64) +{ + ASSERT(m_pBuffer); + m_pBuffer->Release(); + + m_pBuffer = new CLBPInternalBuffer(sBase64); + return *this; +} + +CLBPBuffer::~CLBPBuffer() +{ + ASSERT(m_pBuffer); + m_pBuffer->Release(); +} + +int CLBPBuffer::Compare(const CLBPBuffer& b) const +{ + ASSERT(m_pBuffer); + ASSERT(b.m_pBuffer); + + return m_pBuffer->Compare(*b.m_pBuffer); +} + +void CLBPBuffer::Set(const void* pBuffer, size_t size) +{ + ASSERT(m_pBuffer); + + if (m_pBuffer->m_iRefCount > 1) + { + //We are not the only owners - need to clone up + *this = CLBPBuffer(pBuffer, size); + } + else + { + m_pBuffer->Set(pBuffer, size); + } +} + +void CLBPBuffer::Set(size_t size) +{ + Set(NULL, size); +} + +BYTE* CLBPBuffer::GetBuffer(size_t size) +{ + *this = CLBPBuffer(size); + + return m_pBuffer->m_pBytes; +} + +bool CLBPBuffer::operator == (const CLBPBuffer& b) const +{ + return Compare(b) == 0; +} + +bool CLBPBuffer::operator > (const CLBPBuffer& b) const +{ + return Compare(b) > 0; +} + +bool CLBPBuffer::operator < (const CLBPBuffer& b) const +{ + return Compare(b) < 0; +} + +const CLBPString& CLBPBuffer::Base64() const +{ + return m_pBuffer->Base64(); +} + +void CLBPBuffer::Base64(CLBPString& s) const +{ + m_pBuffer->Base64(s); +} diff --git a/LBPBuffer.h b/LBPBuffer.h new file mode 100644 index 0000000..b35f600 --- /dev/null +++ b/LBPBuffer.h @@ -0,0 +1,104 @@ +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#if defined (LBPLIB_APPLE_SPECIFIC) +#define REFCOUNT_INT int32_t +#endif + +#if defined (LBPLIB_WINDOWS_SPECIFIC) +#define REFCOUNT_INT LONG +#endif + +class CLBPBuffer; +#include "LBPString.h" + +class LBPEXPORT CLBPInternalBuffer +{ +public: + CLBPInternalBuffer(const void* pBuffer, size_t size); + CLBPInternalBuffer(const CLBPString& s); + CLBPInternalBuffer(size_t size); //Creates an allocated buffer containing random data + CLBPInternalBuffer(); + ~CLBPInternalBuffer(); + + int AddRef() { return ::InterlockedIncrement(&m_iRefCount); } + int Release(); + + void Set(const void* pBuffer, size_t size); + void Set(size_t size); + + void Clear() { Set(0); } + + int Compare(const CLBPInternalBuffer& b) const; + + const CLBPInternalBuffer& operator = (const CLBPInternalBuffer& b); + const CLBPInternalBuffer& operator = (const CLBPString& sBase64); + +public: + const CLBPString& Base64() const; + void Base64(CLBPString& s) const; + + DWORD CRC(void) const; + +private: + BYTE* m_pBytes; + size_t m_NumBytes; + volatile REFCOUNT_INT m_iRefCount; + + mutable CLBPString m_sBase64; + mutable bool m_bBase64Dirty = true; + + mutable DWORD m_dwCRC = 0; + mutable bool m_bCRCDirty = true; + + friend class CLBPBuffer; +}; + +class LBPEXPORT CLBPBuffer +{ +public: + CLBPBuffer(); + CLBPBuffer(const void* pBuffer, size_t size); + CLBPBuffer(const CLBPBuffer& src); + CLBPBuffer(const CLBPString& buffer); + CLBPBuffer(size_t size); //Creates an allocated buffer containing random data + ~CLBPBuffer(); + + void Clear() { Set(0); } + + static const WCHAR* Base64Prefix(); + +public: + + const CLBPBuffer& operator = (const CLBPBuffer& b); + const CLBPBuffer& operator = (const CLBPString& sBase64); + +public: + const CLBPString& Base64() const; + void Base64(CLBPString& s) const; + + DWORD CRC(void) const { return m_pBuffer->CRC(); } + +public: + void Set(const void* pBuffer, size_t size); + void Set(size_t size); + + BYTE* GetBuffer(size_t size); + void ReleaseBuffer(); + + int Compare(const CLBPBuffer& b) const; + + bool operator == (const CLBPBuffer& b) const; + bool operator > (const CLBPBuffer& b) const; + bool operator < (const CLBPBuffer& b) const; + + const BYTE* Bytes(void) const { return NULL == m_pBuffer ? NULL : m_pBuffer->m_pBytes; } + size_t NumBytes(void) const { return NULL == m_pBuffer ? 0 : m_pBuffer->m_NumBytes; } + +private: + CLBPInternalBuffer* m_pBuffer; +}; + diff --git a/LBPColor.cpp b/LBPColor.cpp new file mode 100644 index 0000000..e86c41a --- /dev/null +++ b/LBPColor.cpp @@ -0,0 +1,120 @@ + +#include "stdafx.h" +#include "LBPColor.h" + +CLBPColor::CLBPColor() +{ + m_Array[0] = 0.0; + m_Array[1] = 0.0; + m_Array[2] = 0.0; + m_Array[3] = 0.0; +} + +CLBPColor::CLBPColor(float fRed, float fGreen, float fBlue, float fAlpha) +{ + Set(fRed, fGreen, fBlue, fAlpha); +} + +CLBPColor::CLBPColor(double dRed, double dGreen, double dBlue, double dAlpha) +{ + Set(dRed, dGreen, dBlue, dAlpha); +} + +CLBPColor::CLBPColor(int iRed, int iGreen, int iBlue, int iAlpha) +{ + Set(iRed, iGreen, iBlue, iAlpha); +} + +CLBPColor::CLBPColor(const CLBPColor& src) +{ + *this=src; +} + +CLBPColor& CLBPColor::operator=(const CLBPColor& src) +{ + Set(src.DRed(), src.DGreen(), src.DBlue(), src.DAlpha()); + return *this; +} + + +void CLBPColor::Set(float fRed, float fGreen, float fBlue, float fAlpha) +{ + m_Array[0] = fRed; + m_Array[1] = fGreen; + m_Array[2] = fBlue; + m_Array[3] = fAlpha; +} + +void CLBPColor::Set(double dRed, double dGreen, double dBlue, double dAlpha) +{ + m_Array[0] = dRed; + m_Array[1] = dGreen; + m_Array[2] = dBlue; + m_Array[3] = dAlpha; +} + +void CLBPColor::Set(int iRed, int iGreen, int iBlue, int iAlpha) +{ + m_Array[0] = (double)iRed / 255.0; + m_Array[1] = (double)iGreen / 255.0; + m_Array[2] = (double)iBlue / 255.0; + m_Array[3] = (double)iAlpha / 255.0; +} + + +bool CLBPColor::operator== (const CLBPColor& c2) const +{ + if(DRed()!=c2.DRed()) return false; + if(DGreen()!=c2.DGreen()) return false; + if(DBlue()!=c2.DBlue()) return false; + if(DAlpha()!=c2.DAlpha()) return false; + + return TRUE; +} + +bool CLBPColor::operator!= (const CLBPColor& c2) const +{ + return !(*this==c2); +} + +double CLBPColor::DRed() const +{ + return m_Array[0]; +} + +double CLBPColor::DGreen() const +{ + return m_Array[1]; +} + +double CLBPColor::DBlue() const +{ + return m_Array[2]; +} + +double CLBPColor::DAlpha() const +{ + return m_Array[3]; +} + +UINT CLBPColor::IRed() const +{ + return (UINT)((DRed()*255.0)+0.01); +} + +UINT CLBPColor::IGreen() const +{ + return (UINT)((DGreen()*255.0)+0.01); +} + +UINT CLBPColor::IBlue() const +{ + return (UINT)((DBlue()*255.0)+0.01); +} + +UINT CLBPColor::IAlpha() const +{ + return (UINT)((DAlpha()*255.0+0.01)); +} + + diff --git a/LBPColor.h b/LBPColor.h new file mode 100644 index 0000000..72c7826 --- /dev/null +++ b/LBPColor.h @@ -0,0 +1,39 @@ + +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +class LBPEXPORT CLBPColor +{ +public: + CLBPColor(); + + CLBPColor(float fRed, float fGreen, float fBlue, float fAlpha = 1.f); + CLBPColor(double dRed, double dGreen, double dBlue, double dAlpha = 1.0); + CLBPColor(int iRed, int iGreen, int iBlue, int iAlpha = 255); + + CLBPColor(const CLBPColor& src); + CLBPColor& operator=(const CLBPColor& src); + + bool operator== (const CLBPColor& c2) const; + bool operator!= (const CLBPColor& c2) const; + + void Set(float fRed, float fGreen, float fBlue, float fAlpha = 1.f); + void Set(double dRed, double dGreen, double dBlue, double fAlpha = 1.0); + void Set(int iRed, int iGreen, int iBlue, int iAlpha = 255); + + double DRed() const; + double DGreen() const; + double DBlue() const; + double DAlpha() const; + + UINT IRed() const; + UINT IGreen() const; + UINT IBlue() const; + UINT IAlpha() const; + +protected: + double m_Array[4]; +}; diff --git a/LBPException.h b/LBPException.h new file mode 100644 index 0000000..e393cce --- /dev/null +++ b/LBPException.h @@ -0,0 +1,34 @@ + +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#include "LBPString.h" + +class LBPEXPORT CLBPException +{ +public: + CLBPException(const wchar_t* sMess); + virtual ~CLBPException() { } + + virtual void Report(void) const = 0; + +protected: + CLBPString m_sMessage; +}; + +class LBPEXPORT CLBPRunTimeException : public CLBPException +{ +public: + CLBPRunTimeException(const wchar_t* sMess); + + virtual void Report(void) const; +}; + +class LBPEXPORT CLBPProgramException : public CLBPException +{ +public: + CLBPProgramException(const wchar_t* sMess); +}; diff --git a/LBPFileMgr2.cpp b/LBPFileMgr2.cpp new file mode 100644 index 0000000..0b0b408 --- /dev/null +++ b/LBPFileMgr2.cpp @@ -0,0 +1,47 @@ + +#include "stdafx.h" +#include "LBPFileMgr2.h" +#include "LBPString.h" + +#define LBP_DIRECTORY_SLASH L'\\' + +CLBPString CLBPFileMgr2::GetPathOnly(const CLBPString& sPath) +{ + return CLBPFileMgr2::Truncate(sPath); +} + +CLBPString CLBPFileMgr2::Truncate(const CLBPString& sPath) +{ + if(sPath.IsEmpty()) + return L""; + + int iPos = GetWithTrailingSlashRemoved(sPath).ReverseFind(LBP_DIRECTORY_SLASH); + + if(-1 == iPos) + { + //Not a path - just a name + return L""; + } + + return sPath.Left(iPos); +} + +CLBPString CLBPFileMgr2::GetWithTrailingSlashRemoved(const CLBPString& s) +{ + CLBPString sPath(s); + RemoveTrailingSlash(sPath); + return sPath; +} + +void CLBPFileMgr2::RemoveTrailingSlash(CLBPString& sPath) +{ + if (sPath.IsEmpty()) + return; + + int iLast = sPath.GetLength()-1; + + if(LBP_DIRECTORY_SLASH == sPath.GetAtWide(iLast)) + { + sPath = sPath.Left(iLast); + } +} diff --git a/LBPFileMgr2.h b/LBPFileMgr2.h new file mode 100644 index 0000000..d271786 --- /dev/null +++ b/LBPFileMgr2.h @@ -0,0 +1,19 @@ + +#pragma once + +class CLBPString; + +class CLBPFileMgr2 +{ +private: + CLBPFileMgr2() {}; + virtual ~CLBPFileMgr2() {}; + +public: + static CLBPString GetPathOnly(const CLBPString& sPath); //Doesn't include trailing slash + static CLBPString Truncate(const CLBPString& sPath); //Will first remove filename, then each folder until empty + + static CLBPString GetWithTrailingSlashRemoved(const CLBPString& sPath); + static void RemoveTrailingSlash(CLBPString& sPath); +}; + diff --git a/LBPLibUtilities.cpp b/LBPLibUtilities.cpp new file mode 100644 index 0000000..29c1d93 --- /dev/null +++ b/LBPLibUtilities.cpp @@ -0,0 +1,314 @@ + +#include "stdafx.h" +#include "LBPFileMgr2.h" +#include "LBPString.h" +#include "LBPLibUtilities.h" +#include "LBPRhException.h" + + +BOOL LBPIsDoubleEqual(double d1, double d2, int places) +{ + const double thresh = (places < 0) ? 1e-10 : (1.0 / pow(10.0, places)); + + const double d3 = fabs(d1 - d2); + + if (d3 < thresh) + return TRUE; + + return FALSE; +} + +BOOL LBPIsFloatEqual(float f1, float f2) +{ + const float f3 = fabsf(f1 - f2); + + if (f3 < 1e-6) + return TRUE; + + return FALSE; +} + +#if defined LBPLIB_WINDOWS_SPECIFIC + +_locale_t LBP_Locale(void) +{ + static _locale_t loc = NULL; + + if (NULL == loc) + { + loc = _create_locale(LC_ALL, "C"); + } + + return loc; +} + +CFont* LBP_GetDialogFont(void) +{ +#ifdef LBPRHLIB + + // 4th August 2017, John Croudy. Brian G says we should use this function for fonts. + // Fixes https://mcneel.myjetbrains.com/youtrack/issue/RH-40635 + return RhinoApp().RhinoUiPaintManager().GetUiFont(); + +#else + + // 14th September 2015, John Croudy. Fixes RH-30857 + // + // The problem is now obvious and I don't know why I didn't see it before. + // To get the same font dimensions as a dialog, we have to measure MS Shell Dlg + // and not DEFAULT_GUI_FONT, because although they are the same on English Windows, + // they are not necessarily the same in other locales. We have to use the same + // settings as all our dialogs: + // + // FONT 8, "MS Shell Dlg", 400, 0, 0x1 + // FONT pointsize, "typeface", weight, italic, charset + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa381013(v=vs.85).aspx + // + // Unfortunately this issue is far more complicated than you would think: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173(v=vs.85).aspx + + static CFont font; + + if (font.GetSafeHandle() == NULL) + { + // http://www.winprog.org/tutorial/fonts.html + const int pointSize = 8; + HDC hDC = ::GetDC(NULL); + const int logPixY = ::GetDeviceCaps(hDC, LOGPIXELSY); + ::ReleaseDC(NULL, hDC); + + LOGFONT lf = { 0 }; + lf.lfHeight = -MulDiv(pointSize, logPixY, 72); + lf.lfWeight = 400; + lf.lfCharSet = 0x1; + Checked::wcsncpy_s(lf.lfFaceName, _countof(lf.lfFaceName), L"MS Shell Dlg", _TRUNCATE); + font.CreateFontIndirect(&lf); + } + + return &font; + +#endif +} + + +/* https://msdn.microsoft.com/en-us/library/windows/desktop/ms645475(v=vs.85).aspx + + For a dialog box that does not use the system font, the base units are the average width and height, + in pixels, of the characters in the dialog's font. You can use the GetTextMetrics and GetTextExtentPoint32 + functions to calculate these values for a selected font. However, by using the MapDialogRect function, you + can avoid errors that might result if your calculations differ from those performed by the system. + Each horizontal base unit is equal to 4 horizontal dialog template units; each vertical base unit is equal + to 8 vertical dialog template units. +*/ + +CSize LBP_MeasureFont(CFont* pFont) +{ + if (NULL == pFont) + { + pFont = LBP_GetDialogFont(); + if (NULL == pFont) + return CSize(0, 0); + } + + CDC dc; + dc.CreateCompatibleDC(NULL); + CFont* pOldFont = dc.SelectObject(pFont); + + //TEXTMETRIC tm = { 0 }; + //::GetTextMetrics(dc.GetSafeHdc(), &tm); + +// // Microsoft says that MapDialogRect() uses the average character width, but it doesn't. +// // The only way I can get this to return the same width as MapDialogRect() is to add +// // one to the average character width. How typical. "I don't know why I need to add 1". +// return CSize(tm.tmAveCharWidth+1, tm.tmHeight); + + // 11th September 2015, John Croudy. + // + // https://support.microsoft.com/en-us/kb/125681 + // + // The tmAveCharWidth field of the TEXTMETRIC structure only approximates the actual average character + // width (usually it gives the width of the letter "x") and so the true average character width must + // be calculated to match the value used by the system. + + // The letter "x"? Excuse me? Even Microsoft thinks it's OK to just use Roman letters for everything. + // I knew I'd got the idea for LBP_Alphabet from somewhere. But what about Chinese fonts? + // This is like the blind leading the blind. Welcome back, covert LBP_Alphabet. The letter "x", lol. + + CSize size; + ::GetTextExtentPoint32(dc.GetSafeHdc(), L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &size); + + dc.SelectObject(pOldFont); + + size.cx = (size.cx / 26 + 1) / 2; + + // 11th September 2015, John Croudy. + // On English Windows, size.cy is the same as tm.tmHeight -- 13. + // The previous code just used size.cy. $100 says that value is + // 12 for Chinese Windows' DEFAULT_GUI_FONT (PMingLiu). + // I can reproduce RH-30857 by setting size.cy to 12. + + // 14th September 2015, John Croudy. + // Kelvin's test confirms that size.cy is 12 -- but so is tm.tmHeight. + // So it doesn't matter which one you use -- which makes sense. + //size.cy = tm.tmHeight; + + return size; +} + +int LBP_DialogUnitsToPixelsX(int du, CFont* pFont) +{ +#if LBPLIB_RHINOVER >= 6 + if ((NULL == pFont) && (du >= 0) && (du < 64)) + { + static int aDU[64] = // Optimizes most common du values for default GUI font. + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + if (aDU[du] < 0) + { + const CSize size = LBP_MeasureFont(pFont); + const int baseUnit = size.cx; + aDU[du] = MulDiv(du, baseUnit, 4); + } + + return aDU[du]; + } +#endif + + const CSize size = LBP_MeasureFont(pFont); + const int baseUnit = size.cx; + + return MulDiv(du, baseUnit, 4); +} + +int LBP_DialogUnitsToPixelsY(int du, CFont* pFont) +{ +#if LBPLIB_RHINOVER >= 6 + if ((NULL == pFont) && (du >= 0) && (du < 64)) + { + static int aDU[64] = // Optimizes most common du values for default GUI font. + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + if (aDU[du] < 0) + { + const CSize size = LBP_MeasureFont(pFont); + const int baseUnit = size.cy; + aDU[du] = MulDiv(du, baseUnit, 8); + } + + return aDU[du]; + } +#endif + + const CSize size = LBP_MeasureFont(pFont); + const int baseUnit = size.cy; + + return MulDiv(du, baseUnit, 8); +} +#endif + + +CLBPException::CLBPException(const wchar_t* sMess) +{ + m_sMessage = sMess; +} + +CLBPRunTimeException::CLBPRunTimeException(const wchar_t* sMess) + : CLBPException(sMess) +{ +} + +void CLBPRunTimeException::Report(void) const +{ + ::MessageBox(NULL, m_sMessage.AsTString(), _T("Error"), MB_ICONSTOP); +} + +static const CLBPString sProgramError = _T("**** Program Error ****\n\n"); + +CLBPProgramException::CLBPProgramException(const wchar_t* sMess) + : CLBPException(sProgramError + sMess) +{ +} + +void CLBPRhRunTimeException::Report(void) const +{ + ::RhinoMessageBox(m_sMessage, "Error", MB_ICONSTOP); + + RhinoApp().Print(m_sMessage.Wide()); +} + +#ifdef LBPRHLIB + + +int CountSelectedObjects(const CRhinoDoc& doc) +{ + CRhinoObjectIterator it(doc, CRhinoObjectIterator::normal_objects, CRhinoObjectIterator::active_and_reference_objects); + + it.EnableSelectedFilter(); + it.IncludeLights(false); + + int iCount = 0; + + CRhinoObject* pObject = it.First(); + while (pObject != NULL) + { + if (pObject->IsSelected()) + iCount++; + + pObject = it.Next(); + } + + return iCount; +} + +#ifdef _DEBUG +void LBPRhAssert(bool bPredicate) +{ + if (bPredicate) return; + ::MessageBox(NULL, _T("LBPRhLib Assert"), _T("LBPRhLib"), MB_OK | MB_ICONEXCLAMATION); + DebugBreak(); +} +#endif + +unsigned int LBPRhDocRuntimeSerialNumber(const CRhinoDoc& doc) +{ +#ifdef RHINO_6_OR_LATER + return doc.RuntimeSerialNumber(); // Rhino 6 or later. +#else + return (unsigned int)doc.m_runtime_doc_serial_number; +#endif +} + +CRhinoDoc* LBPRhDocFromRuntimeSerialNumber(unsigned int serial) +{ +#ifdef RHINO_6_OR_LATER + return CRhinoDoc::FromRuntimeSerialNumber(serial); // Rhino 6 or later. +#else + return RhinoApp().GetDocument(serial); +#endif +} + +unsigned int LBPRhObjectDocSerialNumber(const CRhinoObject* pObject) +{ + if (NULL == pObject) + return 0; + +#ifdef RHINO_6_OR_LATER + return pObject->DocumentRuntimeSerialNumber(); // Rhino 6 or later. +#else + const CRhinoDoc* pDoc = pObject->Document(); + return (NULL != pDoc) ? (unsigned int)pDoc->m_runtime_doc_serial_number : 0; +#endif +} + +#endif + diff --git a/LBPLibUtilities.h b/LBPLibUtilities.h new file mode 100644 index 0000000..6c05d29 --- /dev/null +++ b/LBPLibUtilities.h @@ -0,0 +1,44 @@ + +#pragma once + +BOOL LBPIsDoubleEqual(double, double, int places=-1); +BOOL LBPIsFloatEqual(float, float); + +#if defined LBPLIB_WINDOWS_SPECIFIC +_locale_t LBP_Locale(void); +inline double LBP_wtof(const wchar_t* s) { return _wtof_l (s, LBP_Locale()); } +inline int LBP_wtoi(const wchar_t* s) { return _wtoi_l (s, LBP_Locale()); } +#endif + +#if defined LBPLIB_WINDOWS_SPECIFIC + +// Measures a font's average character width and height, +// If pFont is null, it uses the Rhino UI font if LBPRHLIB is defined +// or the MS Shell Dlg font if LBPRHLIB is not defined. +CSize LBP_MeasureFont(CFont* pFont=NULL); + +int LBP_DialogUnitsToPixelsX(int du, CFont* pFont=NULL); +int LBP_DialogUnitsToPixelsY(int du, CFont* pFont=NULL); + +#define D2PX(x) LBP_DialogUnitsToPixelsX(x) +#define D2PY(y) LBP_DialogUnitsToPixelsY(y) + +// Gets a font for use in dialogs. Matches the dialog template's FONT 8, "MS Shell Dlg", 400, 0, 0x1 +CFont* LBP_GetDialogFont(void); + +#endif + +int CountSelectedObjects(const CRhinoDoc& doc); + +#ifdef _DEBUG +void LBPRhAssert(bool bPredicate); +#else +inline void LBPRhAssert(bool) {} +#endif + + +unsigned int LBPRhDocRuntimeSerialNumber(const CRhinoDoc& doc); + +CRhinoDoc* LBPRhDocFromRuntimeSerialNumber(unsigned int serial); + +unsigned int LBPRhObjectDocSerialNumber(const CRhinoObject* pObject); diff --git a/LBPPidlMgr.cpp b/LBPPidlMgr.cpp new file mode 100644 index 0000000..3bf34a1 --- /dev/null +++ b/LBPPidlMgr.cpp @@ -0,0 +1,244 @@ +// PidlMgr.cpp: implementation of the CLBPPidlMgr class. +// +////////////////////////////////////////////////////////////////////// + + +#include "stdafx.h" +#if !defined LBPLIB_DISABLE_SHELL +#include "lbppidlmgr.h" + + +//////////////////////////////////////////////////////////////// +// CLBPPidlMgr : Class to manage pidls + +void CLBPPidlMgr::Delete(LPITEMIDLIST pidl) +{ + if (pidl) ::ILFree(pidl); +} + +LPCITEMIDLIST CLBPPidlMgr::GetNextItem(LPCITEMIDLIST pidl) +{ + if (pidl) + { + return ::ILGetNext(pidl); + //return (LPITEMIDLIST)(LPBYTE) ( ((LPBYTE)pidl) + pidl->mkid.cb); + } + return (NULL); +} + +LPITEMIDLIST CLBPPidlMgr::Copy(LPCITEMIDLIST pidlSrc) +{ + if(pidlSrc) return ::ILClone(pidlSrc); + return 0; +} + +UINT CLBPPidlMgr::GetSize(LPCITEMIDLIST pidl) +{ + if(pidl) return ::ILGetSize(pidl); + return 0; +} + + +LPCITEMIDLIST CLBPPidlMgr::GetLastItem(LPCITEMIDLIST pidl) +{ + if(pidl) return ::ILFindLastID(pidl); + return 0; +} + +UINT CLBPPidlMgr::GetPidlLength(LPCITEMIDLIST pidl) +{ + UINT length = 0; + + if(pidl) + { + while(pidl->mkid.cb) + { + length++; + pidl = GetNextItem(pidl); + } + } + return length; +} + + +LPITEMIDLIST CLBPPidlMgr::Concatenate(LPCITEMIDLIST pidl1, + LPCITEMIDLIST pidl2) +{ + return ::ILCombine(pidl1, pidl2); +} + +LPITEMIDLIST CLBPPidlMgr::Truncate(LPCITEMIDLIST pidl) +{ + //TRACE("Truncate\n"); + if(pidl == NULL) + { + return NULL; + } + + UINT cbTotal = 0; + UINT cbThis = 0; + UINT length = GetPidlLength(pidl); + + if(length < 2) return NULL; + length--; + + LPCITEMIDLIST pidlTemp = (LPITEMIDLIST) pidl; + + for(UINT i = 0; i < length; i++) + { + cbThis = pidlTemp->mkid.cb; + cbTotal += cbThis; + pidlTemp = GetNextItem(pidlTemp); + + + } + + LPITEMIDLIST pidlNew = (LPITEMIDLIST)CoTaskMemAlloc(cbTotal + sizeof(ITEMIDLIST)); + + if(pidlNew) + { + ZeroMemory(pidlNew, cbTotal + sizeof(ITEMIDLIST)); + CopyMemory(pidlNew, pidl, cbTotal); + } + + return pidlNew; +} + + + + + + + +LPITEMIDLIST CLBPPidlMgr::GetSimplePidl(LPCITEMIDLIST pidl, int iItem) +{ + if(pidl == NULL) + { + return NULL; + } + + LPCITEMIDLIST pidlTemp = (LPCITEMIDLIST)pidl; + + for (int i=0; i < iItem; i++) + { + pidlTemp = GetNextItem(pidlTemp); + } + + pidl->mkid.cb; + LPITEMIDLIST pidlNew = (LPITEMIDLIST)CoTaskMemAlloc(pidlTemp->mkid.cb + sizeof(ITEMIDLIST)); + if(pidlNew) + { + ZeroMemory(pidlNew, pidlTemp->mkid.cb + sizeof(ITEMIDLIST)); + CopyMemory(pidlNew, pidlTemp, pidlTemp->mkid.cb); + } + return pidlNew; + +} + +bool CLBPPidlMgr::IsDesktop(LPCITEMIDLIST pidl) +{ + if(!pidl) return true; + + if(GetPidlLength(pidl) == 0) return true; + if(GetSize(pidl)== 0) return true; + + LPITEMIDLIST pidlDesktop = GetDesktopPidl(); + if(!pidlDesktop) + { + return false; + } + + UINT iSize = GetSize(pidlDesktop); + //UINT iLength = GetPidlLength(pidlDesktop); + + bool bSame = false; + + if(GetSize(pidl) == iSize) + { + bSame = 0==memcmp(pidl, pidlDesktop, iSize); + } + + Delete(pidlDesktop); + + return bSame; + +} + +LPITEMIDLIST CLBPPidlMgr::GetDesktopPidl() +{ + LPITEMIDLIST pidlDesktop = NULL; + ::SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop); + + return pidlDesktop; +} + +LPITEMIDLIST CLBPPidlMgr::PidlFromPath(LPCTSTR pszPath, HWND hOwner) +{ + USES_CONVERSION; + + ULONG count = 0; + LPITEMIDLIST pidlNew = NULL; + + IShellFolder* pDesktop; + HRESULT hr = SHGetDesktopFolder(&pDesktop); + + if(FAILED(hr)) + return NULL; + + hr = pDesktop->ParseDisplayName(hOwner, NULL, T2OLE(const_cast(pszPath)), &count, &pidlNew, NULL); + pDesktop->Release(); + + if(FAILED(hr)) + return NULL; + + return pidlNew; +} + +bool CLBPPidlMgr::IsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) +{ + LPSHELLFOLDER psfThis = nullptr; + LPSHELLFOLDER psfLast = nullptr; + + if (pidl1 == nullptr && pidl2 == nullptr) + { + return true; + } + + if (pidl1 == nullptr || pidl2 == nullptr) + { + return false; + } + + const auto iLength1 = GetPidlLength(pidl1); + const auto iLength2 = GetPidlLength(pidl2); + if (iLength1 != iLength2) + return false; + + SHGetDesktopFolder(&psfLast); + + LPITEMIDLIST pidlSimple1 = nullptr, pidlSimple2 = nullptr; + + for (UINT i = 0; i < GetPidlLength(pidl1); i++) + { + pidlSimple1 = GetSimplePidl(pidl1, i); + pidlSimple2 = GetSimplePidl(pidl2, i); + + if (psfLast->CompareIDs(0, pidlSimple1, pidlSimple2) != 0) + { + return false; + } + + psfLast->BindToObject(pidlSimple1, NULL, IID_IShellFolder, (LPVOID*)&psfThis); + psfLast->Release(); + psfLast = psfThis; + + Delete(pidlSimple1); + Delete(pidlSimple2); + } + + return true; +} + + + +#endif diff --git a/LBPPidlMgr.h b/LBPPidlMgr.h new file mode 100644 index 0000000..a24ea25 --- /dev/null +++ b/LBPPidlMgr.h @@ -0,0 +1,44 @@ +// PidlMgr.h: interface for the CPidlMgr class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_LBPPIDLMGR_H__F27C435D_4A2E_4E97_9F62_67D19D99D792__INCLUDED_) +#define AFX_LBPPIDLMGR_H__F27C435D_4A2E_4E97_9F62_67D19D99D792__INCLUDED_ + +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#if !defined LBPLIB_DISABLE_SHELL + + + +//////////////////////////////////////////////////// +// CPidlMgr : Class for managing Pidls +class LBPEXPORT CLBPPidlMgr +{ +public: + static void Delete(LPITEMIDLIST); + static LPCITEMIDLIST GetNextItem(LPCITEMIDLIST); + static LPITEMIDLIST Copy(LPCITEMIDLIST); + static UINT GetSize(LPCITEMIDLIST); + static UINT GetPidlLength(LPCITEMIDLIST); + static LPCITEMIDLIST GetLastItem(LPCITEMIDLIST pidl); + static LPITEMIDLIST Concatenate(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2); + static LPITEMIDLIST Truncate(LPCITEMIDLIST pidl); + static bool IsEqual(LPCITEMIDLIST pidlFQ1, LPCITEMIDLIST pidlFQ2); //Pidls must be fully qualified - ie, root at the desktop. + static bool IsDesktop(LPCITEMIDLIST pidl); + static LPITEMIDLIST GetDesktopPidl(); + static LPITEMIDLIST PidlFromPath(LPCTSTR pszPath, HWND hOwner = 0); + static LPITEMIDLIST GetSimplePidl(LPCITEMIDLIST pidl, int iItem); +}; + +#endif + +#endif // !defined(AFX_LBPPIDLMGR_H__F27C435D_4A2E_4E97_9F62_67D19D99D792__INCLUDED_) + + + + diff --git a/LBPPreviewingFileDialog.cpp b/LBPPreviewingFileDialog.cpp new file mode 100644 index 0000000..9b6f3f9 --- /dev/null +++ b/LBPPreviewingFileDialog.cpp @@ -0,0 +1,389 @@ + +#include "stdafx.h" + +#if !defined LBPLIB_DISABLE_MFC_CONTROLS + +#include "LBPPreviewingFileDialog.h" +#include "LBPBitmapPreview.h" +#include "LBPPidlMgr.h" +#include "LBPLibUtilities.h" +#include "Resource.h" + +bool CLBPPreviewingFileDialog::m_bShowPreview = true; + +// 3 Feb. 2011 S. Baer +// This dialog will throw exceptions under VC2010 if bVistaStyle is set to true (which is the default unless overridden). +// Had to call a different constructor which is only available in VC2010 to get things to work. +CLBPPreviewingFileDialog::CLBPPreviewingFileDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, + DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) +#if defined(_MFC_VER) && _MFC_VER >= 0x0A00 +: CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, 0, FALSE) // bVistaStyle = FALSE; dialog will downgrade on Vista upwards. +#else +: CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd) +#endif +{ + m_ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLESIZING; + + m_pPreview = new CLBPBitmapPreview; + + m_iPreviewSize = 128; + +#ifdef _DEBUG + for (int i = 0; i < 4; i++) + { + //Set these up to bad values just to help debugging + m_iControls[i] = -1; + } +#endif +} + +CLBPPreviewingFileDialog::~CLBPPreviewingFileDialog() +{ + delete m_pPreview; +} + +INT_PTR CLBPPreviewingFileDialog::DoModal() +{ + m_ofn.hInstance = ResourceInstance(); + + for(int i=0;i<4;i++) + { + m_iControls[i] = ResourceID(i); + } + + SetTemplate(NULL, m_iControls[0]); + + return CFileDialog::DoModal(); +} + +HINSTANCE CLBPPreviewingFileDialog::ResourceInstance() const +{ + return NULL; +} + +DWORD CLBPPreviewingFileDialog::ResourceID(DWORD OriginalID) const +{ + switch (OriginalID) + { + case 0: return IDD_PREVIEWING_FILE_DIALOG; + case 1: return IDC_CHECK_PREVIEW; + case 2: return IDC_STATIC_PREVIEW; + case 3: return IDC_PROGRESS1; + } + + return (DWORD)-1; +} + +BEGIN_MESSAGE_MAP(CLBPPreviewingFileDialog, CFileDialog) + ON_WM_SIZE() +END_MESSAGE_MAP() + +BOOL CLBPPreviewingFileDialog::OnCommand(WPARAM wParam,LPARAM lParam) +{ + DWORD iID = LOWORD(wParam); + DWORD iCommand = HIWORD(wParam); + + if(ResourceID(1) == iID) //Preview check box + { + if(BN_CLICKED == iCommand) + { + OnCheckPreview(); + return TRUE; + } + } + + //Not handled - pass on to next window + return CFileDialog::OnCommand(wParam, lParam); +} + +void CLBPPreviewingFileDialog::DoDataExchange(CDataExchange* pDX) +{ + CFileDialog::DoDataExchange(pDX); + + // We can't do DDX/DDV if we're not using our own custom template. + DDX_Control(pDX, m_iControls[2], m_static); + DDX_Control(pDX, m_iControls[1], m_checkPreview); + DDX_Control(pDX, m_iControls[3], m_progress); +} + +int CLBPPreviewingFileDialog::PreviewSize(void) const +{ + return m_iPreviewSize; +} + +void CLBPPreviewingFileDialog::SetPreviewSize(int iSize) +{ + m_iPreviewSize = iSize; +} + +BOOL CLBPPreviewingFileDialog::OnInitDialog() +{ + CFileDialog::OnInitDialog(); + + m_checkPreview.SetCheck(ShowPreviewOn()); + + m_progress.SetRange(0, 100); + + m_pPreview->CreateWnd(CRect(0, 0, 0, 0), this, 12345); + + m_pPreview->SetSunken(); + + return TRUE; +} + +void CLBPPreviewingFileDialog::OnSize(UINT nType, int cx, int cy) +{ + const int size = 2 + m_iPreviewSize + 2; + + CRect rectStatic; + m_static.GetWindowRect(rectStatic); + ScreenToClient(rectStatic); + rectStatic.right = rectStatic.left + size; + rectStatic.bottom = rectStatic.top + size; + m_pPreview->MoveWindow(rectStatic); + + CRect rectProgress = rectStatic; + m_progress.GetWindowRect(rectProgress); + ScreenToClient(rectProgress); + rectProgress.right = rectProgress.left + size; + m_progress.MoveWindow(rectProgress); + + CRect rectCheck = rectProgress; + rectCheck.top = rectProgress.bottom + D2PY(4); + rectCheck.bottom = rectCheck.top + D2PY(12); + m_checkPreview.MoveWindow(rectCheck); +} + +IShellBrowser* CLBPPreviewingFileDialog::GetBrowserInterface(void) const +{ + // Get the IShellBrowser interface. + + DWORD_PTR dwResult = 0; + if (0 == ::SendMessageTimeout(GetParent()->GetSafeHwnd(), WM_USER+7, NULL, NULL, SMTO_NORMAL, 100, &dwResult)) + return NULL; + + return (IShellBrowser*)dwResult; +} + +HRESULT CLBPPreviewingFileDialog::GetSelectionObject(IDataObject ** ppDO, bool bSelected) +{ + *ppDO = NULL; + + IShellBrowser* pSB = GetBrowserInterface(); + if (NULL == pSB) + return E_FAIL; + + IShellView* pSV = NULL; + HRESULT result = pSB->QueryActiveShellView(&pSV); + if (FAILED(result) || (NULL == pSV)) + return E_FAIL; + + IDataObject* pDO = NULL; + result = pSV->GetItemObject(bSelected ? SVGIO_SELECTION : SVGIO_ALLVIEW, IID_IDataObject, (LPVOID*)&pDO); + + pSV->Release(); + + if (FAILED(result) || (NULL == pDO)) + return E_FAIL; + + *ppDO = pDO; + + return S_OK; +} + +HRESULT CLBPPreviewingFileDialog::GetFolderViewObject(IFolderView ** ppFV) +{ + *ppFV = NULL; + + IShellBrowser* pSB = GetBrowserInterface(); + if (NULL == pSB) + return E_FAIL; + + IShellView* pSV = NULL; + HRESULT result = pSB->QueryActiveShellView(&pSV); + if (FAILED(result) || (NULL == pSV)) + return E_FAIL; + + result = pSV->QueryInterface(IID_IFolderView, (LPVOID*)ppFV); + + return result; +} + +LPITEMIDLIST CLBPPreviewingFileDialog::GetSelectionPidl(LPDATAOBJECT pDO) +{ + FORMATETC formatEtc = { 0 }; + formatEtc.lindex = -1; + formatEtc.tymed = TYMED_HGLOBAL; + formatEtc.dwAspect = DVASPECT_CONTENT; + formatEtc.cfFormat = (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_SHELLIDLIST); + + HRESULT result = pDO->QueryGetData(&formatEtc); + if (FAILED(result)) + return NULL; + + STGMEDIUM stg = { 0 }; + result = pDO->GetData(&formatEtc, &stg); + if (FAILED(result)) + return NULL; + + LPITEMIDLIST pidl = NULL; + + if (stg.tymed == TYMED_HGLOBAL) + { + LPIDA pida = (LPIDA)::GlobalLock(stg.hGlobal); + if (pida->cidl > 0) + { + // Single selection. + LPCITEMIDLIST pidlFolder = (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0]); + LPCITEMIDLIST pidlFile = (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[1]); + if (pidlFolder && pidlFile) + { + CLBPPidlMgr pm; + pidl = pm.Concatenate(pidlFolder, pidlFile); + } + } + + ::GlobalUnlock(stg.hGlobal); + } + + ReleaseStgMedium(&stg); + + return pidl; +} + +void CLBPPreviewingFileDialog::OnFileNameChange() +{ + CFileDialog::OnFileNameChange(); + + m_progress.SetPos(0); + + m_pPreview->SetBitmap(NULL); + + if (ShowPreviewOn()) + { + const HBITMAP hBitmap = GetPreviewBitmap(CSize(m_iPreviewSize, m_iPreviewSize), m_progress); + + m_pPreview->SetBitmap(hBitmap); + + m_progress.SetPos(NULL == hBitmap ? 0 : 100); + } +} + +HBITMAP CLBPPreviewingFileDialog::GetPreviewBitmap(const CSize& size, CProgressCtrl& progress) +{ + IDataObject* pDO = NULL; + if (FAILED(GetSelectionObject(&pDO, true))) + return NULL; + + m_progress.SetPos(10); + + LPITEMIDLIST pidl = GetSelectionPidl(pDO); + + pDO->Release(); + + if (NULL == pidl) + return NULL; + + m_progress.SetPos(20); + + const HBITMAP hBitmap = ExtractThumbnailImage(size, pidl); + + m_progress.SetPos(80); + + CLBPPidlMgr pm; + pm.Delete(pidl); + + return hBitmap; +} + +HBITMAP CLBPPreviewingFileDialog::ExtractThumbnailImage(const CSize& size, LPCITEMIDLIST pidl) +{ + IShellFolder* pSFDesktop = NULL; + HRESULT result = SHGetDesktopFolder(&pSFDesktop); + if (FAILED(result)) + return NULL; + + CLBPPidlMgr pm; + LPITEMIDLIST pidlFolder = pm.Truncate(pidl); + + IShellFolder* pSF = NULL; + + const int iLength = pm.GetPidlLength(pidlFolder); + if (iLength > 0) + { + result = pSFDesktop->BindToObject(pidlFolder, NULL, IID_IShellFolder, (LPVOID*)&pSF); + pSFDesktop->Release(); + } + else + { + // The object is on the desktop itself. + result = S_OK; + pSF = pSFDesktop; + } + + pm.Delete(pidlFolder); + + if (FAILED(result)) + return NULL; + + LPCITEMIDLIST cpidlItem = pm.GetLastItem(pidl); + + SFGAOF s = SFGAO_FOLDER | SFGAO_LINK; + result = pSF->GetAttributesOf(1, &cpidlItem, &s); + if (FAILED(result)) + return NULL; + + if (0 != (s & SFGAO_FOLDER)) + return NULL; + + if (0 != (s & SFGAO_LINK)) + return NULL; + + LPITEMIDLIST pidlItem = pm.Copy(pm.GetLastItem(pidl)); + + IExtractImage* pIEI = NULL; + result = pSF->GetUIObjectOf(NULL, 1, (LPCITEMIDLIST*)&pidlItem, IID_IExtractImage, NULL, (LPVOID*)&pIEI); + pm.Delete(pidlItem); + pSF->Release(); + + if (FAILED(result)) + return NULL; + + // 25th February 2016, John Croudy. RH-33114: Use IEIFLAG_QUALITY to prevent use of a JPEG's internal thumbnail. + WCHAR wszBuffer[MAX_PATH]; + DWORD dwPriority = 1; + DWORD dwFlags = IEIFLAG_CACHE | IEIFLAG_QUALITY; + result = pIEI->GetLocation(wszBuffer, MAX_PATH, &dwPriority, &size, 24, &dwFlags); + + if (FAILED(result)) + { + pIEI->Release(); + return NULL; + } + + HBITMAP hBitmap = NULL; + result = pIEI->Extract(&hBitmap); + pIEI->Release(); + + if (FAILED(result)) + return NULL; + + return hBitmap; +} + +void CLBPPreviewingFileDialog::OnFolderChange() +{ + CFileDialog::OnFolderChange(); + + OnFileNameChange(); +} + +void CLBPPreviewingFileDialog::OnCheckPreview() +{ + const bool bShowPreview = (0 == m_checkPreview.GetCheck()) ? false : true; + SetShowPreviewOn(bShowPreview); + + OnFileNameChange(); +} + +#endif diff --git a/LBPPreviewingFileDialog.h b/LBPPreviewingFileDialog.h new file mode 100644 index 0000000..d4820ef --- /dev/null +++ b/LBPPreviewingFileDialog.h @@ -0,0 +1,72 @@ + +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#if !defined LBPLIB_DISABLE_MFC_CONTROLS + +class CLBPBitmapPreview; + +class LBPEXPORT CLBPPreviewingFileDialog : public CFileDialog +{ +public: + CLBPPreviewingFileDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt=NULL, LPCTSTR lpszFileName=NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter=NULL, CWnd* pParentWnd=NULL); + virtual ~CLBPPreviewingFileDialog(); + + int PreviewSize(void) const; + void SetPreviewSize(int iSize); + + virtual INT_PTR DoModal(); + + virtual bool ShowPreviewOn(void) const { return m_bShowPreview; } + virtual void SetShowPreviewOn(bool b) { m_bShowPreview = b; } + +protected: + afx_msg void OnCheckPreview(); + DECLARE_MESSAGE_MAP() + +protected: + // Override these functions to provide custom resource support (ie - not LBPLibResources.dll) + virtual HINSTANCE ResourceInstance() const; + +/* case 0: return IDD_LBP_PREVIEWING_FILE_DIALOG; + case 1: return IDC_CHECK_PREVIEW; + case 2: return IDC_STATIC_PREVIEW; + case 3: return IDC_PROGRESS1; +*/ virtual DWORD ResourceID(DWORD OriginalID) const; + +protected: + // Override this to get the preview bitmap in a different way than using the shell. + virtual HBITMAP GetPreviewBitmap(const CSize& size, CProgressCtrl& progress); + +protected: + virtual void DoDataExchange(CDataExchange* pDX); + virtual BOOL OnInitDialog(); + virtual void OnSize(UINT nType, int cx, int cy); + virtual void OnFileNameChange(); + virtual void OnFolderChange(); + virtual BOOL OnCommand(WPARAM wParam,LPARAM lParam); + + IShellBrowser* GetBrowserInterface(void) const; + LPITEMIDLIST GetSelectionPidl(LPDATAOBJECT pDO); + HRESULT GetFolderViewObject(IFolderView** ppFV); + HRESULT GetSelectionObject(IDataObject** ppDO, bool bSelected); + HBITMAP ExtractThumbnailImage(const CSize& size, LPCITEMIDLIST pidl); + + CButton& PreviewCheck(void) { return m_checkPreview; } + +private: + CStatic m_static; + CButton m_checkPreview; + int m_iPreviewSize; + CProgressCtrl m_progress; + CLBPBitmapPreview* m_pPreview; + int m_iControls[4]; + + static bool m_bShowPreview; +}; + +#endif diff --git a/LBPRhException.h b/LBPRhException.h new file mode 100644 index 0000000..8517b79 --- /dev/null +++ b/LBPRhException.h @@ -0,0 +1,27 @@ + +#pragma once +#ifndef LBPRHLIB +#error RHLIB header included in non-Rhino compile +#endif + +#include "LBPString.h" + +class CLBPRhExceptionMessage +{ +public: + CLBPRhExceptionMessage(const CLBPString& sMess, const TCHAR * tszFile, int iLine) + { + m_sMess.Format("%s\n\nFile: %s\n\nLine: %u", sMess.Mbcs(), tszFile, iLine); + } + +public: + CLBPString m_sMess; +}; + +class CLBPRhRunTimeException : public CLBPException +{ +public: + CLBPRhRunTimeException(const CLBPString& sMess) : CLBPException(sMess) { } + + virtual void Report(void) const; +}; diff --git a/LBPRhObjectSelection.cpp b/LBPRhObjectSelection.cpp new file mode 100644 index 0000000..7532e58 --- /dev/null +++ b/LBPRhObjectSelection.cpp @@ -0,0 +1,301 @@ +// GAWK-MARKER + +#include "stdafx.h" + +#ifdef LBPRHLIB + +#include "LBPRhObjectSelection.h" +#include "LBPLibUtilities.h" + +static IRhinoPropertiesPanelPageEventArgs* Args(const IRhinoPropertiesPanelPage* page) +{ + return IRhinoPropertiesPanelPageEventArgs::FromPage(page); +} + +int LBPRhCountMeshableObjects(const IRhinoPropertiesPanelPage* pDialogPage) +{ + IRhinoPropertiesPanelPageEventArgs* args = IRhinoPropertiesPanelPageEventArgs::FromPage(pDialogPage); + if (args == nullptr) + return 0; + const int iCount = args->ObjectCount(); + int iMeshableCount = 0; + + for (int i = 0; i < iCount; i++) + { + const CRhinoObject* pObject = args->ObjectAt(i); + if(pObject && pObject->IsMeshable(ON::render_mesh)) + { + iMeshableCount++; + } + } + + return iMeshableCount; +} + +bool LBPRhContainsMeshableObjects(const IRhinoPropertiesPanelPage* pDialogPage) +{ + IRhinoPropertiesPanelPageEventArgs* args = IRhinoPropertiesPanelPageEventArgs::FromPage(pDialogPage); + if (args == nullptr) + return 0; + + const int iCount = args->ObjectCount(); + + for (int i = 0; i < iCount; i++) + { + const CRhinoObject* pObject = args->ObjectAt(i); + if(pObject && pObject->IsMeshable(ON::render_mesh)) + { + return true; + } + } + + return false; +} + +CLBPRhObjectSelection::CLBPRhObjectSelection(const CRhinoDoc& doc) + : m_runtime_document_serial_number(doc.RuntimeSerialNumber()) +{ + m_iIterationIndex = -1; +} + +CLBPRhObjectSelection::CLBPRhObjectSelection(const IRhinoPropertiesPanelPage* pDialogPage, bool bMeshableOnly) + : m_aObjects(Args(pDialogPage) ? Args(pDialogPage)->ObjectCount() : 1), + m_runtime_document_serial_number(Args(pDialogPage) ? Args(pDialogPage)->DocumentRuntimeSerialNumber() : CRhinoDoc::NullRuntimeSerialNumber) +{ + m_iIterationIndex = -1; + + IRhinoPropertiesPanelPageEventArgs* args = IRhinoPropertiesPanelPageEventArgs::FromPage(pDialogPage); + const int iCount = args == nullptr ? 0 : args->ObjectCount(); + + m_aObjects.SetCapacity(iCount); + + for (int i = 0; i < iCount; i++) + { + const CRhinoObject* pObject = args->ObjectAt(i); + if(pObject) + { + if(!bMeshableOnly || pObject->IsMeshable(ON::render_mesh)) + { + m_aObjects.Append(pObject->Attributes().m_uuid); + } + } + } +} + +CLBPRhObjectSelection::CLBPRhObjectSelection(const CRhinoDoc& doc, bool bMeshableOnly) + : m_aObjects(CountSelectedObjects(doc)), + m_runtime_document_serial_number(doc.RuntimeSerialNumber()) +{ + CRhinoObjectIterator it(doc, CRhinoObjectIterator::normal_or_locked_objects, CRhinoObjectIterator::active_and_reference_objects); + const CRhinoObject* pObject = it.First(); + + while(pObject != NULL) + { + if(pObject->IsSelected() && (!bMeshableOnly || pObject->IsMeshable(ON::render_mesh))) + { + m_aObjects.Append(pObject->Attributes().m_uuid); + } + + pObject = it.Next(); + } +} + + + +CLBPRhObjectSelection::CLBPRhObjectSelection(const IRhinoPropertiesPanelPage* pDialogPage, ON::object_type ot) + : m_aObjects(Args(pDialogPage) ? Args(pDialogPage)->ObjectCount() : 1), + m_runtime_document_serial_number(Args(pDialogPage) ? Args(pDialogPage)->DocumentRuntimeSerialNumber() : CRhinoDoc::NullRuntimeSerialNumber) +{ + m_iIterationIndex = -1; + + auto* args = Args(pDialogPage); + const int iCount = args == nullptr ? 0 : args->ObjectCount(); + + m_aObjects.SetCapacity(iCount); + + for (int i = 0; i < iCount; i++) + { + const CRhinoObject* pObject = args->ObjectAt(i); + if(pObject) + { + if(ot & pObject->ObjectType()) + { + m_aObjects.Append(pObject->Attributes().m_uuid); + } + } + } +} + +CLBPRhObjectSelection::CLBPRhObjectSelection(const CLBPRhObjectSelection& sel) +: m_aObjects(sel.Count()), + m_runtime_document_serial_number(sel.m_runtime_document_serial_number) +{ + m_iIterationIndex = -1; + + const int iCount = sel.m_aObjects.Count(); + + m_aObjects.SetCapacity(iCount); + + for (int i = 0; i < iCount; i++) + { + m_aObjects.Append(sel.m_aObjects[i]); + } +} + +CLBPRhObjectSelection::CLBPRhObjectSelection(const CRhinoDoc& doc, const ON_SimpleArray& aObjects) + : m_aObjects(aObjects.Count()), + m_runtime_document_serial_number(doc.RuntimeSerialNumber()) + +{ + m_iIterationIndex = -1; + + const int iCount = aObjects.Count(); + m_aObjects.SetCapacity(iCount); + + for (int i=0; i= m_aObjects.Count()) + return NULL; + + const CRhinoDoc* pDoc = LBPRhDocFromRuntimeSerialNumber(m_runtime_document_serial_number); + if (pDoc != NULL) + { + return pDoc->LookupObject(m_aObjects[m_iIterationIndex]); + } + return NULL; +} + + +DWORD CLBPRhObjectSelection::CRC(void) const +{ + //May not work if the objects are in a different order + const int iCount = m_aObjects.Count(); + if(0==iCount) return 0; + + UUID* pBuffer = new UUID[iCount]; + + int i; + + for (i = 0; i < iCount; i++) + { + const UUID uuid = m_aObjects[i]; + memcpy((void*)(&pBuffer[i]), &uuid, sizeof(m_aObjects[i])); + } + + DWORD dw = 123456; + + dw = ON_CRC32(dw, sizeof(m_aObjects[i]) * iCount, (void*)pBuffer); + + delete [] pBuffer; + return dw; +} + +void CLBPRhObjectSelection::BoundingBox(ON_BoundingBox& boxOut) const +{ + for (int i = 0; i < m_aObjects.Count(); i++) + { + auto* pDoc = CRhinoDoc::FromRuntimeSerialNumber(DocumentSerialNumber()); + if (pDoc) + { + const CRhinoObject* pObject = pDoc->LookupObject(m_aObjects[i]); + if (NULL != pObject) + { + if (0 == i) + { + boxOut = pObject->BoundingBox(); + } + else + { + boxOut.Union(pObject->BoundingBox()); + } + } + } + } +} + +bool CLBPRhObjectSelection::operator==(const CLBPRhObjectSelection& selection) +{ + return CRC() == selection.CRC(); +} + + + + + + + +CLBPRhObjectList::CLBPRhObjectList(const CRhinoDoc& doc) + : m_runtime_document_serial_number(doc.RuntimeSerialNumber()) +{ +} + +int CLBPRhObjectList::Count(void) const +{ + ASSERT(m_aObjects.Count() == (int)m_mapObjects.Count()); + return m_aObjects.Count(); +} + +void CLBPRhObjectList::AddObjects(const CLBPRhObjectSelection& sel) +{ + const CRhinoObject* pObject = sel.First(); + while (NULL != pObject) + { + Add(pObject); + + pObject = sel.Next(); + } +} + +void CLBPRhObjectList::Add(const CRhinoObject* pObject) +{ + const UUID& uuid = pObject->Attributes().m_uuid; + + int i; + if (m_mapObjects.Lookup(uuid, i)) + return; + + m_aObjects.Append(uuid); + m_mapObjects.SetAt(uuid, 0); +} + +void CLBPRhObjectList::Clear(void) +{ + m_aObjects.Destroy(); + m_mapObjects.Destroy(); +} + + + +const CRhinoObject* CLBPRhObjectList::Object(int iIndex) const +{ + if ((iIndex < 0) || (iIndex >= m_aObjects.Count())) + return NULL; + + const CRhinoDoc* pDoc = LBPRhDocFromRuntimeSerialNumber(m_runtime_document_serial_number); + if (pDoc != NULL) + { + return pDoc->LookupObject(m_aObjects[iIndex]); + } + return NULL; +} + + +#endif diff --git a/LBPRhObjectSelection.h b/LBPRhObjectSelection.h new file mode 100644 index 0000000..e5415b3 --- /dev/null +++ b/LBPRhObjectSelection.h @@ -0,0 +1,63 @@ +// GAWK-MARKER + +#pragma once +#ifndef LBPRHLIB +#error RHLIB header included in non-Rhino compile +#endif + +class CLBPRhObjectChangeTracker +{ +public: + CLBPRhObjectChangeTracker() { m_dwSignature = 0; } + +public: + DWORD m_dwSignature; +}; + +class CLBPRhObjectSelection +{ +public: + CLBPRhObjectSelection(const class IRhinoPropertiesPanelPage* pDialogPage, bool bMeshableOnly); + CLBPRhObjectSelection(const class IRhinoPropertiesPanelPage* pDialogPage, ON::object_type types); + CLBPRhObjectSelection(const CRhinoDoc& doc, bool bMeshableOnly); //Use the document iterator + CLBPRhObjectSelection(const CLBPRhObjectSelection& sel); + CLBPRhObjectSelection(const CRhinoDoc& doc, const ON_SimpleArray& aObjects); + CLBPRhObjectSelection(const CRhinoDoc& doc); //Create an empty selection + + bool operator==(const CLBPRhObjectSelection& selection); + bool operator!=(const CLBPRhObjectSelection& selection) { return !(*this==selection); } + + int Count(void) const; + const CRhinoObject * First(void) const; + const CRhinoObject * Next(void) const; + void BoundingBox(ON_BoundingBox& boxOut) const; + DWORD CRC(void) const; + unsigned int DocumentSerialNumber(void) const { return m_runtime_document_serial_number; } + +private: + mutable int m_iIterationIndex; + ON_SimpleArray m_aObjects; + const unsigned int m_runtime_document_serial_number; +}; + +#include "ON_SimpleMap.h" +class CLBPRhObjectList +{ +public: + CLBPRhObjectList(const CRhinoDoc& doc); + + int Count(void) const; + const CRhinoObject* Object(int iIndex) const; + void Add(const CRhinoObject* pObject); + void AddObjects(const CLBPRhObjectSelection& sel); + void Clear(void); + unsigned int DocumentSerialNumber(void) const { return m_runtime_document_serial_number; } + +private: + ON_SimpleArray m_aObjects; + ON_SimpleUuidMap m_mapObjects; + const unsigned int m_runtime_document_serial_number; +}; + +int LBPRhCountMeshableObjects(const IRhinoPropertiesPanelPage* pDialogPage); +bool LBPRhContainsMeshableObjects(const IRhinoPropertiesPanelPage* pDialogPage); diff --git a/LBPRhObjectWrapper.h b/LBPRhObjectWrapper.h new file mode 100644 index 0000000..516db64 --- /dev/null +++ b/LBPRhObjectWrapper.h @@ -0,0 +1,1060 @@ +#pragma once +#ifndef LBPRHLIB +#error RHLIB header included in non-Rhino compile +#endif + +#ifdef _DEBUG +#include "LBPRhException.h" +#endif + +#include "LBPLibUtilities.h" +#include "LBP_UUID.h" +#include "ILBPRhWrapper.h" +#include "ON_SimpleMap.h" + +#if defined _MSC_VER && _MSC_VER < 1400 +#define RHWRAP_VER 3 +#error "This should no longer happen" +#else +#define RHWRAP_VER 4 +#endif + + +#if RHWRAP_VER == 4 + enum lbprh_wrapper_eType { lwt_geometry,/*lwt_material,lwt_light,*/lwt_attributes,lwt_object, }; +#endif +#if RHWRAP_VER == 3 + enum lbprh_wrapper_eType { lwt_geometry,/*lwt_material,lwt_light,*/ }; +#endif + +template class CLBPRhObjectWrapper : public ILBPRhWrapper +{ +public: + + CLBPRhObjectWrapper(const CRhinoDoc& doc, const UUID& uuidObject, lbprh_wrapper_eType type = lwt_geometry); + CLBPRhObjectWrapper(const CRhinoObject * pRhinoObject, lbprh_wrapper_eType type = lwt_geometry); + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + CLBPRhObjectWrapper(const CRhinoDoc& doc, const CRhinoObjectAttributes* pAttributes); +#endif +//END RHINO 4+ SPECIFIC CODE + + virtual ~CLBPRhObjectWrapper(); + + virtual eLBPRhWrapperType WrapperType(void) const { return lbprh_wt_object; } + + //type = attributes, returns CRhinoObjectAttributes* + //type = geometry, returns CRhinoObject* + //type = object, returns CRhinoObject* + inline const ON_Object* OriginalObject() const {return m_pObject;} + inline UUID OriginalUuid() const { return m_uuid; } + + //Cause the wrapper to put the object into a writable state + inline void Modify() { UserDataToModify(); } + + + //DEPRECATED - use "Suspend" instead + bool TransformObject(const ON_Xform& xform); + //Forces the object to be committed and replaced immediately - ie, bypasses the lazy update + //feature. + //Implemented using CRhinoDoc::TransformObject + + virtual CRhinoObject* ModifyObject(); + //Force the wrapper to modify the actual object + //This is useful when the type does not cause a CRhinoObject to be + //cloned - eg. Attributes. After this, the object will be + //closed with a full "ReplaceObject" call instead of a ModifyObjectAttributes + //Note: Multiple calls to this function do nothing, so you can use the + //function to get the modified object over and over + + CRhinoObject* ModifiedObject() const; + //Call this one to get the ready-modified CRhinoObject clone + //Will return NULL if this clone is not a CRhinoObject or there are no modifications + + //Helper functions to get around the const_casting problem + //inherent in modifying duplicated objects + //Note "ModifiedAttributes" does not require ModifyObject to be called first. + //"ModifiedGeometry" does require a call to ModifyObject first. + CRhinoObjectAttributes* ModifiedAttributes() const; + ON_Geometry* ModifiedGeometry() const; + + //Nasty access to reference count for people using the back-door + //modification capture method + bool LastReference(void) const { return 1==m_dwRefCount; } + + inline bool ModificationsPending() const { return NULL != m_pClone;} + //Determines whether there is a current clone + //(ie - Modify() or UserDataToModify() has been called) + + //Note - the following 2 functions have no effect if the original object was not in the database. + //In that case, modifications are made directly on the object. + virtual void UndoModifications(); //Revert the wrapper back to the read-state, and undo all modifications + + //Note -the function cannot be correctly virtualized because it is called from the destructor + /*virtual*/bool CommitChanges(); //Call to write changes to the document before destruction + //leaves the wrapper in a re-usabled state +public: + class Suspend + { + public: + Suspend(CLBPRhObjectWrapper& wrapper) + : m_wrapper(wrapper) + { m_wrapper.ReleaseObject(); } + + ~Suspend() {m_wrapper.RecaptureObject();} + + const CRhinoObject* Object() const { return m_wrapper.OriginalRhinoObject(); } + UUID Uuid() const { return m_wrapper.OriginalUuid(); } + private: + CLBPRhObjectWrapper& m_wrapper; + }; + + +public: + virtual bool RemoveData(); + virtual bool UserDataPresent() const; + +public: + //Copying data + //Ensure that a wrapper is not constructed for the target + //(debug builds will prohibit this) + bool CopyToObject(const CRhinoObject* pObject) const; + bool CopyToObject(const UUID& uuidObject) const; + + //Ensure that a wrapper is not constructed for the source + //(debug builds will prohibit this) + bool CopyFromObject(const CRhinoObject* pObject); + bool CopyFromObject(const UUID& uuidObject); + + static const T* DefaultData() { return &g_ud; } + +protected: + + //Implement your setters and getters based on these functions + virtual const T* UserData(eLBPRhWrapper_DefaultUse du = lbprh_dont_use_defaults) const; + virtual T* UserDataToModify(void); + + //Override this to get modification events + virtual void OnModification() const {}; + + const CRhinoObject* OriginalRhinoObject() const; + + CRhinoDoc* Document() const { return LBPRhDocFromRuntimeSerialNumber(m_runtime_doc_serial_number); } + +private: + //IMPLEMENTATION DETAILS ONLY + //Null attaches blank user data, otherwise the data is copied + T* AttachData(const T* pExistingData = NULL); + + void CommonCtor(const ON_Object* pObject, lbprh_wrapper_eType type); + void Clone() const; + ON_Object* GetTarget() const; + bool OriginalObjectIsCRhinoObject() const; + + //const CRhinoObject* OriginalRhinoObject() const; + const CRhinoObjectAttributes* OriginalAttributes() const; + + void RenewOriginalObject(const UUID& uuid); + + //Make copying illegal + CLBPRhObjectWrapper(const CLBPRhObjectWrapper& src); + const CLBPRhObjectWrapper& operator=(const CLBPRhObjectWrapper& src); + +private: + const ON_Object* m_pObject; + mutable ON_Object* m_pClone; + lbprh_wrapper_eType m_type; + bool m_bObjectInDatabase; + UUID m_uuid; + const unsigned int m_runtime_doc_serial_number; + +private: + //For defaults implementation + static T g_ud; + static bool g_bDefaultsSet; + +//Reference counted map implementation +public: + DWORD AddRef() { return ++m_dwRefCount; } + DWORD ReleaseRef() { return --m_dwRefCount; } + +private: + friend class Suspend; + void ReleaseObject(); + void RecaptureObject(); + +private: + DWORD m_dwRefCount; + +}; + + + +template T CLBPRhObjectWrapper::g_ud; +template bool CLBPRhObjectWrapper::g_bDefaultsSet = false; + + +////////////////////////////////////////////////////////////////////////////////// +// Template implementation below + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////////////////// +// +// Template implementation +// +template +CLBPRhObjectWrapper::CLBPRhObjectWrapper(const CRhinoDoc& doc, const UUID& uuidObject, lbprh_wrapper_eType type) + : m_runtime_doc_serial_number(LBPRhDocRuntimeSerialNumber(doc)) +{ + const CRhinoObject* pObject = doc.LookupObject(uuidObject); + ASSERT(NULL != pObject); + + m_uuid = uuidObject; + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + if(lwt_attributes == type) + CommonCtor(&pObject->Attributes(), type); + else +#endif +//END RHINO 4+ SPECIFIC CODE + CommonCtor(pObject, type); +} + +template +CLBPRhObjectWrapper::CLBPRhObjectWrapper(const CRhinoObject* pObject, lbprh_wrapper_eType type) + : m_runtime_doc_serial_number(LBPRhObjectDocSerialNumber(pObject)) +{ +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + if(lwt_attributes == type) + CommonCtor(&pObject->Attributes(), type); + else +#endif +//END RHINO 4+ SPECIFIC CODE + CommonCtor(pObject, type); + + //This object may not be in the database. + //Test to see if it is: + + //Done here because it's more efficient - otherwise we have to do two lookups + //in the UUID constructor unnecessarily +// if(!IsObjectInDatabase(pObject)) + if (NULL == pObject->Document()) + { + m_bObjectInDatabase = false; + } + + m_uuid = pObject->Attributes().m_uuid; +} + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 +template +CLBPRhObjectWrapper::CLBPRhObjectWrapper(const CRhinoDoc& doc, const CRhinoObjectAttributes* pAttributes) +: m_runtime_doc_serial_number(LBPRhDocRuntimeSerialNumber(doc)) +{ + CommonCtor(pAttributes, lwt_attributes); + m_uuid = pAttributes->m_uuid; +} +#endif +//END RHINO 4+ SPECIFIC CODE + +template +void CLBPRhObjectWrapper::CommonCtor(const ON_Object* pObject, lbprh_wrapper_eType type) +{ + m_pClone = NULL; + + if(!g_bDefaultsSet) + { + g_ud.SetToDefaultsImpl(lbprh_wt_object); + g_bDefaultsSet = true; + } + + m_type = type; + m_pObject = pObject; + m_bObjectInDatabase = true; + m_dwRefCount = 0; +} + +template +CLBPRhObjectWrapper::~CLBPRhObjectWrapper() +{ + CommitChanges(); +} + + +template +void CLBPRhObjectWrapper::UndoModifications() +{ + if(m_pClone) + { + if(m_bObjectInDatabase) + { + delete m_pClone; + } + m_pClone = NULL; + } +} + +template +void CLBPRhObjectWrapper::Clone() const +{ + ASSERT(NULL == m_pClone); + if(m_pClone) return; + + switch(m_type) + { + case lwt_geometry: + if(m_bObjectInDatabase) + m_pClone = m_pObject->Duplicate(); + else + //Const cast is OK because we know this object can be modified + m_pClone = const_cast(m_pObject); + break; + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + case lwt_object: + if(m_bObjectInDatabase) + m_pClone = m_pObject->Duplicate(); + else + //Const cast is OK because we know this object can be modified + m_pClone = const_cast(m_pObject); + break; + case lwt_attributes: + if(m_bObjectInDatabase) + m_pClone = new CRhinoObjectAttributes(*OriginalAttributes()); + else + //Const cast is OK because we know this object can be modified + m_pClone = const_cast(m_pObject); + break; +#endif +//END RHINO 4+ SPECIFIC CODE + + default: + //TODO: implement other types + break; + } + +} + +template +ON_Object* CLBPRhObjectWrapper::GetTarget() const +{ + switch(m_type) + { + case lwt_geometry: + if(m_pClone) + return const_cast(static_cast(m_pClone)->Geometry()); + else + return const_cast(OriginalRhinoObject()->Geometry()); + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + case lwt_attributes: + if(m_pClone) + return CRhinoObjectAttributes::Cast(m_pClone) ? + m_pClone + : + const_cast(&static_cast(m_pClone)->Attributes()); + else + //The original object is "attributes" in all cases + return const_cast(m_pObject); + case lwt_object: + if(m_pClone) + return m_pClone; + else + return const_cast(m_pObject); +#endif +//END RHINO 4+ SPECIFIC CODE + + default: + //TODO: Implement other types + return NULL; + } + +} + +template +bool CLBPRhObjectWrapper::RemoveData() +{ + if(!UserDataPresent()) return true; + + bool bWasCloned = false; + if (!m_pClone) + { + Clone(); + bWasCloned = true; + } + + if (!m_pClone) return NULL; + //T* pUserData = NULL; + + ON_Object* pTarget = GetTarget();//m_pClone; + if(pTarget) + { + BOOL bDetached = FALSE; + + ON_UserData* pUD = UserDataToModify(); + if (pUD != NULL) + { + bDetached = pTarget->DetachUserData(pUD); + if (bDetached) + { + //Notify clients that a modification was made and that the UserData pointer has changed + OnModification(); + } + delete pUD; + } + + return bDetached ? true : false; + } + + if(bWasCloned) + { + if(m_bObjectInDatabase) + { + delete m_pClone; + } + m_pClone = NULL; + } + + return false; +} + +template +bool CLBPRhObjectWrapper::UserDataPresent() const +{ + return (NULL == UserData(lbprh_dont_use_defaults) ? false : true); +} + +template +bool CLBPRhObjectWrapper::CopyToObject(const UUID& uuid) const +{ + CRhinoDoc* pDoc = Document(); + if (pDoc) + { + return CopyToObject(pDoc->LookupObject(uuid)); + } + return false; +} + +template +bool CLBPRhObjectWrapper::CopyToObject(const CRhinoObject* pObject) const +{ + if(!UserDataPresent()) return false; + + CLBPRhObjectWrapper target(pObject, m_type); + return NULL == target.AttachData(UserData()) ? false : true; +} + +template +bool CLBPRhObjectWrapper::CopyFromObject(const UUID& uuid) +{ + const CRhinoDoc* pDoc = Document(); + if (pDoc) + { + return CopyFromObject(pDoc->LookupObject(uuid)); + } + return false; +} + +template +bool CLBPRhObjectWrapper::CopyFromObject(const CRhinoObject* pObject) +{ + CLBPRhObjectWrapper source(pObject, m_type); + if(!source.UserDataPresent()) return false; + + return NULL == AttachData(source.UserData()) ? false : true; +} + +template +const T* CLBPRhObjectWrapper::UserData(eLBPRhWrapper_DefaultUse du) const +{ + ASSERT(NULL != m_pObject); + + const ON_Object* pTarget = GetTarget(); + if(!pTarget) return NULL; + + const T* pUD = T::Cast(pTarget->GetUserData(g_ud.m_userdata_uuid)); + if(pUD) return pUD; + + if(lbprh_use_defaults == du) + return &g_ud; + + return NULL; +} + +template +CRhinoObject* CLBPRhObjectWrapper::ModifiedObject() const +{ + if(CRhinoObject::Cast(m_pClone)) return static_cast(m_pClone); + return NULL; +} + +template +CRhinoObjectAttributes* CLBPRhObjectWrapper::ModifiedAttributes() const +{ + if(CRhinoObjectAttributes::Cast(m_pClone)) + return static_cast(m_pClone); + + if(CRhinoObject::Cast(m_pClone)) + return const_cast(&static_cast(m_pClone)->Attributes()); + + return NULL; +} + +template +ON_Geometry* CLBPRhObjectWrapper::ModifiedGeometry() const +{ + if(CRhinoObject::Cast(m_pClone)) + return const_cast(static_cast(m_pClone)->Geometry()); + return NULL; +} + +template +CRhinoObject* CLBPRhObjectWrapper::ModifyObject(void) +{ + //If we're not already in a cloned state, get cloned. + if(!ModificationsPending()) + { + Modify(); + } + + //We may already have a cloned full object. If so, return that. + if(CRhinoObject::Cast(m_pClone)) return static_cast(m_pClone); + + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + //We currently have a modification which didn't result in a cloned object + //Should never get here - but this is a placeholder for stuff which really can't happen + //line for viewport, material and layer wrappers. + if(m_type != lwt_attributes) return NULL; +#ifdef _DEBUG + if(NULL == CRhinoObjectAttributes::Cast(m_pClone)) + { + CLBPRhRunTimeException re(L"Wrong original Object type"); + throw re; + } +#endif + + const CRhinoDoc* pDoc = Document(); + const CRhinoObject* pOriginalObject = pDoc ? pDoc->LookupObject(OriginalUuid()) : NULL; + if(!pOriginalObject) return NULL; + + CRhinoObjectAttributes* pAttributesClone = static_cast(m_pClone); + CRhinoObject* pObject = pOriginalObject->Duplicate(); + if(!pObject) return NULL; + + //Copy the changes across to the newly duplicated object + const_cast(pObject->Attributes()) = *pAttributesClone; + + //Delete the old attributes + delete m_pClone; + + //Set the new clone to an actual object. + m_pClone = pObject; + return pObject; +#else + return NULL; +#endif +} + + +template +T* CLBPRhObjectWrapper::UserDataToModify(void) +{ + if (!m_pObject) return NULL; + + T* pExistingData = NULL; + + if(m_pClone) + { + //We know the data is coming from the clone, so we can + //safely cast away the const-ness to enable us to reuse + //the UserData function + pExistingData = const_cast(UserData()); + if(pExistingData) + { + return pExistingData; + } + } + + return AttachData(); +} + +template +void CLBPRhObjectWrapper::RenewOriginalObject(const UUID& uuid) +{ + const CRhinoDoc* pDoc = Document(); + if (NULL == pDoc) + { + m_pObject = NULL; + return; + } + + //THe underlying object has changed - renew the pointer + if(OriginalObjectIsCRhinoObject()) + { + m_pObject = pDoc->LookupObject(uuid); + } + else + { +#if RHWRAP_VER == 4 + //This happens in the case of attributes + m_pObject = &pDoc->LookupObject(uuid)->Attributes(); +#else + m_pObject = NULL; +#endif + } +} + +template +void CLBPRhObjectWrapper::ReleaseObject() +{ + CommitChanges(); +} + +template +void CLBPRhObjectWrapper::RecaptureObject() +{ + RenewOriginalObject(OriginalUuid()); +} + +template +bool CLBPRhObjectWrapper::TransformObject(const ON_Xform& xform) +{ + CRhinoDoc* pDoc = Document(); + LBPRhAssert(NULL != pDoc); + if(NULL == pDoc) + return false; + + //Suspend ownership of the object + Suspend o(*this); + + const UUID uuid = o.Uuid(); + + CRhinoObjRef ref(uuid); + + CRhinoObject* pNewObject = pDoc->TransformObject(ref, xform); +#if RHWRAP_VER == 4 + LBPRhAssert(NULL != pNewObject); +#endif + if(NULL == pNewObject) + return false; + + return true; +} + + + +template +bool CLBPRhObjectWrapper::CommitChanges() +{ + if (NULL == m_pClone) + return false; + + if (!m_bObjectInDatabase) + { + //In this case, the changes are already committed + m_pClone = NULL; + return true; + } + + CRhinoDoc* pDoc = Document(); + if(NULL == pDoc) + return false; + + //Attributes does not change the object pointer, so this isn't needed + //ON_UUID uuidOriginalId = OriginalUuid(); + + bool bSucceeded = false; + switch(m_type) + { + case lwt_geometry: + bSucceeded = pDoc->ReplaceObject(CRhinoObjRef(OriginalRhinoObject()), + static_cast(m_pClone) + ); + break; + +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + case lwt_object: + bSucceeded = pDoc->ReplaceObject(CRhinoObjRef(OriginalRhinoObject()), + static_cast(m_pClone) + ); + break; + case lwt_attributes: + { + //Attribute user data requires that the copy count is incremented before + //ModifyObjectAttributes is called + T* pUD = UserDataPresent() ? UserDataToModify() : NULL; + if(NULL != pUD) + { + pUD->m_userdata_copycount++; + } + + if(CRhinoObjectAttributes::Cast(m_pClone)) + { + //The clone is an attributes object + ON_UUID uuidObject = OriginalUuid(); + + const CRhinoObject* pObject = pDoc ? pDoc->LookupObject(uuidObject) : NULL; + if(pObject) + { + bSucceeded = pDoc->ModifyObjectAttributes( CRhinoObjRef(pObject), + *(static_cast(m_pClone)) + ); + + //ModifyObjectAttributes does not involve a change of ownership on the close, unlike + //the geometry or object attach sites. So at this point, we have to delete the clone if + //successful to avoid a memory leak. + if (bSucceeded) + { + delete m_pClone; + m_pClone = NULL; + } + } + } + else + { + //Copy the attributes + CRhinoObjectAttributes attr = static_cast(m_pClone)->Attributes(); + //First replace the object + bSucceeded = pDoc->ReplaceObject(CRhinoObjRef(OriginalUuid()), + static_cast(m_pClone) + ); + + //Then modify the attributes + if(bSucceeded) + { + bSucceeded = pDoc->ModifyObjectAttributes(CRhinoObjRef(OriginalUuid()), attr); + } + } + } + break; +#endif +//END RHINO 4+ SPECIFIC CODE + + default: + //TODO: Implement other types + break; + } + + if(!bSucceeded) + { + //The process did not succeed - we basically have to get rid of the modifications + if(m_bObjectInDatabase) + { + delete m_pClone; + } + } + else + { + //THe underlying object has changed - renew the pointer + RenewOriginalObject(OriginalUuid()); + } + + m_pClone = NULL; + return bSucceeded; +} + +template +bool CLBPRhObjectWrapper::OriginalObjectIsCRhinoObject() const +{ +//BEGIN RHINO 4+ SPECIFIC CODE +#if RHWRAP_VER == 4 + return m_type != lwt_attributes; +#else + return true; +#endif +//END RHINO 4+ SPECIFIC CODE +} + +template +T* CLBPRhObjectWrapper::AttachData(const T* pData) +{ + bool bWasCloned = false; + if (!m_pClone) + { + Clone(); + bWasCloned = true; + } + + if (!m_pClone) return NULL; + + // We need non const user data + ON_Object* pTarget = GetTarget();// + if(NULL == pTarget) return NULL; + + T* pCloneUserData = T::Cast(pTarget->GetUserData(g_ud.m_userdata_uuid)); + if(pCloneUserData) + { + if(pData) + { + *pCloneUserData = *pData; + } + + if(pData || bWasCloned) + { + OnModification(); + } + + return pCloneUserData; + } + + //There's no data on the original source - we have to attach new data. + //Either we're cloning for modications + //or we're attaching new data from another source + + //We are now sure we can add the user data. + pCloneUserData = new T; + if(pData) + { + *pCloneUserData = *pData; + } + + if(!pTarget->AttachUserData(pCloneUserData)) + { + delete pCloneUserData; + pCloneUserData = NULL; + + //There is a critical bug - we should never reach this point + ASSERT(false); + } + + if(NULL == pData) + { + pCloneUserData->SetToDefaultsImpl(lbprh_wt_object); + } + + OnModification(); + return pCloneUserData; +} + +template +const CRhinoObject* CLBPRhObjectWrapper::OriginalRhinoObject() const +{ + if(OriginalObjectIsCRhinoObject()) + { + return static_cast(m_pObject); + } + else + { + const CRhinoDoc* pDoc = Document(); + + return pDoc ? pDoc->LookupObject(OriginalUuid()) : NULL; + } +} + + +template +const CRhinoObjectAttributes* CLBPRhObjectWrapper::OriginalAttributes() const +{ +#if RHWRAP_VER == 4 + LBPRhAssert(!OriginalObjectIsCRhinoObject()); + return static_cast(m_pObject); +#else + return NULL; +#endif +} + + + + + + +template class ILBPRhWrapperMap +{ +public: + virtual ~ILBPRhWrapperMap() {}; + + //AddWrapperRef passes ownership to the map implementation + virtual bool AddWrapper(TWrapper* wrapper) = 0; + + //This should delete the wrapper from the map if the reference count is 0 + virtual bool RemoveWrapper(const UUID& uuid) = 0; + + //Returns the one wrapper with the UUID in question + virtual TWrapper* Wrapper(const UUID& uuid) const = 0; +}; + + +template class CLBPRhWrapperMap : public ILBPRhWrapperMap +{ +public: + + virtual bool AddWrapper(TWrapper* wrapper) + { + m_map.SetAt(wrapper->OriginalUuid(), wrapper); + return true; + } + + virtual bool RemoveWrapper(const UUID& uuid) + { + delete Wrapper(uuid); + return m_map.Remove(uuid) ? true : false; + } + + virtual TWrapper* Wrapper(const UUID& uuid) const + { + TWrapper* p = NULL; + if (!m_map.Lookup(uuid, p)) + return NULL; + return p; + } + +private: + ON_SimpleUuidMap m_map; + +}; + + +template class CLBPRhObjectWrapperRef +{ +public: + CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoDoc& doc, + const UUID& uuidObject, + lbprh_wrapper_eType type = lwt_geometry); + + CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoObject * pRhinoObject, + lbprh_wrapper_eType type = lwt_geometry); + + CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoDoc& doc, + const CRhinoObjectAttributes* pAttributes); + + ~CLBPRhObjectWrapperRef(); + +public: + + TWrapper& Wrapper(); + TWrapper* operator->() { return &Wrapper(); } + +private: + void CommonCtor(const UUID& uuid, ILBPRhWrapperMap* pMap); + + UUID m_uuid; + ILBPRhWrapperMap* m_pMap; + TWrapper* m_pWrapper; + CCriticalSection m_cs; + +}; + +template +TWrapper& CLBPRhObjectWrapperRef::Wrapper() +{ + LBPRhAssert(NULL != m_pMap); + LBPRhAssert(NULL != m_pMap->Wrapper(m_uuid)); + + return *m_pWrapper; +} + +template +void CLBPRhObjectWrapperRef::CommonCtor(const UUID& uuid, ILBPRhWrapperMap* pMap) +{ + m_pMap = pMap; + + m_uuid = uuid; + LBPRhAssert(NULL != m_pWrapper); + + m_pWrapper->AddRef(); +} + + +template +CLBPRhObjectWrapperRef::CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoDoc& doc, + const UUID& uuidObject, + lbprh_wrapper_eType type) +{ + m_pWrapper = map.Wrapper(uuidObject); + + if(NULL == m_pWrapper) + { + m_pWrapper = new TWrapper(uuidObject, type); + bool bRet = map.AddWrapper(doc, m_pWrapper); + LBPRhAssert(bRet); + } + + CommonCtor(uuidObject, &map); + +} + +template +CLBPRhObjectWrapperRef::CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoObject * pRhinoObject, + lbprh_wrapper_eType type) +{ + LBPRhAssert(NULL != pRhinoObject); + + UUID uuid = pRhinoObject->Attributes().m_uuid; + + m_pWrapper = map.Wrapper(uuid); + + if(NULL == m_pWrapper) + { + m_pWrapper = new TWrapper(pRhinoObject, type); + bool bRet = map.AddWrapper(m_pWrapper); + LBPRhAssert(bRet); + } + + CommonCtor(uuid, &map); +} + + +template +CLBPRhObjectWrapperRef::CLBPRhObjectWrapperRef(ILBPRhWrapperMap& map, + const CRhinoDoc& doc, + const CRhinoObjectAttributes* pAttributes) +{ + m_cs.Lock(); + LBPRhAssert(NULL != pAttributes); + + UUID uuid = pAttributes->m_uuid; + + m_pWrapper = map.Wrapper(uuid); + + if(NULL == m_pWrapper) + { + m_pWrapper = new TWrapper(doc, pAttributes); + bool bRet = map.AddWrapper(m_pWrapper); + LBPRhAssert(bRet); + } + + CommonCtor(uuid, &map); + m_cs.Unlock(); +} + + +template +CLBPRhObjectWrapperRef::~CLBPRhObjectWrapperRef() +{ + m_cs.Lock(); + LBPRhAssert(NULL != m_pMap); + LBPRhAssert(NULL != m_pWrapper); + + if(m_pWrapper != NULL) + { + if(0 == m_pWrapper->ReleaseRef()) + { + m_pMap->RemoveWrapper(m_uuid); + } + } + m_cs.Unlock(); +} diff --git a/LBPRh_XMLUserData.cpp b/LBPRh_XMLUserData.cpp new file mode 100644 index 0000000..eeb77c0 --- /dev/null +++ b/LBPRh_XMLUserData.cpp @@ -0,0 +1,109 @@ + +#include "stdafx.h" + +#ifdef LBPRHLIB + +#include "LBPRh_XMLUserData.h" +//#include "LBPRhUtilities.h" +#include "LBP_XML.h" +#include "LBP_UTF.h" +#include "ILBPRhWrapper.h" + +#ifdef RHINO_V6_READY +#define ON_BOOL32 bool +#endif + +ON_BOOL32 CLBPRh_XMLUserData::Read(ON_BinaryArchive& archive) +{ + // Read the version number. + int iVersion = 0; + if (!archive.ReadInt(&iVersion)) + return FALSE; + + // Cannot load user data with a version number greater than mine. + if (iVersion > Version()) + { + ReportVersionError(); + return FALSE; + } + + // Clear any existing XML data. + XMLRootForWrite().Clear(); + + if (1 == iVersion) + { + // Original version. +// LBPRH_TRACE("RHLIB: Begin version 1 (uncompressed) loading\n"); + + // Read the archive into a string. + ON_wString s; + if (!archive.ReadString(s)) + return FALSE; + + // Read the string into the XML root. + XMLRootForWrite().ReadFromStreamNoThrow(s); + } + else + { + // UTF8 version. +// LBPRH_TRACE("RHLIB: Begin version 2 (UTF8) loading\n"); + + // Read the length of the UTF8 buffer from the archive. + int iLength = 0; + if (!archive.ReadInt(&iLength)) + return FALSE; + + // Read the UTF8 buffer from the archive. + BYTE* pBuffer = new BYTE[iLength+1]; + bool bOk = archive.ReadChar(iLength, pBuffer); + if (bOk) + { + // Convert the UTF8 data to UTF16 and read it into the root node. + CLBP_UTF u; + pBuffer[iLength] = 0; // Terminator. + u.SetUTF8(pBuffer); + XMLRootForWrite().ReadFromStreamNoThrow(u.AsWide()); + } + delete[] pBuffer; + + if (!bOk) + return FALSE; + } + +// LBPRH_TRACE("RHLIB: Loading complete\n"); + + return TRUE; +} + +ON_BOOL32 CLBPRh_XMLUserData::Write(ON_BinaryArchive& archive) const +{ +// LBPRH_TRACE("RHLIB: Begin version 2 (UTF8) saving\n"); + + // Write the version number to the archive. + if (!archive.WriteInt(Version())) + return FALSE; + + // Convert the XML string to UTF8. + CLBP_UTF u; + u.SetString(XMLRootForRead().String()); + const BYTE* pUTF8 = u.UTF8(); + + // Write the length of the UTF8 buffer to the archive. + const int iLength = (int)(strlen(reinterpret_cast(pUTF8))); + if (!archive.WriteInt(iLength)) + return FALSE; + + // Write the UTF8 buffer to the archive. + if (!archive.WriteChar(iLength, pUTF8)) + return FALSE; + +// LBPRH_TRACE("RHLIB: Saving complete\n"); + + return TRUE; +} + +#ifdef RHINO_V6_READY +#undef ON_BOOL32 +#endif + +#endif diff --git a/LBPRh_XMLUserData.h b/LBPRh_XMLUserData.h new file mode 100644 index 0000000..37dba51 --- /dev/null +++ b/LBPRh_XMLUserData.h @@ -0,0 +1,212 @@ + +#pragma once + +#ifndef LBPRHLIB +#error RHLIB header included in non-Rhino compile +#endif + +class CLBPVariant; +class CLBP_XMLNode; +class CLBP_XMLRootNode; +class CLBP_XMLProperty; + +#include "ILBPRhWrapper.h" +#include "LBPRh_XMLUserData.h" +//#include "LBPRhUtilities.h" +#include "LBP_XML.h" +#include "LBP_UTF.h" + +template +class TLBPRh_XMLUserData : public BASE +{ +public: + TLBPRh_XMLUserData() + { + m_pXMLRoot = new CLBP_XMLRootNode_RC; + m_wt = lbprh_wt_none; + } + + TLBPRh_XMLUserData(const TLBPRh_XMLUserData& ud) : BASE(ud) + { + m_pXMLRoot = new CLBP_XMLRootNode_RC; + m_wt = lbprh_wt_none; + } + + virtual ~TLBPRh_XMLUserData() + { + delete m_pXMLRoot; + } + + TLBPRh_XMLUserData& operator = (const TLBPRh_XMLUserData& ud) + { + BASE::operator = (ud); // CRITICAL - Be sure to call base class. + *m_pXMLRoot = *ud.m_pXMLRoot; + + return *this; + } + + virtual void AssignFast(const TLBPRh_XMLUserData& ud) + { +#ifdef USE_REFERENCE_COUNTED_ROOTNODE + operator = (ud); +#else + BASE::operator = (ud); // CRITICAL - Be sure to call base class. + m_pXMLRoot->AssignFast(*ud.m_pXMLRoot); +#endif + } + + CLBP_XMLRootNode& XMLRoot(void) const { return m_pXMLRoot->Node(); } + +#ifdef USE_REFERENCE_COUNTED_ROOTNODE + const CLBP_XMLRootNode& XMLRootForRead(void) const { return m_pXMLRoot->NodeForRead(); } // TEMP for compatibility. + CLBP_XMLRootNode& XMLRootForWrite(void) const { return m_pXMLRoot->NodeForWrite(); } // TEMP for compatibility. +#else + const CLBP_XMLRootNode& XMLRootForRead(void) const { return m_pXMLRoot->Node(); } // TEMP for compatibility. + CLBP_XMLRootNode& XMLRootForWrite(void) const { return m_pXMLRoot->Node(); } // TEMP for compatibility. +#endif + + CLBP_XMLProperty * Property(const WCHAR* wszXMLPath, const WCHAR* wszPropertyName) const + { + CLBP_XMLProperty* pProp = InternalProperty(wszXMLPath, wszPropertyName); + if (NULL == pProp) + { + // Failed to get the property. This probably means that the XML strings have been changed + // and this is old user data. Reset to defaults. + SetToDefaults(); + + // *-NOTE-* This can still return NULL if the path and/or property were not set + // as one of the defaults. + return InternalProperty(wszXMLPath, wszPropertyName); + } + + return pProp; + } + + CLBPVariant Value(const WCHAR * wszXMLPath, const WCHAR * wszPropertyName = L"") const + { + const CLBP_XMLProperty* pProp = Property(wszXMLPath, wszPropertyName); + if (pProp != NULL) + { + return pProp->GetValue(); + } + + throw CException(); + } + + void SetValue(const WCHAR * wszXMLPath, const WCHAR * wszPropertyName, const CLBPVariant& value) + { + CLBP_XMLProperty* pProp = Property(wszXMLPath, wszPropertyName); + if (pProp != NULL) + { + pProp->SetValue(value); + } + else + { + throw CException(); + } + } + void SetValue(const WCHAR * wszXMLPath, const CLBPVariant& value) + { + SetValue(wszXMLPath, L"", value); + } + + void Clear(void) const + { + m_pXMLRoot->Clear(); + m_pXMLRoot->SetTagName(L"xml"); + } + + int Version(void) const { return 2; } + void Dump(const TCHAR* szFileName) const { m_pXMLRoot->WriteToFile(szFileName); } + +protected: + CLBP_XMLProperty* InternalProperty(const WCHAR * wszXMLPath, const WCHAR * wszPropertyName) const + { + const CLBP_XMLNode* pNode = m_pXMLRoot->Node().GetNodeAtPath(wszXMLPath); + if (NULL == pNode) + return NULL; + + return pNode->GetNamedProperty(wszPropertyName); + } + +#ifdef RHINO_V6_READY +#define ON_BOOL32 bool +#endif + + virtual ON_BOOL32 Archive(void) const { return true; } + +#ifdef RHINO_V6_READY +#undef ON_BOOL32 +#endif + +protected: + virtual void SetToDefaults() const = 0; + virtual void ReportVersionError(void) const = 0; + +public: + void SetToDefaultsImpl(eLBPRhWrapperType wt) const + { + m_wt = wt; + SetToDefaults(); + } + + eLBPRhWrapperType WrapperType(void) const { return m_wt; } + +private: + CLBP_XMLRootNode_RC* m_pXMLRoot; + mutable eLBPRhWrapperType m_wt; + + class CException : public CLBPProgramException + { + public: + CException() : CLBPProgramException(L"Property not found in user data") { } + + virtual void Report(void) const + { + ::RhinoMessageBox(m_sMessage, L"Error", MB_ICONSTOP); + } + }; + + friend class CLBPRh_XMLUserData; + friend class CLBPRhRdkContent_XMLUserData; +}; + +class CLBPRh_XMLUserData : public TLBPRh_XMLUserData +{ + typedef TLBPRh_XMLUserData _super; +public: + CLBPRh_XMLUserData() + { + m_userdata_copycount = 1; + m_userdata_uuid = ON_nil_uuid; + } + + CLBPRh_XMLUserData(const CLBPRh_XMLUserData& ud) : TLBPRh_XMLUserData(ud) + { + m_userdata_copycount = ud.m_userdata_copycount; + m_userdata_uuid = ud.m_userdata_uuid; + } + + CLBPRh_XMLUserData& operator= (const CLBPRh_XMLUserData& ud) + { + __super::operator = (ud); // CRITICAL - Be sure to call base class. + + m_userdata_uuid = ud.m_userdata_uuid; + + return *this; + } + + virtual void AssignFast(const TLBPRh_XMLUserData& ud) + { + __super::AssignFast(ud); + + m_userdata_uuid = ud.m_userdata_uuid; + } + + virtual bool Write(ON_BinaryArchive&) const override; + virtual bool Read(ON_BinaryArchive&) override; +}; + +#ifdef RHINO_V6_READY +#undef ON_BOOL32 +#endif diff --git a/LBPSimpleByteArray.h b/LBPSimpleByteArray.h new file mode 100644 index 0000000..6738eeb --- /dev/null +++ b/LBPSimpleByteArray.h @@ -0,0 +1,83 @@ + +#pragma once + +template +class CLBPSimpleArray +{ +public: + CLBPSimpleArray(size_t iInitialAllocationOfObjects = 100) + : m_pBuffer((T*)malloc(iInitialAllocationOfObjects * sizeof(T))), + m_dwSizeOfAllocationInObjects(iInitialAllocationOfObjects), + m_iNumObjects(0) + {} + + virtual ~CLBPSimpleArray() + { + if (m_pBuffer) + { + free(m_pBuffer); + } + } + +public: + void Append(const T* p, size_t iSize) + { + Allocate(m_iNumObjects + iSize); + memcpy(m_pBuffer + m_iNumObjects, p, iSize*sizeof(T)); + m_iNumObjects += iSize; + } + + + void Append(T b) { Append(&b, 1); } + size_t Count(void) const { return m_iNumObjects; } + + //operator const BYTE*() const { return m_pBuffer; } + const T* Bytes(void) const { return m_pBuffer; } + +private: + size_t Allocate(size_t iTotalObjects, bool bExact = false) + { + if (iTotalObjects < m_dwSizeOfAllocationInObjects) + return m_dwSizeOfAllocationInObjects; + + size_t dwNewAllocationInObjects = iTotalObjects; + + if (!bExact) + { + if (dwNewAllocationInObjects < 32) + { + // Never allocate less than 32 bytes. + dwNewAllocationInObjects = 32; + } + else + if (dwNewAllocationInObjects > 100000) + { + // If the allocation is really big, don't bloat it - just add a reasonable amount on the end. + dwNewAllocationInObjects = dwNewAllocationInObjects + 10000; + } + else + { + dwNewAllocationInObjects = dwNewAllocationInObjects * 4; + } + } + + if (dwNewAllocationInObjects > m_dwSizeOfAllocationInObjects) + { + m_dwSizeOfAllocationInObjects = dwNewAllocationInObjects; + + // realloc size plus 1 to make sure we never realloc zero bytes; probably can't happen. + const size_t dwNumBytes = (m_dwSizeOfAllocationInObjects + 1) * sizeof(T); + m_pBuffer = (T*)LBPREALLOC(m_pBuffer, dwNumBytes); + } + + return m_dwSizeOfAllocationInObjects; + } + +private: + T* m_pBuffer; + size_t m_iNumObjects = 0; + size_t m_dwSizeOfAllocationInObjects; +}; + +using CLBPSimpleByteArray = CLBPSimpleArray; + diff --git a/LBPString.cpp b/LBPString.cpp new file mode 100644 index 0000000..293db82 --- /dev/null +++ b/LBPString.cpp @@ -0,0 +1,2636 @@ + +#include "stdafx.h" +#include "LBPString.h" +#include "LBPLibUtilities.h" +#include "LBP_CRC32.h" + +//#define LBP_STRING_STATISTICS + +#ifdef LBP_STRING_STATISTICS +static unsigned int g_iCount = 0; +static size_t g_TotalAllocated = 0; +#define INCREMENT_COUNT g_iCount++; +#else +#define INCREMENT_COUNT +#endif + +wchar_t* CLBPString::m_wszEmpty = L""; + +CLBPString::CLBPString() + : + m_wsz(m_wszEmpty), + m_dwLengthCache(0) +{ + INCREMENT_COUNT +} + +CLBPString::~CLBPString() +{ +#ifdef LBP_STRING_STATISTICS + g_iCount--; + g_TotalAllocated -= m_dwSizeAllocation; +#endif + +#ifdef _DEBUG + //Deliberately demolish the contents of the string to make sure + //out of scope errors are easy to see + + if(!IsEmpty()) + { + const size_t dwWideSize = wcslen(m_wsz); + for(size_t i=0;i(AsWideString()); +} + +char* CLBPString::AsNonConstMBCSString() const +{ + return const_cast(AsMBCSString()); +} + +#ifdef _UNICODE +wchar_t* CLBPString::AsNonConstTString() const +{ + return AsNonConstWideString(); +} + +const wchar_t* CLBPString::AsTString() const +{ + return AsWideString(); +} + +const wchar_t* CLBPString::T() const +{ + return AsWideString(); +} + +#else +char* CLBPString::AsNonConstTString() const +{ + return AsNonConstMBCSString(); +} + +const char* CLBPString::AsTString() const +{ + return AsMBCSString(); +} + +const char* CLBPString::T() const +{ + return AsMBCSString(); +} +#endif + + +bool CLBPString::operator==(const wchar_t* wsz) const +{ + return 0==CompareNoCase(wsz); +} + +bool CLBPString::operator==(const char* sz) const +{ + return 0==CompareNoCase(sz); +} + +bool CLBPString::operator!=(const wchar_t* wsz) const +{ + return !(*this==wsz); +} + +bool CLBPString::operator!=(const char* sz) const +{ + return !(*this==sz); +} + +bool CLBPString::operator<(const CLBPString& s2) const +{ + return (CompareNoCase(s2.Wide()) < 0) ? true : false; +} + +bool CLBPString::operator>(const CLBPString& s2) const +{ + return (CompareNoCase(s2.Wide()) > 0) ? true : false; +} + +bool CLBPString::operator<=(const CLBPString& s2) const +{ + return (CompareNoCase(s2.Wide()) <= 0) ? true : false; +} + +bool CLBPString::operator>=(const CLBPString& s2) const +{ + return (CompareNoCase(s2.Wide()) >= 0) ? true : false; +} + + + + +bool AFXAPI operator == (const wchar_t * wsz, const CLBPString& s) +{ + return 0 == s.CompareNoCase(wsz); +} +bool AFXAPI operator != (const wchar_t * wsz, const CLBPString& s) +{ + return 0 != s.CompareNoCase(wsz); +} + +bool AFXAPI operator == (const char * wsz, const CLBPString& s) +{ + return 0 == s.CompareNoCase(wsz); +} +bool AFXAPI operator != (const char * wsz, const CLBPString& s) +{ + return 0 != s.CompareNoCase(wsz); +} + + +// overloaded assignment + +/*const CLBPString& CLBPString::operator=(const CLBPString& stringSrc) const +{ + m_wsz = stringSrc.m_wsz; + m_dwLengthCache = stringSrc.m_dwLengthCache; + m_sz = stringSrc.m_sz; + m_dwSizeAllocation = 0; +}*/ + +const CLBPString& CLBPString::operator=(const CLBPString& stringSrc) +{ + //This is an optimization - we can't put the m_wszEmpty check in the header + //because it won't link, but I don't want to un-inline the IsEmpty call. This is the function + //that most needs the optimization - see also operator=(const wchar_t*) + if(stringSrc.m_wszEmpty == stringSrc.m_wsz || stringSrc.IsEmpty()) + { + Empty(); + return *this; + } + + //Optimization for return by value + if (stringSrc.m_bAllowAutoReleaseOwnership) + { + const int iLength = stringSrc.GetLength(); + wchar_t* p = const_cast(stringSrc).ReleaseOwnership(); + TakeOwnershipOfBuffer(p, iLength); + //TakeOwnershipOfBuffer(p); + m_dwLengthCache = iLength; + } + else + { + const DWORD dwLen = stringSrc.GetLength()+1; + + Allocate(dwLen); + memmove(m_wsz, stringSrc.m_wsz, dwLen*sizeof(wchar_t)); + + m_dwLengthCache = dwLen-1; + } + + return *this; +} + + +const CLBPString& CLBPString::operator=(wchar_t ch) +{ + Allocate(1); + + m_wsz[0] = ch; + m_wsz[1] = 0; + + m_dwLengthCache = 1; + + return *this; + +} + +const CLBPString& CLBPString::operator=(char ch) +{ + return *this = ConvertMBCSCharToWideChar(ch); +} + +const CLBPString& CLBPString::operator=(const char* lpsz) +{ + if(!lpsz) + { + Empty(); + return *this; + } + + size_t dwChars = strlen(lpsz); + Allocate(dwChars); + +#ifdef LBPLIB_WINDOWS_SPECIFIC + MultiByteToWideChar(CP_ACP, 0, lpsz, -1, m_wsz, (int)GetAllocation()); +#else + mbstowcs(m_wsz, lpsz, dwChars+1); +#endif + + m_dwLengthCache = (int)dwChars; + + return *this; +} + +const CLBPString& CLBPString::operator=(const wchar_t* lpsz) +{ + if(m_wszEmpty == lpsz || NULL == lpsz || lpsz[0] == 0) + { + Empty(); + return *this; + } + + if(m_wsz && wcscmp(m_wsz, lpsz) == 0) + { + return *this; + } + + const DWORD dwLen = (DWORD)wcslen(lpsz)+1; + +//#ifdef _DEBUG +// if(dwLen > 15) +// { +// Sleep(0); //Good place for a breakpoint. +// } +//#endif + + Allocate(dwLen); + //wcscpy(m_wsz, lpsz); + //memmove should be a little faster - doesn't have to check for the terminator + memmove(m_wsz, lpsz, dwLen*sizeof(wchar_t)); + + m_dwLengthCache = dwLen-1; + + return *this; +} + +const CLBPString& CLBPString::Set(const wchar_t* lpsz, DWORD dwCharacters) +{ + Allocate(dwCharacters); + memmove(m_wsz, lpsz, dwCharacters * sizeof(wchar_t)); + m_wsz[dwCharacters] = 0; + m_dwLengthCache = dwCharacters; + + return *this; +} + +const CLBPString& CLBPString::Set(const char* lpsz, DWORD dwCharacters) +{ + CLBPString sCopy = lpsz; + + return *this = Set(sCopy.Wide(), dwCharacters); +} + +const CLBPString& CLBPString::Set(double d) +{ + const int iBufferSize = 50; + GetWideBuffer(iBufferSize); + +#ifdef LBPRHLIB + ON_wString::FormatIntoBuffer(m_wsz, iBufferSize, L"%.15g", d); +#else + _swprintf_l(m_wsz, iBufferSize, L"%.15g", LBP_Locale(), d); +#endif + ReleaseWideBuffer(); + return *this; +} + +const CLBPString& CLBPString::Set(float f) +{ + const int iBufferSize = 50; + GetWideBuffer(iBufferSize); + +#ifdef LBPRHLIB + ON_wString::FormatIntoBuffer(m_wsz, iBufferSize, L"%.4f", f); +#else + _swprintf_l(m_wsz, iBufferSize, L"%.4f", LBP_Locale(), f); +#endif + + ReleaseWideBuffer(); + return *this; +} + +const CLBPString& CLBPString::Set(int i) +{ + const int iBufferSize = 20; + GetWideBuffer(iBufferSize); +#ifdef LBPRHLIB + ON_wString::FormatIntoBuffer(m_wsz, iBufferSize, L"%d", i); +#else + _swprintf_l(m_wsz, iBufferSize, L"%d", LBP_Locale(), i); +#endif + ReleaseWideBuffer(); + + return *this; +} + +// string concatenation + +// concatenate from another CLBPString +const CLBPString& CLBPString::operator+=(const CLBPString& string) +{ + //return *this += string.AsWideString(); + if (string.IsEmpty()) + return *this; + + return Append(string.Wide(), string.GetLength()); +} + +// concatenate a single character +const CLBPString& CLBPString::operator+=(char ch) +{ + return *this += ConvertMBCSCharToWideChar(ch); +} + +const CLBPString& CLBPString::operator+=(wchar_t ch) +{ + const DWORD dwRequired = GetLength() + 1; + + Allocate(dwRequired); + + m_wsz[dwRequired-1] = ch; + m_wsz[dwRequired] = 0; + + m_dwLengthCache = dwRequired; + + return *this; +} + +const CLBPString& CLBPString::operator+=(const char* lpsz) +{ + return *this += CLBPString(lpsz); +} + +const CLBPString& CLBPString::operator+=(const wchar_t* lpsz) +{ + if(!lpsz) return *this; + if(0 == *lpsz) return *this; + + const int iLengthIncoming = (int)wcslen(lpsz); + + return Append(lpsz, iLengthIncoming); + +} + +const CLBPString& CLBPString::Append(const wchar_t* lpsz, int iCount) +{ + if(!lpsz) return *this; + if(0 == *lpsz) return *this; + if (0==iCount) + return *this; + + const int iLength = GetLength(); + + ASSERT(*(m_wsz+iLength) == 0); + + CLBPString* pS = NULL; + + const wchar_t* wszToUse = lpsz; + + // Always do this check; we MUST copy the string if it's a subset of the current string. + if ((lpsz >= m_wsz) && (lpsz < m_wsz+iLength+1)) + { + // lpsz is a subset of the current string - this means we have to copy it, unfortunately + pS = new CLBPString(lpsz); + wszToUse = pS->Wide(); + #pragma message ("CLBPString: Subset optimisation") + } + + const int iLengthIncoming = iCount; + const DWORD dwRequired = (DWORD)(iLength + iLengthIncoming + 1); + + Allocate(dwRequired); + + memmove(m_wsz+iLength, wszToUse, iLengthIncoming*sizeof(wchar_t)); + *(m_wsz+iLength+iLengthIncoming) = 0; + + m_dwLengthCache = dwRequired - 1; + + delete pS; + + return *this; +} + + +//These functions rely on automatic conversion - it's critical they don't use the BV form +#define LBPSTRING_IMPL_OPERATORADD CLBPStringBV s(string);s+=ch;return s +#define LBPSTRING_IMPL_OPERATORADD_PREFIX CLBPStringBV s(ch);s+=string;return s + +CLBPStringBV AFXAPI operator+(const CLBPString& string1, const CLBPString& string2) +{ + CLBPStringBV s(string1); + s+=string2; + + return s; +} + +CLBPStringBV AFXAPI operator+(const CLBPString& string, char ch) {LBPSTRING_IMPL_OPERATORADD;} +CLBPStringBV AFXAPI operator+(const CLBPString& string, wchar_t ch) {LBPSTRING_IMPL_OPERATORADD;} +CLBPStringBV AFXAPI operator+(const CLBPString& string, const char* ch) {LBPSTRING_IMPL_OPERATORADD;} +CLBPStringBV AFXAPI operator+(const CLBPString& string, const wchar_t* ch) {LBPSTRING_IMPL_OPERATORADD;} + +CLBPStringBV AFXAPI operator+(char ch, const CLBPString& string) {LBPSTRING_IMPL_OPERATORADD_PREFIX;} +CLBPStringBV AFXAPI operator+(wchar_t ch, const CLBPString& string) {LBPSTRING_IMPL_OPERATORADD_PREFIX;} +CLBPStringBV AFXAPI operator+(const char* ch, const CLBPString& string) {LBPSTRING_IMPL_OPERATORADD_PREFIX;} +CLBPStringBV AFXAPI operator+(const wchar_t* ch, const CLBPString& string) {LBPSTRING_IMPL_OPERATORADD_PREFIX;} + +#ifdef _NATIVE_WCHAR_T_DEFINED +CLBPStringBV AFXAPI operator+(const CLBPString& string, const USHORT* lpsz) { return operator+(string, (const wchar_t*)lpsz); } +CLBPStringBV AFXAPI operator+(const USHORT* lpsz, const CLBPString& string) { return operator+((const wchar_t*)lpsz, string); } +CLBPStringBV AFXAPI operator+(USHORT ch, const CLBPString& string) { return operator+((wchar_t)ch, string); } +CLBPStringBV AFXAPI operator+(const CLBPString& string, USHORT ch) { return operator+(string, (wchar_t)ch); } +#endif +// string comparison +int CLBPString::Compare(const wchar_t* wsz) const +{ + if ((NULL == wsz) && IsEmpty()) + return 0; + + return wcscmp(m_wsz, (NULL == wsz) ? L"" : wsz); +} + +int CLBPString::CompareNoCase(const wchar_t* wsz) const +{ + if ((NULL == wsz) && IsEmpty()) + return 0; + + return _wcsicmp(m_wsz, (NULL == wsz) ? L"" : wsz); +} + +int CLBPString::Compare(const char* wsz) const +{ + return Compare(CLBPString(wsz).AsWideString()); +} + +int CLBPString::CompareNoCase(const char* wsz) const +{ + return CompareNoCase(CLBPString(wsz).AsWideString()); +} + +// NLS aware comparison, case sensitive +int CLBPString::Collate(const wchar_t* wsz) const +{ + if ((NULL == wsz) && IsEmpty()) + return 0; + +#ifdef LBPLIB_APPLE_SPECIFIC + return wcscoll(m_wsz, (NULL == wsz) ? L"" : wsz); +#else + return _wcsicoll(m_wsz, (NULL == wsz) ? L"" : wsz); +#endif +} + +int CLBPString::CollateNoCase(const wchar_t* wsz) const +{ + if ((NULL == wsz) && IsEmpty()) + return 0; + + return wcscoll(m_wsz, (NULL == wsz) ? L"" : wsz); +} + +int CLBPString::Collate(const char* wsz) const +{ + return Collate(CLBPString(wsz).AsWideString()); +} + +int CLBPString::CollateNoCase(const char* wsz) const +{ + return CollateNoCase(CLBPString(wsz).AsWideString()); +} + +// simple sub-string extraction +CLBPStringBV CLBPString::Mid(int nFirst, int nCount) const +{ + if (nFirst < 0) nFirst = 0; + if (nCount < 0) nCount = 0; + + const int iLength = GetLength(); + + if (nFirst + nCount > iLength) nCount = iLength - nFirst; + if (nFirst > iLength) nCount = 0; + + // optimize case of returning entire string + if (nFirst == 0 && nCount == iLength) + return *this; + + //Cut the string so that the copy will only take the correct bit + wchar_t w = m_wsz[nFirst+nCount]; + m_wsz[nFirst+nCount] = 0; + + const CLBPString s(m_wsz+nFirst, nCount); //Copy the string from the start of the block + s.SetAllowAutoReleaseOwnership(); + + //Put the character back + m_wsz[nFirst+nCount] = w; + + return s; +} + +const wchar_t* CLBPString::Mid(int nFirst) const +{ + if (nFirst < 0) nFirst = 0; + + const wchar_t* p = m_wsz; + + const int iLength = GetLength(); + + if (nFirst <= iLength) + p = m_wsz + nFirst; + +#ifdef _DEBUG + #pragma message("CLBPString::Mid(int) - regression test included.\n") + ASSERT(Mid(nFirst, GetLength()-nFirst).Compare(p)==0); +#endif + + return p; +} + +CLBPStringBV CLBPString::Left(int nCount) const +{ + return Mid(0, nCount); +} + +void CLBPString::Truncate(int nCount) +{ + if (nCount < 0) nCount = 0; + +#ifdef _DEBUG + m_bAllowAutoReleaseOwnership = false; + CLBPString sCopy(*this); +#endif + if (0==nCount) + { + Empty(); + } + else + { + const int iLength = GetLength(); + if (nCount < iLength) + { + m_wsz[nCount] = 0; + m_dwLengthCache = nCount; + } + } + +#ifdef _DEBUG + #pragma message("CLBPString::Truncate - regression test included.\n") + ASSERT(Compare(sCopy.Left(nCount).Wide())==0); +#endif + + return; +} + +void CLBPString::TruncateMid(int nPos) +{ + if (nPos < 0) nPos = 0; + +#ifdef _DEBUG + #pragma message("CLBPString::TruncateMid - regression test included.\n") + CLBPString s = Mid(nPos); +#endif + + const int iLength = GetLength(); + + if (nPos <= iLength) + { + memmove(m_wsz, m_wsz+nPos, (iLength-nPos+1)*sizeof(wchar_t)); + + m_dwLengthCache = iLength-nPos; + } + + ASSERT(s.Compare(m_wsz)==0); +} + +const wchar_t* CLBPString::Right(int nCount) const +{ + if (nCount < 0) nCount = 0; + + const wchar_t* p = m_wsz; + + const int iLength = GetLength(); + + if (nCount < iLength) + p = m_wsz + (iLength-nCount); + +#ifdef _DEBUG + #pragma message("CLBPString::Right - regression test included.\n") + ASSERT(CLBPString(Mid(GetLength()-nCount)).Compare(p)==0); +#endif + + return p; +} + + + +// characters from beginning that are also in passed string +CLBPStringBV CLBPString::SpanIncluding(const char* lpszCharSet) const +{ + return SpanIncluding(CLBPString(lpszCharSet).AsWideString()); +} + +CLBPStringBV CLBPString::SpanIncluding(const wchar_t* lpszCharSet) const +{ + return Left((int)wcsspn(m_wsz, lpszCharSet)); +} + +CLBPStringBV CLBPString::SpanExcluding(const char* lpszCharSet) const +{ + return SpanExcluding(CLBPString(lpszCharSet).AsWideString()); +} + +CLBPStringBV CLBPString::SpanExcluding(const wchar_t* lpszCharSet) const +{ + return Left((int)wcscspn(m_wsz, lpszCharSet)); +} + +#ifdef _NATIVE_WCHAR_T_DEFINED +CLBPStringBV CLBPString::SpanIncluding(const USHORT* lpszCharSet) const { return SpanIncluding((const wchar_t*)lpszCharSet); } +CLBPStringBV CLBPString::SpanExcluding(const USHORT* lpszCharSet) const { return SpanExcluding((const wchar_t*)lpszCharSet); } +#endif + +void CLBPString::MakeUpper() +{ + if(IsEmpty()) return; + _wcsupr(m_wsz); +} + +void CLBPString::MakeLower() +{ + if(IsEmpty()) return; + _wcslwr(m_wsz); +} + +void CLBPString::MakeReverse() +{ + if(IsEmpty()) return; + _wcsrev(m_wsz); +} + + +void CLBPString::TrimRight() +{ + const int iLength = GetLength(); + + for (int i=iLength-1; i>=0; i--) + { + if (iswspace(m_wsz[i])) + { + m_dwLengthCache--; + m_wsz[i] = 0; + } + else + { + break; + } + } +} + +void CLBPString::TrimLeft() +{ + wchar_t* lpsz = m_wsz; + int iCut = 0; + + while (*lpsz != '\0') + { + if (!iswspace(*lpsz)) break; + lpsz++; + iCut++; + } + + if (lpsz != m_wsz) + { + // fix up data and length + int nDataLength = (int)(GetLength() - (lpsz - m_wsz)); + memmove(m_wsz, lpsz, (nDataLength+1)*sizeof(wchar_t)); + } + + m_dwLengthCache -= iCut; +} + + +// trimming anything (either side) + +// remove continuous occurrences of chTarget starting from right +void CLBPString::TrimRight(char chTarget) +{ + TrimRight(ConvertMBCSCharToWideChar(chTarget)); +} + +void CLBPString::TrimRight(const char* lpszTargets) +{ + TrimRight(CLBPString(lpszTargets).AsWideString()); +} + +void CLBPString::TrimLeft(char chTarget) +{ + TrimLeft(ConvertMBCSCharToWideChar(chTarget)); +} + +void CLBPString::TrimLeft(const char* lpszTargets) +{ + TrimLeft(CLBPString(lpszTargets).AsWideString()); +} + +void CLBPString::TrimRight(wchar_t w) +{ + //TrimRight(CLBPString(w).AsWideString()); + + const int iLength = GetLength(); + + for (int i=iLength-1; i>=0; i--) + { + if (m_wsz[i] == w) + { + m_dwLengthCache--; + m_wsz[i] = 0; + } + else + { + break; + } + } +} + +static __forceinline bool is_char_in_list(wchar_t ch, const wchar_t* list, size_t count) +{ + for (size_t i=0;i=0; i--) + { + //if (tl.Find(m_wsz[i]) != -1) + if (is_char_in_list(m_wsz[i], lpszTargetList, iTargetCount)) + { + m_wsz[i] = 0; + m_dwLengthCache--; + } + else + { + break; + } + } +} + +void CLBPString::TrimLeft(wchar_t chTarget) +{ + //TrimLeft(CLBPString(chTarget).AsWideString()); + + wchar_t* lpsz = m_wsz; + int iCut = 0; + + while (*lpsz != '\0') + { + //if (!iswspace(*lpsz)) break; + if (*lpsz != chTarget) break; + lpsz++; + iCut++; + } + + if (lpsz != m_wsz) + { + // fix up data and length + int nDataLength = (int)(GetLength() - (lpsz - m_wsz)); + memmove(m_wsz, lpsz, (nDataLength+1)*sizeof(wchar_t)); + } + + m_dwLengthCache -= iCut; +} + +void CLBPString::TrimLeft(const wchar_t* lpszTargets) +{ + if (wcslen(lpszTargets) == 0) return; + + wchar_t* lpsz = m_wsz; + int iCut = 0; + + while (*lpsz != '\0') + { + if (wcschr(lpszTargets, *lpsz) == NULL) + break; + lpsz++; + iCut++; + } + + if (lpsz != m_wsz) + { + // fix up data and length + int nDataLength = (int)(GetLength() - (lpsz - m_wsz)); + memmove(m_wsz, lpsz, (nDataLength+1)*sizeof(wchar_t)); + } + + m_dwLengthCache-=iCut; +} + +// advanced manipulation + +int CLBPString::Replace(char chOld, char chNew) +{ + return Replace(ConvertMBCSCharToWideChar(chOld), ConvertMBCSCharToWideChar(chNew)); +} + +int CLBPString::Replace(wchar_t chOld, wchar_t chNew) +{ + int nCount = 0; + + const int iLength = GetLength(); + + if (chOld != chNew) + { + for(int i=0;i 0) + { + Allocate(iLength+iGrow+1); + } + + const int iCrop = i+iOldLength; + sSuffix = Mid(iCrop); + const int iSufLen = iLength - (iCrop); + + memmove(m_wsz+i, lpszNew, iNewLength*sizeof(wchar_t)); + memmove(m_wsz+i+iNewLength, sSuffix.Wide(), iSufLen*sizeof(wchar_t)); + + m_wsz[i+iNewLength+iSufLen]=0; + + m_dwLengthCache+=iGrow; + } + iPos =i+iNewLength; + } + + return iCount; +} + +int CLBPString::Remove(char chRemove) +{ + return Remove(ConvertMBCSCharToWideChar(chRemove)); + +} + +/*int CLBPString::Remove(wchar_t chRemove) +{ + return Replace(chRemove, L''); +}*/ + +// Remove all occurrences of character 'chRemove' +int CLBPString::Remove(wchar_t chRemove) +{ + const int nLength = GetLength(); + if (0 == nLength) return 0; + + wchar_t* pszBuffer = m_wsz; + + wchar_t* pszSource = pszBuffer; + wchar_t* pszDest = pszBuffer; + wchar_t* pszEnd = pszBuffer+nLength; + + while( pszSource < pszEnd ) + { + wchar_t* pszNewSource = pszSource+1; + if( *pszSource != chRemove ) + { + // Copy the source to the destination. Remember to copy all bytes of an MBCS character + const size_t NewSourceGap = 1; + wchar_t* pszNewDest = pszDest + NewSourceGap; + size_t i = 0; + for (i = 0; pszDest != pszNewDest && i < NewSourceGap; i++) + { + *pszDest = *pszSource; + pszSource++; + pszDest++; + } + } + pszSource = pszNewSource; + } + *pszDest = 0; + int nCount = int( pszSource-pszDest ); + + m_dwLengthCache = -1; + + return nCount; +} + + + +int CLBPString::Insert(int nIndex, char ch, int iCount) +{ + //return Insert(nIndex, CLBPString(ch).AsWideString()); + return Insert(nIndex, ConvertMBCSCharToWideChar(ch), iCount); +} + +int CLBPString::Insert(int nIndex, wchar_t ch, int iCount) +{ + if(nIndex < 0) nIndex = 0; + if(nIndex > GetLength()) nIndex = GetLength(); + + const int iLength = GetLength(); + const int iNewLength = iLength + iCount; + + Allocate(iNewLength); + + memmove(m_wsz+iCount+nIndex, m_wsz+nIndex, (iLength-nIndex+1)*sizeof(wchar_t)); // 29.8.2011 John Croudy, subtract index from length. + + for (int i=0;i GetLength()) nIndex = GetLength(); + + //const CLBPString sSuffix = Mid(nIndex); + + const int iLength = GetLength(); + const size_t iLengthSource = wcslen(pSource); + const int iNewLength = (int)(iLengthSource+iLength); + + Allocate(iNewLength); + + memmove(m_wsz+iLengthSource+nIndex, m_wsz+nIndex, (iLength-nIndex+1)*sizeof(wchar_t)); // 29.8.2011 John Croudy, subtract index from length. + memmove(m_wsz+nIndex, pSource, iLengthSource*sizeof(wchar_t)); + + //m_wsz[nIndex] = 0; + //wcscat(m_wsz, pSource); + //wcscat(m_wsz, sSuffix); + + m_dwLengthCache = iNewLength; + + return GetLength(); +} + +int CLBPString::Delete(int nIndex, int nCount) +{ + CLBPString sSuffix = Mid(nIndex+nCount); + m_wsz[nIndex] = 0; + wcscat(m_wsz, sSuffix); + + m_dwLengthCache = -1; + + return GetLength(); +} + + + // searching +int CLBPString::FindOneOf(const char* lpszCharSet) const { return FindOneOf(CLBPString(lpszCharSet).AsWideString());} +int CLBPString::ReverseFind(char ch) const { return ReverseFind(ConvertMBCSCharToWideChar(ch)); } + +int CLBPString::ReverseFind(wchar_t ch) const +{ + const int iLength = GetLength(); + + for(int i=iLength-1; i>=0;i--) + { + if(m_wsz[i]==ch) + { + return i; + } + } + + return -1; +} + +int CLBPString::FindOneOf(const wchar_t* lpszCharSet) const +{ + int iSetSize = (int)wcslen(lpszCharSet); + if(iSetSize == 0) return -1; + + const int iLength = GetLength(); + + for(int i=0; i GetLength()) return false; + + CLBPString sStart = Left(sSub.GetLength()); + + return sStart.Compare(sSub.AsWideString())==0; +} + +bool CLBPString::StartsWithNoCase(const CLBPString& sSub) const +{ + if(sSub.IsEmpty()) return false; + if(sSub.GetLength() > GetLength()) return false; + + CLBPString sStart = Left(sSub.GetLength()); + + return sStart.CompareNoCase(sSub.AsWideString())==0; +} + +bool CLBPString::EndsWith(const CLBPString& sSub) const +{ + if(sSub.IsEmpty()) return false; + if(sSub.GetLength() > GetLength()) return false; + + CLBPString sEnd = Right(sSub.GetLength()); + return sEnd.Compare(sSub.AsWideString()) == 0; +} + +bool CLBPString::EndsWithNoCase(const CLBPString& sSub) const +{ + if(sSub.IsEmpty()) return false; + if(sSub.GetLength() > GetLength()) return false; + + CLBPString sEnd = Right(sSub.GetLength()); + return sEnd.CompareNoCase(sSub.AsWideString()) == 0; +} + + +int CLBPString::Count(char ch) const { return Count(ConvertMBCSCharToWideChar(ch));} + +int CLBPString::Count(wchar_t ch) const +{ + int iCount = 0; + { + const wchar_t* p = m_wsz; + while(*p != 0) + { + if (*p == ch) + iCount++; + p++; + } + } + +#ifdef _DEBUG + #pragma message("CLBPString::Count - regression test included.\n") + { + int iCount2 = 0; + int iLength = GetLength(); + for(int i=0; i 1 || iECount > 1) return false; + + ASSERT(OLD_IsValidRealNumber()); + + return true; +} + +bool CLBPString::IsValid4dPoint() const +{ + return IsCommaDelimitedDoubleArray(4); +} + +bool CLBPString::IsValid3dPoint() const +{ + return IsCommaDelimitedDoubleArray(3); +} + +bool CLBPString::IsValid2dPoint() const +{ + return IsCommaDelimitedDoubleArray(2); +} + +bool CLBPString::IsValidMatrix() const +{ + return IsCommaDelimitedDoubleArray(16); +} + +bool CLBPString::IsCommaDelimitedDoubleArray(int iDoubles) const +{ + if(IsEmpty()) + { ASSERT(!OLD_IsCommaDelimitedDoubleArray(iDoubles)); return false; } + + if (iDoubles < 1) + { ASSERT(!OLD_IsCommaDelimitedDoubleArray(iDoubles)); return false; } + + if (1 == iDoubles) + return IsValidRealNumber(); + + CLBPString s(*this); + s+=L","; + + for (int i = 0; i < iDoubles; i++) + { + const int iPos = s.Find(L','); + + if(iPos < 0) return false; + + const CLBPString sNum = s.Left(iPos); + if (!sNum.IsValidRealNumber()) + return false; + + s = s.Mid(iPos + 1); + } + + ASSERT(OLD_IsCommaDelimitedDoubleArray(iDoubles)); + + return true; +} + +#ifdef _DEBUG + +bool CLBPString::OLD_IsValidIntegerNumber() const +{ + if(IsEmpty()) return false; + + CLBPString s(m_wsz); + s.TrimLeft(); + s.TrimRight(); + + const int iLength = s.GetLength(); + + for(int i=0; i 1 || iECount > 1) return false; + + return true; +} + +bool CLBPString::OLD_IsCommaDelimitedDoubleArray(int iDoubles) const +{ + if(IsEmpty()) return false; + + if (iDoubles < 1) + return FALSE; + + if (1 == iDoubles) + return IsValidRealNumber(); + + CLBPString s = m_wsz + CLBPString(","); + + for (int i = 0; i < iDoubles; i++) + { + const int iPos = s.Find(L','); + + if(iPos < 0) return FALSE; + + const CLBPString sNum = s.Left(iPos); + if (!sNum.IsValidRealNumber()) + return FALSE; + + s = s.Mid(iPos + 1); + } + + return TRUE; +} + +#endif + +/*bool CLBPString::IsCommaDelimitedDoubleArray(int iDoubles) const +{ + if(iDoubles < 1) return FALSE; + if(iDoubles == 1) return IsValidRealNumber(); + + CLBPString sValue(m_wsz); + + for(int i=0;i m_dwSizeAllocation) + { +#ifdef LBP_STRING_STATISTICS + g_TotalAllocated -= m_dwSizeAllocation; +#endif + m_dwSizeAllocation = dwAlloc; + +#ifdef LBP_STRING_STATISTICS + g_TotalAllocated += m_dwSizeAllocation; +#endif + const size_t dwNumBytes = m_dwSizeAllocation * sizeof(wchar_t); + m_wsz = (wchar_t*)LBPREALLOC(m_wsz, dwNumBytes); + } + + return m_dwSizeAllocation; +} + +size_t CLBPString::Allocate(size_t dwAlloc, bool bExact) +{ + if (dwAlloc+1 <= m_dwSizeAllocation) + return m_dwSizeAllocation; + + size_t dwNewAllocation = dwAlloc; // In characters. + + if (!bExact) + { + if (dwAlloc < 32) + { + // Never allocate less than 32 characters. + dwNewAllocation = 32; + } + else + if (dwAlloc > 100000) + { + // If the allocation is really big, don't bloat it - just add a reasonable amount on the end. + dwNewAllocation = dwAlloc + 10000; + } + else + { + dwNewAllocation = dwAlloc * 4; + } + } + + return AllocateExact(dwNewAllocation); +} + +size_t CLBPString::GetAllocation() const +{ + return m_dwSizeAllocation; +} + +//Never called +void CLBPString::Destroy() +{ +} + + + +#define TCHAR_ARG TCHAR +#define WCHAR_ARG WCHAR +#define CHAR_ARG char + +#ifdef _X86_ + #define DOUBLE_ARG _AFX_DOUBLE +#else + #define DOUBLE_ARG double +#endif + +#define FORCE_ANSI 0x10000 +#define FORCE_UNICODE 0x20000 +#define FORCE_INT64 0x40000 + + +void SwitchStringFormatMarkers(CLBPString& s) +{ + int iLwrStore[50]; + int iUprStore[50]; + + iLwrStore[0] = -1; + iUprStore[0] = -1; + + int nPos = 0, i=0, iCounter = 0; + + while((i=s.Find(L"%s", nPos)) != -1) + { + iLwrStore[iCounter++] = i+1; + iLwrStore[iCounter] = -1; + nPos = i+1; + + } + + iCounter = 0;nPos = 0; + while((i=s.Find(L"%S", nPos)) != -1) + { + iUprStore[iCounter++] = i+1; + iUprStore[iCounter] = -1; + nPos = i+1; + } + + iCounter = 0; + while(iLwrStore[iCounter] != -1) + { + s.SetAt(iLwrStore[iCounter++], 'S'); + } + + iCounter = 0; + while(iUprStore[iCounter] != -1) + { + s.SetAt(iUprStore[iCounter++], 's'); + } +} + +void CLBPString::InternalFormatV(const wchar_t* lpszFormat, va_list argList) +{ +#if LBPLIB_RHINOVER < 6 || !defined LBPRHLIB + ASSERT(AfxIsValidString(lpszFormat)); + va_list argListSave = argList; +#endif + + CLBPString sFormat = lpszFormat; + +#ifdef _UNICODE + SwitchStringFormatMarkers(sFormat); +#endif + +#if LBPLIB_RHINOVER >= 6 && defined LBPRHLIB + const int nMaxLen = ON_wString::FormatVargsOutputCount(sFormat, argList); +#else + //Use CString to figure out the length of the thing: + //Ouch - but better than the potentially bug happy checking code from CString + CString s; + s.FormatV(sFormat.AsTString(), argList); + const int nMaxLen = s.GetLength(); +#endif + + //We have to use a string in case the buffer is already used in the arg list + CLBPString sTemp; + wchar_t* pBuffer = sTemp.GetWideBuffer(nMaxLen+1); + + CLBPString sFormatString(lpszFormat); + SwitchStringFormatMarkers(sFormatString); + +#if LBPLIB_RHINOVER >= 6 && defined LBPRHLIB + + ON_wString::FormatVargsIntoBuffer(pBuffer, nMaxLen+1, sFormatString.Wide(), argList); + sTemp.ReleaseWideBuffer(); + + #ifdef _DEBUG + ON_wString s; + s.FormatVargs(sFormatString, argList); + VERIFY(sTemp == (const wchar_t*)s); + #endif + +#else + + // ############################################################################################### + // + // 12th August 2015 John Croudy. Non-V6 and Non-Rhino stuff still needs this code! + // + // This is a temp fix while Andy is on the road. Andy needs to look at this when he gets back to Finland. + + VERIFY(_vswprintf_l(pBuffer, sFormatString.AsWideString(), LBP_Locale(), argListSave) <= (int)sTemp.GetAllocation()); + sTemp.ReleaseWideBuffer(); + va_end(argListSave); + + // ############################################################################################### + +#endif + + *this = sTemp; +} + +//#define TEST_BAD_LOCALE + +void CLBPString::FormatV(const wchar_t* lpszFormat, va_list argList) +{ +#ifdef TEST_BAD_LOCALE + _wsetlocale(LC_ALL, L"German_Germany.1258"); + InternalFormatV(lpszFormat, argList); + const CLBPString save = *this; + _wsetlocale(LC_ALL, L"C"); + InternalFormatV(lpszFormat, argList); + ASSERT(save == Wide()); +#else + InternalFormatV(lpszFormat, argList); +#endif +} + +void CLBPString::ConvertLineEndingsToLFs(void) +{ + // Convert any CR-LF pairs to lone LFs. Convert any lone CRs to LFs. + int length = GetLength(); + for (int i = 0; i < length; i++) + { + const wchar_t c1 = GetAtWide(i); + const wchar_t c2 = (i < (length-1)) ? GetAtWide(i+1) : 0; + + if (c1 == L'\r') + { + if (c2 == L'\n') + { + Delete(i); + length--; + } + else + { + SetAt(i, L'\n'); + } + } + } +} + +void CLBPString::ConvertLineEndingsToCRLFs(void) +{ + // Convert any CRs to CR-LFs. Convert any LFs to CR-LFs. + int length = GetLength(); + for (int i = 0; i < length; i++) + { + const wchar_t c1 = GetAtWide(i); + const wchar_t c2 = (i < (length-1)) ? GetAtWide(i+1) : 0; + + if ((c1 == L'\r') || (c1 == L'\n')) + { + if ((c1 == L'\r') && (c2 == L'\n')) + { + i++; + } + else + { + Insert(i, L'\r'); + SetAt(++i, L'\n'); + length++; + } + } + } +} + +void CLBPString::URLEncode(bool bForANSI) +{ + if (IsEmpty()) + return; + + const auto iLength = GetLength(); + + size_t calculatedLength = 0; + for (int i = 0; i < iLength; i++) + { + const auto& w = m_wsz[i]; + + switch (w) + { + case L'&': + case L'\n': + calculatedLength += 5; + break; + case L'\"': + case L'\'': + calculatedLength += 6; + break; + case L'<': + case L'>': + calculatedLength += 4; + break; + case L'\r': + break; + default: + calculatedLength++; + } + } + + const size_t allocation = (calculatedLength + 1) * sizeof(wchar_t); + + wchar_t* pBuffer = (wchar_t*)LBPMALLOC(allocation); + auto* p = pBuffer; + + for (int i = 0; i < iLength; i++) + { + const auto& w = m_wsz[i]; + switch (w) + { + case L'&': + *p++ = L'&'; + *p++ = L'a'; + *p++ = L'm'; + *p++ = L'p'; + *p++ = L';'; + break; + case L'\"': + *p++ = L'&'; + *p++ = L'q'; + *p++ = L'u'; + *p++ = L'o'; + *p++ = L't'; + *p++ = L';'; + break; + case L'\'': + *p++ = L'&'; + *p++ = L'a'; + *p++ = L'p'; + *p++ = L'o'; + *p++ = L's'; + *p++ = L';'; + break; + case L'<': + *p++ = L'&'; + *p++ = L'l'; + *p++ = L't'; + *p++ = L';'; + break; + case L'>': + *p++ = L'&'; + *p++ = L'g'; + *p++ = L't'; + *p++ = L';'; + break; + case L'\r': + break; + case L'\n': + *p++ = L'&'; + *p++ = L'#'; + *p++ = L'1'; + *p++ = L'0'; + *p++ = L';'; + break; + default: + *p++ = w; + } + } + + *p = 0; + + const size_t l = p - pBuffer; + if (l != calculatedLength) + { + Sleep(0); + } + + TakeOwnershipOfBuffer(pBuffer, (int)calculatedLength); + + if (bForANSI) + { + ConvertNonAsciiCharactersToEntities(); + } +} + +bool CLBPString::NeedsURLEncode(bool bForANSI) const +{ + if (IsEmpty()) + return false; + + if (GetLength() > 6) + { + if (m_wsz[0] == L'b' && + m_wsz[1] == L'a' && + m_wsz[2] == L's' && + m_wsz[3] == L'e' && + m_wsz[4] == L'6' && + m_wsz[5] == L'4' && + m_wsz[6] == L':') + { + return false; + } + } + + /* https://mcneel.myjetbrains.com/youtrack/issue/RH-31874 - this is slow, replace with FindOneOf + if (-1 != Find(L'&' )) return true; + if (-1 != Find(L'\"')) return true; + if (-1 != Find(L'\'')) return true; + if (-1 != Find(L'<' )) return true; + if (-1 != Find(L'>' )) return true; + if (-1 != Find(L'\n')) return true; + if (-1 != Find(L'\r')) return true; + */ + + if (-1 != FindOneOf(L"&\"\'<>\n\r")) + return true; + + if (!bForANSI) + return false; + + if (-1 != FindFirstNonAsciiCharacter()) + return true; + + return false; +} + +void CLBPString::ConvertNonAsciiCharactersToEntities() +{ + int iPos = -1; + while(-1 != (iPos = FindFirstNonAsciiCharacter())) + { + unsigned int iCode = GetAtWide(iPos); + CLBPString sEntity; + sEntity.Format(L"&#%d;", iCode); + + CLBPString s = GetAtWide(iPos); + + Replace(s.AsWideString(), sEntity); + } +} + +int CLBPString::FindFirstNonAsciiCharacter() const +{ + const int iLength = GetLength(); + + for(int i=0;i 127) return i; + } + + return -1; +} + +void CLBPString::URLUnencode() +{ + if(IsEmpty()) + return; + + if(-1 == Find(L'&')) + return; + + const size_t length = GetLength(); + + //No need to calculate a new length - the string is only going to get smaller, so just start with the + //same size. + wchar_t* pBuffer = (wchar_t*)LBPMALLOC((length + 1) * sizeof(wchar_t)); + + wchar_t* pNew = pBuffer; + const wchar_t* p = m_wsz; + + while (*p != 0) + { + const wchar_t p0 = *p; + + if (p0 == L'&') + { + const wchar_t p1 = *(p + 1); + if (p1 == L'q') + { + if (L'u' == *(p + 2) && L'o' == *(p + 3) && L't' == *(p + 4) && L';' == *(p + 5)) + { + p += 6; + *pNew++ = L'\"'; + continue; + } + } + else if (p1 == L'a') + { + if (L'p' == *(p + 2) && L'o' == *(p + 3) && L's' == *(p + 4) && L';' == *(p + 5)) + { + p += 6; + *pNew++ = L'\''; + continue; + } + if (L'm' == *(p + 2) && L'p' == *(p + 3) && L';' == *(p + 4)) + { + p += 5; + *pNew++ = L'&'; + continue; + } + } + else if (p1 == L'l') + { + if (L't' == *(p + 2) && L';' == *(p + 3)) + { + p += 4; + *pNew++ = L'<'; + continue; + } + } + else if (p1 == L'g') + { + if (L't' == *(p + 2) && L';' == *(p + 3)) + { + p += 4; + *pNew++ = L'>'; + continue; + } + } + else if (p1 == L'#') + { + if (L'1' == *(p + 2) && L'0' == *(p + 3) && L';' == *(p + 4)) + { + p += 5; + *pNew++ = L'\n'; + continue; + } + } + } + + p++; + *pNew++ = p0; + } + + //Terminate + *pNew = 0; + + TakeOwnershipOfBuffer(pBuffer, (int)(pNew - pBuffer)); +} + +bool CLBPString::IsURLEscapeSequence() const +{ + if(0==CompareNoCase(L""")) return true; + if(0==CompareNoCase(L"'")) return true; + if(0==CompareNoCase(L"<")) return true; + if(0==CompareNoCase(L">")) return true; + if(0==CompareNoCase(L"&")) return true; + if(0==CompareNoCase(L" ")) return true; + + return false; +} + +#if !defined LBPLIB_DISABLE_MFC_CONTROLS +CLBPStringBV CLBPString::WindowText(HWND hWnd, UINT uBufferLength) +{ + CLBPStringBV s; + + if (::IsWindow(hWnd)) + { + ::GetWindowText(hWnd, s.GetTBuffer(uBufferLength), uBufferLength-1); + s.ReleaseTBuffer(); + } + + return s; +} + +CLBPStringBV CLBPString::WindowText(CWnd* pWnd, UINT uBufferLength) +{ + return WindowText(pWnd->GetSafeHwnd(), uBufferLength); +} +#endif + +CLBPStringBV CLBPString::HexString() +{ + CLBPStringBV sHex; + + const int iLength = GetLength(); + + for(int i=0;i((f * 90) + 32); + + *this+=w; + } +} + +CLBPString FormatString(const wchar_t* szFormatString, ...) +{ + va_list argList; + va_start(argList, szFormatString); + + CLBPString s; + s.FormatV(szFormatString, argList); + va_end(argList); + + return s; +} + +#if !defined LBPLIB_DISABLE_OLE +bool CLBPString::SetToClipboard(HWND hWnd) const +{ + if (!::OpenClipboard(hWnd)) + return false; + + ::EmptyClipboard(); + + const int iNumCharsIncTerm = GetLength() + 1; + + HGLOBAL h = ::GlobalAlloc(GMEM_DDESHARE, iNumCharsIncTerm * sizeof(wchar_t)); + if (NULL == h) + { + ::CloseClipboard(); + return false; + } + + wchar_t* wszBuffer = (wchar_t*)::GlobalLock(h); + if (NULL == wszBuffer) + { + ::CloseClipboard(); + return false; + } + + wcsncpy(wszBuffer, m_wsz, iNumCharsIncTerm); + + ::GlobalUnlock(h); + + ::SetClipboardData(CF_UNICODETEXT, h); + ::CloseClipboard(); + + return true; +} + +bool CLBPString::GetFromClipboard(HWND hWnd) +{ + if (!::OpenClipboard(hWnd)) + return false; + + HGLOBAL h = ::GetClipboardData(CF_UNICODETEXT); + if (NULL == h) + { + ::CloseClipboard(); + return false; + } + + wchar_t* wszBuffer = (wchar_t*)::GlobalLock(h); + if (NULL == wszBuffer) + { + ::CloseClipboard(); + return false; + } + + const int iNumCharsIncTerm = (int)wcslen(wszBuffer) + 1; + wchar_t* wsz = GetWideBuffer(iNumCharsIncTerm); + wcsncpy(wsz, wszBuffer, iNumCharsIncTerm); + ReleaseWideBuffer(); + + ::GlobalUnlock(h); + + ::CloseClipboard(); + + return true; +} +#endif + +//See notes in the header about WIN64 optimizer. +#ifdef WIN64 +int CLBPString::GetLengthImpl() const +{ + m_dwLengthCache = ((int)wcslen(m_wsz)); + return (int)m_dwLengthCache; +} +#endif + +//Sets the string to the actual buffer passed on - no copy is made. +void CLBPString::TakeOwnershipOfBuffer(wchar_t* wsz, int iLengthOfBuffer) +{ + if (m_wszEmpty != m_wsz && NULL != m_wsz) + { + LBPFREE(m_wsz); + } + + if (m_sz) LBPFREE(m_sz); + m_sz = NULL; + + int iLength; + if (iLengthOfBuffer == -1) + { + iLength = (int)wcslen(wsz); + } + else + { + iLength=iLengthOfBuffer; + } + + //const size_t iLength = (-1==iLengthOfBuffer) ? wcslen(wsz) : iLengthOfBuffer; + +#ifdef LBP_STRING_STATISTICS + g_TotalAllocated -= m_dwSizeAllocation; +#endif + m_dwSizeAllocation = 1 + iLength; + +#ifdef LBP_STRING_STATISTICS + g_TotalAllocated += m_dwSizeAllocation; +#endif + + m_dwLengthCache = iLength; + m_wsz = wsz; +} + +//Releases the string buffer to be owned by the caller. String is set to empty. +wchar_t* CLBPString::ReleaseOwnership(void) +{ + if (m_wszEmpty == m_wsz) + { + //We are pointing to the static empty buffer - we have to cook up a real buffer to make + //this work + ASSERT(0 == m_dwSizeAllocation); + AllocateExact(0); + m_wsz[0] = 0; + } + +#ifdef LBP_STRING_STATISTICS + g_TotalAllocated -= m_dwSizeAllocation; +#endif + + wchar_t* pOut = m_wsz; + m_wsz = NULL; + + if (m_sz) LBPFREE(m_sz); + m_sz = NULL; + + m_dwSizeAllocation = 0; + m_dwLengthCache = -1; + + Empty(); + + return pOut; +} + +void CLBPString::DumpStatistics(void) // Static. +{ + OutputDebugString(L"**** CLBPString: Statistics disabled; #define LBP_STRING_STATISTICS"); +} + +#ifdef USE_BYVALUE_OPTIMIZATION +CLBPStringBV::CLBPStringBV(const CLBPString& src) +: CLBPString(src) +{ + m_bAllowAutoReleaseOwnership = true; +} + +CLBPStringBV::CLBPStringBV() +: CLBPString() +{ + m_bAllowAutoReleaseOwnership = true; +} +#endif + +static LONG g_lStringCount = 0; + +int LBP_GetStringCount() { return g_lStringCount; } + +#ifdef USE_REFERENCE_COUNTED_STRINGS + +CLBPString_RC::CLBPString_RC() + : + m_pData(NULL) +{ +} + +CLBPString_RC::CLBPString_RC(const CLBPString_RC& src) +{ + if (NULL != src.m_pData) + { + src.m_pData->AddRef(); + } + + m_pData = src.m_pData; +} + +CLBPString_RC::~CLBPString_RC() +{ + if (NULL != m_pData) + { + m_pData->Release(); + } +} + +CLBPString_RC& CLBPString_RC::operator = (const CLBPString_RC& src) +{ + if (NULL != src.m_pData) + { + src.m_pData->AddRef(); + } + + if (NULL != m_pData) + { + m_pData->Release(); + } + + m_pData = src.m_pData; + + return *this; +} + +void CLBPString_RC::Empty(void) +{ + if (NULL != m_pData) + { + m_pData->Release(); + m_pData = NULL; + } +} + +CLBPString& CLBPString_RC::EmptyStringForWrite(void) +{ + //Cause the string to be independent + SetString(L""); + ASSERT(NULL != m_pData); + ASSERT(m_pData->m_iRefCount == 1); + return m_pData->m_string; +} + +void CLBPString_RC::SetString(const CLBPString& s) +{ + if (NULL != m_pData) + { + m_pData->Release(); + } + + m_pData = new CLBPString_RC_Data(s); +} + +//CLBPString CLBPString_RC::m_empty; +const CLBPString& CLBPString_RC::_empty(void) +{ + static const CLBPString _e; + return _e; +} + +#pragma message ("CLBPString_RC is reference-counted") + +#else + +#pragma message ("CLBPString_RC is a CLBPString") + +#endif //USE_REFERENCE_COUNTED_STRINGS diff --git a/LBPString.h b/LBPString.h new file mode 100644 index 0000000..5dbe577 --- /dev/null +++ b/LBPString.h @@ -0,0 +1,590 @@ + +#pragma once + +#ifdef _DEBUG +#define USE_REFERENCE_COUNTED_STRINGS +#endif + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#if defined (LBPLIB_APPLE_SPECIFIC) +#define REFCOUNT_INT int32_t +#define InterlockedIncrement OSAtomicIncrement32 +#define InterlockedDecrement OSAtomicDecrement32 +#endif + +#if defined (LBPLIB_WINDOWS_SPECIFIC) +#define REFCOUNT_INT LONG +#endif + +#ifdef USE_BYVALUE_OPTIMIZATION +#class CLBPStringBV +#else +#define CLBPStringBV CLBPString +#endif + +class LBPEXPORT CLBPString +{ +public: + CLBPString(); + CLBPString(const CLBPString& stringSrc ); + CLBPString(const char* sz ); + CLBPString(const wchar_t* wsz ); + CLBPString(wchar_t wch); + CLBPString(char ch); + CLBPString(const wchar_t* wsz, DWORD dwCharacters); + //CLBPString(const CString s); + +#ifdef _NATIVE_WCHAR_T_DEFINED + CLBPString(USHORT wch); + CLBPString(const USHORT* wsz); + CLBPString(const USHORT* wsz, DWORD dwCharacters); +#endif + + ~CLBPString(); + +public: + static void DumpStatistics(void); + + __forceinline int GetLength() const { return m_dwLengthCache != -1 ? (int)m_dwLengthCache : GetLengthImpl(); } + __forceinline bool IsEmpty() const { return NULL == m_wsz || 0 == m_wsz[0]; } + __forceinline void Empty() //{ AllocateExact(0);m_wsz[0] = 0;m_dwLengthCache = 0; } + { + m_dwLengthCache = 0; + + if (m_wszEmpty == m_wsz) + { + return; + } + else if (NULL == m_wsz) + { + m_wsz = m_wszEmpty; + } + else + { + AllocateExact(0);m_wsz[0] = 0; + } + } + + DWORD CRC() const; + + char GetAtMultibyte(int nIndex) const; + __forceinline wchar_t operator[](int nIndex) const { return GetAtWide(nIndex); } + __forceinline wchar_t GetAtWide(int nIndex) const { return m_wsz[nIndex]; } + __forceinline void SetAt(int nIndex, wchar_t ch) { m_wsz[nIndex] = ch; } + void SetAt(int nIndex, char ch); + + __forceinline operator const char*() const { return AsMBCSString(); } + __forceinline operator const wchar_t*() const { return AsWideString(); } + operator CString() const { return T(); } + +#ifdef _NATIVE_WCHAR_T_DEFINED + operator const USHORT*() const { return (USHORT*)Wide(); } + void SetAt(int nIndex, USHORT ch) { SetAt(nIndex, (WCHAR)ch); } +#endif + + USHORT GetAtUShort(int nIndex) const { return (USHORT)GetAtWide(nIndex);} + + + //For extremely crappy non-const correct code (like Roy's). + //operator char*() const; + //operator WCHAR*() const; + + const char* AsMBCSString() const; + __forceinline const wchar_t* AsWideString() const { return m_wsz; } + + const char* Mbcs() const; + __forceinline const wchar_t* Wide() const { return m_wsz; } + + const USHORT* UShort() const { return ((const USHORT*)Wide()); } + USHORT* AsNonConstUShortString() const { return ((USHORT*)Wide()); } + + char* AsNonConstMBCSString() const; + WCHAR* AsNonConstWideString() const; + +#ifdef _UNICODE + wchar_t* AsNonConstTString() const; + const wchar_t* AsTString() const; + const wchar_t* T() const; +#else + char* AsNonConstTString() const; + const char* AsTString() const; + const char* T() const; +#endif + + + // overloaded assignment + + const CLBPString& operator=(const CLBPString& stringSrc); + //const CLBPString& operator=(const CLBPString& stringSrc) const; + const CLBPString& operator=(wchar_t ch); + const CLBPString& operator=(char ch); + const CLBPString& operator=(const char* lpsz); + const CLBPString& operator=(const wchar_t* lpsz); + //const CLBPString& operator=(const WCHAR* lpsz) const; + + //__forceinline const CLBPString& operator=(const CString s) { return *this = (const TCHAR*)s; } + + const CLBPString& Set(const wchar_t* lpsz, DWORD dwCharacters); + const CLBPString& Set(const char* lpsz, DWORD dwCharacters); + + const CLBPString& Set(double d); + const CLBPString& Set(float f); + const CLBPString& Set(int i); + +#ifdef _NATIVE_WCHAR_T_DEFINED + const CLBPString& operator=(const USHORT* lpsz) { return operator=((const wchar_t*)lpsz); } + const CLBPString& operator=(USHORT ch) { return operator=((wchar_t)ch); } +#endif + + + + // string concatenation + + // concatenate from another CLBPString + const CLBPString& operator+=(const CLBPString& string); + + // concatenate a single character + const CLBPString& operator+=(char ch); + const CLBPString& operator+=(wchar_t ch); + const CLBPString& operator+=(const char* lpsz); + const CLBPString& operator+=(const wchar_t* lpsz); + + const CLBPString& Append(const wchar_t* lpsz, int iCount); + +#ifdef _NATIVE_WCHAR_T_DEFINED + const CLBPString& operator+=(const USHORT* lpsz) { return operator+=((const wchar_t*)lpsz); } + const CLBPString& operator+=(USHORT ch) { return operator+=((wchar_t)ch); } +#endif + + friend CLBPStringBV AFXAPI operator+(const CLBPString& string1, const CLBPString& string2); + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, char ch); + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, WCHAR ch); + friend CLBPStringBV AFXAPI operator+(char ch, const CLBPString& string); + friend CLBPStringBV AFXAPI operator+(wchar_t ch, const CLBPString& string); +// the following are duplicates +// friend CLBPStringBV AFXAPI operator+(const CLBPString& string, char ch); +// friend CLBPStringBV AFXAPI operator+(char ch, const CLBPString& string); +// friend CLBPStringBV AFXAPI operator+(WCHAR ch, const CLBPString& string); + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, const char* lpsz); + friend CLBPStringBV AFXAPI operator+(const char* lpsz, const CLBPString& string); + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, const WCHAR* lpsz); + friend CLBPStringBV AFXAPI operator+(const wchar_t* lpsz, const CLBPString& string); + +#ifdef _NATIVE_WCHAR_T_DEFINED + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, const USHORT* lpsz); + friend CLBPStringBV AFXAPI operator+(const USHORT* lpsz, const CLBPString& string); + friend CLBPStringBV AFXAPI operator+(USHORT ch, const CLBPString& string); + friend CLBPStringBV AFXAPI operator+(const CLBPString& string, USHORT ch); +#endif + + // string comparison + bool operator==(const wchar_t* wsz) const; + bool operator==(const char* sz) const; + + bool operator!=(const wchar_t* wsz) const; + bool operator!=(const char* sz) const; + + bool operator<(const CLBPString&)const; + bool operator>(const CLBPString&)const; + bool operator<=(const CLBPString&)const; + bool operator>=(const CLBPString&)const; + + friend bool AFXAPI operator == (const wchar_t * wsz, const CLBPString& s); + friend bool AFXAPI operator != (const wchar_t * wsz, const CLBPString& s); + friend bool AFXAPI operator == (const char * wsz, const CLBPString& s); + friend bool AFXAPI operator != (const char * wsz, const CLBPString& s); + +#ifdef _NATIVE_WCHAR_T_DEFINED + bool operator==(const USHORT* wsz) const { return operator==((const wchar_t*)wsz); } + bool operator!=(const USHORT* wsz) const { return operator!=((const wchar_t*)wsz); } + friend bool AFXAPI operator == (const USHORT * wsz, const CLBPString& s) + { return ::operator==((const wchar_t*)wsz, s); } + friend bool AFXAPI operator != (const USHORT * wsz, const CLBPString& s) + { return ::operator!=((const wchar_t*)wsz, s); } +#endif + + int Compare(const char* lpsz) const; + int CompareNoCase(const char* lpsz) const; + int Compare(const wchar_t* lpsz) const; + int CompareNoCase(const wchar_t* lpsz) const; + // NLS aware comparison, case sensitive + int Collate(const wchar_t* lpsz) const; + int CollateNoCase(const wchar_t* lpsz) const; + int Collate(const char* lpsz) const; + int CollateNoCase(const char* lpsz) const; + +#ifdef _NATIVE_WCHAR_T_DEFINED + __forceinline int Compare(const USHORT* lpsz) const { return Compare((const wchar_t*)lpsz); } + __forceinline int CompareNoCase(const USHORT* lpsz) const { return CompareNoCase((const wchar_t*)lpsz); } + __forceinline int Collate(const USHORT* lpsz) const { return Collate((const wchar_t*)lpsz); } + __forceinline int CollateNoCase(const USHORT* lpsz) const { return CollateNoCase((const wchar_t*)lpsz); } +#endif + + // simple sub-string extraction + + CLBPStringBV Mid(int nFirst, int nCount) const; + const wchar_t* Mid(int nFirst) const; + CLBPStringBV Left(int nCount) const; + const wchar_t* Right(int nCount) const; + + // optimized sub-string functions + void Truncate(int nCount); + void TruncateMid(int nPos); + + // characters from beginning that are also in passed string + CLBPStringBV SpanIncluding(const char* lpszCharSet) const; + CLBPStringBV SpanIncluding(const wchar_t* lpszCharSet) const; + CLBPStringBV SpanExcluding(const char* lpszCharSet) const; + CLBPStringBV SpanExcluding(const wchar_t* lpszCharSet) const; + +#ifdef _NATIVE_WCHAR_T_DEFINED + CLBPStringBV SpanIncluding(const USHORT* lpszCharSet) const; + CLBPStringBV SpanExcluding(const USHORT* lpszCharSet) const; +#endif + + // upper/lower/reverse conversion + + void MakeUpper(); + void MakeLower(); + void MakeReverse(); + + // trimming whitespace (either side) + + void TrimRight(); + void TrimLeft(); + + //URL encoding + void URLEncode(bool bForANSI = false); + void URLUnencode(); + + bool IsURLEscapeSequence() const; + bool NeedsURLEncode(bool bForANSI = false) const; + + // trimming anything (either side) + + // remove continuous occurrences of chTarget starting from right + void TrimRight(char chTarget); + void TrimRight(const char* lpszTargets); + void TrimLeft(char chTarget); + void TrimLeft(const char* lpszTargets); + void TrimRight(wchar_t chTarget); + void TrimRight(const wchar_t* lpszTargets); + void TrimLeft(wchar_t chTarget); + void TrimLeft(const wchar_t* lpszTargets); + +#ifdef _NATIVE_WCHAR_T_DEFINED + __forceinline void TrimRight(USHORT chTarget) { TrimRight((wchar_t)chTarget); } + __forceinline void TrimRight(const USHORT* lpszTargets) { TrimRight((const wchar_t*)lpszTargets); } + __forceinline void TrimLeft(USHORT chTarget) { TrimLeft((wchar_t)chTarget); } + __forceinline void TrimLeft(const USHORT* lpszTargets) { TrimLeft((const wchar_t*)lpszTargets); } +#endif + + // advanced manipulation + + int Replace(char chOld, char chNew); + int Replace(wchar_t chOld, wchar_t chNew); + int Replace(const char* lpszOld, const char* lpszNew); + int Replace(const wchar_t* lpszOld, const wchar_t* lpszNew); + int Remove(char chRemove); + int Remove(wchar_t chRemove); + int Insert(int nIndex, char ch, int iCount = 1); + int Insert(int nIndex, wchar_t ch, int iCount = 1); + int Insert(int nIndex, const char* pstr); + int Insert(int nIndex, const wchar_t* pstr); + int Delete(int nIndex, int nCount = 1); + +#ifdef _NATIVE_WCHAR_T_DEFINED + __forceinline int Replace(USHORT chOld, USHORT chNew) { return Replace((wchar_t)chOld, (wchar_t)chNew); } + __forceinline int Replace(const USHORT* lpszOld, const USHORT* lpszNew) { return Replace((const wchar_t*)lpszOld, (const wchar_t*)lpszNew); } + __forceinline int Remove(USHORT chRemove) { return Remove((wchar_t)chRemove); } + __forceinline int Insert(int nIndex, USHORT ch, int iCount = 1) { return Insert(nIndex, (wchar_t)ch, iCount); } + __forceinline int Insert(int nIndex, const USHORT* pstr) { return Insert(nIndex, (const wchar_t*)pstr); } +#endif + + // searching + + // find character starting at left, -1 if not found + int ReverseFind(char ch) const; + int Find(char ch, int nStart) const; + int FindOneOf(const char* lpszCharSet) const; + __forceinline int Find(const char* lpszSub) const { return Find(CLBPString(lpszSub).AsWideString(), 0); } + __forceinline int Find(const char* lpszSub, int nStart) const { return Find(CLBPString(lpszSub).AsWideString(), nStart);} + __forceinline int Find(wchar_t ch) const { return Find(ch, 0);} + int ReverseFind(wchar_t ch) const; + int Find(char ch) const; + + __forceinline int Find(wchar_t wch, int nStart) const + { + const wchar_t* pstr = wcschr(m_wsz+nStart, wch); + return pstr != NULL ? (int)(pstr-m_wsz) : -1; + } + + int FindOneOf(const wchar_t* lpszCharSet) const; + __forceinline int Find(const wchar_t* lpszSub) const { return Find(lpszSub, 0);} + + __forceinline int Find(const wchar_t* lpszSub, int nStart) const + { + const wchar_t* pstr = wcsstr(m_wsz+nStart, lpszSub); + return pstr != NULL ? (int)(pstr-m_wsz) : -1; + } + +#ifdef _NATIVE_WCHAR_T_DEFINED + __forceinline int Find(USHORT ch) const { return Find((wchar_t)ch); } + __forceinline int ReverseFind(USHORT ch) const { return ReverseFind((wchar_t)ch); } + __forceinline int Find(USHORT ch, int nStart) const { return Find((wchar_t)ch, nStart); } + __forceinline int FindOneOf(const USHORT* lpszCharSet) const { return FindOneOf((const wchar_t*)lpszCharSet); } + __forceinline int Find(const USHORT* lpszSub) const { return Find((const wchar_t*)lpszSub); } + __forceinline int Find(const USHORT* lpszSub, int nStart) const { return Find((const wchar_t*)lpszSub, nStart); } +#endif + + //Simple versions + + __forceinline bool Contains(const CLBPString& sSub) const { return -1 != Find(sSub.AsWideString(), 0); } + __forceinline bool Contains(const wchar_t ch) const { return -1 != Find(ch, 0); } + bool ContainsNoCase(const CLBPString& sSub) const; + + bool StartsWith(const CLBPString& sSub) const; + bool StartsWithNoCase(const CLBPString& sSub) const; + + bool EndsWith(const CLBPString& sSub) const; + bool EndsWithNoCase(const CLBPString& sSub) const; + + //counting + + int Count(char ch) const; + int Count(wchar_t ch) const; + +#ifdef _NATIVE_WCHAR_T_DEFINED + int Count(USHORT ch) const { return Count((wchar_t)ch); } +#endif + + // simple formatting + + // printf-like formatting using passed string + void AFX_CDECL FormatV(const wchar_t* lpszFormat, va_list argList); + void AFX_CDECL Format(const char* lpszFormat, ...); + void AFX_CDECL Format(const wchar_t* lpszFormat, ...); + +#ifdef _NATIVE_WCHAR_T_DEFINED + void AFX_CDECL FormatV(const USHORT* lpszFormat, va_list argList) { FormatV((const wchar_t*)lpszFormat, argList); } + void AFX_CDECL Format(const USHORT* lpszFormat, ...); +#endif + +#if !defined LBPLIB_DISABLE_OLE + BSTR AllocSysString() const; + BSTR SetSysString(BSTR* pbstr) const; +#endif + + // Access to string implementation buffer as "C" character array + + // get pointer to modifiable buffer at least as long as nMinBufLength + char* GetMBCSBuffer(int nMinBufLength); + WCHAR* GetWideBuffer(int nMinBufLength); + + // release buffer, setting length to nNewLength (or to first nul if -1) + void ReleaseWideBuffer(int nNewLength = -1); + void ReleaseMBCSBuffer(int nNewLength = -1); + + char* GetMBCSBufferSetLength(int nNewLength); + WCHAR* GetWideBufferSetLength(int nNewLength); + + //Sets the string to the actual buffer passed on - no copy is made. + void TakeOwnershipOfBuffer(wchar_t* wsz, int iLengthOfBuffer=-1); + + //Releases the string buffer to be owned by the caller. String is set to empty. + wchar_t* ReleaseOwnership(void); + + void SetAllowAutoReleaseOwnership(void) const { m_bAllowAutoReleaseOwnership = true; } + +#ifdef _UNICODE + WCHAR* GetTBuffer(int nMinBufLength); + WCHAR* GetTBufferSetLength(int nNewLength); + void ReleaseTBuffer(int nNewLength = -1); +#else + char* GetTBuffer(int nMinBufLength); + char* GetTBufferSetLength(int nNewLength); + void ReleaseTBuffer(int nNewLength = -1); +#endif + + void RandomCharacters(int iLength = 12); + +public: + bool IsValidRealNumber() const; //must not contain a decimal part - (ie 1.0 is not valid) + bool IsValidIntegerNumber() const; //also accepts integers + bool IsValid4dPoint() const; //reals must have decimal point "." + bool IsValid3dPoint() const; //reals must have decimal point "." + bool IsValid2dPoint() const; //reals must have decimal point "." + bool IsValidMatrix() const; //similar to points, but with 16 doubles + +public: // Clipboard +#if !defined LBPLIB_DISABLE_OLE + bool SetToClipboard(HWND hWnd=NULL) const; + bool GetFromClipboard(HWND hWnd=NULL); +#endif + +public: // Windows specific +#if !defined LBPLIB_DISABLE_MFC_CONTROLS + static CLBPStringBV WindowText(HWND hwnd, UINT uBufferLength=300); + static CLBPStringBV WindowText(CWnd* pWnd, UINT uBufferLength=300); +#endif + +public: // Conversion to Hex strings + CLBPStringBV HexString(); + CLBPStringBV HexStringMBCS(); + +public: // Simple conversions + static char ConvertWideCharToMBCSChar(wchar_t w); + static WCHAR ConvertMBCSCharToWideChar(char w); + + void ConvertLineEndingsToLFs(void); // CRLF -> LF ; CR -> LF + void ConvertLineEndingsToCRLFs(void); // CR -> CRLF ; LF -> CRLF + +public: //To make it easier to write reference-counted-agnostic code +#ifndef USE_REFERENCE_COUNTED_STRINGS + CLBPString& EmptyStringForWrite(void) + { + Empty(); + return *this; + } +#endif + +protected: + void InternalFormatV(const wchar_t* lpszFormat, va_list argList); + + void SyncMBCS() const; //Synchronise the MBCS string for output + + size_t Allocate(size_t dwAlloc, bool bExact = false); //Returns the actual amount allocated + size_t AllocateExact(size_t dwAlloc); + inline size_t GetAllocation() const; + + bool IsCommaDelimitedDoubleArray(int iDoubles) const; + void ConvertNonAsciiCharactersToEntities(); + int FindFirstNonAsciiCharacter() const; + void Destroy(); + +#ifdef WIN64 + int GetLengthImpl() const; +#else + __forceinline int GetLengthImpl() const { return (int)(m_dwLengthCache = ((INT_PTR)wcslen(m_wsz))); } +#endif + +#ifdef _DEBUG + bool OLD_IsValidIntegerNumber() const; + bool OLD_IsValidRealNumber() const; + bool OLD_IsCommaDelimitedDoubleArray(int iDoubles) const; +#endif + +protected: + static wchar_t* m_wszEmpty; + wchar_t* m_wsz = nullptr; + mutable char* m_sz = nullptr; + DWORD_PTR m_dwSizeAllocation = 0; + mutable INT_PTR m_dwLengthCache = -1; + mutable bool m_bAllowAutoReleaseOwnership = false; +}; + +CLBPString FormatString(const wchar_t* sFormatString, ...); + +void LBP_SetStringTestOptimizationsOn(void); + +#ifdef USE_BYVALUE_OPTIMIZATION +//Special optimized class for returning by value +class CLBPStringBV : public CLBPString +{ +public: + CLBPStringBV(); + CLBPStringBV(const CLBPString&); +}; +#endif + + +#ifdef USE_REFERENCE_COUNTED_STRINGS + +class CLBPString_RC_Data +{ + CLBPString_RC_Data(const CLBPString& s) + : m_iRefCount(1), + m_string(s) + { + } + + REFCOUNT_INT AddRef(void) + { + return InterlockedIncrement(&m_iRefCount); + } + + REFCOUNT_INT Release(void) + { + const REFCOUNT_INT iRef = InterlockedDecrement(&m_iRefCount); + if (0 == iRef) + { + delete this; + } + return iRef; + } + + volatile REFCOUNT_INT m_iRefCount; + CLBPString m_string; + + friend class CLBPString_RC; +}; + +class CLBPString_RC +{ +public: + CLBPString_RC(); + CLBPString_RC(const CLBPString_RC& src); + ~CLBPString_RC(); + + bool operator==(const wchar_t* other) { return String().operator==(other); } + bool operator!=(const wchar_t* other) { return String().operator!=(other); } + + int CompareNoCase(const wchar_t* lpsz) const { return String().CompareNoCase(lpsz); } + + CLBPString_RC& operator = (const CLBPString_RC& src); + + int GetLength(void) const { return String().GetLength(); } + bool IsEmpty(void) const { return String().IsEmpty(); } + + CLBPString& EmptyStringForWrite(void); + + void SetString(const CLBPString& s); + + //operator const wchar_t*(void) const { return String().Wide(); } + //operator const char*(void) const { return String().Mbcs(); } + operator const CLBPString&(void) const { return String(); } + + const wchar_t* Wide(void) const { return String().Wide(); } + const char* Mbcs(void) const { return String().Mbcs(); } + const wchar_t* AsWideString(void) const { return String().AsWideString(); } + const char* AsMBCSString(void) const { return String().AsMBCSString(); } + + DWORD CRC(void) const { return String().CRC(); } + + bool IsValidRealNumber() const { return String().IsValidRealNumber(); } + bool IsValidIntegerNumber() const { return String().IsValidIntegerNumber(); } + bool IsValid4dPoint() const { return String().IsValid4dPoint(); } + bool IsValid3dPoint() const { return String().IsValid3dPoint(); } + bool IsValid2dPoint() const { return String().IsValid2dPoint(); } + bool IsValidMatrix() const { return String().IsValidMatrix(); } + + void Empty(void); + +private: + CLBPString_RC_Data* m_pData; + + static const CLBPString& _empty(void); + + //static CLBPString m_empty; + + const CLBPString& String(void) const { return m_pData ? m_pData->m_string : _empty(); } +}; + +#else + +#define CLBPString_RC CLBPString + +#endif // USE_REFERENCE_COUNTED_STRINGS diff --git a/LBPTime.cpp b/LBPTime.cpp new file mode 100644 index 0000000..292003a --- /dev/null +++ b/LBPTime.cpp @@ -0,0 +1,80 @@ + +#include "stdafx.h" +#include "LBPTime.h" +#include "LBPLibUtilities.h" + + +CLBPTime::CLBPTime(const CLBPString& s) +{ + SetAsString(s); +} + +CLBPTime& CLBPTime::operator=(const CLBPString& s) +{ + if(IsValidTime(s)) + { + SetAsString(s); + } + else + { + *this = CLBPTime(0); + } + return *this; +} + + +CLBPString CLBPTime::AsString(void) const +{ + CLBPString s; + s.Format(L"%04u.%02u.%02u_%02u:%02u:%02u", GetYear(), GetMonth(), GetDay(), + GetHour(), GetMinute(), GetSecond()); + return s; +} + +bool CLBPTime::SetAsString(const CLBPString& s) +{ + if(!IsValidTime(s)) + return false; + + const WCHAR* const wsz = s; + *this = CLBPTime(LBP_wtoi(wsz), LBP_wtoi(wsz+5), LBP_wtoi(wsz+8), LBP_wtoi(wsz+11), LBP_wtoi(wsz+14), LBP_wtoi(wsz+17)); + return true; +} + +bool CLBPTime::IsValidTime(const CLBPString& sTime) +{ + if (19 != sTime.GetLength()) + return false; + + const wchar_t* sz = sTime.Wide(); + + if ((sz[4] != L'.') || (sz[7] != L'.') || (sz[10] != L'_') || (sz[13] != L':') || (sz[16] != L':')) + return false; + + const int y = LBP_wtoi(sz); + const int m = LBP_wtoi(sz+5); + const int d = LBP_wtoi(sz+8); + const int h = LBP_wtoi(sz+11); + const int n = LBP_wtoi(sz+14); + const int s = LBP_wtoi(sz+17); + + if ((y < 1900) || (y > 2500)) + return false; + + if ((m < 1) || (m > 12)) + return false; + + if ((d < 1) || (d > 31)) + return false; + + if ((h < 0) || (h > 23)) + return false; + + if ((n < 0) || (n > 59)) + return false; + + if ((s < 0) || (s > 59)) + return false; + + return true; +} diff --git a/LBPTime.h b/LBPTime.h new file mode 100644 index 0000000..e8d343e --- /dev/null +++ b/LBPTime.h @@ -0,0 +1,37 @@ +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#include "LBPString.h" + +class LBPEXPORT CLBPTime : public CTime +{ +public: + CLBPTime() { } + + CLBPTime(time_t time) : CTime(time) { } + + CLBPTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1) + : CTime(nYear, nMonth, nDay, nHour, nMin, nSec, nDST) { } + +#if defined LBPLIB_WINDOWS_SPECIFIC + CLBPTime(WORD wDosDate, WORD wDosTime, int nDST = -1) + : CTime(wDosDate, wDosTime, nDST) { } + + CLBPTime(const SYSTEMTIME& st, int nDST = -1) + : CTime(st, nDST) { } + + CLBPTime(const FILETIME& ft, int nDST = -1) + : CTime(ft, nDST) { } +#endif + + CLBPTime(const CLBPString& s); + CLBPTime& operator=(const CLBPString& s); + + CLBPString AsString(void) const; + bool SetAsString(const CLBPString& s); + + static bool IsValidTime(const CLBPString& s); +}; diff --git a/LBPUnicodeTextFile.cpp b/LBPUnicodeTextFile.cpp new file mode 100644 index 0000000..9107a90 --- /dev/null +++ b/LBPUnicodeTextFile.cpp @@ -0,0 +1,243 @@ + +#include "stdafx.h" + +#include "LBPUnicodeTextFile.h" +#include "LBPString.h" +#include "LBP_UTF.h" + +CLBPUnicodeTextFile::CLBPUnicodeTextFile(bool bIsUTF8) : CFile() +{ + m_bIsUTF8 = bIsUTF8; +} + +CLBPUnicodeTextFile::CLBPUnicodeTextFile(LPCTSTR lpszFileName, UINT nOpenFlags, bool bIsUTF8) : CFile(lpszFileName, nOpenFlags) +{ + m_bIsUTF8 = bIsUTF8; + + WriteOrReadHeader(nOpenFlags); +} + +BOOL CLBPUnicodeTextFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError) +{ + BOOL bRet = FALSE; + int iAttemptsCounter = 0; + srand((unsigned)time(NULL)); + + while (!bRet && (iAttemptsCounter < 100)) + { +//#if _MFC_VER >= 0x0800 +// bRet = Open2(lpszFileName, nOpenFlags, pError); +//#else + bRet = CFile::Open(lpszFileName, nOpenFlags, pError); +//#endif + if (!bRet) + { + iAttemptsCounter++; + + // Calculate random sleep time (approx 100ms). + double dRandom = rand(); + dRandom /= RAND_MAX; + dRandom -= 0.5; + dRandom *= 10; + Sleep((ULONG)(100+dRandom)); + } + } + + if (bRet) + { + bRet = WriteOrReadHeader(nOpenFlags); + } + + return bRet; +} + +bool CLBPUnicodeTextFile::WriteOrReadHeader(UINT iFlags) +{ + // Skip or write the unicode flags + if (iFlags & modeCreate) + { + UCHAR pBuf[3]; + int iBOMSize = 2; + + if (m_bIsUTF8) + { + pBuf[0] = (UCHAR)0xEF; + pBuf[1] = (UCHAR)0xBB; + pBuf[2] = (UCHAR)0xBF; + + iBOMSize = 3; + } + else + { + pBuf[0] = (UCHAR)0xFF; + pBuf[1] = (UCHAR)0xFE; + } + + Write(pBuf, iBOMSize); + + return true; + } + + return SkipOverBOM(m_bIsUTF8); +} + +bool CLBPUnicodeTextFile::SkipOverBOM(bool& bIsUTF8) +{ + UCHAR pBuf[3]; + + if (2 != Read(pBuf, 2)) + return false; + + if (pBuf[0] == (UCHAR)0xFF) + { + if (pBuf[1] == (UCHAR)0xFE) + { + bIsUTF8 = false; + return true; + } + } + + // This might be UTF8 + if (pBuf[0] == (UCHAR)0xEF) + { + if (pBuf[1] == (UCHAR)0xBB) + { + if (1 == Read(pBuf + 2, 1)) + { + if (pBuf[2] == (UCHAR)0xBF) + { + bIsUTF8 = true; + return true; + } + } + } + } + + return false; +} + +bool CLBPUnicodeTextFile::ReadEverything(CLBPString& sEverything) +{ + const DWORD dwSize = (DWORD)(GetLength() - GetPosition()); + + const DWORD dwSizeIncTerm = dwSize + 2; // Terminator for UTF8 or UTF16. + + BYTE* pBuffer = new BYTE[dwSizeIncTerm]; + ::ZeroMemory(pBuffer, dwSizeIncTerm); + + Read(pBuffer, dwSize); + + CLBP_UTF utf; + if (m_bIsUTF8) + { + utf.SetUTF8((const LBP_UTF8_CHAR*)pBuffer); + } + else + { + utf.SetUTF16((const LBP_UTF16_CHAR*)pBuffer); + } + + sEverything = utf.String(); + delete [] pBuffer; + + return !sEverything.IsEmpty(); +} + + +// 15th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46786 +// Length is in Chars. Multiply by size of the char for writing to the file. + +bool CLBPUnicodeTextFile::WriteUTF8Line(const CLBPString& sLine) +{ + CLBPString sLine2 = sLine; + sLine2 += L"\r\n"; + + CLBP_UTF utf; + utf.Set(sLine2); + const auto lengthInChars = LBP_UTF_LengthInChars(utf.UTF8()); + Write(utf.UTF8(), (UINT)(lengthInChars * sizeof(LBP_UTF8_CHAR))); + + return true; +} + +bool CLBPUnicodeTextFile::WriteUTF16Line(const CLBPString& sLine) +{ + CLBPString sLine2 = sLine; + sLine2 += L"\r\n"; + + CLBP_UTF utf; + utf.Set(sLine2); + const auto lengthInChars = LBP_UTF_LengthInChars(utf.UTF16()); + Write(utf.UTF16(), (UINT)(lengthInChars * sizeof(LBP_UTF16_CHAR))); + + return true; +} + +bool CLBPUnicodeTextFile::WriteLine(const CLBPString& sLine) +{ + if (m_bIsUTF8) + { + return WriteUTF8Line(sLine); + } + else + { + return WriteUTF16Line(sLine); + } +} + +#ifdef _MFC42 +void CLBPUnicodeTextFile::SetLength(DWORD dwNewLen) +#else +void CLBPUnicodeTextFile::SetLength(ULONGLONG dwNewLen) +#endif +{ + const USHORT uBOMLength = m_bIsUTF8 ? 3 : 2; + if (dwNewLen < uBOMLength) + { + // This will destroy the BOM. Clear the file and re-write the BOM. + CFile::SetLength(0); + WriteOrReadHeader(CFile::modeCreate); + } + else + { + CFile::SetLength(dwNewLen); + } +} + +CLBPFile::CLBPFile() : CFile() +{ +} + +CLBPFile::CLBPFile(LPCTSTR lpszFileName, UINT nOpenFlags) : CFile(lpszFileName, nOpenFlags) +{ +} + +CLBPFile::~CLBPFile() +{ +} + +char CLBPFile::putc(char c) +{ + Write(&c, sizeof(char)); + return c; +} + +short CLBPFile::putshort(short s) +{ + Write(&s, sizeof(short)); + return s; +} + +char CLBPFile::getc() +{ + char c; + Read(&c, sizeof(char)); + return c; +} + +short CLBPFile::getshort() +{ + short s; + Read(&s, sizeof(short)); + return s; +} diff --git a/LBPUnicodeTextFile.h b/LBPUnicodeTextFile.h new file mode 100644 index 0000000..11efe78 --- /dev/null +++ b/LBPUnicodeTextFile.h @@ -0,0 +1,55 @@ + +#pragma once + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#include "LBPString.h" + +class LBPEXPORT CLBPFile : public CFile +{ +public: + CLBPFile(); + CLBPFile(LPCTSTR lpszFileName, UINT nOpenFlags); + + virtual ~CLBPFile(); + +public: + + char putc(char c); + char getc(); + + short putshort(short s); + short getshort(); + + + +}; + +class LBPEXPORT CLBPUnicodeTextFile : public CFile +{ +public: + CLBPUnicodeTextFile(bool bIsUTF8 = false); + CLBPUnicodeTextFile(LPCTSTR lpszFileName, UINT nOpenFlags, bool bIsUTF8 = false); + + bool WriteLine(const CLBPString& sLine); + bool ReadEverything(CLBPString& sEverything); + +public: +#ifdef _MFC42 + virtual void SetLength(DWORD dwNewLen); +#else + virtual void SetLength(ULONGLONG dwNewLen); +#endif + virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL); + +protected: + bool WriteOrReadHeader(UINT iFlags); + bool WriteUTF8Line(const CLBPString& sLine); + bool WriteUTF16Line(const CLBPString& sLine); + bool SkipOverBOM(bool& bIsUTF8); + +protected: + bool m_bIsUTF8; +}; diff --git a/LBP_CRC32.cpp b/LBP_CRC32.cpp new file mode 100644 index 0000000..d3eab6e --- /dev/null +++ b/LBP_CRC32.cpp @@ -0,0 +1,137 @@ + +#include "stdafx.h" +#include "LBP_CRC32.h" +#include "LBPString.h" +#include "LBPSimpleByteArray.h" + +CLBP_CRC32::CLBP_CRC32(size_t iInitialSize) + : + m_dChop(1e4) +{ + m_pArray = new CLBPSimpleByteArray(iInitialSize); +} + +CLBP_CRC32::~CLBP_CRC32() +{ + delete m_pArray; +} + +void CLBP_CRC32::SetDecimalPlaces(int places) +{ + // Optimization. + switch (places) + { + case 0: m_dChop = 1e0; return; + case 1: m_dChop = 1e1; return; + case 2: m_dChop = 1e2; return; + case 3: m_dChop = 1e3; return; + case 4: m_dChop = 1e4; return; + case 5: m_dChop = 1e5; return; + case 6: m_dChop = 1e6; return; + } + + // Other cases (slower). + m_dChop = pow(10.0, places); +} + +#ifdef LBPRHLIB +void CLBP_CRC32::SmartAdd(const ON_Interval& in) +{ + SmartAdd(in[0]); + SmartAdd(in[1]); +} + +void CLBP_CRC32::SmartAdd(const ON_Xform& x) +{ + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + SmartAdd(x[i][j]); + } + } +} +#endif + +void CLBP_CRC32::SmartBegin(void) +{ +} + +void CLBP_CRC32::SmartAddImpl(const BYTE* pBytes, const size_t size) +{ + m_pArray->Append(pBytes, size); +} + +DWORD CLBP_CRC32::SmartEnd(void) const +{ + return Calculate(m_pArray->Bytes(), m_pArray->Count()); +} + +void CLBP_CRC32::SmartAdd(const WCHAR * wsz) +{ + if ((NULL == wsz) || (0 == *wsz)) + { + // Empty string - add a zero. + m_pArray->Append(0); + } + else + { + const size_t length = wcslen(wsz) * sizeof(WCHAR); + SmartAddImpl(reinterpret_cast(wsz), length); + } +} + +void CLBP_CRC32::SmartAdd(const char * wsz) +{ + const CLBPString s(wsz); + SmartAdd(s.Wide()); +} + +DWORD CLBP_CRC32::Calculate(const void * pBuffer, size_t iBufferNumBytes) // Static. +{ + DWORD dwCRC = 0xFFFFFFFF; + + const BYTE * p = reinterpret_cast(pBuffer); + while (iBufferNumBytes-- > 0) + { + dwCRC = (dwCRC >> 8) ^ m_Table[(dwCRC & 0xFF) ^ *p++]; + } + + return dwCRC ^ 0xFFFFFFFF; +} + +const DWORD CLBP_CRC32::m_Table[256] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; diff --git a/LBP_CRC32.h b/LBP_CRC32.h new file mode 100644 index 0000000..1ac4db1 --- /dev/null +++ b/LBP_CRC32.h @@ -0,0 +1,51 @@ + +#pragma once + +#include "LBPSimpleByteArray.h" + +class CLBP_CRC32 +{ +public: + CLBP_CRC32(size_t iInitialSize = 100); + ~CLBP_CRC32(); + + static DWORD Calculate(const void * pBuffer, size_t iBufferNumBytes); + + void SmartBegin(void); + DWORD SmartEnd(void) const; + + // Strings not declared inline - slightly more complicated. + void SmartAdd(const WCHAR * sz); + void SmartAdd(const char * sz); + + void SetDecimalPlaces(int places); + + void SmartAdd(const BYTE* pBytes, const size_t size) + { + const DWORD dwBufferCRC = Calculate(pBytes, size); + SmartAddImpl(reinterpret_cast(&dwBufferCRC), sizeof(DWORD)); + } + + void SmartAdd(const int v) { SmartAddImpl(reinterpret_cast(&v), sizeof(v)); } + void SmartAdd(const float v) { const int i = Chop(v); SmartAddImpl(reinterpret_cast(&i), sizeof(i)); } + void SmartAdd(const double v) { const int i = Chop(v); SmartAddImpl(reinterpret_cast(&i), sizeof(i)); } + void SmartAdd(const UUID& v) { SmartAddImpl(reinterpret_cast(&v), sizeof(v)); } + +#ifdef LBPRHLIB + void SmartAdd(const ON_Interval& in); + void SmartAdd(const ON_Xform& x); + void SmartAdd(const ON_Color& c) { SmartAddImpl(reinterpret_cast(&c), sizeof(c)); } +#endif + +protected: + inline int Chop(float f) { f = ((f + 1e-5f) * float(m_dChop)) + 0.5f; return (int)f; } + inline int Chop(double d) { d = ((d + 1e-10) * double(m_dChop)) + 0.5; return (int)d; } + +private: + void SmartAddImpl(const BYTE* pBytes, const size_t size); + +private: + static const DWORD m_Table[256]; + CLBPSimpleByteArray* m_pArray; + double m_dChop; +}; diff --git a/LBP_UTF.cpp b/LBP_UTF.cpp new file mode 100644 index 0000000..07b097c --- /dev/null +++ b/LBP_UTF.cpp @@ -0,0 +1,577 @@ + +#include "stdafx.h" +#include "LBP_UTF.h" + +enum +{ + eSurrogateLead = 0x00D800, + eSurrogateTrail = 0x00DC00, + eSurrogateEnd = 0x00DFFF, + eMaxCodePoint = 0x10FFFD, +}; + +class CLBP_UTF::Impl +{ +public: + Impl(); + ~Impl(); + + const LBP_UTF8_CHAR * UTF8(void) const; + const LBP_UTF16_CHAR* UTF16(void) const; + const LBP_UTF32_CHAR* UTF32(void) const; + void SetUTF8(const LBP_UTF8_CHAR* sz); + void SetUTF16(const LBP_UTF16_CHAR* wsz); + void SetUTF32(const LBP_UTF32_CHAR* dwsz); + + void ClearUTF8Buffer(void); + void ClearUTF16Buffer(void); + void ClearUTF32Buffer(void); + +private: + void Clear(void); + + const LBP_UTF32_CHAR* UTF8toUTF32(const LBP_UTF8_CHAR* sz) const; + const LBP_UTF8_CHAR * UTF32toUTF8(const LBP_UTF32_CHAR* dwsz) const; + const LBP_UTF32_CHAR* UTF16toUTF32(const LBP_UTF16_CHAR* wsz) const; + const LBP_UTF16_CHAR* UTF32toUTF16(const LBP_UTF32_CHAR* dwsz) const; + + int SizeUTF8Sequence(LBP_UTF8_CHAR iLead) const; + int SizeUTF8Sequence(LBP_UTF32_CHAR dwCodePoint) const; + int CountUTF8Sequences(const LBP_UTF8_CHAR* sz) const; + bool SurrogatePairToCodePoint(LBP_UTF32_CHAR& dwCodePointOut, LBP_UTF16_CHAR wCharLead, LBP_UTF16_CHAR wCharTrail) const; + bool SurrogatePairFromCodePoint(LBP_UTF32_CHAR dwCodePoint, LBP_UTF16_CHAR& wCharLeadOut, LBP_UTF16_CHAR& wCharTrailOut) const; + +private: // UTF8 storage. + mutable LBP_UTF8_CHAR * m_aUTF8; + mutable int m_iSizeUTF8; + +private: // UTF16 storage. + mutable LBP_UTF16_CHAR * m_aUTF16; + mutable int m_iSizeUTF16; + +private: // UTF32 storage. + mutable LBP_UTF32_CHAR * m_aUTF32; + mutable int m_iSizeUTF32; +}; + +CLBP_UTF::Impl::Impl() +{ + m_aUTF32 = nullptr; + m_iSizeUTF32 = 0; + + m_aUTF8 = nullptr; + m_iSizeUTF8 = 0; + + m_aUTF16 = nullptr; + m_iSizeUTF16 = 0; +} + +CLBP_UTF::Impl::~Impl() +{ + Clear(); +} + +void CLBP_UTF::Impl::ClearUTF32Buffer(void) +{ + delete[] m_aUTF32; + m_aUTF32 = nullptr; + m_iSizeUTF32 = 0; +} + +void CLBP_UTF::Impl::ClearUTF16Buffer(void) +{ + + delete[] m_aUTF16; + m_aUTF16 = nullptr; + m_iSizeUTF16 = 0; +} + +void CLBP_UTF::Impl::ClearUTF8Buffer(void) +{ + delete[] m_aUTF8; + m_aUTF8 = nullptr; + m_iSizeUTF8 = 0; +} + +void CLBP_UTF::Impl::Clear(void) +{ + ClearUTF8Buffer(); + ClearUTF16Buffer(); + ClearUTF32Buffer(); +} + +const LBP_UTF8_CHAR* CLBP_UTF::Impl::UTF8(void) const +{ + if (0 == m_iSizeUTF8) + { + if (m_iSizeUTF16 > 0) + UTF16toUTF32(m_aUTF16); + + if (m_iSizeUTF32 > 0) + UTF32toUTF8(m_aUTF32); + } + + return m_aUTF8; +} + +const LBP_UTF16_CHAR* CLBP_UTF::Impl::UTF16(void) const +{ + if (0 == m_iSizeUTF16) + { + if (m_iSizeUTF8 > 0) + UTF8toUTF32(m_aUTF8); + + if (m_iSizeUTF32 > 0) + UTF32toUTF16(m_aUTF32); + } + + return m_aUTF16; +} + +const LBP_UTF32_CHAR* CLBP_UTF::Impl::UTF32(void) const +{ + if (0 == m_iSizeUTF32) + { + if (m_iSizeUTF16 > 0) + UTF16toUTF32(m_aUTF16); + else + if (m_iSizeUTF8 > 0) + UTF8toUTF32(m_aUTF8); + } + + return m_aUTF32; +} + +void CLBP_UTF::Impl::SetUTF8(const LBP_UTF8_CHAR* sz) +{ + if (sz == m_aUTF8) + return; + + Clear(); + + const int numChars = LBP_UTF_LengthInChars(sz); + + const size_t numCharsIncTerm = numChars + 1; + + m_aUTF8 = new LBP_UTF8_CHAR[numCharsIncTerm]; + memcpy(m_aUTF8, sz, numCharsIncTerm * sizeof(LBP_UTF8_CHAR)); + + m_iSizeUTF8 = numChars; +} + +void CLBP_UTF::Impl::SetUTF16(const LBP_UTF16_CHAR* wsz) +{ + if (wsz == m_aUTF16) + return; + + Clear(); + + const int numChars = LBP_UTF_LengthInChars(wsz); + + const size_t numCharsIncTerm = numChars + 1; + + m_aUTF16 = new LBP_UTF16_CHAR[numCharsIncTerm]; + memcpy(m_aUTF16, wsz, numCharsIncTerm * sizeof(LBP_UTF16_CHAR)); + + // 15th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46786 + // When this was changed from a string to a buffer, this line was left out. + // This didn't actually cause RH-46786, but it still needs fixing. + m_iSizeUTF16 = numChars; +} + +void CLBP_UTF::Impl::SetUTF32(const LBP_UTF32_CHAR* dwsz) +{ + if (dwsz == m_aUTF32) + return; + + Clear(); + + const int numChars = LBP_UTF_LengthInChars(dwsz); + + const size_t numCharsIncTerm = numChars + 1; + + m_aUTF32 = new LBP_UTF32_CHAR[numCharsIncTerm]; + memcpy(m_aUTF32, dwsz, numCharsIncTerm * sizeof(LBP_UTF32_CHAR)); + + m_iSizeUTF32 = numChars; +} + +const LBP_UTF8_CHAR* CLBP_UTF::Impl::UTF32toUTF8(const LBP_UTF32_CHAR * dwsz) const +{ + if (NULL == dwsz) + return NULL; + + for (int i = 0; i < m_iSizeUTF32; i++) + { + const int iSeqSize = SizeUTF8Sequence(dwsz[i]); + if (0 == iSeqSize) + return NULL; + + m_iSizeUTF8 += iSeqSize; + } + + delete[] m_aUTF8; + m_aUTF8 = new LBP_UTF8_CHAR[m_iSizeUTF8 + 1]; + + LBP_UTF8_CHAR* pSequence = m_aUTF8; + + for (int i = 0; i < m_iSizeUTF32; i++) + { + const LBP_UTF32_CHAR dwCodePoint = dwsz[i]; + + const int iSeqSize = SizeUTF8Sequence(dwCodePoint); + switch (iSeqSize) + { + case 1: + *pSequence++ = (LBP_UTF8_CHAR)dwCodePoint; + break; + + case 2: + *pSequence++ = (LBP_UTF8_CHAR)(0xC0 | ((dwCodePoint >> 6) )); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint ) & 0x3F)); + break; + + case 3: + *pSequence++ = (LBP_UTF8_CHAR)(0xE0 | ((dwCodePoint >> 12) )); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint >> 6) & 0x3F)); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint ) & 0x3F)); + break; + + case 4: + *pSequence++ = (LBP_UTF8_CHAR)(0xF0 | ((dwCodePoint >> 18) )); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint >> 12) & 0x3F)); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint >> 6) & 0x3F)); + *pSequence++ = (LBP_UTF8_CHAR)(0x80 | ((dwCodePoint ) & 0x3F)); + } + } + + *pSequence = 0; // Terminator. + + return m_aUTF8; +} + +const LBP_UTF32_CHAR* CLBP_UTF::Impl::UTF8toUTF32(const LBP_UTF8_CHAR* sz) const +{ + if (NULL == sz) + return NULL; + + m_iSizeUTF32 = CountUTF8Sequences(sz); + + delete[] m_aUTF32; + m_aUTF32 = new LBP_UTF32_CHAR[m_iSizeUTF32 + 1]; + + LBP_UTF32_CHAR* pCodePoint = m_aUTF32; + + LBP_UTF8_CHAR byte2, byte3, byte4; + + for (int i = 0; i < m_iSizeUTF32; i++) + { + const LBP_UTF8_CHAR byteLead = *sz++; + + const int iSeqSize = SizeUTF8Sequence(byteLead); + switch (iSeqSize) + { + case 1: + *pCodePoint++ = byteLead; + break; + + case 2: + byte2 = *sz++ & 0x3F; + *pCodePoint++ = ((byteLead & 0x1F) << 6) | byte2; + break; + + case 3: + byte2 = *sz++ & 0x3F; + byte3 = *sz++ & 0x3F; + *pCodePoint++ = ((byteLead & 0x0F) << 12) | (byte2 << 6) | byte3; + break; + + case 4: + byte2 = *sz++ & 0x3F; + byte3 = *sz++ & 0x3F; + byte4 = *sz++ & 0x3F; + *pCodePoint++ = ((byteLead & 0x07) << 18) | (byte2 << 12) | (byte3 << 6) | byte4; + } + } + + *pCodePoint = 0; // Terminator. + + return m_aUTF32; +} + +const LBP_UTF32_CHAR* CLBP_UTF::Impl::UTF16toUTF32(const LBP_UTF16_CHAR * wsz) const +{ + if (NULL == wsz) + return NULL; + + // Create UTF32 buffer. This requires the same number of characters + // as UTF16, or fewer if UTF16 contains one or more surrogate pairs, but it + // will never require more. + const size_t lengthInChars = LBP_UTF_LengthInChars(wsz); + + delete[] m_aUTF32; + m_aUTF32 = new LBP_UTF32_CHAR[lengthInChars + 1]; + + LBP_UTF32_CHAR* pCodePoint = m_aUTF32; + + m_iSizeUTF32 = 0; + + while (0 != *wsz) + { + const LBP_UTF16_CHAR wCharLead = *wsz++; + + LBP_UTF32_CHAR dwCodePoint = wCharLead; + + if ((wCharLead >= eSurrogateLead) && (wCharLead < eSurrogateTrail)) + { + if (0 == *wsz) + return NULL; // Trailing surrogate missing. + + const LBP_UTF16_CHAR wCharTrail = *wsz++; + if (!SurrogatePairToCodePoint(dwCodePoint, wCharLead, wCharTrail)) + return NULL; + } + else + if ((wCharLead >= eSurrogateTrail) && (wCharLead <= eSurrogateEnd)) + return NULL; // Invalid surrogate. + + *pCodePoint++ = dwCodePoint; + + m_iSizeUTF32++; + } + + *pCodePoint = 0; // Terminator. + + return m_aUTF32; +} + +const LBP_UTF16_CHAR* CLBP_UTF::Impl::UTF32toUTF16(const LBP_UTF32_CHAR * dwsz) const +{ + if (NULL == dwsz) + return NULL; + + LBP_UTF32_CHAR dwCodePoint = 0; + + int iCount = 0; + + const LBP_UTF32_CHAR* dwszTmp = dwsz; + while (0 != (dwCodePoint = *dwszTmp++)) + { + if (!IsValidCodePoint(dwCodePoint)) + return NULL; + + if (dwCodePoint >= 0x10000) + iCount++; + + iCount++; + } + + if (0 == iCount) + return NULL; + + const size_t numCharsIncTerm = iCount + 1; + + delete[] m_aUTF16; + m_aUTF16 = new LBP_UTF16_CHAR[numCharsIncTerm]; + + int iIndex = 0; + while (0 != (dwCodePoint = *dwsz++)) + { + if (dwCodePoint < 0x10000) + { + m_aUTF16[iIndex++] = (LBP_UTF16_CHAR)dwCodePoint; + } + else + { + // Too big for 16 bits so make a surrogate pair. + LBP_UTF16_CHAR wCharLead = 0, wCharTrail = 0; + if (!SurrogatePairFromCodePoint(dwCodePoint, wCharLead, wCharTrail)) + return NULL; + + m_aUTF16[iIndex++] = wCharLead; + m_aUTF16[iIndex++] = wCharTrail; + } + } + + // 15th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46786 + // When this was changed from a string to a buffer, this line was left out. + // This didn't actually cause RH-46786, but it still needs fixing. + m_iSizeUTF16 = iIndex; + + m_aUTF16[iIndex] = 0; // Terminator. + + ASSERT(iCount == iIndex); + + return m_aUTF16; +} + +int CLBP_UTF::Impl::CountUTF8Sequences(const LBP_UTF8_CHAR* sz) const +{ + int iCount = 0, iIndex = 0; + + const int iSizeUTF8 = (int)strlen((const char*)sz); + while (iIndex < iSizeUTF8) + { + const int iSeqSize = SizeUTF8Sequence(sz[iIndex]); + if (0 == iSeqSize) + return -1; + + iIndex += iSeqSize; + iCount++; + } + + return iCount; +} + +int CLBP_UTF::Impl::SizeUTF8Sequence(LBP_UTF8_CHAR byteLead) const +{ + if (byteLead >= 0xF0) + return 4; + + if (byteLead >= 0xE0) + return 3; + + if (byteLead >= 0xC0) + return 2; + + // What about invalid lead bytes? return 0; + + return 1; +} + +int CLBP_UTF::Impl::SizeUTF8Sequence(LBP_UTF32_CHAR dwCodePoint) const +{ + if (!IsValidCodePoint(dwCodePoint)) + return 0; + + if (dwCodePoint < 0x007F) + return 1; + + if (dwCodePoint < 0x07FF) + return 2; + + if (dwCodePoint < 0xFFFF) + return 3; + + return 4; +} + +bool CLBP_UTF::Impl::SurrogatePairToCodePoint(LBP_UTF32_CHAR& dwCodePointOut, LBP_UTF16_CHAR wLead, LBP_UTF16_CHAR wTrail) const +{ +// TODO: if wLead or wTrail are invalid surrogates, return false. + + const LBP_UTF32_CHAR hi = wLead & 0x03FF; + const LBP_UTF32_CHAR lo = wTrail & 0x03FF; + + dwCodePointOut = ((hi << 10) | lo) + 0x10000; + + return true; +} + +bool CLBP_UTF::Impl::SurrogatePairFromCodePoint(LBP_UTF32_CHAR dwCodePoint, LBP_UTF16_CHAR& wCharLeadOut, LBP_UTF16_CHAR& wCharTrailOut) const +{ + if (dwCodePoint < 0x10000) + return false; + + const LBP_UTF32_CHAR a = dwCodePoint - 0x10000; + wCharLeadOut = (LBP_UTF16_CHAR)(eSurrogateLead | (a >> 10)); + wCharTrailOut = (LBP_UTF16_CHAR)(eSurrogateTrail | (a & 0x3FF)); + + return true; +} + +//--------------------------------------------------------------------------------------- + +CLBP_UTF::CLBP_UTF() +{ + m_pImpl = new Impl; +} + +CLBP_UTF::~CLBP_UTF() +{ + delete m_pImpl; +} + +CLBPString CLBP_UTF::String(void) const +{ +#ifdef RHINO_APPLE + return (const wchar_t*)m_pImpl->UTF32(); +#else + return (const wchar_t*)m_pImpl->UTF16(); +#endif +} + +void CLBP_UTF::SetString(const CLBPString& s) +{ +#ifdef RHINO_APPLE + m_pImpl->SetUTF32((const LBP_UTF32_CHAR*)s.Wide()); +#else + m_pImpl->SetUTF16((const LBP_UTF16_CHAR*)s.Wide()); +#endif +} + +const wchar_t* CLBP_UTF::AsWide(void) const +{ +#ifdef RHINO_APPLE + return (const wchar_t*)UTF32(); +#else + return (const wchar_t*)UTF16(); +#endif +} + +const LBP_UTF8_CHAR* CLBP_UTF::UTF8(void) const +{ + return m_pImpl->UTF8(); +} + +const LBP_UTF16_CHAR* CLBP_UTF::UTF16(void) const +{ + return m_pImpl->UTF16(); +} + +const LBP_UTF32_CHAR* CLBP_UTF::UTF32(void) const +{ + return m_pImpl->UTF32(); +} + +void CLBP_UTF::Set(const wchar_t* sz) +{ +#ifdef RHINO_APPLE + SetUTF32((const LBP_UTF32_CHAR*)sz); +#else + SetUTF16((const LBP_UTF16_CHAR*)sz); +#endif +} + +void CLBP_UTF::SetUTF8(const LBP_UTF8_CHAR* sz) +{ + m_pImpl->SetUTF8(sz); +} + +void CLBP_UTF::SetUTF16(const LBP_UTF16_CHAR* wsz) +{ + m_pImpl->SetUTF16(wsz); +} + +void CLBP_UTF::SetUTF32(const LBP_UTF32_CHAR* dwsz) +{ + m_pImpl->SetUTF32(dwsz); +} + +void CLBP_UTF::ClearUTFBuffers(void) +{ + m_pImpl->ClearUTF8Buffer(); + m_pImpl->ClearUTF16Buffer(); + m_pImpl->ClearUTF32Buffer(); +} + +bool CLBP_UTF::IsValidCodePoint(LBP_UTF32_CHAR dwCodePoint) +{ + if ((dwCodePoint >= eSurrogateLead) && (dwCodePoint <= eSurrogateEnd)) + return false; // Code point is not allowed to be within surrogate range. + + if (dwCodePoint > eMaxCodePoint) + return false; // Code point out of range. + + return true; +} diff --git a/LBP_UTF.h b/LBP_UTF.h new file mode 100644 index 0000000..248a15d --- /dev/null +++ b/LBP_UTF.h @@ -0,0 +1,70 @@ + +#pragma once + +#include "LBPString.h" + +#define LBP_UTF8_CHAR unsigned char +#define LBP_UTF16_CHAR unsigned short +#define LBP_UTF32_CHAR unsigned int + +template int LBP_UTF_LengthInChars(const T* wsz) +{ + int count = 0; + auto* p = wsz; + while (*p++ != 0) + { + count++; + } + return count; +} + +class CLBP_UTF +{ +public: + CLBP_UTF(); + virtual ~CLBP_UTF(); + + /** Get contents as a string. */ + CLBPString String(void) const; + + /** Set contents to a specific string. */ + void SetString(const CLBPString& s); + + /** Set contents from a zero-terminated wchar_t string. */ + void Set(const wchar_t * sz); + + /** Set contents from a zero-terminated UTF8 string. */ + void SetUTF8(const LBP_UTF8_CHAR * sz); + + /** Set contents from a zero-terminated UTF16 string. */ + void SetUTF16(const LBP_UTF16_CHAR * wsz); + + /** Set contents from a zero-terminated UTF32 string. */ + void SetUTF32(const LBP_UTF32_CHAR * dwsz); + + /** Returns the zero-terminated UTF8 representation of the + contents or NULL if the contents are invalid. */ + const LBP_UTF8_CHAR* UTF8(void) const; + + /** Returns the zero-terminated UTF16 representation of the + contents or NULL if the contents are invalid. */ + const LBP_UTF16_CHAR* UTF16(void) const; + + /** Returns the zero-terminated UTF32 representation of the + contents or NULL if the contents are invalid. */ + const LBP_UTF32_CHAR* UTF32(void) const; + + /** Returns the zero-terminated wchar_t* representation of the + contents or NULL if the contents are invalid. */ + const wchar_t* AsWide(void) const; + + // 18th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46786 + // This clears ALL THREE buffers. + void ClearUTFBuffers(void); + + static bool IsValidCodePoint(LBP_UTF32_CHAR dwCodePoint); + +private: + class Impl; + Impl * m_pImpl; +}; diff --git a/LBP_UUID.cpp b/LBP_UUID.cpp new file mode 100644 index 0000000..69560c4 --- /dev/null +++ b/LBP_UUID.cpp @@ -0,0 +1,330 @@ + +#include "stdafx.h" +#include "LBP_UUID.h" +#include "LBP_CRC32.h" + +#pragma comment (lib, "rpcrt4.lib") + +CLBP_UUID::CLBP_UUID() +{ + // The UUID must be initialized to zero. + memset((UUID*)this, 0, sizeof(UUID)); +} + +CLBP_UUID::CLBP_UUID(const UUID& uuid) +{ + *(UUID*)this = uuid; +} + +CLBP_UUID::CLBP_UUID(const CLBPString& s) +{ + SetAsString(s); +} + +CLBP_UUID::CLBP_UUID(const char * sz) +{ + SetAsString(sz); +} + +CLBP_UUID::CLBP_UUID(const WCHAR * wsz) +{ + SetAsString(wsz); +} + +void CLBP_UUID::Create(void) +{ + UuidCreate((UUID*)this); +} + +bool CLBP_UUID::IsNil(void) const +{ +#ifdef LBPRHLIB + return ON_UuidIsNil((UUID&)*this); +#else + RPC_STATUS s; + return UuidIsNil((UUID*)this, &s) ? true : false; +#endif +} + +int CLBP_UUID::Compare(const UUID& uuid) const +{ +#ifdef LBPRHLIB + return ON_UuidCompare((UUID*)this, (UUID*)&uuid); +#else + RPC_STATUS s; + return UuidCompare((UUID*)this, (UUID*)&uuid, &s); +#endif +} + +int CLBP_UUID::Compare(const char * sz) const +{ +#ifdef LBPRHLIB + const ON_UUID u = ON_UuidFromString(sz); + return Compare(u); +#else + UUID u; + UuidFromStringA((unsigned char *)sz, &u); + return Compare(u); +#endif +} + +int CLBP_UUID::Compare(const WCHAR * wsz) const +{ +#ifdef LBPRHLIB + const ON_UUID u = ON_UuidFromString(wsz); + return Compare(u); +#else + UUID u; + UuidFromStringW(reinterpret_cast(CLBPString(wsz).AsNonConstWideString()), &u); + return Compare(u); +#endif +} + +CLBP_UUID& CLBP_UUID::operator = (const UUID& uuid) +{ + *(UUID*)this = uuid; + + return *this; +} + +CLBP_UUID& CLBP_UUID::operator = (const char * sz) +{ + SetAsString(sz); + + return *this; +} + +CLBP_UUID& CLBP_UUID::operator = (const WCHAR * wsz) +{ + SetAsString(wsz); + + return *this; +} + +CLBP_UUID& CLBP_UUID::operator = (const CLBPString& s) +{ + SetAsString(s); + + return *this; +} + +bool CLBP_UUID::operator == (const char * sz) const +{ + return sz == *this; +} + +bool CLBP_UUID::operator == (const WCHAR * wsz) const +{ + return wsz == *this; +} + +bool CLBP_UUID::operator == (const UUID& uuid) const +{ +#ifdef LBPRHLIB + return 0==ON_UuidCompare(this, &uuid); +#else + return IsEqualGUID(*this, uuid) ? true : false; +#endif +} + +bool CLBP_UUID::operator != (const char * sz) const +{ + return !operator==(sz); +} + +bool CLBP_UUID::operator != (const WCHAR * wsz) const +{ + return !operator==(wsz); +} + +bool CLBP_UUID::operator != (const UUID& uuid) const +{ + return !operator==(uuid); +} + +bool operator == (const char * sz, const UUID& uuid) +{ +#ifdef LBPRHLIB + const ON_UUID u = ON_UuidFromString(sz); + return InlineIsEqualGUID(u, uuid) ? true : false; +#else + UUID u; + UuidFromStringA((UCHAR*)sz, &u); + return IsEqualGUID(u, uuid) ? true : false; +#endif +} + +bool operator == (const WCHAR * wsz, const UUID& uuid) +{ +#ifdef LBPRHLIB + const ON_UUID u = ON_UuidFromString(wsz); + return InlineIsEqualGUID(u, uuid) ? true : false; +#else + UUID u; + UuidFromStringW(reinterpret_cast(CLBPString(wsz).AsNonConstWideString()), &u); + return IsEqualGUID(u, uuid) ? true : false; +#endif +} + +bool operator != (const char * sz, const UUID& uuid) +{ + return !(operator == (sz, uuid)); +} + +bool operator != (const WCHAR * wsz, const UUID& uuid) +{ + return !(operator == (wsz, uuid)); +} + +CLBPString CLBP_UUID::String(int numChars) const +{ +#ifndef LBPRHLIB + USHORT* pString = NULL; + UuidToStringW((UUID*)this, &pString); + + ASSERT(wcslen((WCHAR*)pString) == ciNumChars); + + CLBPString s((WCHAR*)pString, ciNumChars); + + RpcStringFreeW(&pString); +#else + wchar_t str[ciNumChars+1]; + ON_UuidToString((UUID&)*this, str); + + ASSERT(wcslen(str) == ciNumChars); + + CLBPString s(str, ciNumChars); +#endif + + s.MakeUpper(); + + if (numChars < ciNumChars) + { + s = s.Left(max(0, numChars)) + L"..."; + } + + return s; +} + +CLBPString CLBP_UUID::RegistryFormat(int numChars) const +{ + return L"{" + String(numChars) + L"}"; +} + +void CLBP_UUID::SetAsString(const char* sz) +{ + SetAsString(CLBPString(sz).Wide()); +} + +void CLBP_UUID::SetAsString(const WCHAR* wsz) +{ + ASSERT(NULL != wsz); + if (NULL == wsz) + return; + + if (*wsz == L'{') + { + ASSERT(wcslen(wsz) == 38); + + // Support for registry style strings + CLBPString szNew(wsz+1, 37); + szNew = szNew.Left(szNew.GetLength()-1); + SetAsString(szNew.Wide()); + return; + } + +#ifdef LBPRHLIB + *this = ON_UuidFromString(wsz); +#else + if (UuidFromStringW((unsigned short *)wsz, this) != RPC_S_OK) + { + UuidCreateNil((UUID*)this); + } +#endif +} + +void CLBP_UUID::SetAsString(const CLBPString& s) +{ + SetAsString(s.AsWideString()); +} + +bool CLBP_UUID::CreateMashFromString(const char* sz) +{ + CLBPString s(sz); + + // Example UUID + // {1B6FE7D6-A246-4134-A32B-88B2966C76D3} - 32 Hex characters + + // Maximum length of string is 16 - we need to hash the last bit if it's longer. + // Hash will take up 8 hex characters which means if the string is longer than 16 + // we need to shorten it to 12. + + CLBPString sHex; + + if (s.GetLength() > 16) + { + const int iLen = (int)strlen(s.AsMBCSString()); + + const DWORD dw = CLBP_CRC32::Calculate((const void*)(s.AsMBCSString()), iLen); + + s = s.Left(12); + + sHex = s.HexStringMBCS(); + + CLBPString s2; + s2.Format(L"%X", dw); + sHex += s2; + } + else + if (s.GetLength() < 16) + { + s += L"00000000000000000"; + s = s.Left(16); + sHex = s.HexStringMBCS(); + } + else + { + sHex = s.HexStringMBCS(); + } + + CLBPString sUUID; + sUUID.Format("%S-%S-%S-%S-%S", sHex.Left(8) .Wide(), + sHex.Mid(8,4) .Wide(), + sHex.Mid(12,4).Wide(), + sHex.Mid(16,4).Wide(), + sHex.Right(12)); + sUUID.MakeUpper(); + SetAsString(sUUID); + + return true; +} + +DWORD CLBP_UUID::Hash(void) const +{ + return CLBP_CRC32::Calculate(this, sizeof(UUID)); +} + +bool CLBP_UUID::IsValidUuid(const WCHAR* wsz) // Static. +{ + // Format is XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. + + const int len = int(wcslen(wsz)); + if (len != ciNumChars) + return false; + + for (int i = 0; i < len; i++) + { + const wchar_t c = wsz[i]; + + if ((8 == i) || (13 == i) || (18 == i) || (23 == i)) + { + if ('-' != c) + return false; + } + else + if ( ! (((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f')))) + return false; + } + + return true; +} diff --git a/LBP_UUID.h b/LBP_UUID.h new file mode 100644 index 0000000..8a2c805 --- /dev/null +++ b/LBP_UUID.h @@ -0,0 +1,60 @@ + +#pragma once + +#include "LBPString.h" + +class CLBP_UUID : public UUID +{ +public: + CLBP_UUID(); // Initialised to nil uuid. + CLBP_UUID(const UUID& uuid); + CLBP_UUID(const char* sz); + CLBP_UUID(const WCHAR* wsz); + CLBP_UUID(const CLBPString& s); + + ~CLBP_UUID() { } // No virtual functions are allowed in this class. + +public: // UUID operations. + void Create(void); + bool IsNil(void) const; + DWORD Hash(void) const; + + int Compare(const char* sz) const; + int Compare(const WCHAR* wsz) const; + int Compare(const UUID& uuid) const; + + CLBP_UUID& operator = (const char* sz); + CLBP_UUID& operator = (const WCHAR* wsz); + CLBP_UUID& operator = (const CLBPString& s); + CLBP_UUID& operator = (const UUID& uuid); + + bool operator == (const char* sz) const; + bool operator == (const WCHAR* wsz) const; + bool operator == (const UUID& uuid) const; + + bool operator != (const char* sz) const; + bool operator != (const WCHAR* wsz) const; + bool operator != (const UUID& uuid) const; + + friend bool operator == (const char * sz, const UUID& uuid); + friend bool operator == (const WCHAR * wsz, const UUID& uuid); + friend bool operator != (const char * sz, const UUID& uuid); + friend bool operator != (const WCHAR * wsz, const UUID& uuid); + +public: // String operations. + static const int ciNumChars = 36; + CLBPString String(int numChars=ciNumChars) const; + CLBPString RegistryFormat(int numChars=ciNumChars) const; + operator CLBPString(void) const { return String(); } + + // Input any string. The UUID produced from the string will always be the same. Note - ASCII strings only. + bool CreateMashFromString(const char* sz); + + // Returns true if string is a valid UUID. + static bool IsValidUuid(const WCHAR* wsz); + +protected: + void SetAsString(const char* sz); + void SetAsString(const WCHAR* wsz); + void SetAsString(const CLBPString& s); +}; diff --git a/LBP_XML.cpp b/LBP_XML.cpp new file mode 100644 index 0000000..5fabcbf --- /dev/null +++ b/LBP_XML.cpp @@ -0,0 +1,3462 @@ + +#include "stdafx.h" +#include "LBP_XML.h" +#include "LBPUnicodeTextFile.h" +#include "LBPColor.h" +#include "LBPException.h" +#include "LBP_UTF.h" +#include "LBPBase64.h" +#include "LBP_CRC32.h" +#include "LBPLibUtilities.h" + +class CXMLCriticalSection +{ +public: + CXMLCriticalSection(CCriticalSection& cs) : m_CS(cs) { m_CS.Lock(); } + ~CXMLCriticalSection() { m_CS.Unlock(); } + +private: + CCriticalSection& m_CS; +}; + +bool CLBP_XMLNode::m_bAutoTypePropValue = false; + +static const wchar_t* StringFromPropType(CLBPVariant::vt vt) +{ + switch (vt) + { + case CLBPVariant::vt_Integer: return L"int"; + case CLBPVariant::vt_Float: return L"float"; + case CLBPVariant::vt_Double: return L"double"; + case CLBPVariant::vt_String: return L"string"; + case CLBPVariant::vt_Bool: return L"bool"; + case CLBPVariant::vt_Matrix: return L"matrix"; + case CLBPVariant::vt_Uuid: return L"uuid"; + case CLBPVariant::vt_Time: return L"time"; + case CLBPVariant::vt_Buffer: return L"buffer"; + case CLBPVariant::vt_4_double_color: return L"color"; + case CLBPVariant::vt_2_double_array: return L"2da"; + case CLBPVariant::vt_3_double_array: return L"3da"; + case CLBPVariant::vt_4_double_array: return L"4da"; + } + + return L"null"; +} + +static CLBPVariant::vt PropTypeFromString(const CLBPString& s) +{ + if (L"int" == s) return CLBPVariant::vt_Integer; + if (L"float" == s) return CLBPVariant::vt_Float; + if (L"double" == s) return CLBPVariant::vt_Double; + if (L"string" == s) return CLBPVariant::vt_String; + if (L"bool" == s) return CLBPVariant::vt_Bool; + if (L"matrix" == s) return CLBPVariant::vt_Matrix; + if (L"uuid" == s) return CLBPVariant::vt_Uuid; + if (L"time" == s) return CLBPVariant::vt_Time; + if (L"buffer" == s) return CLBPVariant::vt_Buffer; + if (L"color" == s) return CLBPVariant::vt_4_double_color; + if (L"2da" == s) return CLBPVariant::vt_2_double_array; + if (L"3da" == s) return CLBPVariant::vt_3_double_array; + if (L"4da" == s) return CLBPVariant::vt_4_double_array; + + return CLBPVariant::vt_Null; +} + +REFCOUNT_INT g_lNodeCount = 0; +int LBP_GetNodeCount() { return g_lNodeCount; } + +REFCOUNT_INT g_lPropertyCount = 0; +int LBP_GetPropertyCount() { return g_lPropertyCount; } + +#ifdef _DEBUG + #define LBPXML_PROFILER_START XMLProfiler.StartWatch() + #define LBPXML_PROFILER_STOP XMLProfiler.StopWatch() +#else + #define LBPXML_PROFILER_START + #define LBPXML_PROFILER_STOP +#endif + +int g_iWarningsFlagCounter = 0; +bool ThrowWarningExceptions() +{ + return g_iWarningsFlagCounter > 0; +} + +void AutoTypeVariant(CLBPVariant& v); + +CLBP_XMLNode::CLBP_XMLNode(const CLBPString& sName) +{ + ::InterlockedIncrement(&g_lNodeCount); + + SetTagName(sName); + + AddEmptyDefaultProperty(); +} + +CLBP_XMLNode::CLBP_XMLNode(const CLBP_XMLNode& src) +{ + ::InterlockedIncrement(&g_lNodeCount); + + *this = src; +} + +CLBP_XMLNode::~CLBP_XMLNode() +{ + InternalRemoveAllProperties(); + RemoveAllChildren(); + + ::InterlockedDecrement(&g_lNodeCount); +} + +CLBP_XMLNode& CLBP_XMLNode::operator=(const CLBP_XMLNode& src) +{ + CXMLCriticalSection cs1(m_CS); + CXMLCriticalSection cs2(src.m_CS); + + InternalRemoveAllProperties(); + RemoveAllChildren(); + + m_sName = src.m_sName; + + // Copy in the properties. + CLBP_XMLProperty* pProperty = NULL; + CPropertyIterator pi = src.GetPropertyIterator(); + while (NULL != (pProperty = pi.GetNextProperty())) + { + InternalAddProperty(*pProperty); // This does a copy anyway - no need to call the copy constructor + } + + // Copy in the children. + CLBP_XMLNode* pChild = NULL; + CChildIterator ci = src.GetChildIterator(); + + while (NULL != (pChild = ci.GetNextChild())) + { + AddChildNode(new CLBP_XMLNode(*pChild)); + } + + return *this; +} + +void CLBP_XMLNode::AssignFast(const CLBP_XMLNode& src) +{ + *this = src; +} + +bool CLBP_XMLNode::MergeFrom(const CLBP_XMLNode& src) +{ + CXMLCriticalSection cs1(m_CS); + CXMLCriticalSection cs2(src.m_CS); + + if (m_sName.operator != (src.TagName())) + return false; + + //Copy in the parameters + CLBP_XMLProperty* pProperty = NULL; + + CPropertyIterator pi = src.GetPropertyIterator(); + while(NULL != (pProperty = pi.GetNextProperty())) + { + //Replaces any that are already there. + AddProperty(pProperty); + } + + //Copy in the children + CLBP_XMLNode* pChild = NULL; + CChildIterator ci = src.GetChildIterator(); + + BOOL bNeedToMerge = ChildrenCount() != 0; + + while (NULL != (pChild = ci.GetNextChild())) + { + CLBPString sName = pChild->TagName(); + CLBP_XMLNode* pLocalChildNode = bNeedToMerge ? GetNodeAtPath(pChild->TagName()) : NULL; + + if (pLocalChildNode) + { + pLocalChildNode->MergeFrom(*pChild); + } + else + { + AddChildNode(new CLBP_XMLNode(*pChild)); + } + } + + return true; +} + +void CLBP_XMLNode::Clear(void) +{ + SetTagName(L""); + RemoveAllProperties(); + RemoveAllChildren(); +} + +void CLBP_XMLNode::AddEmptyDefaultProperty(void) +{ + static CLBP_XMLProperty empty_prop(L""); + AddProperty(&empty_prop); +} + +void CLBP_XMLNode::RemoveAllChildren(void) +{ + CXMLCriticalSection cs(m_CS); + + if (NULL != m_pFirstChild) + { + CChildIterator it(this); + CLBP_XMLNode* pNode = NULL; + do + { + pNode = it.GetNextChild(); + delete pNode; + } + while (NULL != pNode); + + m_pFirstChild = NULL; + m_pLastChild = NULL; + } +} + +void CLBP_XMLNode::InternalRemoveAllProperties(void) +{ + if (NULL != m_pFirstProperty) + { + CLBP_XMLProperty* pProp = NULL; + CPropertyIterator pit(this); + do + { + pProp = pit.GetNextProperty(); + delete pProp; + } + while (NULL != pProp); + + m_pFirstProperty = NULL; + } +} + +CLBP_XMLNode* CLBP_XMLNode::InternalUnhookChild(CLBP_XMLNode& childNode) +{ + CLBP_XMLNode* pChildNode = &childNode; + + if (pChildNode->m_pParent != this) + return NULL; + + if (m_pFirstChild == pChildNode) + { + if (m_pLastChild == m_pFirstChild) + { + m_pLastChild = pChildNode->m_pNextSibling; + } + + m_pFirstChild = pChildNode->m_pNextSibling; + + return pChildNode; + } + else + { + //First the child which points to this one + CLBP_XMLNode* pNode = m_pFirstChild; + while (NULL != pNode) + { + if (pNode->m_pNextSibling == pChildNode) + { + pNode->m_pNextSibling = pChildNode->m_pNextSibling; + + if (NULL == pNode->m_pNextSibling) + { + m_pLastChild = pNode; + } + + return pChildNode; + } + + pNode = pNode->m_pNextSibling; + } + } + + return NULL; +} + +void CLBP_XMLNode::RemoveAllProperties(void) +{ + CXMLCriticalSection cs(m_CS); + + InternalRemoveAllProperties(); + AddEmptyDefaultProperty(); +} + +const char* CLBP_XMLNode::TagNameMBCS(void) const +{ + CXMLCriticalSection cs(m_CS); + + return m_sName.AsMBCSString(); +} + +const WCHAR* CLBP_XMLNode::TagName(void) const +{ + CXMLCriticalSection cs(m_CS); + + return m_sName.AsWideString(); +} + +void CLBP_XMLNode::SetTagName(const CLBPString& sName) +{ + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + if (sName.IsEmpty()) + { + m_sName.Empty(); + } + else + { + CLBPString& s = m_sName.EmptyStringForWrite(); + s = sName; + s.TrimLeft(); + s.TrimRight(); + } +} + +bool CLBP_XMLNode::IsValidXMLName(const CLBPString& sTagName) // Static. +{ + const int iLength = sTagName.GetLength(); + for (int i = 0; i < iLength; i++) + { + const wchar_t wc = towlower(sTagName[i]); + + if ((wc >= L'a') && (wc <= L'z')) + continue; + + if (wc == L'_') + continue; + + if ((wc >= L'0') && (wc <= L'9')) + continue; + + if (i > 0) + { + if (wc == L'-') + continue; + } + + OutputDebugString(_T("Invalid XML tag syntax - ")); + OutputDebugString(sTagName.T()); + OutputDebugString(_T("\n")); + + ASSERT(false); + + return false; + } + + return true; +} + +CLBP_XMLNode* CLBP_XMLNode::GetParent(void) const +{ + return m_pParent; +} + +const CLBP_XMLNode& CLBP_XMLNode::TopmostParent(void) const +{ + CXMLCriticalSection cs(m_CS); + + const CLBP_XMLNode* p = this; + while (p->m_pParent) + { + p = p->m_pParent; + } + + return *p; +} + +CLBP_XMLNode* CLBP_XMLNode::AddChildNode(CLBP_XMLNode* pNode) +{ + if (NULL == pNode) + return NULL; + + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + if (NULL == m_pFirstChild) + { + //There are no children - just add one + pNode->m_pNextSibling = NULL; + m_pFirstChild = pNode; + m_pLastChild = pNode; + + } + else + { + //There are children - add one to the end + ASSERT(m_pLastChild != NULL); + m_pLastChild->m_pNextSibling = pNode; + pNode->m_pNextSibling = NULL; + m_pLastChild = pNode; + } + + pNode->m_pParent = this; + + return pNode; +} + +CLBP_XMLProperty* CLBP_XMLNode::AddProperty(const CLBP_XMLProperty* pProp) +{ + if (NULL == pProp) + return NULL; + + return AddProperty(*pProp); +} + +CLBP_XMLProperty* CLBP_XMLNode::AddProperty(const CLBP_XMLProperty& prop) +{ + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + if (ThrowWarningExceptions()) + { + const CLBPString sName = prop.Name(); + if (sName.Contains(L"\n") || sName.Contains(L"\r")) + { + // The string contain LF or CR codes - this is likely to cause problems but + // is still valid XML. + throw CLBP_XMLException(CLBP_XMLException::eBadCharactersInString, sName); + } + + const CLBPString sValue = prop.GetValue(); + if (sValue.Contains(L"\n") || sValue.Contains(L"\r")) + { + // The string contain LF or CR codes - this is likely to cause problems but + // is still valid XML. + throw CLBP_XMLException(CLBP_XMLException::eBadCharactersInString, sValue); + } + } + + InternalRemoveProperty(prop.Name()); + + // Copy the property, then add it to the tree + return InternalAddProperty(prop); +} + +CLBP_XMLProperty* CLBP_XMLNode::AttachProperty(CLBP_XMLProperty* pProp) +{ + if (NULL == pProp) + return NULL; + + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + InternalRemoveProperty(pProp->Name()); + + pProp->m_pNextProperty = m_pFirstProperty; + m_pFirstProperty = pProp; + m_pFirstProperty->m_pOwner = this; + + return pProp; +} + +bool CLBP_XMLNode::RemoveProperty(const CLBPString& sPropertyName) +{ + CXMLCriticalSection cs(m_CS); + + return InternalRemoveProperty(sPropertyName); +} + +CLBP_XMLProperty* CLBP_XMLNode::InternalAddProperty(const CLBP_XMLProperty& prop) +{ + CLBP_XMLProperty* pProp = new CLBP_XMLProperty(prop); + pProp->m_pOwner = this; + pProp->m_pNextProperty = m_pFirstProperty; + m_pFirstProperty = pProp; + + return pProp; +} + +bool CLBP_XMLNode::InternalRemoveProperty(const CLBPString& sName) +{ + CLBP_XMLProperty* pPrevious = NULL; + CLBP_XMLProperty* pProp = m_pFirstProperty; + + while (NULL != pProp) + { + if (pProp->Name().CompareNoCase(sName.AsWideString()) == 0) + { + if (NULL == pPrevious) + { + m_pFirstProperty = pProp->m_pNextProperty; + } + else + { + pPrevious->m_pNextProperty = pProp->m_pNextProperty; + } + + delete pProp; + return true; + } + + pPrevious = pProp; + pProp = pProp->m_pNextProperty; + } + + return false; +} + +void CLBP_XMLNode::Remove(void) +{ + CLBP_XMLNode* pParent = GetParent(); + if (NULL != pParent) + { + pParent->RemoveChild(this); + } + else + { + delete this; + } +} + +CLBP_XMLNode* CLBP_XMLNode::UnhookChild(CLBP_XMLNode* pChildNode) +{ + if (NULL == pChildNode) + return NULL; + + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + return InternalUnhookChild(*pChildNode); +} + +bool CLBP_XMLNode::RemoveChild(CLBP_XMLNode* pChildNode) +{ + if (NULL == pChildNode) + return NULL; + + CXMLCriticalSection cs(m_CS); + + ASSERT(!ReadOnly()); + + CLBP_XMLNode* pChild = InternalUnhookChild(*pChildNode); + delete pChild; + + return pChild != NULL; +} + +CLBP_XMLNode::CChildIterator CLBP_XMLNode::GetChildIterator() const +{ + return CChildIterator(this); +} + +CLBP_XMLNode::CPropertyIterator CLBP_XMLNode::GetPropertyIterator(bool bAlphabetized) const +{ + return CPropertyIterator(this, bAlphabetized); +} + +DWORD CLBP_XMLNode::PropertyCount() const +{ + CXMLCriticalSection cs(m_CS); + + CPropertyIterator it = GetPropertyIterator(); + + DWORD iCount = 0; + while (it.GetNextProperty()) + { + iCount++; + } + + return iCount; +} + +DWORD CLBP_XMLNode::ChildrenCount() const +{ + CXMLCriticalSection cs(m_CS); + + CChildIterator it = GetChildIterator(); + + DWORD iCount = 0; + while (it.GetNextChild()) + { + iCount++; + } + + return iCount; +} + +bool CLBP_XMLNode::HasDefaultProperty() const +{ + return true; // !!GetNamedProperty(L""); +} + +int CLBP_XMLNode::GetNestedDepth() const +{ + CXMLCriticalSection cs(m_CS); + + int iDepth = 0; + + const CLBP_XMLNode* pNode = this; + while (NULL != pNode->m_pParent) + { + pNode = pNode->m_pParent; + iDepth++; + } + + return iDepth; +} + +static bool PrependNodeToStringAndRecurseParents(const CLBP_XMLNode* pNode, CLBPString& s) +{ + // Recursive function to pile up the path. + + if (NULL == pNode) + return false; + + CLBP_XMLNode* pParent = pNode->GetParent(); + if (NULL != pParent) + { + //CLBPString sNodeName = pNode->TagName(); + //s = sNodeName + L"/" + s; + s.Insert(0, L'/', 1); + s.Insert(0, pNode->TagName()); + + PrependNodeToStringAndRecurseParents(pParent, s); + + return true; + } + + return false; +} + +CLBPStringBV CLBP_XMLNode::GetPathFromRoot() const +{ + CXMLCriticalSection cs(m_CS); + + CLBPStringBV sPath(TagName()); + PrependNodeToStringAndRecurseParents(GetParent(), sPath); + + return sPath; +} + +CLBP_XMLProperty& CLBP_XMLNode::GetDefaultProperty(void) const +{ + static CLBPString empty(L""); + CLBP_XMLProperty* pProp = GetNamedProperty(empty); + ASSERT(NULL != pProp); // Always created by constructor. + + return *pProp; +} + +CLBP_XMLProperty* CLBP_XMLNode::GetNamedProperty(const wchar_t* wszName) const +{ + CXMLCriticalSection cs(m_CS); + + CPropertyIterator it = GetPropertyIterator(); + CLBP_XMLProperty* pProp = NULL; + + while (NULL != (pProp = it.GetNextProperty())) + { + if (_wcsicmp(wszName, pProp->Name()) == 0) + return pProp; + } + + return NULL; +} + +CLBP_XMLNode* CLBP_XMLNode::GetNamedChild(const wchar_t* wszName) const +{ + CXMLCriticalSection cs(m_CS); + + CChildIterator it = GetChildIterator(); + + CLBP_XMLNode* pNode = NULL; + while (NULL != (pNode = it.GetNextChild())) + { + if (_wcsicmp(wszName, pNode->TagName()) == 0) + return pNode; + } + + return NULL; +} + +CLBP_XMLNode* CLBP_XMLNode::GetNodeAtPathImpl(const wchar_t* wszPath, bool bCreate) +{ + //Forward slash "/" is the separator + + if (NULL == wszPath) + return const_cast(this); + + const wchar_t* p = wszPath; + while (*p != 0) + { + if (!iswspace(*p) && *p!=L'/') + break; + p++; + } + + //The input string was empty + if (*p == 0) + return const_cast(this); + + WCHAR wsz[260]; + wcsncpy(wsz, p, 260); + + //Now right trim out the whitespace + const int iLength = (int)wcslen(wsz); + + for (int i = iLength - 1; i >= 0; i--) + { + if (iswspace(wsz[i])) + wsz[i] = 0; + else + break; + } + + //Check for a resultant empty string + if (*wsz == 0) + return const_cast(this); + + const wchar_t* pstr = wcschr(wsz, L'/'); + const int iPos = pstr != NULL ? (int)(pstr-wsz) : -1; + + const wchar_t* pNext = NULL; + + if (-1 != iPos) + { + //sNext is the rest of the string that we're going to resurse through + pNext = wsz + iPos + 1; + wsz[iPos] = 0; + } + + CChildIterator it = GetChildIterator(); + + CLBP_XMLNode* pChild = NULL; + while (NULL != (pChild = it.GetNextChild())) + { + if (_wcsicmp(wsz, pChild->TagName()) == 0) + { + return pChild->GetNodeAtPathImpl(pNext, bCreate); + } + } + + // The child was not found. + + if (bCreate) + { + return AddChildNode(new CLBP_XMLNode(wsz))->GetNodeAtPathImpl(pNext, bCreate); + } + + return NULL; +} + +LPVOID CLBP_XMLNode::LastReadBufferPointer() const +{ + return m_pLastReadBufferPointer; +} + +void CLBP_XMLNode::SetLastReadBufferPointer(LPVOID p) +{ + m_pLastReadBufferPointer = p; +} + +DWORD CLBP_XMLNode::ReadFromStreamNoThrow(const WCHAR* wszStream, bool bWarningsAsErrors, bool bValidateTags) +{ + // Added a simple check for an empty input stream to stop this thing from outputting first-chance exceptions + // to the output line in normal operation and freaking out Dale. + if (NULL == wszStream || 0 == *wszStream) + { + return LBP_XML_READ_ERROR; + } + + try + { + return ReadFromStreamThrow(wszStream, bWarningsAsErrors, bValidateTags); + } + catch(...) + { + return LBP_XML_READ_ERROR; + } +} + +DWORD CLBP_XMLNode::ReadFromStreamThrow(const WCHAR* wszStream, bool bWarningsAsErrors, bool bValidateTags) +{ + if(bWarningsAsErrors) g_iWarningsFlagCounter++; + + Clear(); + //Search for the opening tag "<" character + WCHAR* pBuffer = const_cast(wszStream); + + CLBPString tag; + GetNextTag(tag, pBuffer, bValidateTags); + + SetLastReadBufferPointer((LPVOID)wszStream); + + if (!tag.IsEmpty()) + { + GetPropertiesFromTag(tag); + } + + if (tag.GetAtWide(tag.GetLength()-2) != L'/') + { + //This tag either has children, or a default property + CLBPString sDefaultProperty; + + BOOL bClosingTag = FALSE; + do + { + WCHAR* pStoreBuffer = pBuffer; + + //Get any crap between the tags and store as the default parameter + + WCHAR* pStart = pBuffer; + + while (*pStart != L'<' && *pStart != 0) pStart++; + + //These two lines account for 5% of the time + //CLBPString s(pBuffer, (int)(pStart-pBuffer)); + + //We ran off the end of the buffer - no idea why, but this is bad, so throw an exception + //See http://mcneel.myjetbrains.com/youtrack/issue/RH-16605 for example + if (0 == *pStart) + { + throw CLBP_XMLException(CLBP_XMLException::eMalformedTagOnRead, tag); + } + + ASSERT(*pStart==L'<'); + + if (NULL!=pStart && *pStart==L'<') + { + sDefaultProperty.Append(pBuffer, (int)(pStart-pBuffer)); + } + + pBuffer = pStart; + GetNextTag(tag, pBuffer, bValidateTags); + + bClosingTag = IsClosingTag(tag); + + if (!bClosingTag) + { + CLBP_XMLNode* pNode = new CLBP_XMLNode(L""); + AddChildNode(pNode); + pBuffer = pStoreBuffer + pNode->ReadFromStreamThrow(pStoreBuffer, bWarningsAsErrors, bValidateTags); + } + } + while (!bClosingTag); + + sDefaultProperty.TrimLeft(); + sDefaultProperty.TrimRight(); + sDefaultProperty.URLUnencode(); + + if (!sDefaultProperty.IsEmpty()) + { + CLBP_XMLProperty* pProp = new CLBP_XMLProperty; + AttachProperty(pProp); + + const int pos = m_bAutoTypePropValue ? sDefaultProperty.Find(L":") : -1; + if (pos > 0) + { + // The type is encoded in the value. + const CLBPVariant::vt type = PropTypeFromString(sDefaultProperty.Left(pos)); + CLBPVariant& v = pProp->GetNonConstValue(); + v = sDefaultProperty.Mid(pos+1); + v.SetTypePendingFlag(true); + v.DoAutoTyping(type); + } + else + { + const int iLength = sDefaultProperty.GetLength(); + pProp->SetHugeStringValue(sDefaultProperty.ReleaseOwnership(), iLength); + pProp->GetNonConstValue().SetTypePendingFlag(true); + } + } + } + + if (bWarningsAsErrors) g_iWarningsFlagCounter--; + + TopmostParent().OnNodeReadFromStream(this); + + return (DWORD)(pBuffer-wszStream); +} + +bool CLBP_XMLNode::GetPropertiesFromTag(const CLBPString& sTag) +{ + SetTagName(GetNameFromTag(sTag)); + + CLBPString tag(sTag); + tag.TrimLeft(L"/ "); + + int iPos = tag.Find(L' '); + if (iPos == -1) + return true; //No properties + + tag.TruncateMid(iPos+1); + + CLBPString sPropertyName, sPropertyValue; + CLBPVariant vValue; + + //We are now at the start of the properties + + bool bClear = false; + + while (!tag.IsEmpty()) + { + iPos = tag.Find(L'='); + if (iPos != -1) + { + sPropertyName = tag.Left(iPos); + sPropertyName.TrimLeft(); + + //tag = tag.Mid(iPos); + tag.TruncateMid(iPos); + + iPos = tag.Find(L'\"'); + + if (-1 != iPos) + { + //0.061 - this bit + tag.TruncateMid(iPos+1); + iPos = tag.Find(L'\"'); + // + + if (-1 != iPos) + { + sPropertyValue = tag.Left(iPos); + tag.TruncateMid(iPos+1); + tag.TrimLeft(); + sPropertyValue.URLUnencode(); + + const int pos = m_bAutoTypePropValue ? sPropertyValue.Find(L':') : -1; + if (pos > 0) + { + // The type is encoded in the value. + const CLBPVariant::vt type = PropTypeFromString(sPropertyValue.Left(pos)); + vValue = sPropertyValue.Mid(pos+1); + vValue.SetTypePendingFlag(true); + vValue.DoAutoTyping(type); + } + else + { + // This allows for lazy typing of the property + vValue.SetTypePendingFlag(true); + vValue = sPropertyValue; +// AutoTypeVariant(vValue); John commented this out because it made some of his strings turn into ints. But is this right? + } + + AttachProperty(new CLBP_XMLProperty(sPropertyName, vValue)); + + bClear = true; + } + } + } + + if (!bClear) + { + throw CLBP_XMLException(CLBP_XMLException::eMalformedTagOnRead, sTag); + } + + bClear = false; + + tag.TrimLeft(); + } + + return true; +} + +bool CLBP_XMLNode::IsClosingTag(const CLBPString& sTag) const +{ + if (sTag.GetAtWide(1) != L'/') + return false; + + if (GetNameFromTag(sTag).CompareNoCase(TagName()) == 0) + return true; + + return false; +} + +CLBPStringBV CLBP_XMLNode::GetNameFromTag(const CLBPString& sTag) // Static. +{ + CLBPStringBV tag(sTag); + tag.TrimLeft(L"/ "); + + const int iPos = tag.Find(L' '); + if (iPos != -1) + { + tag.Truncate(iPos); + } + + return tag; +} + +bool CLBP_XMLNode::RecoverProperty(const CLBPString& tag, int iEqualSign, CLBPString& sProp) // Static. +{ + //Move left, looking for a space and ensuring every character is a valid name char + ASSERT(tag.GetAtWide(iEqualSign) == L'='); + + int iLeftScan = iEqualSign-1; + + CLBPString sName; + while(iLeftScan > 0 && tag.GetAtWide(iLeftScan)!=L' ') + { + sName = tag.GetAtWide(iLeftScan--) + sName; + } + + if (!IsValidXMLName(sName) || sName.IsEmpty()) + { + return false; + } + + const int iStart = iEqualSign + 2; + + //Now try to recover the value + if(tag.GetLength() <= iStart) + { + return false; + } + + if(tag.GetAtWide(iEqualSign+1) != L'\"') + return false; + + const int iSecondQuote = tag.Find(L'\"', iStart); + if(iSecondQuote == -1) + return false; + + CLBPString sValue = tag.Mid(iStart, iSecondQuote - iStart); + + sValue.URLEncode(); + + sProp = sName + L"=\"" + sValue + L"\""; + + return true; +} + +void CLBP_XMLNode::AttemptToFixTag(CLBPString& tag) // Static. +{ + // We're going to rebuild the tag from the name and the number of valid properties we find. + const CLBPString sName = GetNameFromTag(tag); + if (sName.IsEmpty() || tag.GetLength() < 2) + return; + + const bool bSelfClosingTag = (0 == wcscmp(tag.Right(2), L"/>")); + + CLBPString sNewTag = L"<" + sName; + + // Now find all of the potential properties - looking for = signs. + int iPos = 0; + int iEqualSign = -1; + CLBPString sProp; + + while (-1 != (iEqualSign = tag.Find(L'=', iPos))) + { + // Move the search past this one for next time around. + iPos = iEqualSign + 1; + + if (RecoverProperty(tag, iEqualSign, sProp)) + { + sNewTag += L" "; + sNewTag += sProp; + } + } + + if (bSelfClosingTag) + { + sNewTag += L"/>"; + } + else + { + sNewTag += L">"; + } + + tag = sNewTag; +} + +void CLBP_XMLNode::GetNextTag(CLBPString& tag, WCHAR*& pBuffer, bool bValidateTag) // Static. +{ + WCHAR* pStart = pBuffer; + while (*pStart != L'<') + { + if (0 == *pStart) + { + CLBP_XMLException e(CLBP_XMLException::eTagNotFound, pBuffer); + throw(e); + } + pStart++; + } + + while (pStart[1] == L'?') + { + // This is a nasty document description tag - need to look for the end tag and skip it. + while (!(pStart[0]==L'?' && pStart[1]==L'>')) + { + if (0 == *pStart) + { + CLBP_XMLException e(CLBP_XMLException::eEndTagNotFound, pBuffer); + throw(e); + } + + pStart++; + } + + while (*pStart != L'<') pStart++; + } + + while (pStart[1] == L'!' && pStart[2] == L'-' && pStart[3] == L'-') + { + // This is a comment tag - need to look for the end tag and skip it. + while (!(pStart[0]==L'-' && pStart[1]==L'-' && pStart[2]==L'>')) + { + if (0 == *pStart) + { + CLBP_XMLException e(CLBP_XMLException::eEndTagNotFound, pBuffer); + throw(e); + } + + pStart++; + } + + while (*pStart != L'<') pStart++; + } + + WCHAR* pEnd = pStart; + while (*pEnd != L'>') + { + if (0 == *pEnd) + { + CLBP_XMLException e(CLBP_XMLException::eEndTagNotFound, pBuffer); + throw(e); + } + pEnd++; + } + + pBuffer = pEnd + 1; + + // Copy the tag into the ready-made string. + const DWORD dwCharacters = (DWORD)(pEnd - pStart + 1); + + tag.Set(pStart, dwCharacters); + + if (bValidateTag) + { + try + { + AssertValidTag(tag); + } + catch (CLBP_XMLException&) + { + AttemptToFixTag(tag); + AssertValidTag(tag); + } + } +} + +void CLBP_XMLNode::AssertValidTag(const CLBPString& tag) // Static. +{ + //Check for an even number of quotes - odd means there are quotes in the strings + + const int iQuoteCount = tag.Count(L'\"'); + + if ((iQuoteCount % 2) != 0) + { + //Odd number of quotes - boom! + CLBP_XMLException e(CLBP_XMLException::eMalformedTagOnRead, tag); + throw(e); + } + + int iGTCount = tag.Count(L'<'); + int iLTCount = tag.Count(L'>'); + + if (iGTCount != iLTCount) + { + //Bad tag format - even on nested tags, < and > should be equal + CLBP_XMLException e(CLBP_XMLException::eMalformedTagOnRead, tag); + throw(e); + } + + //Check for lone ampersands + int iPos = 0; + int iFind = 0; + do + { + iFind = tag.Find(L'&', iPos); + if (iFind != -1) + { + iPos = iFind + 1; + const int iSemicolon = tag.Find(L';', iFind); + if (-1 == iSemicolon) + { + //Unterminated escape sequence + throw CLBP_XMLException(CLBP_XMLException::eMalformedTagOnRead, tag); + } + + CLBPString sEsc = tag.Mid(iPos-1, iSemicolon-iPos+2); + if (!sEsc.IsURLEscapeSequence()) + { + throw CLBP_XMLException(CLBP_XMLException::eMalformedTagOnRead, tag); + } + } + + } + while (iFind != -1); +} + +DWORD CLBP_XMLNode::WriteHeaderToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + CXMLCriticalSection cs(m_CS); + + DWORD dwCharactersWritten = 0; + const bool bWrite = (cchMax != 0); + + //Form the tag in a string + + CLBPString tag = L"<"; + tag += m_sName; + + //Start the count + size_t iTagLength = tag.GetLength(); + + if (bIncludeFormatting) + { + const int iDepth = GetNestedDepth(); + if (iDepth) + { + tag.Insert(0, L'\t', iDepth); + iTagLength += iDepth; + } + } + + CPropertyIterator it = GetPropertyIterator(bSortedProperties); + CLBP_XMLProperty* pProp = NULL; + CLBPString sDefaultProperty; + const CLBPString* psDefaultProperty = NULL; + + while (NULL != (pProp=it.GetNextProperty())) + { + if (pProp->IsDefaultProperty()) + { + const auto& vDP = pProp->GetValue(); + + if (vDP.NeedsURLEncode()) + { + sDefaultProperty = vDP.AsString(); + sDefaultProperty.URLEncode(); + + if (!sDefaultProperty.IsEmpty()) + { + psDefaultProperty = &sDefaultProperty; + } + } + else + { + const CLBPString& sDP = vDP.AsString(); + if (!sDP.IsEmpty()) + { + psDefaultProperty = &sDP; + } + } + continue; + } + + if (bWrite) + { + tag += L' '; + tag += pProp->Name(); + tag += L'='; + tag += L'\"'; + } + + iTagLength += 3; + iTagLength += pProp->Name().GetLength(); + + const auto& vValue = pProp->GetValue(); + + if (m_bAutoTypePropValue) + { + const CLBPString sType = StringFromPropType(pProp->GetValue().Type()); + if (bWrite) tag += sType + L":"; + iTagLength += sType.GetLength() + 1; // 1 for colon. + } + + if (!vValue.NeedsURLEncode()) + { + const auto& sValue = vValue.AsString(); + if (bWrite) tag += sValue; + iTagLength += sValue.GetLength(); + } + else + { + CLBPString sValueCopy = vValue.AsString(); + sValueCopy.URLEncode(); + if (bWrite) tag += sValueCopy; + iTagLength += sValueCopy.GetLength(); + } + + if (bWrite) tag += L'\"'; // John added if (bWrite) + iTagLength++; + } + + if (NULL != psDefaultProperty || ChildrenCount() || bForceLongFormat) + { + const bool bType = m_bAutoTypePropValue && (NULL != psDefaultProperty); + + CLBPString sType; + if (bType) + sType = StringFromPropType(GetDefaultProperty().GetValue().Type()); + + if (bWrite) + { + tag += L'>'; + if (bType) tag += sType + L':'; + + if (psDefaultProperty) + tag += *psDefaultProperty; // which might be nothing + } + + iTagLength++; + + if (bType) iTagLength += sType.GetLength() + 1; // 1 for colon. + + iTagLength += (NULL == psDefaultProperty) ? 0 : psDefaultProperty->GetLength(); + + if (ChildrenCount() && bIncludeFormatting) + { + if (bWrite) + { + tag += L'\r'; + tag += L'\n'; + } + + iTagLength += 2; + } + } + + // Write the tag itself + the default property + if (bWrite) + { + const int iCopy = min((int)cchMax, (tag.GetLength() + 1)) * sizeof(WCHAR); + memcpy(wszStream, tag.AsWideString(), iCopy); + } + + dwCharactersWritten += (DWORD)iTagLength; + + ASSERT(!bWrite || (iTagLength == (size_t)tag.GetLength())); + + return dwCharactersWritten; +} + +DWORD CLBP_XMLNode::WriteChildrenToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + DWORD dwCharactersWritten = 0; + bool bWrite = (0 != cchMax); + + CChildIterator it = GetChildIterator(); + CLBP_XMLNode* pChild = NULL; + + WCHAR* pStart = wszStream; + + while(NULL != (pChild = it.GetNextChild())) + { + const DWORD dw = pChild->WriteToStream(pStart, bWrite ? cchMax-dwCharactersWritten : 0, bIncludeFormatting, bForceLongFormat, bSortedProperties); + dwCharactersWritten+=dw; + pStart+=dw; + bWrite = cchMax > dwCharactersWritten; + } + + return dwCharactersWritten; +} + +DWORD CLBP_XMLNode::WriteFooterToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting, bool bForceLongFormat) const +{ + const CLBPString& sDefaultPropertyValue = GetDefaultProperty().GetValue(); + + const bool bDefaultProperty = !sDefaultPropertyValue.IsEmpty(); + + const bool bWrite = 0 != cchMax; + WCHAR* pStart = wszStream; + + DWORD dwCharactersWritten = 0; + + if (bDefaultProperty || ChildrenCount() || bForceLongFormat) + { + //Write the end tag + + CLBPString sEndTag = L"'; + //sEndTag.Format("", TagName()); + + if(bIncludeFormatting) + { + sEndTag+=L'\r'; + sEndTag+=L'\n'; + } + + + if(bIncludeFormatting && ChildrenCount()) + { + const int iDepth = GetNestedDepth(); + if(iDepth) + { + sEndTag.Insert(0, L'\t', iDepth); + } + } + + if(bWrite) + { + const int iCopy = min((int)(cchMax-dwCharactersWritten), (sEndTag.GetLength() + 1))*sizeof(WCHAR); + + memcpy(pStart, sEndTag.AsWideString(), iCopy); + //wcsncpy(pStart, sEndTag.AsWideString(), cchMax-dwCharactersWritten); + } + + dwCharactersWritten+=sEndTag.GetLength(); + } + else + { + CLBPString tag = L"/>"; + + if(bIncludeFormatting) + { + tag+=L'\r'; + tag+=L'\n'; + } + + if(bWrite) + { +#if _MSC_VER < 1400 + wcscpy(wszStream, tag.AsWideString()); +#else + Checked::wcsncpy_s(wszStream, cchMax, tag.AsWideString(), _TRUNCATE); +#endif + } + + dwCharactersWritten += tag.GetLength(); + } + + return dwCharactersWritten; +} + +CLBP_XMLNode::operator CLBPString() const +{ + return String(); +} + + +CLBPStringBV CLBP_XMLNode::String(bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + CLBPStringBV s; + + // Pretend to write the tree to get the buffer size that needs to be allocated. + const DWORD dwNumCharsIncTerm = WriteToStream(NULL, 0, bIncludeFormatting, bForceLongFormat, bSortedProperties) + 1; // Add 1 for terminator. + + // Now really write the tree to the allocated buffer, including the terminator. + WriteToStream(s.GetWideBufferSetLength(dwNumCharsIncTerm), dwNumCharsIncTerm, bIncludeFormatting, bForceLongFormat, bSortedProperties); + s.ReleaseWideBuffer(); + + return s; +} + + +DWORD CLBP_XMLNode::WriteToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + CXMLCriticalSection cs(m_CS); + + DWORD dwCharactersWritten = 0; + + DWORD dw = WriteHeaderToStream(wszStream, cchMax, bIncludeFormatting, bForceLongFormat, bSortedProperties); + + dwCharactersWritten += dw; + + const bool bWrite = (cchMax != 0); + if (bWrite) + wszStream += dw; + + if (cchMax > dw) cchMax -= dw; + else cchMax = 0; + + dw = WriteChildrenToStream(wszStream, cchMax, bIncludeFormatting, bForceLongFormat, bSortedProperties); + + dwCharactersWritten += dw; + if (bWrite) + wszStream += dw; + + if (cchMax > dw) cchMax -= dw; + else cchMax = 0; + + dw = WriteFooterToStream(wszStream, cchMax, bIncludeFormatting, bForceLongFormat); + + dwCharactersWritten += dw; + + return dwCharactersWritten; +} + +bool CLBP_XMLNode::WriteToSegmentedStream(CLBP_XMLSegmentedStream& segs, bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + CXMLCriticalSection cs(m_CS); + + WCHAR* p; + + DWORD dw = WriteHeaderToStream(NULL, NULL, bIncludeFormatting, bForceLongFormat, bSortedProperties); + segs.Append(p = new WCHAR[dw+1]); + WriteHeaderToStream(p, dw+1, bIncludeFormatting, bForceLongFormat, bSortedProperties); + + WriteChildrenToSegmentedStream(segs, bIncludeFormatting, bForceLongFormat, bSortedProperties); + + dw = WriteFooterToStream(NULL, NULL, bIncludeFormatting, bForceLongFormat); + segs.Append(p = new WCHAR[dw+1]); + WriteFooterToStream(p, dw+1, bIncludeFormatting, bForceLongFormat); + + return true; +} + +bool CLBP_XMLNode::WriteChildrenToSegmentedStream(CLBP_XMLSegmentedStream& segs, bool bIncludeFormatting, bool bForceLongFormat, bool bSortedProperties) const +{ + CXMLCriticalSection cs(m_CS); + + CChildIterator it = GetChildIterator(); + CLBP_XMLNode* pChild = NULL; + + while (NULL != (pChild = it.GetNextChild())) + { + pChild->WriteToSegmentedStream(segs, bIncludeFormatting, bForceLongFormat, bSortedProperties); + } + + return true; +} + +void CLBP_XMLNode::ReleaseSegmentedStream(CLBP_XMLSegmentedStream& segs) // Static. +{ + for (int i = 0; i < segs.Count(); i++) + { + delete[] segs[i]; + } +} + +CLBP_XMLNode* CLBP_XMLNode::FirstChild(void) const +{ + return m_pFirstChild; +} + +CLBP_XMLNode* CLBP_XMLNode::PrevSibling(void) const +{ + CXMLCriticalSection cs(m_CS); + + CLBP_XMLNode* pPrev = m_pParent->m_pFirstChild; + if (pPrev == this) + return NULL; + + while (pPrev->m_pNextSibling != this) + { + pPrev = pPrev->m_pNextSibling; + } + + return pPrev; +} + +CLBP_XMLNode* CLBP_XMLNode::NextSibling(void) const +{ + return m_pNextSibling; +} + +void CLBP_XMLNode::MoveBefore(CLBP_XMLNode& other) +{ + if (&other == this) + return; + + CXMLCriticalSection cs(m_CS); + + CLBP_XMLNode* pBeforeOther = other.PrevSibling(); + if (pBeforeOther == this) + return; + + CLBP_XMLNode* pPrev = PrevSibling(); + if (NULL != pPrev) + { + pPrev->m_pNextSibling = m_pNextSibling; + } + else + { + // 'this' was the head; redirect the parent's first child. + m_pParent->m_pFirstChild = m_pNextSibling; + } + + m_pNextSibling = &other; + + if (NULL == pBeforeOther) + { + // 'other' was the head; redirect the parent's first child. + m_pParent->m_pFirstChild = this; + } + else + { + pBeforeOther->m_pNextSibling = this; + } +} + +void CLBP_XMLNode::MoveAfter(CLBP_XMLNode& other) +{ + if (&other == this) + return; + + CXMLCriticalSection cs(m_CS); + + CLBP_XMLNode* pPrev = PrevSibling(); + if (pPrev == &other) + return; + + if (NULL != pPrev) + { + pPrev->m_pNextSibling = m_pNextSibling; + } + else + { + // 'this' was the head; redirect the parent's first child. + m_pParent->m_pFirstChild = m_pNextSibling; + } + + m_pNextSibling = other.m_pNextSibling; + + other.m_pNextSibling = this; +} + +//////////////////////////////////////////////// +// +// Node and Property Iterator Implementation +// +//////////////////////////////////////////////// + +CLBP_XMLNode::CChildIterator::CChildIterator(const CLBP_XMLNode* pParent) +{ + if (NULL != pParent) + { + m_pCurrent = pParent->m_pFirstChild; + } + else + { + m_pCurrent = NULL; + } +} + +CLBP_XMLNode* CLBP_XMLNode::CChildIterator::GetNextChild(void) +{ + CLBP_XMLNode* p = m_pCurrent; + if (NULL != m_pCurrent) + { + m_pCurrent = m_pCurrent->m_pNextSibling; + } + + return p; +} + +CLBP_XMLNode::CPropertyIterator::CPropertyIterator(const CLBP_XMLNode* pParent, bool bAlphabetized) +{ + m_bSorted = bAlphabetized; + m_apSortedProperties = 0; + + if (m_bSorted) + { + const int iPropertyCount = pParent->PropertyCount(); + + if (iPropertyCount > 1) + { + m_pParent = pParent; + m_iIndex = 0; + } + else + { + m_bSorted = false; + } + } + + if (!m_bSorted) + { + if (NULL != pParent) + { + m_pCurrent = pParent->m_pFirstProperty; + } + else + { + m_pCurrent = NULL; + } + } +} + +CLBP_XMLNode::CPropertyIterator::~CPropertyIterator() +{ + delete m_apSortedProperties; +} + +CLBP_XMLProperty* CLBP_XMLNode::CPropertyIterator::GetNextPropertySorted() +{ + //First time through, we build the list. + if (m_iIndex == 0) + { + CPropertyIterator pi(m_pParent, false); + CLBP_XMLProperty* p = NULL; + + m_apSortedProperties = new ON_ClassArray; + while(NULL != (p = pi.GetNextProperty())) + { + m_apSortedProperties->Append(*p); + } + m_apSortedProperties->QuickSort(ON_CompareIncreasing); + } + + ASSERT(m_apSortedProperties != NULL); + +#ifdef LBPRHLIB + if (m_iIndex > m_apSortedProperties->Count()-1) + return NULL; + +#else + if (m_iIndex > m_apSortedProperties->GetUpperBound()) + return NULL; + +#endif + return &(*m_apSortedProperties)[m_iIndex++]; +} + +//////////////////////////////////////////////// +// +// Property Implementation +// +//////////////////////////////////////////////// + +#ifdef USE_REFERENCE_COUNTED_PROPERTY +CLBP_XMLProperty& CLBP_XMLProperty::operator = (const CLBP_XMLProperty& prop) +{ + if (NULL != prop.m_pData) + { + prop.m_pData->AddRef(); + } + + if (NULL != m_pData) + { + m_pData->Release(); + } + + m_pData = prop.m_pData; + + return *this; +} +#endif + +CLBP_XMLPropertyData::CLBP_XMLPropertyData() +{ + CommonCtor(); +} + +CLBP_XMLPropertyData::CLBP_XMLPropertyData(const CLBP_XMLPropertyData& src) +{ + CommonCtor(); + + m_sName = src.m_sName; + m_value = src.m_value; +} + +CLBP_XMLPropertyData::CLBP_XMLPropertyData(const CLBPVariant& value) +{ + CommonCtor(); + m_value = value; +} + +CLBP_XMLPropertyData::CLBP_XMLPropertyData(const CLBPString& sName, const CLBPVariant& v) +{ + CommonCtor(); + SetName(sName); + SetValue(v); +} + +CLBP_XMLPropertyData::~CLBP_XMLPropertyData() +{ +#ifdef USE_REFERENCE_COUNTED_PROPERTY + ASSERT(m_iRefCount == 0); +#endif + + ::InterlockedDecrement(&g_lPropertyCount); +} + +void CLBP_XMLPropertyData::CommonCtor() +{ +#ifdef USE_REFERENCE_COUNTED_PROPERTY + m_iRefCount = 1; +#endif + + ::InterlockedIncrement(&g_lPropertyCount); +} + +const CLBPString& CLBP_XMLPropertyData::Name(void) const +{ + return m_sName; +} + +const char* CLBP_XMLPropertyData::NameMBCS(void) const +{ + return m_sName.AsMBCSString(); +} + +void CLBP_XMLPropertyData::SetName(const CLBPString& sName) +{ + CLBPString& s = m_sName.EmptyStringForWrite(); + s = sName; + s.TrimLeft(); + s.TrimRight(); + +/*#ifdef _DEBUG + if(!CLBP_XMLNode::IsValidXMLName(m_sName)) + { + throw CLBP_XMLException(CLBP_XMLException::eBadTagName, s); + } +#endif*/ +} + +bool CLBP_XMLPropertyData::IsDefaultProperty(void) const +{ + return m_sName.IsEmpty(); +} + +void CLBP_XMLPropertyData::SetValue(const CLBPVariant& value) +{ + m_value = value; +} + +//Takes ownership of the string +void CLBP_XMLPropertyData::SetHugeStringValue(wchar_t* wsz, int iLengthOfBuffer) +{ + //Ensure the value is set to a string + m_value = L""; + + //Get direct access to the string + CLBPString& s = const_cast(m_value.AsString()); + + s.TakeOwnershipOfBuffer(wsz, iLengthOfBuffer); +} + +const CLBPVariant& CLBP_XMLPropertyData::GetValue() const +{ + return m_value; +} + +bool CLBP_XMLPropertyData::operator < (const CLBP_XMLPropertyData& prop) const +{ + if (prop.m_sName.IsEmpty()) + return false; + + if (m_sName.IsEmpty()) + return true; + + const int i = m_sName.CompareNoCase(prop.m_sName.Wide()); + + return i < 0; +} + +bool CLBP_XMLPropertyData::operator > (const CLBP_XMLPropertyData& prop) const +{ + if (m_sName.IsEmpty()) + return false; + + if (prop.m_sName.IsEmpty()) + return true; + + const int i = m_sName.CompareNoCase(prop.m_sName.Wide()); + + return i > 0; +} + +DWORD CLBP_XMLPropertyData::CRC(void) const +{ + return m_value.CRC() ^ m_sName.CRC(); +} + +//////////////////////////////////////////////// +// +// ROOT NODE Implementation +// +//////////////////////////////////////////////// + +static const CLBPString sXMLRootNodeName(L"xml"); +CLBP_XMLRootNode::CLBP_XMLRootNode() : CLBP_XMLNode(sXMLRootNodeName) +{ + m_wszBuffer = NULL; +} + +CLBP_XMLRootNode::CLBP_XMLRootNode(const CLBP_XMLRootNode& src) : CLBP_XMLNode(L"xml") +{ + m_wszBuffer = NULL; + + *this = src; +} + +CLBP_XMLRootNode::~CLBP_XMLRootNode() +{ + delete [] m_wszBuffer; +} + +CLBP_XMLRootNode& CLBP_XMLRootNode::operator = (const CLBP_XMLRootNode& src) +{ + CLBP_XMLNode& node = *this; + node = src; + + return *this; +} + +void CLBP_XMLRootNode::ReadFromFile(const char* szPath, bool bWarningsAsErrors, bool bValidateTags) +{ + CLBPString sPath(szPath); + ReadFromFile(sPath.AsWideString(), bWarningsAsErrors, bValidateTags); +} + +void CLBP_XMLRootNode::ReadFromFile(const WCHAR* szPath, bool bWarningsAsErrors, bool bValidateTags) +{ + if (bWarningsAsErrors) + g_iWarningsFlagCounter++; + + CLBPString sPath(szPath); + + CLBPString s; + + WIN32_FIND_DATA ffd = { 0 }; + HANDLE h = FindFirstFile(sPath, &ffd); + if (h == INVALID_HANDLE_VALUE) + { + s.Format(L"The file was not found %S\n", sPath.Wide()); + throw CLBP_XMLException(CLBP_XMLException::eFileNotFound, s); + } + + FindClose(h); + + CLBPUnicodeTextFile file; + if (!file.Open(sPath, CFile::modeRead | CFile::shareDenyWrite)) + { + s.Format(L"The file could not be read or was not Unicode %S\n", sPath.Wide()); + throw CLBP_XMLException(CLBP_XMLException::eBadFile, s); + }; + + CLBPString sFile; + if (!file.ReadEverything(sFile)) + { + s.Format(L"The file could not be read %S\n", sPath.Wide()); + throw CLBP_XMLException(CLBP_XMLException::eBadFile, s); + } + + delete[] m_wszBuffer; + const DWORD dwNumChars = sFile.GetLength(); + m_wszBuffer = new WCHAR[dwNumChars + 1]; + wcsncpy(m_wszBuffer, sFile.Wide(), dwNumChars); + m_wszBuffer[dwNumChars] = 0; + + DWORD dwRead = 0; + try + { + dwRead = ReadFromStreamThrow(m_wszBuffer, bWarningsAsErrors, bValidateTags); + } + catch (CLBP_XMLException& e) + { + delete [] m_wszBuffer; + m_wszBuffer = NULL; + throw e; + } + + if (0 == dwRead) + { + s.Format(L"No valid XML was read from the file %S\n", sPath.Wide()); + throw CLBP_XMLException(CLBP_XMLException::eNoValidXML, s); + } + + if (bWarningsAsErrors) + g_iWarningsFlagCounter--; +} + +bool CLBP_XMLRootNode::WriteToFile(const char* szPath, bool bIncludeFormatting, bool bUTF8, bool bSortedProperties) const +{ + CLBPString sPath(szPath); + return WriteToFile(sPath.AsWideString(), bIncludeFormatting, bUTF8, bSortedProperties); +} + +// 15th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46786 +// Length is in Chars. Multiply by size of the char for writing to the file. + +bool CLBP_XMLRootNode::WriteToFile(const WCHAR* wszPath, bool bIncludeFormatting, bool bUTF8, bool bSortedProperties) const +{ + CLBPString sPath(wszPath); + + CLBPUnicodeTextFile file(bUTF8); + if (!file.Open(sPath.T(), CFile::modeCreate | CFile::modeWrite)) + return false; + + CLBP_XMLSegmentedStream segs; + WriteToSegmentedStream(segs, bIncludeFormatting, false, bSortedProperties); + + for(int i=0;i(szUTF8); + file.Write(szUTF8, (UINT)(lengthInChars * sizeof(LBP_UTF8_CHAR))); + } + else + { + CLBP_UTF utf; + utf.Set(segs[i]); + const auto* szUTF16 = utf.UTF16(); + const auto lengthInChars = LBP_UTF_LengthInChars(szUTF16); + file.Write(szUTF16, (UINT)(lengthInChars * sizeof(LBP_UTF16_CHAR))); + } + } + + ReleaseSegmentedStream(segs); + + return true; +} + +#ifdef USE_REFERENCE_COUNTED_ROOTNODE + +CLBP_XMLRootNode_RC::CLBP_XMLRootNode_RC() +{ + m_pData = new CLBP_XMLRootNode_RC_Data; +} + +CLBP_XMLRootNode_RC::CLBP_XMLRootNode_RC(const CLBP_XMLRootNode_RC& src) + : + m_pData(NULL) +{ + *this = src; +} + +CLBP_XMLRootNode_RC::~CLBP_XMLRootNode_RC() +{ + if (NULL != m_pData) + { + m_pData->Release(); + } +} + +CLBP_XMLRootNode_RC& CLBP_XMLRootNode_RC::operator = (const CLBP_XMLRootNode_RC& src) +{ + if (NULL != src.m_pData) + { + src.m_pData->AddRef(); + } + + if (NULL != m_pData) + { + m_pData->Release(); + } + + m_pData = src.m_pData; + + return *this; +} + +#endif + + + +bool CLBP_XMLNode::RecurseChildren(LBPXML_ScanTreeCallback pCallback, void* pv) const +{ + CChildIterator it = GetChildIterator(); + + CLBP_XMLNode* pChild = NULL; + while (NULL != (pChild = it.GetNextChild())) + { + if (!pCallback(pChild, pv)) + return false; + + if (!pChild->RecurseChildren(pCallback, pv)) + return false; + } + + return true; +} +typedef ON_SimpleArray IntArray; + + +DWORD CLBP_XMLNode::CRC(void) const +{ + DWORD crc = m_sName.CRC(); + + int iCount = 0; + + IntArray propCRCs; + IntArray childCRCs; + + int iPropCount = 0; + int iChildCount = 0; + + CPropertyIterator pi = GetPropertyIterator(); + while (CLBP_XMLProperty* pProp = pi.GetNextProperty()) + { + propCRCs.Append(pProp->CRC()); + iPropCount++; + } + + CChildIterator ci = GetChildIterator(); + while (CLBP_XMLNode* pNode = ci.GetNextChild()) + { + childCRCs.Append(pNode->CRC()); + iChildCount++; + } + + + propCRCs.QuickSort(ON_CompareIncreasing); + childCRCs.QuickSort(ON_CompareIncreasing); + + ASSERT(iChildCount == childCRCs.Count()); + ASSERT(iPropCount == propCRCs.Count()); + + int i=0; + for (i=0;i aItems(iLines); + + // Isolate each line and add it to a sortable array. + const WCHAR* pLineScale = sXML.Wide(); + const WCHAR* pLast = pLineScale; + + while (0 != *pLineScale) + { + if (*pLineScale++ == L'\n') + { + const CLBPString line(pLast, (DWORD)(pLineScale-pLast)); + + aItems.Append(line); + pLast = pLineScale; + } + } + + //Now sort the array. This could be further optimized by making it into an array of pointers + //to strings + aItems.QuickSort(ON_CompareIncreasing); + + const int iLineCount = aItems.Count(); + + //Everything is going to be spat out into the same string, so we need to empty it + //without reducing the allocation so that it doesn't end up doing hundreds of REALLOCs + sXML.Empty(); + + // Build the output string by concatenating the array items. + for (int i = 0; i < iLineCount; i++) + { + sXML += aItems[i]; + } +} + +DWORD CLBP_XMLNode::ComputeCRC(void) const +{ + CLBPString s = String(true, false, true); + + NormalizeXML(s); + + return CLBP_CRC32::Calculate(s.Wide(), s.GetLength() * sizeof(WCHAR)); +} + +CLBP_XMLException::CLBP_XMLException() : CLBPRunTimeException(L"") +{ + m_error = eNoProblems; +} + +CLBP_XMLException::CLBP_XMLException(lbpxml_error_code error, const WCHAR* wszExtraInfo) + : CLBPRunTimeException(L"") +{ + m_error = error; + m_sExtraInfo = wszExtraInfo; + + m_sMessage.Format("Type: %S, Context: %S", What(), Context()); + +#ifdef _DEBUG + + CLBPString sError = wszExtraInfo; + OutputDebugString(sError); +#endif +} + +CLBP_XMLException::lbpxml_error_code CLBP_XMLException::GetErrorCode() const +{ + return m_error; +} + +const WCHAR* CLBP_XMLException::What() const +{ + switch(m_error) + { + case eNoProblems: + return L"OK"; + case eMalformedTagOnRead: + return L"Malformed tag on read"; + case eTagNotFound: + return L"Tag not found"; + case eEndTagNotFound: + return L"End tag not found"; + case eBadFile: + return L"Bad File"; + case eFileNotFound: + return L"File Not Found"; + case eBadType: + return L"Bad Type"; + case eNoValidXML: + return L"No Valid XML"; + case eBadTagName: + return L"Bad Tag Name"; + case eBadCharactersInString: + return L"Bad Characters in String"; + } + + return L"Unknown error"; +} + +const WCHAR* CLBP_XMLException::Context() const +{ + return m_sExtraInfo; +} + +///////////////////////////////////////////////////////////////// +// +// Variant implementation +// +///////////////////////////////////////////////////////////////// + +CLBPVariant::CLBPVariant() +{ + m_type = vt_String; + m_bTypePending = false; + m_pBuffer = NULL; +} + +CLBPVariant::CLBPVariant(int iValue) +{ + m_pBuffer = NULL; + SetValue(iValue); +} + +CLBPVariant::CLBPVariant(double dValue) +{ + m_pBuffer = NULL; + SetValue(dValue); +} + +CLBPVariant::CLBPVariant(float fValue) +{ + m_pBuffer = NULL; + SetValue(fValue); +} + +CLBPVariant::CLBPVariant(const CLBPString& s) +{ + m_pBuffer = NULL; + SetValue(s); +} + +CLBPVariant::CLBPVariant(const double* point, vt_arraytype at) +{ + m_pBuffer = NULL; + SetValue(point, at); +} + +CLBPVariant::CLBPVariant(const float* point, vt_arraytype at) +{ + m_pBuffer = NULL; + SetValue(point, at); +} + +CLBPVariant::CLBPVariant(const WCHAR* wsz) +{ + m_pBuffer = NULL; + CLBPString s(wsz); + SetValue(s); +} + +CLBPVariant::CLBPVariant(const char* sz) +{ + m_pBuffer = NULL; + CLBPString s(sz); + SetValue(s); +} + +CLBPVariant::CLBPVariant(const CLBPColor& c) +{ + m_pBuffer = NULL; + SetValue(c); +} + +CLBPVariant::CLBPVariant(bool b) +{ + m_pBuffer = NULL; + SetValue(b); +} + +CLBPVariant::CLBPVariant(const UUID& uuid) +{ + m_pBuffer = NULL; + SetValue(uuid); +} + +CLBPVariant::CLBPVariant(const CTime& time) +{ + m_pBuffer = NULL; + SetValue(time); +} + +CLBPVariant::CLBPVariant(const void* pBuffer, size_t size) +{ + m_pBuffer = NULL; + SetValue(pBuffer, size); +} + +CLBPVariant::CLBPVariant(const CLBPBuffer& buffer) +{ + m_pBuffer = NULL; + SetValue(buffer); +} + +CLBPVariant::CLBPVariant(const CLBPVariant& src) +{ + m_pBuffer = NULL; + *this = src; +} + +CLBPBuffer& CLBPVariant::Buffer() const +{ + if(!m_pBuffer) + { + m_pBuffer = new CLBPBuffer; + } + return *m_pBuffer; +} + +void CLBPVariant::BufferClear() +{ + if(m_pBuffer) + { + delete m_pBuffer; + m_pBuffer = NULL; + } +} + +CLBPVariant& CLBPVariant::operator=(const CLBPVariant& src) +{ + m_type = src.Type(); + m_bTypePending = src.TypePending(); + + if(m_type != vt_Buffer) + { + BufferClear(); + } + + switch(m_type) + { + case vt_String: + m_sVal.EmptyStringForWrite() = src.AsString(); + break; + case vt_Double: + m_dVal = src.AsDouble(); + break; + case vt_Float: + m_fVal = src.AsFloat(); + break; + case vt_Integer: + m_iVal = src.AsInteger(); + break; + case vt_3_double_array: + src.As3dPoint(m_aVal); + break; + case vt_2_double_array: + src.As2dPoint(m_aVal); + break; + case vt_4_double_array: + case vt_4_double_color: + src.As4dPoint(m_aVal); + break; + case vt_Bool: + m_bVal = src.AsBool(); + break; + case vt_Matrix: + src.AsMatrix(m_matrix); + break; + case vt_Null: + break; + case vt_Uuid: + m_uuidVal = src.AsUuid(); + break; + case vt_Time: + m_timeVal = (time_t)src.AsTime().GetTime(); + break; + case vt_Buffer: + Buffer() = src.AsBuffer(); + break; + default: + throw CLBP_XMLException(CLBP_XMLException::eBadType, L"Source has unknown type"); + } + + //The above calls can reset the type pending flag on the source + //make sure the source is set back to the original condition. + src.SetTypePendingFlag(m_bTypePending); + + return *this; +} + +CLBPVariant::~CLBPVariant() +{ + delete m_pBuffer; +} + +CLBPVariant::vt CLBPVariant::Type() const +{ + return m_type; +} + +bool CLBPVariant::IsNull() const +{ + return vt_Null == m_type; +} + +void CLBPVariant::SetNull() +{ + m_type = vt_Null; +} + +bool CLBPVariant::NeedsURLEncode(bool bForAnsi) const +{ + switch (Type()) + { + case vt_Buffer: + case vt_Integer: + case vt_Float: + case vt_Double: + case vt_Bool: + case vt_Null: + case vt_Uuid: + case vt_2_double_array: + case vt_3_double_array: + case vt_4_double_array: + case vt_4_double_color: + case vt_Matrix: + return false; + default: + return AsString().NeedsURLEncode(bForAnsi); + } +} + +void CLBPVariant::SetValue(int v) +{ + BufferClear(); + m_type = vt_Integer; + m_iVal = v; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(double v) +{ + BufferClear(); + m_type = vt_Double; + m_dVal = v; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(float v) +{ + BufferClear(); + m_type = vt_Float; + m_fVal = v; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const CLBPString& s) +{ + BufferClear(); + m_type = vt_String; + m_sVal.EmptyStringForWrite() = s; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const double* p, vt_arraytype at) +{ + BufferClear(); + + if(vt_array_16 == at) + { + m_type = vt_Matrix; + for(int i=0; i<16; i++) + { + m_matrix[i] = p[i]; + } + } + + if(vt_array_3 == at) + { + m_type = vt_3_double_array; + for(int i=0; i<3; i++) + { + m_aVal[i] = p[i]; + } + } + + if(vt_array_4 == at) + { + m_type = vt_4_double_array; + for(int i=0; i<4; i++) + { + m_aVal[i] = p[i]; + } + } + + if(vt_array_2 == at) + { + m_type = vt_2_double_array; + for(int i=0; i<2; i++) + { + m_aVal[i] = p[i]; + } + } + + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const float* p, vt_arraytype at) +{ + BufferClear(); + if(vt_array_16 == at) + { + m_type = vt_Matrix; + for(int i=0; i<16; i++) + { + m_matrix[i] = (double)p[i]; + } + } + + if(vt_array_3 == at) + { + m_type = vt_3_double_array; + for(int i=0; i<3; i++) + { + m_aVal[i] = (double)p[i]; + } + } + + if(vt_array_4 == at) + { + m_type = vt_4_double_array; + for(int i=0; i<4; i++) + { + m_aVal[i] = (double)p[i]; + } + } + + if(vt_array_2 == at) + { + m_type = vt_2_double_array; + for(int i=0; i<2; i++) + { + m_aVal[i] = (double)p[i]; + } + } + + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const CLBPColor& c) +{ + BufferClear(); + m_type = vt_4_double_color; + + m_aVal[0] = c.DRed(); + m_aVal[1] = c.DGreen(); + m_aVal[2] = c.DBlue(); + m_aVal[3] = c.DAlpha(); + + m_bTypePending = false; +} + +void CLBPVariant::SetValue(bool b) +{ + BufferClear(); + m_type = vt_Bool; + m_bVal = b; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const UUID& uuid) +{ + BufferClear(); + m_type = vt_Uuid; + m_uuidVal = uuid; + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const CTime& time) +{ + BufferClear(); + m_type = vt_Time; + m_timeVal = (time_t)time.GetTime(); + m_bTypePending = false; +} + +void CLBPVariant::SetValue(const void* pBuffer, size_t size) +{ + BufferClear(); + m_type = vt_Buffer; + m_bTypePending = false; + + Buffer() = CLBPBuffer(pBuffer, size); +} + +void CLBPVariant::SetValue(const CLBPBuffer& buffer) +{ + m_type = vt_Buffer; + m_bTypePending = false; + + Buffer() = buffer; +} + +int CLBPVariant::AsInteger() const +{ + DoAutoTyping(vt_Integer); + switch(Type()) + { + case vt_String: + //Sort out the BOOL case + if(m_sVal.CompareNoCase(L"true") ==0) return 1; + if(m_sVal.CompareNoCase(L"t")==0) return true; + return LBP_wtoi(m_sVal.Wide()); + case vt_Double: + return (int)m_dVal; + case vt_Float: + return (int)m_fVal; + case vt_Integer: + return m_iVal; + case vt_Bool: + return m_bVal ? 1 : 0; + default: + return 0; + } +} + +bool CLBPVariant::AsBool() const +{ + DoAutoTyping(vt_Bool); + switch(Type()) + { + case vt_String: + if(m_sVal.CompareNoCase(L"true")==0) return true; + if(m_sVal.CompareNoCase(L"t")==0) return true; + return LBP_wtoi(m_sVal.Wide()) != 0; + case vt_Integer: + return m_iVal != 0; + case vt_Bool: + return m_bVal; + default: + return false; + } +} + +double CLBPVariant::AsDouble() const +{ + DoAutoTyping(vt_Double); + switch(Type()) + { + case vt_String: + return LBP_wtof(m_sVal.Wide()); + case vt_Double: + return m_dVal; + case vt_Float: + return m_fVal; + case vt_Integer: + return (double)m_iVal; + case vt_Bool: + return m_bVal ? 1.0 : 0.0; + default: + return 0.0; + } +} + +float CLBPVariant::AsFloat() const +{ + DoAutoTyping(vt_Float); + switch(Type()) + { + case vt_String: + return (float)LBP_wtof(m_sVal.Wide()); + case vt_Double: + return (float)m_dVal; + case vt_Float: + return m_fVal; + case vt_Integer: + return (float)m_iVal; + case vt_Bool: + return m_bVal ? 1.0f : 0.0f; + default: + return 0.0f; + } +} + +const double* CLBPVariant::As3dPoint() const +{ + DoAutoTyping(vt_3_double_array); + switch(Type()) + { + case vt_2_double_array: + m_aVal[2] = 0.0; + case vt_3_double_array: + case vt_4_double_color: + case vt_4_double_array: + return m_aVal; + break; + case vt_String: + if(m_sVal.IsValid3dPoint()) + { + String2Point(3); + return m_aVal; + } + default: + { + static double d3[3]; + d3[0] = 0.0; + d3[1] = 0.0; + d3[2] = 0.0; + return d3; + } + } +} + +const double* CLBPVariant::As4dPoint() const +{ + DoAutoTyping(vt_4_double_array); + switch(Type()) + { + case vt_2_double_array: + m_aVal[2] = 0.0; + case vt_3_double_array: + m_aVal[3] = 0.0; + case vt_4_double_array: + case vt_4_double_color: + return m_aVal; + break; + case vt_String: + if(m_sVal.IsValid4dPoint()) + { + String2Point(4); + return m_aVal; + } + default: + { + static double d4[4]; + d4[0] = 0.0; + d4[1] = 0.0; + d4[2] = 0.0; + d4[3] = 0.0; + return d4; + } + } +} + +bool CLBPVariant::As3dPoint(double* /*[3]*/d) const +{ + DoAutoTyping(vt_3_double_array); + + double* pd = NULL; + + switch(Type()) + { + case vt_2_double_array: + m_aVal[2] = 0.0; + case vt_3_double_array: + case vt_4_double_color: + case vt_4_double_array: + pd = m_aVal; + break; + + case vt_String: + if(m_sVal.IsValid3dPoint()) + { + String2Point(3); + pd = m_aVal; + } + } + + if(NULL == pd) return false; + + for(int i=0;i<3;i++) + { + d[i] = pd[i]; + } + + return true; +} + +bool CLBPVariant::As4dPoint(double* /*[4]*/d) const +{ + DoAutoTyping(vt_4_double_array); + + double* pd = NULL; + + switch(Type()) + { + case vt_2_double_array: + m_aVal[2] = 0.0; + case vt_3_double_array: + m_aVal[3] = 0.0; + case vt_4_double_color: + case vt_4_double_array: + pd = m_aVal; + break; + + case vt_String: + if(m_sVal.IsValid4dPoint()) + { + String2Point(4); + pd = m_aVal; + } + } + + if(NULL == pd) return false; + + for(int i=0;i<4;i++) + { + d[i] = pd[i]; + } + + return true; +} + +bool CLBPVariant::As3dPoint(float* /*[3]*/f) const +{ + double d[3]; + bool b = As3dPoint(d); + if(!b) return false; + + for(int i=0;i<3;i++) + { + f[i] = (float)d[i]; + } + + return true; +} + +bool CLBPVariant::As4dPoint(float* /*[4]*/f) const +{ + double d[4]; + bool b = As4dPoint(d); + if(!b) return false; + + for(int i=0;i<4;i++) + { + f[i] = (float)d[i]; + } + + return true; +} + +const double* CLBPVariant::As2dPoint() const +{ + DoAutoTyping(vt_2_double_array); + switch(Type()) + { + case vt_2_double_array: + case vt_3_double_array: + case vt_4_double_color: + case vt_4_double_array: + return m_aVal; + break; + case vt_String: + if(m_sVal.IsValid2dPoint()) + { + String2Point(2); + return m_aVal; + } + default: + { + static double d3[3]; + d3[0] = 0.0; + d3[1] = 0.0; + d3[2] = 0.0; + return d3; + } + } +} + +bool CLBPVariant::As2dPoint(double* /*[2]*/d) const +{ + DoAutoTyping(vt_2_double_array); + + double* pd = NULL; + + switch(Type()) + { + case vt_2_double_array: + case vt_3_double_array: + case vt_4_double_color: + case vt_4_double_array: + pd = m_aVal; + break; + + case vt_String: + if(m_sVal.IsValid2dPoint()) + { + String2Point(2); + pd = m_aVal; + } + } + + if(NULL == pd) return false; + + for(int i=0;i<2;i++) + { + d[i] = pd[i]; + } + + return true; +} + +bool CLBPVariant::As2dPoint(float* /*[2]*/f) const +{ + double d[2]; + bool b = As2dPoint(d); + if(!b) return false; + + for(int i=0;i<2;i++) + { + f[i] = (float)d[i]; + } + + return true; +} + +const double* CLBPVariant::AsMatrix() const +{ + DoAutoTyping(vt_Matrix); + switch(Type()) + { + case vt_Matrix: + return m_matrix; + break; + case vt_String: + if(m_sVal.IsValidMatrix()) + { + String2Point(16); + return m_matrix; + } + default: + { + static double d[16]; + for(int i=0;i<16;i++) + { + d[i] = 0.0; + } + return d; + } + } +} + +bool CLBPVariant::AsMatrix(double* /*[16]*/d) const +{ + DoAutoTyping(vt_Matrix); + + double* pd = NULL; + + switch(Type()) + { + case vt_Matrix: + pd = m_matrix; + break; + case vt_String: + if(m_sVal.IsValidMatrix()) + { + String2Point(16); + pd = m_matrix; + } + break; + default: + return false; + } + + if(NULL == pd) return false; + + for(int i=0;i<16;i++) + { + d[i] = pd[i]; + } + + return true; +} + +bool CLBPVariant::AsMatrix(float* /*[16]*/f) const +{ + double d[16]; + bool b = AsMatrix(d); + if(!b) return false; + + for(int i=0;i<16;i++) + { + f[i] = (float)d[i]; + } + + return true; +} + +CLBPColor CLBPVariant::AsColor() const +{ + DoAutoTyping(vt_4_double_color); + + switch(Type()) + { + case vt_String: + String2Point(4); + case vt_4_double_color: + CLBPColor c(m_aVal[0], m_aVal[1], m_aVal[2], m_aVal[3]); + return c; + } + + return CLBPColor(); +} + +UUID CLBPVariant::AsUuid() const +{ + DoAutoTyping(vt_Uuid); + switch(Type()) + { + case vt_String: + return CLBP_UUID(m_sVal); + case vt_Uuid: + return m_uuidVal; + default: + static const UUID uuidNil = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }; + return uuidNil; + } +} + +CTime CLBPVariant::AsTime() const +{ + DoAutoTyping(vt_Time); + + switch(Type()) + { + case vt_String: + return CLBPTime(m_sVal); + case vt_Time: + return m_timeVal; + } + + return 0; +} + +const CLBPBuffer CLBPVariant::AsBuffer() const +{ + DoAutoTyping(vt_Buffer); + + switch(Type()) + { + case vt_String: + //Who owns the resultant buffer? + return CLBPBuffer(m_sVal); + case vt_Buffer: + return Buffer(); + } + + return CLBPBuffer(); +} + +void DoubleArrayString(const double* array, int iCount, CLBPString& s) +{ + s.Empty(); + s.GetWideBuffer(20*iCount); + s.ReleaseWideBuffer(); + + CLBPString sThis; + + for(int i=0;i(this); + + switch(type) + { + case vt_String: + break; + case vt_Double: + pThis->SetValue(AsDouble()); + break; + case vt_Float: + pThis->SetValue(AsFloat()); + break; + case vt_Integer: + pThis->SetValue(AsInteger()); + break; + case vt_4_double_array: + pThis->SetValue(As4dPoint(), vt_array_4); + break; + case vt_3_double_array: + pThis->SetValue(As3dPoint(), vt_array_3); + break; + case vt_2_double_array: + pThis->SetValue(As2dPoint(), vt_array_2); + break; + case vt_4_double_color: + pThis->SetValue(AsColor()); + break; + case vt_Matrix: + pThis->SetValue(AsMatrix(), vt_array_16); + break; + case vt_Bool: + pThis->SetValue(AsBool()); + break; + case vt_Uuid: + pThis->SetValue(AsUuid()); + break; + case vt_Time: + pThis->SetValue(AsTime()); + break; + case vt_Buffer: + pThis->SetValue(AsBuffer()); + break; + } + + //Otherwise we assume the conversion is not supported and just go on with life. +} + +void AutoTypeVariant(CLBPVariant& v) +{ + //Used by the XML reader to try to invent sensible types + //for variants read in from the stream + if(v.Type() != CLBPVariant::vt_String) + return; //The variant already has a type + + CLBPString s = v.AsString(); + v.SetTypePendingFlag(true); + + static const CLBPString sBase64Prefix = CLBPBuffer::Base64Prefix(); + static const int iBase64PrefixLength = sBase64Prefix.GetLength(); + + if(s == L"true" || s == L"false") + { + v.AsBool(); + return; + } + + if(s.Left(iBase64PrefixLength) == sBase64Prefix.Wide()) + { + v.AsBuffer(); + return; + } + + if(CLBPTime::IsValidTime(s)) + { + v.AsTime(); + return; + } + + if(s.IsValidIntegerNumber()) + { + v.AsInteger(); + return; + } + + if(s.IsValidMatrix()) + { + v.AsMatrix(); + return; + } + + if(s.IsValid4dPoint()) + { + v.As4dPoint(); + return; + } + + if(s.IsValid3dPoint()) + { + v.As3dPoint(); + return; + } + + if(s.IsValid2dPoint()) + { + v.As2dPoint(); + return; + } + + if(s.IsValidRealNumber()) + { + v.AsDouble(); + return; + } + + CLBP_UUID uuid(s); + if(!uuid.IsNil()) + { + v.AsUuid(); + return; + } +} + +void CLBP_XMLNode::OnNodeReadFromStream(const CLBP_XMLNode* pNode) const +{ +} + +#ifdef _DEBUG + +void CLBP_XMLNode::SetReadOnly(bool b) const +{ +} + +#endif diff --git a/LBP_XML.h b/LBP_XML.h new file mode 100644 index 0000000..1c1061c --- /dev/null +++ b/LBP_XML.h @@ -0,0 +1,649 @@ + +#pragma once + +class CLBP_XMLRootNode; +class CLBP_XMLNode; +class CLBP_XMLProperty; +class CLBPColor; + +#ifndef LBPEXPORT +#define LBPEXPORT +#endif + +#ifdef _DEBUG +#define USE_REFERENCE_COUNTED_ROOTNODE +#define USE_REFERENCE_COUNTED_PROPERTY +#endif + +#include "LBPException.h" +#include "LBPString.h" +#include "LBP_UUID.h" +#include "LBPLibUtilities.h" + +#ifndef __AFXTEMPL_H__ +#pragma message("XML classes require afxtempl.h in your PCH") +#endif + +#if defined (LBPLIB_APPLE_SPECIFIC) +#define REFCOUNT_INT int32_t +#define InterlockedIncrement OSAtomicIncrement32 +#define InterlockedDecrement OSAtomicDecrement32 +#endif + +#if defined (LBPLIB_WINDOWS_SPECIFIC) +#define REFCOUNT_INT LONG +#endif + +typedef bool (*LBPXML_ScanTreeCallback) (CLBP_XMLNode*, void* dwUser); + +class LBPEXPORT CLBP_XMLException : public CLBPRunTimeException +{ +public: + + typedef enum taglbpxml_error_code + { + eNoProblems, + eMalformedTagOnRead, + eTagNotFound, + eEndTagNotFound, + eBadFile, + eFileNotFound, + eBadType, + eNoValidXML, + eBadTagName, + eBadCharactersInString + + } lbpxml_error_code; + + CLBP_XMLException(); + CLBP_XMLException(lbpxml_error_code error, const WCHAR* wszExtraInfo = NULL); + + lbpxml_error_code GetErrorCode() const; + const WCHAR* What() const; + const WCHAR* Context() const; + +protected: + lbpxml_error_code m_error; + CLBPString m_sExtraInfo; +}; + +int LBPEXPORT LBP_GetNodeCount(void); +int LBPEXPORT LBP_GetPropertyCount(void); + +class LBPEXPORT CLBP_XMLNode +{ +public: + CLBP_XMLNode(const CLBPString& sName); // Always creates empty default property. + CLBP_XMLNode(const CLBP_XMLNode&); // Copy constructor. + virtual ~CLBP_XMLNode(); + + CLBP_XMLNode& operator=(const CLBP_XMLNode&); + + void AssignFast(const CLBP_XMLNode& src); // DEPRECATED - just calls operator = + + bool MergeFrom(const CLBP_XMLNode& src); // src node must have the same name + + CLBP_XMLNode* FirstChild(void) const; + CLBP_XMLNode* PrevSibling(void) const; + CLBP_XMLNode* NextSibling(void) const; + +public: // Tag ID + const char* TagNameMBCS(void) const; + const WCHAR* TagName(void) const; + void SetTagName(const CLBPString& sName); + + CLBP_XMLNode* GetParent(void) const; + const CLBP_XMLNode& TopmostParent(void) const; + + DWORD CRC(void) const; + + static bool IsValidXMLName(const CLBPString& sTagName); + +public: // Children scan + bool RecurseChildren(LBPXML_ScanTreeCallback, void* pvData) const; + +public: // Change data + CLBP_XMLNode* AddChildNode(CLBP_XMLNode* pNode); // The ownership transfers to the tree - do not delete this pointer. + CLBP_XMLProperty* AddProperty(const CLBP_XMLProperty* pProperty); // The property object is copied - you should delete it. + CLBP_XMLProperty* AddProperty(const CLBP_XMLProperty& prop); // John added this; it's handier than the pointer one. + CLBP_XMLProperty* AttachProperty(CLBP_XMLProperty* pProp); // This takes ownership of the property you made on the heap. + + // 19th June 2018 John Croudy, https://mcneel.myjetbrains.com/youtrack/issue/RH-46772 -- Crashes on the Mac. + //void SafeRemove(void); // John added this; removes and deletes this node. Safe to call even if 'this' is NULL. + + void Remove(void); // Removes and deletes this node. + bool RemoveProperty(const CLBPString& sPropertyName); + bool RemoveChild(CLBP_XMLNode* pChildNode); + CLBP_XMLNode* UnhookChild(CLBP_XMLNode* pChildNode); // Same as RemoveChild, but doesn't delete the child - instead it passes ownership to the caller. + + void RemoveAllChildren(void); + void RemoveAllProperties(void); + + void Clear(void); // Gets rid of all data on this node. + + void MoveBefore(CLBP_XMLNode& other); + void MoveAfter (CLBP_XMLNode& other); + +public: // Serialization + DWORD ReadFromStreamThrow(const WCHAR* wszStream, bool bWarningsAsErrors = false, bool bValidateTags = true); //Throws CLBP_XMLException + +#define LBP_XML_READ_ERROR 0xFFFFFFFF + //Returns LBP_XML_READ_ERROR if it fails + DWORD ReadFromStreamNoThrow(const WCHAR* wszStream, bool bWarningsAsErrors = false, bool bValidateTags = true); + + LPVOID LastReadBufferPointer(void) const; + + //This function is called on the top-most node during the reading process. + virtual void OnNodeReadFromStream(const CLBP_XMLNode* pNode) const; + + CLBPStringBV String(bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + operator CLBPString() const; + + //Input NULL for cchMax to return the number of characters which will be written + DWORD WriteToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + DWORD WriteHeaderToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + DWORD WriteChildrenToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + DWORD WriteFooterToStream(WCHAR* wszStream, DWORD cchMax, bool bIncludeFormatting = true, bool bForceLongFormat = false) const; + + typedef ON_SimpleArray CLBP_XMLSegmentedStream; + + bool WriteToSegmentedStream(CLBP_XMLSegmentedStream&, bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + bool WriteChildrenToSegmentedStream (CLBP_XMLSegmentedStream&, bool bIncludeFormatting = true, bool bForceLongFormat = false, bool bSortedProperties = false) const; + static void ReleaseSegmentedStream(CLBP_XMLSegmentedStream&); + +public: // Utilities + DWORD PropertyCount(void) const; + DWORD ChildrenCount(void) const; + bool HasDefaultProperty(void) const; // DEPRECATED - Always returns true. + int GetNestedDepth(void) const; + + DWORD ComputeCRC(void) const; // This function is a bug factory. There is probably a better way. Try to find it. + + // Use this when you are looking for a node that is only one child below - non-recursive, but fast. + CLBP_XMLNode* GetNamedChild(const wchar_t* wszName) const; + + CLBP_XMLProperty* GetNamedProperty(const wchar_t* wszName) const; + + CLBP_XMLProperty& GetDefaultProperty(void) const; + + // Gets at nodes deep into the tree using a slash delimited path - ie "child/grandchild/great-grandchild" + // There's no checking for multiple nodes with the same name at each level of the tree, so if you use this + // stuff, you have to make sure you have unique node names at each level. + + // relative to the current node, use "/" as a separator + __forceinline CLBP_XMLNode* GetNodeAtPath(const CLBPString& s) const { return const_cast(this)->GetNodeAtPathImpl(s); } + __forceinline CLBP_XMLNode* CreateNodeAtPath(const CLBPString& sPath) { return GetNodeAtPathImpl(sPath, true); } + CLBPStringBV GetPathFromRoot(void) const; + +public: // Iteration helpers + class LBPEXPORT CChildIterator + { + public: + CChildIterator(const CLBP_XMLNode* pParent); + + public: + CLBP_XMLNode* GetNextChild(void); + + protected: + CLBP_XMLNode* m_pCurrent; + }; + + class LBPEXPORT CPropertyIterator + { + public: + CPropertyIterator(const CLBP_XMLNode* pParent, bool bSorted = false); + ~CPropertyIterator(); + + public: + __forceinline CLBP_XMLProperty* GetNextProperty(); + + protected: + //Iterator type + bool m_bSorted; + + //Fast, out of order access + CLBP_XMLProperty* m_pCurrent; + + //Slower, presorted access + const CLBP_XMLNode* m_pParent; +#ifdef LBPRHLIB + ON_ClassArray* m_apSortedProperties; +#else + CArray* m_apSortedProperties; +#endif + int m_iIndex; + + private: + CLBP_XMLProperty* GetNextPropertySorted(); + }; + + CChildIterator GetChildIterator(void) const; + CPropertyIterator GetPropertyIterator(bool bAlphabetized = false) const; + + static bool AutoTypePropValue(void) { return m_bAutoTypePropValue; } + static void SetAutoTypePropValue(bool b=true) { m_bAutoTypePropValue = b; } + +private: + static bool m_bAutoTypePropValue; + +private: + bool GetPropertiesFromTag(const CLBPString& sTag); + bool IsClosingTag(const CLBPString& sTag) const; + void SetLastReadBufferPointer(LPVOID pv); + CLBP_XMLNode* GetNodeAtPathImpl(const wchar_t* wszPath, bool bCreate=false); + void AddEmptyDefaultProperty(void); + CLBP_XMLProperty* InternalAddProperty(const CLBP_XMLProperty& prop); + bool InternalRemoveProperty(const CLBPString& sName); + CLBP_XMLNode* InternalUnhookChild(CLBP_XMLNode& childNode); + void InternalRemoveAllProperties(void); + +private: + static CLBPStringBV GetNameFromTag(const CLBPString& sTag); + static void AssertValidTag(const CLBPString& sTag); + static void AttemptToFixTag(CLBPString& tag); + static void GetNextTag(CLBPString& tag, WCHAR*& pBuffer, bool bValidateTag); + static bool RecoverProperty(const CLBPString& tag, int iEqualSign, CLBPString& sProp); + +private: + CLBPString_RC m_sName; + CLBP_XMLNode* m_pNextSibling = nullptr; + CLBP_XMLNode* m_pParent = nullptr; + CLBP_XMLNode* m_pFirstChild = nullptr; + CLBP_XMLNode* m_pLastChild = nullptr; + CLBP_XMLProperty* m_pFirstProperty = nullptr; + void* m_pLastReadBufferPointer = nullptr; + mutable CCriticalSection m_CS; + +#ifdef _DEBUG +public: + virtual bool ReadOnly(void) const { return false; } + virtual void SetReadOnly(bool b) const; +#endif + +protected: + WCHAR* m_wszBuffer = nullptr; // Here to keep the fixed allocator happy. + + friend class CChildIterator; + friend class CPropertyIterator; + friend class CLBP_XMLRootNode; +}; + +class LBPEXPORT CLBP_XMLRootNode : public CLBP_XMLNode +{ +public: + CLBP_XMLRootNode(); + CLBP_XMLRootNode(const CLBP_XMLRootNode&); + virtual ~CLBP_XMLRootNode(); + + CLBP_XMLRootNode& operator=(const CLBP_XMLRootNode&); + +public: // To make it easier to write reference-counted-agnostic code. +#ifndef USE_REFERENCE_COUNTED_ROOTNODE + CLBP_XMLRootNode& Node(void) { return *this; } + const CLBP_XMLRootNode& Node(void) const { return *this; } +#endif + + void ReadFromFile(const WCHAR* szPath, bool bWarningsAsErrors = false, bool bValidateTags = true); //Throws CLBP_XMLException + void ReadFromFile(const char* szPath, bool bWarningsAsErrors = false, bool bValidateTags = true); //Throws CLBP_XMLException + + bool WriteToFile(const WCHAR* szPath, bool bIncludeFormatting = true, bool bUTF8 = false, bool bSortedProperties = false) const; + bool WriteToFile(const char* szPath, bool bIncludeFormatting = true, bool bUTF8 = false, bool bSortedProperties = false) const; +}; + +#ifdef USE_REFERENCE_COUNTED_ROOTNODE + +class CLBP_XMLRootNode_RC; +class CLBP_XMLRootNode_RC_Data +{ + CLBP_XMLRootNode_RC_Data() : m_iRefCount(1) { } + CLBP_XMLRootNode_RC_Data(const CLBP_XMLRootNode& n) : m_iRefCount(1), m_node(n) { } + CLBP_XMLRootNode_RC_Data(const CLBP_XMLRootNode_RC_Data& data) : m_iRefCount(1), m_node(data.m_node) { } + + REFCOUNT_INT AddRef(void) + { + return InterlockedIncrement(&m_iRefCount); + } + + REFCOUNT_INT Release(void) + { + const REFCOUNT_INT iRef = InterlockedDecrement(&m_iRefCount); + if (0 == iRef) + { + delete this; + } + return iRef; + } + + volatile REFCOUNT_INT m_iRefCount; + CLBP_XMLRootNode m_node; + + friend class CLBP_XMLRootNode_RC; + + const CLBP_XMLRootNode_RC& operator=(const CLBP_XMLRootNode_RC&); +}; + +class LBPEXPORT CLBP_XMLRootNode_RC +{ +public: + CLBP_XMLRootNode_RC(); + CLBP_XMLRootNode_RC(const CLBP_XMLRootNode_RC&); + virtual ~CLBP_XMLRootNode_RC(); + + CLBP_XMLRootNode& Node(void) { CopyOnWrite(); return m_pData->m_node; } + const CLBP_XMLRootNode& NodeForRead(void) const { return m_pData->m_node; } + CLBP_XMLRootNode& NodeForWrite(void) { CopyOnWrite(); return m_pData->m_node; } + + void Clear(void) { CopyOnWrite(); m_pData->m_node.Clear(); } + void SetTagName(const CLBPString& sName) { CopyOnWrite(); m_pData->m_node.SetTagName(sName); } + + CLBP_XMLRootNode_RC& operator = (const CLBP_XMLRootNode_RC&); + + bool WriteToFile(const WCHAR* szPath, bool bIncludeFormatting = true, bool bUTF8 = false, bool bSortedProperties = false) const + { return m_pData->m_node.WriteToFile(szPath, bIncludeFormatting, bUTF8, bSortedProperties); } + + bool WriteToFile(const char* szPath, bool bIncludeFormatting = true, bool bUTF8 = false, bool bSortedProperties = false) const + { return m_pData->m_node.WriteToFile(szPath, bIncludeFormatting, bUTF8, bSortedProperties); } + +private: + CLBP_XMLRootNode_RC_Data* m_pData; + + void CopyOnWrite(void) + { + if (m_pData->m_iRefCount > 1) + { + CLBP_XMLRootNode_RC_Data* pData = new CLBP_XMLRootNode_RC_Data(*m_pData); + m_pData->Release(); + m_pData = pData; + } + } +}; + +#else + +#define CLBP_XMLRootNode_RC CLBP_XMLRootNode + +#endif + +#include "LBPTime.h" +#include "LBPBuffer.h" + +//Extra cool variant that handles conversions on the fly +//Getters will throw an exception if the conversion is not possible. + +class LBPEXPORT CLBPVariant +{ +public: + enum vt + { + vt_Integer = 6, + vt_Float = 16, + vt_Double = 17, + vt_3_double_array = 19, // vector xyz 1 double per axis + vt_String = 20, + vt_Bool = 21, + vt_Matrix = 22, // 4x4 double precision matrix + vt_Null = 23, + vt_2_double_array = 24, + vt_4_double_color = 25, + vt_Uuid = 26, + vt_Time = 27, + vt_Buffer = 28, + vt_4_double_array = 29 + + }; + + enum vt_arraytype + { + vt_array_2 = 2, + vt_array_3 = 3, + vt_array_4 = 4, + vt_array_16 = 16, + }; + +public: + CLBPVariant(); //creates an empty string type + CLBPVariant(int iValue); + CLBPVariant(double iValue); + CLBPVariant(float iValue); + CLBPVariant(const CLBPString& s); + CLBPVariant(const double* point, vt_arraytype at = vt_array_3); + CLBPVariant(const float* point, vt_arraytype at = vt_array_3); + CLBPVariant(const WCHAR* wsz); + CLBPVariant(const char* sz); + CLBPVariant(const CLBPColor& c); + CLBPVariant(bool b); + CLBPVariant(const UUID& uuid); + CLBPVariant(const CTime& time); + CLBPVariant(const void* pBuffer, size_t size); + CLBPVariant(const CLBPBuffer& buffer); + + CLBPVariant(const CLBPVariant& src); + virtual ~CLBPVariant(); + + CLBPVariant& operator=(const CLBPVariant& src); + + DWORD CRC(void) const; + +public: + vt Type() const; + CLBPStringBV TypeAsString() const; + bool IsEmpty() const; + bool IsNull() const; + + //Optimized version of CLBPString::NeedsURLEncode - always use this if possible + //because it knows from the type of the variant whether URL encoded output is even possible. + bool NeedsURLEncode(bool bForAnsi = false) const; + +protected: + vt m_type; + mutable CLBPString_RC m_sVal; + union + { + mutable int m_iVal; + mutable double m_dVal; + mutable double m_aVal[4]; + mutable bool m_bVal; + mutable float m_fVal; + mutable double m_matrix[16]; + mutable UUID m_uuidVal; + mutable time_t m_timeVal; + }; + mutable bool m_bTypePending; + mutable CLBPBuffer* m_pBuffer; + + CLBPBuffer& Buffer() const; + void BufferClear(); + +public: + void SetNull(); + void SetValue( int v ); + void SetValue( double v ); + void SetValue( float v); + void SetValue( const CLBPString& string); + void SetValue( const double* p, vt_arraytype at); + void SetValue( const float* p, vt_arraytype at); + void SetValue( const CLBPColor& c); + void SetValue( bool b); + void SetValue( const UUID& uuid); + void SetValue( const CTime& time); + void SetValue( const void* pBuffer, size_t size); + void SetValue( const CLBPBuffer& buffer); + + int AsInteger() const; + double AsDouble() const; + float AsFloat() const; + const double* As3dPoint() const; + bool As3dPoint(double* /*[3]*/) const; + bool As3dPoint(float* /*[3]*/) const; + const double* As4dPoint() const; + bool As4dPoint(double* /*[4]*/) const; + bool As4dPoint(float* /*[4]*/) const; + const double* As2dPoint() const; + bool As2dPoint(double* /*[2]*/) const; + bool As2dPoint(float* /*[2]*/) const; + const CLBPString& AsString() const; + bool AsBool() const; + CLBPColor AsColor() const; + const double* AsMatrix() const; + bool AsMatrix(double* /*[16]*/) const; + bool AsMatrix(float* /*[16]*/) const; + UUID AsUuid() const; + CTime AsTime() const; + const CLBPBuffer AsBuffer() const; + + operator double() const; + operator float() const; + operator int() const; + operator const CLBPString&() const; + operator const double*() const; //unsafe - check the type first + operator bool() const; + operator CLBPColor() const; + operator UUID() const; + operator CTime() const; + operator CLBPBuffer() const; + +public: + bool TypePending() const; + void SetTypePendingFlag(bool bTypePending) const; + +protected: + void String2Point(int iValues) const; + + friend class CLBP_XMLNode; + void DoAutoTyping(vt) const; +}; + +class LBPEXPORT CLBP_XMLPropertyData +{ +public: + CLBP_XMLPropertyData(); + CLBP_XMLPropertyData(const CLBPVariant& sValue); + CLBP_XMLPropertyData(const CLBPString& sName, const CLBPVariant& value); + ~CLBP_XMLPropertyData(); + +public: + const char* NameMBCS(void) const; + const CLBPString& Name(void) const; + void SetName(const CLBPString& sName); + DWORD CRC(void) const; + bool operator < (const CLBP_XMLPropertyData& prop) const; + bool operator > (const CLBP_XMLPropertyData& prop) const; + bool IsDefaultProperty(void) const; + const CLBPVariant& GetValue(void) const; + void SetValue(const CLBPVariant& value); + void SetHugeStringValue(wchar_t* wsz, int iLengthOfBuffer=-1); + +private: + CLBPString_RC m_sName; + CLBPVariant m_value; + +#ifdef USE_REFERENCE_COUNTED_PROPERTY + volatile REFCOUNT_INT m_iRefCount; + + REFCOUNT_INT AddRef() + { + return ::InterlockedIncrement(&m_iRefCount); + } + + REFCOUNT_INT Release() + { + const REFCOUNT_INT iRef = ::InterlockedDecrement(&m_iRefCount); + if (0 == iRef) + { + delete this; + } + return iRef; + } +#endif + CLBP_XMLPropertyData(const CLBP_XMLPropertyData& src); + +protected: + void CommonCtor(); + const CLBP_XMLPropertyData& operator = (const CLBP_XMLPropertyData& d) { m_sName = d.m_sName; m_value = d.m_value; return *this; } + friend class CLBP_XMLProperty; +}; + +#ifdef USE_REFERENCE_COUNTED_PROPERTY + #define LBP_XMLPROPERTY_BASE +#else + #define LBP_XMLPROPERTY_BASE : public CLBP_XMLPropertyData +#endif + +class CLBP_XMLProperty LBP_XMLPROPERTY_BASE +{ +public: +#ifdef USE_REFERENCE_COUNTED_PROPERTY + CLBP_XMLProperty() : m_pOwner(NULL), m_pNextProperty(NULL) + { m_pData = new CLBP_XMLPropertyData; } + + CLBP_XMLProperty(const CLBPVariant& sValue) : m_pOwner(NULL), m_pNextProperty(NULL) + { m_pData = new CLBP_XMLPropertyData(sValue); } + + CLBP_XMLProperty(const CLBPString& sName, const CLBPVariant& value) : m_pOwner(NULL), m_pNextProperty(NULL) + { m_pData = new CLBP_XMLPropertyData(sName, value); } + + CLBP_XMLProperty(const CLBP_XMLProperty& prop) : m_pOwner(NULL), m_pNextProperty(NULL) + { prop.m_pData->AddRef(); m_pData = prop.m_pData; } + + ~CLBP_XMLProperty() { m_pData->Release(); } +#else + CLBP_XMLProperty() : m_pOwner(NULL), m_pNextProperty(NULL) { } + CLBP_XMLProperty(const CLBPVariant& sValue) : CLBP_XMLPropertyData(sValue), m_pOwner(NULL), m_pNextProperty(NULL) { } + CLBP_XMLProperty(const CLBPString& sName, const CLBPVariant& value) : CLBP_XMLPropertyData(sName, value), m_pOwner(NULL), m_pNextProperty(NULL) { } + CLBP_XMLProperty(const CLBP_XMLProperty& prop) : CLBP_XMLPropertyData(prop), m_pOwner(NULL), m_pNextProperty(NULL) { } +#endif + +public: +#ifdef USE_REFERENCE_COUNTED_PROPERTY + const char* NameMBCS() const { return m_pData->NameMBCS(); } + const CLBPString& Name() const { return m_pData->Name(); } + void SetName(const CLBPString& sName) { CopyOnWrite(); m_pData->SetName(sName); } + DWORD CRC(void) const { return m_pData->CRC(); } + bool operator < (const CLBP_XMLProperty& prop) const { return m_pData->operator < (*prop.m_pData); } + bool operator > (const CLBP_XMLProperty& prop) const { return m_pData->operator > (*prop.m_pData); } + CLBP_XMLProperty& operator = (const CLBP_XMLProperty& prop); + bool IsDefaultProperty() const { return m_pData->IsDefaultProperty(); } + const CLBPVariant& GetValue() const { return m_pData->GetValue(); } + void SetValue(const CLBPVariant& value) { CopyOnWrite(); m_pData->SetValue(value); } + void SetHugeStringValue(wchar_t* wsz, int iLen=-1) { CopyOnWrite(); m_pData->SetHugeStringValue(wsz, iLen); } + CLBPVariant& GetNonConstValue() { CopyOnWrite(); return m_pData->m_value; } +#else + CLBPVariant& GetNonConstValue() { return const_cast(GetValue()); } +#endif + +private: + friend class CLBP_XMLNode::CPropertyIterator; + CLBP_XMLProperty* m_pNextProperty; + CLBP_XMLNode* m_pOwner; + +#ifdef USE_REFERENCE_COUNTED_PROPERTY + CLBP_XMLPropertyData* m_pData; + + void CopyOnWrite() + { + ASSERT((NULL == m_pOwner) || !m_pOwner->ReadOnly()); + if (m_pData->m_iRefCount > 1) + { + CLBP_XMLPropertyData* pData = new CLBP_XMLPropertyData(*m_pData); + m_pData->Release(); + m_pData = pData; + } + } +#endif + + friend class CLBP_XMLNode; +}; + +__forceinline CLBP_XMLProperty* CLBP_XMLNode::CPropertyIterator::GetNextProperty() +{ + if (m_bSorted) + return GetNextPropertySorted(); + + CLBP_XMLProperty* p = m_pCurrent; + if (NULL != p) + { + m_pCurrent = p->m_pNextProperty; + } + + return p; +} diff --git a/ON_SimpleMap.h b/ON_SimpleMap.h new file mode 100644 index 0000000..9440e51 --- /dev/null +++ b/ON_SimpleMap.h @@ -0,0 +1,284 @@ + +#pragma once +#ifndef LBPRHLIB +#error RHLIB header included in non-Rhino compile +#endif + +template +__forceinline unsigned int OSM_HashIndex(const KEY& key) +{ + return ON_CRC16(0, sizeof(KEY), &key) % HashTableSize; +} + +template +class ON_SimpleMap +{ +private: + struct HashElement + { + HashElement* m_next; + KEY m_key; + VALUE m_value; + }; + +public: + ON_SimpleMap() + : m_block_list(NULL), + m_element_list(NULL), + m_iCount(0) + { + memset(m_table, 0, sizeof(m_table)); + } + + ~ON_SimpleMap() { Destroy(); } + + VALUE* Lookup(const KEY&) const; + bool Lookup(const KEY&, VALUE&) const; + void Destroy(void); + bool Remove(const KEY&); // **************** Do not call Remove() during iteration of the map. + bool SetAt(const KEY&, const VALUE&); + bool IsEmpty(void) const; + UINT_PTR Count(void) const; + + class Iterator + { + public: + Iterator(HashElement* const * t) : table(t), index(0), element(NULL) {} + + VALUE* Next(void); + bool Next(VALUE&); + bool Next(KEY&, VALUE&); + void Reset(void) { index = 0; element=NULL; } + + protected: + HashElement* const * table; + HashElement* element; + INT_PTR index; + }; + + // **************** Do not call Remove() during iteration of the map. + Iterator GetIterator(void) const { return Iterator(m_table); } + +private: + __forceinline int HashIndex(const KEY& key) const { return OSM_HashIndex(key); } + + struct HashElementBlock + { + HashElementBlock* m_next; + HashElement m_memory_block[1024]; + }; + + HashElement* m_table[HashTableSize]; + HashElement* m_element_list; // unused elements + HashElementBlock* m_block_list; // unused blocks + UINT_PTR m_iCount; + + VALUE GetElementObject(const HashElement*) const; + +private: + // no implementation + ON_SimpleMap(const ON_SimpleMap&); + ON_SimpleMap& operator=(const ON_SimpleMap&); +}; + +template +void ON_SimpleMap::Destroy(void) +{ + HashElementBlock* blk = m_block_list; + while (NULL != blk) + { + void* p = blk; + blk = blk->m_next; + onfree(p); + } + + m_element_list = NULL; + m_block_list = NULL; + memset(m_table, 0, sizeof(m_table)); + + m_iCount = 0; +} + +template +bool ON_SimpleMap::Lookup(const KEY& key, VALUE& val) const +{ + VALUE* p = Lookup(key); + + if (NULL == p) + return false; + + val = *p; + + return true; +} + +template +VALUE* ON_SimpleMap::Lookup(const KEY& key) const +{ + const int hash_index = HashIndex(key); + + HashElement* elem = m_table[hash_index]; + while (NULL != elem) + { + if (key == elem->m_key) + return &elem->m_value; + + elem = elem->m_next; + } + + return NULL; +} + +template +bool ON_SimpleMap::Remove(const KEY& key) +{ + const int hash_index = HashIndex(key); + + HashElement* eprev = NULL; + HashElement* elem = m_table[hash_index]; + while (NULL != elem) + { + if (key == elem->m_key) + { + if (NULL != eprev) + { + eprev->m_next = elem->m_next; + } + else + { + m_table[hash_index] = elem->m_next; + } + + elem->m_next = m_element_list; + m_element_list = elem; + + m_iCount--; + + return true; + } + + eprev = elem; + elem = elem->m_next; + } + + return false; +} + +template +bool ON_SimpleMap::SetAt(const KEY& key, const VALUE& value) +{ + VALUE* p = Lookup(key); + if (NULL != p) + { + *p = value; + return true; + } + + if (NULL == m_element_list) + { + HashElementBlock* pBlock = (HashElementBlock*)onmalloc(sizeof(HashElementBlock)); + memset(pBlock, 0, sizeof(HashElementBlock)); + + for (int i = 0; i < 1023; i++) + { + pBlock->m_memory_block[i].m_next = &pBlock->m_memory_block[i+1]; + } + + pBlock->m_next = m_block_list; + m_block_list = pBlock; + m_element_list = pBlock->m_memory_block; + } + + const int hash_index = HashIndex(key); + + HashElement* elem = m_element_list; + m_element_list = m_element_list->m_next; + + elem->m_key = key; + elem->m_value = value; + elem->m_next = m_table[hash_index]; + + m_table[hash_index] = elem; + + m_iCount++; + + return true; +} + +template +UINT_PTR ON_SimpleMap::Count(void) const +{ + return m_iCount; +} + +template +bool ON_SimpleMap::IsEmpty(void) const +{ + return 0 == m_iCount; + + /*for (int i = 0; i < HashTableSize; i++) + { + if (NULL != m_table[i]) + return false; + } + + return true;*/ +} + +template +bool ON_SimpleMap::Iterator::Next(VALUE& value) +{ + VALUE* p = Next(); + if(NULL == p) return false; + value = *p; + return true; +} + +template +VALUE* ON_SimpleMap::Iterator::Next(void) +{ + if (NULL != element) + { + element = element->m_next; + } + + while (NULL == element) + { + if (index == HashTableSize) + return NULL; + + element = table[index++]; + } + + return &element->m_value; +} + +template +bool ON_SimpleMap::Iterator::Next(KEY& key, VALUE& value) +{ + if (NULL != element) + { + element = element->m_next; + } + + while (NULL == element) + { + if (index == HashTableSize) + return false; + + element = table[index++]; + } + + if(NULL == element) + return false; + + value = element->m_value; + key = element->m_key; + + return true; +} + +template +class ON_SimpleUuidMap : public ON_SimpleMap +{ +}; diff --git a/RPC.def b/RPC.def new file mode 100644 index 0000000..2c84c1a --- /dev/null +++ b/RPC.def @@ -0,0 +1,6 @@ +; RPC.def : Declares the module parameters for the DLL. + +LIBRARY + +EXPORTS + ; Explicit exports can go here diff --git a/RPC.rc b/RPC.rc new file mode 100644 index 0000000..078ddc4 --- /dev/null +++ b/RPC.rc @@ -0,0 +1,162 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Finnish (Finland) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FIN) +LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)\r\n" + "LANGUAGE 9, 2\r\n" + "#pragma code_page(1252)\r\n" + "#include ""res/RPC.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_EMPTY DIALOG 0, 0, 208, 266 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "RPC Dialog" +FONT 8, "MS Sans Serif" +BEGIN +END + +IDD_PARAM DIALOGEX 0, 0, 118, 345 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Edit RPC" +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,33,324,50,14 +END + +IDD_EDIT DIALOGEX 0, 0, 186, 95 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "Edit...",ID_BUTTON_EDIT,31,7,122,14 +END + +IDD_FILE_DIALOG DIALOGEX 0, 0, 266, 120 +STYLE DS_SETFONT | DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_APPWINDOW +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "",1119,0,0,163,113 + LTEXT "",IDC_STATIC_PREVIEW,171,7,56,55,SS_NOTIFY | NOT WS_VISIBLE | WS_BORDER + CONTROL "Preview",IDC_CHECK_PREVIEW,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,171,104,63,8 + CONTROL "",IDC_PROGRESS1,"msctls_progress32",WS_BORDER,171,91,32,7 +END + +IDD_PREVIEWING_FILE_DIALOG DIALOGEX 0, 0, 266, 120 +STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_APPWINDOW +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LTEXT "",1119,0,0,163,113 + LTEXT "",IDC_STATIC_PREVIEW,171,7,56,55,SS_NOTIFY | NOT + WS_VISIBLE | WS_BORDER + CONTROL "Preview",IDC_CHECK_PREVIEW,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,171,104,63,8 + CONTROL "",IDC_PROGRESS1,"msctls_progress32",WS_BORDER,171,91,32, + 7 +END + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_EDIT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 88 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ACM ICON "res\\rpc_acm.ico" +IDI_PROP_RPC ICON "res\\Rpc.ico" +#endif // Finnish (Finland) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +LANGUAGE 9, 2 +#pragma code_page(1252) +#include "res/RPC.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/RPC.vcxproj b/RPC.vcxproj new file mode 100644 index 0000000..df592d0 --- /dev/null +++ b/RPC.vcxproj @@ -0,0 +1,226 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MFCDLLProj + {277AA65D-B590-4156-8AB7-8F864F03D158} + + + + DynamicLibrary + false + v141 + Unicode + Dynamic + + + DynamicLibrary + false + v141 + Unicode + Dynamic + + + + + + + $([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\McNeel\Rhinoceros\SDK\6.0', 'InstallPath', null, RegistryView.Registry64)) + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN64;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + true + EnableFastChecks + true + MultiThreadedDLL + + + Windows + true + + + false + _DEBUG;%(PreprocessorDefinitions) + + + 0x0409 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + copy "$(SolutionDir)$(ProjectName)\SDK\lib\Release\*.*" "$(OutDir)" + + + + + Level3 + Use + MaxSpeed + true + true + WIN64;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + true + + + Windows + true + + + false + NDEBUG;%(PreprocessorDefinitions) + + + 0x0409 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + copy "$(SolutionDir)$(ProjectName)\SDK\lib\Release\*.*" "$(OutDir)" + + + + + + \ No newline at end of file diff --git a/RPC.vcxproj.filters b/RPC.vcxproj.filters new file mode 100644 index 0000000..93a7541 --- /dev/null +++ b/RPC.vcxproj.filters @@ -0,0 +1,343 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8de738fd-7012-49f9-8066-39524646dc68} + + + {35edc08e-bccb-42c3-890b-7aac4394cd56} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + Source Files + + + Source Files + + + Source Files + + + Source Files\LBP + + + Source Files\LBP + + + Source Files + + + Source Files + + + Source Files\LBP + + + Source Files\LBP + + + Source Files\LBP + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files + + + Header Files + + + Header Files + + + Header Files\LBP + + + Header Files\LBP + + + Header Files + + + Header Files + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + Header Files\LBP + + + + + Source Files + + + Resource Files + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/RPCApp.cpp b/RPCApp.cpp new file mode 100644 index 0000000..ab751f0 --- /dev/null +++ b/RPCApp.cpp @@ -0,0 +1,72 @@ +// RPC.cpp : Defines the initialization routines for the DLL. +// + +#include "stdafx.h" +#include "RPCApp.h" + +// +// Note! +// +// A Rhino plug-in is an MFC DLL. +// +// If this DLL is dynamically linked against the MFC +// DLLs, any functions exported from this DLL which +// call into MFC must have the AFX_MANAGE_STATE macro +// added at the very beginning of the function. +// +// For example: +// +// extern "C" BOOL PASCAL EXPORT ExportedFunction() +// { +// AFX_MANAGE_STATE(AfxGetStaticModuleState()); +// // normal function body here +// } +// +// It is very important that this macro appear in each +// function, prior to any calls into MFC. This means that +// it must appear as the first statement within the +// function, even before any object variable declarations +// as their constructors may generate calls into the MFC +// DLL. +// +// Please see MFC Technical Notes 33 and 58 for additional +// details. +// + +// CRPCApp + +BEGIN_MESSAGE_MAP(CRPCApp, CWinApp) +END_MESSAGE_MAP() + +// The one and only CRPCApp object +CRPCApp theApp; + +CRPCApp::CRPCApp(void) +{ +} + +CRPCApp::~CRPCApp(void) +{ +} + +// CRPCApp initialization +BOOL CRPCApp::InitInstance() +{ + // CRITICAL: DO NOT CALL RHINO SDK FUNCTIONS HERE! + // Only standard MFC DLL instance initialization belongs here. + // All other significant initialization should take place in + // CRPCPlugIn::OnLoadPlugIn(). + CWinApp::InitInstance(); + return TRUE; +} + +int CRPCApp::ExitInstance() +{ + // CRITICAL: DO NOT CALL RHINO SDK FUNCTIONS HERE! + // Only standard MFC DLL instance clean up belongs here. + // All other significant cleanup should take place in either + // CRPCPlugIn::OnSaveAllSettings() or + // CRPCPlugIn::OnUnloadPlugIn(). + return CWinApp::ExitInstance(); +} + diff --git a/RPCApp.h b/RPCApp.h new file mode 100644 index 0000000..78c2661 --- /dev/null +++ b/RPCApp.h @@ -0,0 +1,24 @@ +// RPC.h : main header file for the RPC DLL. +// + +#pragma once + +#ifndef __AFXWIN_H__ + #error "include 'stdafx.h' before including this file for PCH" +#endif + +#include "resource.h" // main symbols + + +class CRPCApp : public CWinApp +{ +public: + CRPCApp(void); + virtual ~CRPCApp(void); + +public: + virtual BOOL InitInstance(void); + virtual int ExitInstance(void); + + DECLARE_MESSAGE_MAP() +}; diff --git a/RPCPlugIn.cpp b/RPCPlugIn.cpp new file mode 100644 index 0000000..08b27f3 --- /dev/null +++ b/RPCPlugIn.cpp @@ -0,0 +1,210 @@ +// RPCPlugIn.cpp : defines the initialization routines for the plug-in. +// + +#include "StdAfx.h" +#include "rhinoSdkPlugInDeclare.h" +#include "RPCPlugIn.h" +#include "Resource.h" +#include "RpcMains.h" +#include "RpcPropertiesDlg.h" +#include "RpcEventWatcher.h" +#include "RpcDocument.h" + +// The plug-in object must be constructed before any plug-in classes derived +// from CRhinoCommand. The #pragma init_seg(lib) ensures that this happens. +#pragma warning(push) +#pragma warning(disable : 4073) +#pragma init_seg(lib) +#pragma warning(pop) + +// Rhino plug-in declaration +RHINO_PLUG_IN_DECLARE + +// Rhino plug-in name +// Provide a short, friendly name for this plug-in. +RHINO_PLUG_IN_NAME(L"RPC"); + +// Rhino plug-in id +// Provide a unique uuid for this plug-in. +// {1F908FF5-4984-45A6-95F0-A81CE979A4D7} +RHINO_PLUG_IN_ID(L"1F908FF5-4984-45A6-95F0-A81CE979A4D7"); + +// Rhino plug-in version +// Provide a version number string for this plug-in. +RHINO_PLUG_IN_VERSION(__DATE__ " " __TIME__) + +// Rhino plug-in description +// Provide a description of this plug-in. +RHINO_PLUG_IN_DESCRIPTION(L"RPC plug-in for Rhinoceros®"); + +// Rhino plug-in icon resource id +// Provide an icon resource this plug-in. +// Icon resource should contain 16, 24, 32, 48, and 256-pixel image sizes. +//RHINO_PLUG_IN_ICON_RESOURCE_ID(IDI_ICON); + +// Rhino plug-in developer declarations +// TODO: fill in the following developer declarations with +// your company information. Note, all of these declarations +// must be present or your plug-in will not load. +// +// When completed, delete the following #error directive. +RHINO_PLUG_IN_DEVELOPER_ORGANIZATION(L"Robert McNeel & Associates"); +RHINO_PLUG_IN_DEVELOPER_ADDRESS (L"3670 Woodland Park Avenue North\r\nSeattle WA 98103"); +RHINO_PLUG_IN_DEVELOPER_COUNTRY (L"United States"); +RHINO_PLUG_IN_DEVELOPER_PHONE (L"206-545-7000"); +RHINO_PLUG_IN_DEVELOPER_FAX (L"206-545-7321"); +RHINO_PLUG_IN_DEVELOPER_EMAIL (L"tech@mcneel.com"); +RHINO_PLUG_IN_DEVELOPER_WEBSITE (L"http://www.mcneel.com"); +RHINO_PLUG_IN_UPDATE_URL (L"http://www.mcneel.com"); + +// The one and only CRPCPlugIn object +static class CRPCPlugIn thePlugIn; + +///////////////////////////////////////////////////////////////////////////// +// CRPCPlugIn definition + +CRPCPlugIn& PlugIn() +{ + // Return a reference to the one and only CRPCPlugIn object + return thePlugIn; +} + +CRPCPlugIn::CRPCPlugIn() +{ + // Description: + // CRPCPlugIn constructor. The constructor is called when the + // plug-in is loaded and "thePlugIn" is constructed. Once the plug-in + // is loaded, CRPCPlugIn::OnLoadPlugIn() is called. The + // constructor should be simple and solid. Do anything that might fail in + // CRPCPlugIn::OnLoadPlugIn(). + + // TODO: Add construction code here + m_plugin_version = RhinoPlugInVersion(); + m_pMains = nullptr; +} + +///////////////////////////////////////////////////////////////////////////// +// Required overrides + +const wchar_t* CRPCPlugIn::PlugInName() const +{ + // Description: + // Plug-in name display string. This name is displayed by Rhino when + // loading the plug-in, in the plug-in help menu, and in the Rhino + // interface for managing plug-ins. + + // TODO: Return a short, friendly name for the plug-in. + return RhinoPlugInName(); +} + +const wchar_t* CRPCPlugIn::PlugInVersion() const +{ + // Description: + // Plug-in version display string. This name is displayed by Rhino + // when loading the plug-in and in the Rhino interface for managing + // plug-ins. + + // TODO: Return the version number of the plug-in. + return m_plugin_version; +} + +GUID CRPCPlugIn::PlugInID() const +{ + // Description: + // Plug-in unique identifier. The identifier is used by Rhino to + // manage the plug-ins. + + // TODO: Return a unique identifier for the plug-in. + // {7F806339-3300-4992-B349-AFC72474696C} + return ON_UuidFromString(RhinoPlugInId()); +} + +///////////////////////////////////////////////////////////////////////////// +// Additional overrides + +BOOL CRPCPlugIn::OnLoadPlugIn() +{ + // Description: + // Called after the plug-in is loaded and the constructor has been + // run. This is a good place to perform any significant initialization, + // license checking, and so on. This function must return TRUE for + // the plug-in to continue to load. + + // Remarks: + // Plug-ins are not loaded until after Rhino is started and a default document + // is created. Because the default document already exists + // CRhinoEventWatcher::On????Document() functions are not called for the default + // document. If you need to do any document initialization/synchronization then + // override this function and do it here. It is not necessary to call + // CPlugIn::OnLoadPlugIn() from your derived class. + + // TODO: Add plug-in initialization code here. + m_pMains = new CRpcMains(*this); + if (!m_pMains->Initialize()) + { + delete m_pMains; + m_pMains = nullptr; + + return FALSE; + } + + return TRUE; +} + +void CRPCPlugIn::OnUnloadPlugIn() +{ + // Description: + // Called one time when plug-in is about to be unloaded. By this time, + // Rhino's mainframe window has been destroyed, and some of the SDK + // managers have been deleted. There is also no active document or active + // view at this time. Thus, you should only be manipulating your own objects. + // or tools here. + + // TODO: Add plug-in cleanup code here. + if (nullptr != m_pMains) + { + m_pMains->Uninitialize(); + + delete m_pMains; + m_pMains = nullptr; + } +} + +CRpcMains& CRPCPlugIn::Mains(void) +{ + return *m_pMains; +} + +void CRPCPlugIn::AddPagesToObjectPropertiesDialog(CRhinoPropertiesPanelPageCollection& collection) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + auto page = &Mains().PropertiesDlg(); + if (page != nullptr) + collection.Add(page); +} + +BOOL CRPCPlugIn::CallWriteDocument(const CRhinoFileWriteOptions& options) +{ + return TRUE; +} + +BOOL CRPCPlugIn::ReadDocument(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileReadOptions& options) +{ + if (Mains().EventWatcher().IsReferenceDocument() || Mains().EventWatcher().IsMergingDocument()) + return TRUE; + + if (!Mains().RpcDocument().ReadFromArchive(doc, archive, options)) + return FALSE; + + Mains().EventWatcher().SetReadingRpcData(); + + return TRUE; +} + +BOOL CRPCPlugIn::WriteDocument(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileWriteOptions& options) +{ + if (!Mains().RpcDocument().WriteToArchive(doc, archive, options)) + return FALSE; + + return TRUE; +} diff --git a/RPCPlugIn.h b/RPCPlugIn.h new file mode 100644 index 0000000..65b9571 --- /dev/null +++ b/RPCPlugIn.h @@ -0,0 +1,81 @@ +// RPCPlugIn.h : main header file for the RPC plug-in. +// + +#pragma once + +// CRPCPlugIn +// See RPCPlugIn.cpp for the implementation of this class +// +class CRpcMains; + + +class CRPCPlugIn : public CRhinoUtilityPlugIn +{ +public: + // CRPCPlugIn constructor. The constructor is called when the + // plug-in is loaded and "thePlugIn" is constructed. Once the plug-in + // is loaded, CRPCPlugIn::OnLoadPlugIn() is called. The + // constructor should be simple and solid. Do anything that might fail in + // CRPCPlugIn::OnLoadPlugIn(). + CRPCPlugIn(); + + // CRPCPlugIn destructor. The destructor is called to destroy + // "thePlugIn" when the plug-in is unloaded. Immediately before the + // DLL is unloaded, CRPCPlugIn::OnUnloadPlugin() is called. Do + // not do too much here. Be sure to clean up any memory you have allocated + // with onmalloc(), onrealloc(), oncalloc(), or onstrdup(). + ~CRPCPlugIn() = default; + + // Required overrides + + // Plug-in name display string. This name is displayed by Rhino when + // loading the plug-in, in the plug-in help menu, and in the Rhino + // interface for managing plug-ins. + const wchar_t* PlugInName() const override; + + // Plug-in version display string. This name is displayed by Rhino + // when loading the plug-in and in the Rhino interface for + // managing plug-ins. + const wchar_t* PlugInVersion() const override; + + // Plug-in unique identifier. The identifier is used by Rhino for + // managing plug-ins. + GUID PlugInID() const override; + + // Additional overrides + + // Called after the plug-in is loaded and the constructor has been + // run. This is a good place to perform any significant initialization, + // license checking, and so on. This function must return TRUE for + // the plug-in to continue to load. + BOOL OnLoadPlugIn() override; + + // Called one time when plug-in is about to be unloaded. By this time, + // Rhino's mainframe window has been destroyed, and some of the SDK + // managers have been deleted. There is also no active document or active + // view at this time. Thus, you should only be manipulating your own objects. + // or tools here. + void OnUnloadPlugIn() override; + + plugin_load_time PlugInLoadTime() override { return load_plugin_when_needed_ignore_docked; } + + void AddPagesToObjectPropertiesDialog(CRhinoPropertiesPanelPageCollection& collection) override; + + virtual BOOL CallWriteDocument(const CRhinoFileWriteOptions& options) override; + virtual BOOL ReadDocument(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileReadOptions& options) override; + virtual BOOL WriteDocument(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileWriteOptions& options) override; + + CRpcMains& Mains(void); + +private: + ON_wString m_plugin_version; + + // TODO: Add additional class information here + CRpcMains* m_pMains = nullptr; +}; + +// Return a reference to the one and only CRPCPlugIn object +CRPCPlugIn& PlugIn(); + + + diff --git a/Resource.h b/Resource.h new file mode 100644 index 0000000..4428f3a --- /dev/null +++ b/Resource.h @@ -0,0 +1,26 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by RPC.rc +// +#define IDD_EMPTY 2000 +#define IDD_PARAM 2002 +#define ID_BUTTON_EDIT 2002 +#define IDD_EDIT 2004 +#define IDI_ACM 2005 +#define IDI_PROP_RPC 2007 +#define IDD_FILE_DIALOG 6033 +#define IDC_STATIC_PREVIEW 6129 +#define IDC_CHECK_PREVIEW 6137 +#define IDC_PROGRESS1 6138 +#define IDD_PREVIEWING_FILE_DIALOG 6139 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 2008 +#define _APS_NEXT_COMMAND_VALUE 32772 +#define _APS_NEXT_CONTROL_VALUE 2003 +#define _APS_NEXT_SYMED_VALUE 2002 +#endif +#endif diff --git a/RhRdkRegisteredPropertiesEventSink.h b/RhRdkRegisteredPropertiesEventSink.h new file mode 100644 index 0000000..19de22d --- /dev/null +++ b/RhRdkRegisteredPropertiesEventSink.h @@ -0,0 +1,65 @@ + +#pragma once // SDK header + +class CRhRdkContent; +class IRhRdkContentEditor; +class CRhRdkContentFactory; +class IRhRdkContentList; +class ON_TextureMapping; +class CRhRdkUuidCollection; +class CRhRdkVariant; + +#include "IRhRdkRegisteredProperty.h" + +/** \class CRhRdkRegisteredPropertiesEventSinkBase + + Base class for 'properties event sinks'. Used internally by RDK for event sinks that are not automatically + registered with the event machine system. Generally you should not derive from this class directly. + \see CRhRdkEventSink. +*/ +class RHRDK_SDK CRhRdkRegisteredPropertiesEventSinkBase +{ +public: + CRhRdkRegisteredPropertiesEventSinkBase(); + virtual ~CRhRdkRegisteredPropertiesEventSinkBase(); + + // Registered property events. + virtual void OnPropertyRegistered(const IRhRdkRegisteredProperty&) { } + virtual void OnPropertyUnregistering(const IRhRdkRegisteredProperty&) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredObjectProperty&, const CRhinoDoc&, const UUID& /*objectId*/, ON_COMPONENT_INDEX, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredDocProperty&, const CRhinoDoc&, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredAppProperty&, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredViewProperty&, const CRhinoDoc&, const UUID& /*viewId*/, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredLayerProperty&, const CRhinoDoc&, const UUID& /*layer_id*/, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredRdkContentProperty&, const CRhinoDoc&, const UUID& /*instance_id*/, const CRhRdkVariant& /*valueOld*/) { } + virtual void OnPropertyChanged(const IRhRdkRegisteredLightProperty&, const CRhinoDoc&, const UUID& /*light_id*/, const CRhRdkVariant& /*valueOld*/) { } + + // Future expansion. + + /** Custom events. */ + static const ON_UUID& BeginChanges(void); //Signal to client that a block of changes is about to take place. Do not react until end. pvContext = nullptr + static const ON_UUID& EndChanges(void); //Ends block of changes started by BeginChanges event. pvContext = nullptr + + virtual void OnCustomEvent(const UUID& /*uuidCode*/, void* /*pvContext*/) { } + + /** Emergency virtual function for future expansion. */ + virtual void* EVF(const wchar_t* wszFunc, void* pvData); +}; + +/** \class CRhRdkRegisteredPropertiesEventSink + + Base class for auto-registering 'event sinks'. To allow a class to receive events, derive it from + CRhRdkRegisteredPropertiesEventSink and override the methods of CRhRdkRegisteredPropertiesEventSinkBase + which you want to be called on. + There is no need to call the base class. Some of these methods, particularly those dealing with contents + will be called with NULL pointers where NULL means "no content". Event handlers must check + all incoming pointers for NULL before dereferencing them unless otherwise stated. +*/ +class RHRDK_SDK CRhRdkRegisteredPropertiesEventSink : protected CRhRdkRegisteredPropertiesEventSinkBase +{ +public: + CRhRdkRegisteredPropertiesEventSink(); + virtual ~CRhRdkRegisteredPropertiesEventSink(); + + static void RaiseCustomEvent(const UUID&, void*); +}; diff --git a/RpcAddCmd.cpp b/RpcAddCmd.cpp new file mode 100644 index 0000000..fb47cb5 --- /dev/null +++ b/RpcAddCmd.cpp @@ -0,0 +1,116 @@ + +#include "StdAfx.h" +#include "RpcAddCmd.h" +#include "RpcObject.h" +#include "RpcInstance.h" +#include "RpcSelectDlg.h" +#include "RpcFileDlg.h" +#include "RpcUtilities.h" +#include "RpcEditDlg.h" + +const wchar_t * CRpcAddCmd::EnglishCommandName() +{ + return L"RPC"; +} + +UUID CRpcAddCmd::CommandUUID() +{ + static const UUID uuid = + { + // {49DABEB1-A89E-41a7-9020-D52526CB3AB7} + 0x49dabeb1, 0xa89e, 0x41a7, { 0x90, 0x20, 0xd5, 0x25, 0x26, 0xcb, 0x3a, 0xb7 } + }; + + return uuid; +} + +CRhinoCommand::result CRpcAddCmd::RunRpcCommand(const CRhinoCommandContext& context) +{ + CRhinoDoc* pDoc = context.Document(); + if (NULL == pDoc) + return failure; + + pDoc->UnselectAll(); + + CLBPString sRpc; + if (!GetRpcFileName(*pDoc, sRpc)) + return cancel; + + CRpcInstance rpc(*pDoc, sRpc); + if (!rpc.IsValid()) + return failure; + + ON_SimpleArray aRpc; + aRpc.Append(&rpc); + + CRhinoGetPoint gp; + gp.SetCommandPrompt(_RhLocalizeString( L"RPC base point", 36075)); + if (CRhinoGet::point != gp.GetPoint()) + return cancel; + + const ON_3dPoint ptInsertion = gp.Point(); + + CRhinoInstanceObject* pBlock = rpc.AddToDocument(*pDoc, ptInsertion); + if (NULL == pBlock) + return cancel; + + pBlock->Select(); + + const ON_Xform xfromBlock = pBlock->InstanceXform(); + + ON_3dPoint ptOfRotation; + ptOfRotation.Zero(); + ptOfRotation.Transform(xfromBlock); + + const double dScale = pBlock->BoundingBox().Diagonal().Length() / 2.0; + + ON_3dPoint ptFirstRef; + ptFirstRef = ON_3dVector::UnitVector(1); + ptFirstRef *= dScale; + ptFirstRef.Transform(xfromBlock); + + CLBPString sRotateCmd; + sRotateCmd.Format(L"_Rotate %g,%g,%g %g,%g,%g", ptOfRotation.x, ptOfRotation.y, ptOfRotation.z, + ptFirstRef.x, ptFirstRef.y, ptFirstRef.z); + + RhinoApp().RunScript( context.m_rhino_doc_sn, sRotateCmd.Wide()); + + IRhRdkCustomRenderMeshManager& crmm = ::RhRdkCustomRenderMeshManager(); + crmm.OnRhinoDocumentChanged(*pDoc); + + pDoc->Redraw(); + + return success; +} + +bool CRpcAddCmd::GetRpcFileName(CRhinoDoc& doc, CLBPString& sRpc) +{ + CWnd* pParentWnd = CWnd::FromHandle(RhinoApp().MainWnd()); + + const CLBPString sLastFile; + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + CRpcAdvancedFileDialog dlg(pParentWnd); + if (dlg.DoModal() != IDOK) + { + if (!dlg.Advanced()) + return false; + + CRpcSelectDlg dlgRpcSelect(doc, (const wchar_t*)dlg.GetPathName()); + if (dlgRpcSelect.DoModal() != IDOK) + { + return false; + } + + CLBPString sSel = dlgRpcSelect.Selection(); + if (sSel.IsEmpty()) return false; + + sRpc = sSel; + return true; + } + + sRpc = dlg.GetPathName(); + + return true; +} diff --git a/RpcAddCmd.h b/RpcAddCmd.h new file mode 100644 index 0000000..0fe3017 --- /dev/null +++ b/RpcAddCmd.h @@ -0,0 +1,16 @@ +#pragma once + + +#include "RpcCommand.h" + + +class CRpcAddCmd : public CRpcCommand +{ +public: + virtual CRhinoCommand::result RunRpcCommand(const CRhinoCommandContext& context); + virtual const wchar_t * EnglishCommandName(); + virtual UUID CommandUUID(); + +private: + bool GetRpcFileName(CRhinoDoc& doc, CLBPString& sRpc); +}; diff --git a/RpcAnimationFrameRrp.cpp b/RpcAnimationFrameRrp.cpp new file mode 100644 index 0000000..b76b4f5 --- /dev/null +++ b/RpcAnimationFrameRrp.cpp @@ -0,0 +1,43 @@ +#include "StdAfx.h" +#include "RpcAnimationFrameRrp.h" +#include "RPCPlugIn.h" +#include "RpcMains.h" +#include "RpcRdkPlugIn.h" +#include "RpcInstance.h" +#include "RpcDocument.h" + + +UUID CRpcAnimationFrameRrp::PlugInId(void) const +{ + return PlugIn().PlugInID(); +} + +bool CRpcAnimationFrameRrp::SetValue(CRhinoDoc& doc, const CRhRdkVariant& value) +{ + if (!IsEnabledForDoc(doc)) + return false; + + const int iOldFrame = Mains().RpcDocument().AnimationFrame(); + + if (iOldFrame == value.AsInteger()) + return true; + + Mains().RpcDocument().SetAnimationFrame(doc, value.AsInteger()); + + CallPropertyChangedEvent(doc, iOldFrame); + + return true; +} + +CRhRdkVariant CRpcAnimationFrameRrp::GetValue(const CRhinoDoc& doc) const +{ + if (!IsEnabledForDoc(doc)) + return CRhRdkVariant::Null(); + + return Mains().RpcDocument().AnimationFrame(); +} + +bool CRpcAnimationFrameRrp::IsEnabledForDoc(const CRhinoDoc& doc) const +{ + return IsEnabled(); +} diff --git a/RpcAnimationFrameRrp.h b/RpcAnimationFrameRrp.h new file mode 100644 index 0000000..fde1b75 --- /dev/null +++ b/RpcAnimationFrameRrp.h @@ -0,0 +1,49 @@ + +#pragma once + +// {B5F7D9C7-058B-411e-BE31-417DAA219B41} +static const GUID idAF = { 0xb5f7d9c7, 0x58b, 0x411e, { 0xbe, 0x31, 0x41, 0x7d, 0xaa, 0x21, 0x9b, 0x41 } }; + +class CRpcAnimationFrameRrp : public CRhRdkRegisteredDocProperty +{ +protected: + virtual CSupportChannels RequestedDisplayChannels(void) const { return 0; } + virtual void DeleteThis(void) { delete this; } + + // Property Identification. + virtual ON_wString LocalPropertyName(void) const { return L"Frame Number"; } + virtual ON_wString EnglishPropertyName(void) const { return LocalPropertyName(); } + virtual ON_wString PropertyCategory(void) const { return L"RPC"; } + virtual ON_wString LocalPropertyDescription(void) const { return L"Controls the frame number of the RPC(s)"; } + virtual UUID PropertyUuid(void) const { return idAF; } + + // Plugin identification. + virtual ON_wString PlugInName(void) const { return L"RPC"; } + virtual ON_wString PlugInDescription(void) const { return L"RPC Animation"; } + virtual UUID PlugInId(void) const; + + // For all types. + virtual CRhRdkVariant DefaultValue(void) const { return 0; } + virtual bool IsAnimatable(void) const { return true; } + virtual bool IsReadOnly(void) const { return false; } + virtual bool IsEnabled(void) const { return true; } + + virtual CRhRdkVariant::VariantType ValueType(void) const { return CRhRdkVariant::vtInteger; } + + // For integer and real types. + virtual CRhRdkVariant MaxValue(void) const { return CRhRdkVariant::Null(); } + virtual CRhRdkVariant MinValue(void) const { return CRhRdkVariant::Null(); } + virtual units Units(void) const { return no_units; } + + // For enumeration types. + virtual int NumberOfValidValues(void) const { return 0; } + virtual CRhRdkVariant GetValidValue(int iIndex) const { return CRhRdkVariant::Null(); } + + virtual bool SetValue(CRhinoDoc& doc, const CRhRdkVariant& value); + virtual CRhRdkVariant GetValue(const CRhinoDoc& doc) const; + virtual bool IsEnabledForDoc(const CRhinoDoc& doc) const; + + virtual bool SetDisplayToValue(CRhinoDoc& doc, + ConduitData& conduit, + const CRhRdkVariant& value) const { return false; } +}; diff --git a/RpcClient.cpp b/RpcClient.cpp new file mode 100644 index 0000000..c3bfd2e --- /dev/null +++ b/RpcClient.cpp @@ -0,0 +1,304 @@ + +#include "StdAfx.h" +#include "RpcClient.h" +#include "RPCPlugIn.h" +#include "LBPFileMgr2.h" + +const int NUM_METADATA = 9; + +CRpcClient::CRpcClient(void) +{ + m_pRpcApi = nullptr; + m_RpcPaths = nullptr; + m_hRpcApiHandle = nullptr; + + m_metaKeys = new RPCapi::TStringArg[NUM_METADATA]; + m_metaValues = new RPCapi::Param*[NUM_METADATA]; + m_numMetaValues = 0; +} + +CRpcClient::~CRpcClient(void) +{ + Flush(); +} + +void CRpcClient::Flush(void) +{ + if (0 != m_metaKeys) + { + delete[] m_metaKeys; + m_metaKeys = 0; + } + + if (0 != m_metaValues) + { + for (int i = 0; i < m_numMetaValues; i++) + delete m_metaValues[i]; + delete[] m_metaValues; + m_metaValues = 0; + m_numMetaValues = 0; + } + + if (nullptr != m_RpcPaths) + { + delete[] m_RpcPaths; + m_RpcPaths = nullptr; + } + + if (nullptr != m_pRpcApi) + { + delete m_pRpcApi; + m_pRpcApi = nullptr; + } + + if (nullptr != m_hRpcApiHandle) + { + FreeLibrary(m_hRpcApiHandle); + m_hRpcApiHandle = nullptr; + } +} + +#ifdef _DEBUG +void* track_memory_allocate(size_t size, const char *file, int line) +{ + return malloc(size); +} + +void track_memory_deallocate(void *ptr, const char *file, int line) +{ + free(ptr); +} +#endif + +bool CRpcClient::Initialize(void) +{ + const CLBPString sFullPathToPlugIn = PlugIn().PlugInFileName(); + CLBPString sPathOnly = CLBPFileMgr2::GetPathOnly(sFullPathToPlugIn); + CLBPFileMgr2::RemoveTrailingSlash(sPathOnly); + + const CLBPString sRpcApiDll = sPathOnly + L"\\RPCapi.dll"; + + m_hRpcApiHandle = LoadLibrary(sRpcApiDll); + if (nullptr != m_hRpcApiHandle) + { + +//#ifdef _DEBUG +// RPCsetMemoryAllocationFuncType allocDealloc = (RPCsetMemoryAllocationFuncType)GetProcAddress(m_hRpcApiHandle, "RPCsetMemoryAllocation"); +// allocDealloc(track_memory_allocate, track_memory_deallocate); +//#endif //_DEBUG + + RPCapi::RPCgetAPIFuncType getAPI = (RPCapi::RPCgetAPIFuncType)GetProcAddress(m_hRpcApiHandle, "RPCgetAPI"); + m_pRpcApi = getAPI(NULL); + m_pRpcApi->setClient(this); + } + + return Valid(); +} + +bool CRpcClient::Uninitialize(void) +{ + Flush(); + return true; +} + +bool CRpcClient::Valid(void) const +{ + return nullptr != m_pRpcApi; +} + +RPCapi& CRpcClient::RPC(void) const +{ + ASSERT(Valid()); + return *m_pRpcApi; +} + +TStringArg CRpcClient::RPCgetAboutString(void) +{ + return "Rhino RPC plugin"; +} + +RPCapi* CRpcClient::RPCgetAPI(void) +{ + return m_pRpcApi; +} + +const RPCapi::License* CRpcClient::RPCgetLicense(void) +{ + return m_pRpcApi->getLicense(); +} + +TStringArg* CRpcClient::RPCgetPaths(bool start, int &numPaths) +{ + static HCURSOR prevCursor; + if (!start) + { + SetCursor(prevCursor); + return nullptr; + } + + HCURSOR hCursor = (HCURSOR)::LoadImage(NULL, IDC_WAIT, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + prevCursor = SetCursor(hCursor); + + numPaths = m_aRpcPath.Count(); + + if (nullptr == m_RpcPaths) + { + if (numPaths > 0) + { + m_RpcPaths = new TStringArg[numPaths]; + + for(int i=0; i + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::STRING)); + //apiString->setA("ArchVision"); + //m_metaValues[i] = apiString; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "license"); + //RPCapi::License *apiLicense = dynamic_cast + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::LICENSE)); + //apiLicense->set(true, 1, RPCapi::License::COMMERCIAL); + //m_metaValues[i] = apiLicense; + //++i; + //RPCapi::PrimP *pint; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription4"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription5"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription6"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription7"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription8"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSubscription9"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //++i; + //m_metaKeys[i].init(RPCapi::TString::ACHAR, "archSoftwareLicense"); + //pint = dynamic_cast *> + // (m_pRpcApi->newObject(RPCapi::ObjectCodes::PRIM_INT)); + //pint->setValue(1); + //m_metaValues[i] = pint; + //m_numMetaValues = num = NUM_METADATA; + //keys = m_metaKeys; + //values = (const RPCapi::Param **)m_metaValues; +} + +void CRpcClient::RPCcontentMetadata(int req, int &numsets, const int *&setsize, const TStringArg **&keys, + const RPCapi::Param ***&values) +{ + numsets = 0; + setsize = NULL; + keys = NULL; + values = NULL; +} + +TStringArg CRpcClient::RPCpluginId(void) +{ + return L"20-1cfc-4d06-70d84-2ed4"; +} + +bool CRpcClient::RPCuserMessage(int msgType, bool ret, const TString &title, const TString &msg) +{ + return false; +} + +void CRpcClient::OnRpcInstanceWillBeCreated(UINT idDoc, const CLBPString& sRpc) +{ + CLBPString sRpcPath = CLBPFileMgr2::GetPathOnly(sRpc); + CLBPFileMgr2::RemoveTrailingSlash(sRpcPath); + + if (!CRhinoFileUtilities::FileExists(sRpc.Wide())) + { + ON_wString sPathFound; + if (!RhRdkFindFile(idDoc, sRpc, sPathFound)) + { + return; + } + + sRpcPath = sPathFound; + } + + if (!IsRpcPathInList(sRpcPath)) + { + CLBPFileMgr2::RemoveTrailingSlash(sRpcPath); + AddRpcPathToList(sRpcPath); + } +} + +bool CRpcClient::IsRpcPathInList(const CLBPString& sPath) const +{ + for (int i=0; iupdatePaths(); + } +} diff --git a/RpcClient.h b/RpcClient.h new file mode 100644 index 0000000..5ffde47 --- /dev/null +++ b/RpcClient.h @@ -0,0 +1,48 @@ +#pragma once + +class CRpcClient : public RPCapi::Client +{ +public: + CRpcClient(void); + ~CRpcClient(void); + +public: + bool Initialize(void); + bool Uninitialize(void); + +public: + bool Valid(void) const; + RPCapi& RPC(void) const; + + void OnRpcInstanceWillBeCreated(UINT idDoc, const CLBPString& sRpc); + +public: + // RPCapi::Client overrides + virtual TStringArg RPCgetAboutString(void); + virtual RPCapi* RPCgetAPI(void); + virtual const RPCapi::License* RPCgetLicense(void); + virtual TStringArg* RPCgetPaths(bool start, int &numPaths); + virtual int RPCgetMode(); + virtual TStringArg RPCiniPath(void); + virtual void RPClicenseChange(bool licensed, bool acm); + virtual void RPCpluginMetadata(int &num, const TStringArg*& keys, const RPCapi::Param**& values); + virtual void RPCcontentMetadata(int req, int &numsets, const int *&setsize, const TStringArg **&keys, const RPCapi::Param ***&values); + virtual TStringArg RPCpluginId(void); + virtual bool RPCuserMessage(int msgType, bool ret, const TString &title, const TString &msg); + +private: + bool IsRpcPathInList(const CLBPString& sPath) const; + void AddRpcPathToList(const CLBPString& sPath); + + void Flush(void); + +private: + mutable RPCapi* m_pRpcApi; + RPCapi::TStringArg* m_RpcPaths; + ON_ClassArray m_aRpcPath; + HMODULE m_hRpcApiHandle; + + RPCapi::TStringArg *m_metaKeys; + RPCapi::Param **m_metaValues; + int m_numMetaValues; +}; diff --git a/RpcCommand.cpp b/RpcCommand.cpp new file mode 100644 index 0000000..0bbc8e3 --- /dev/null +++ b/RpcCommand.cpp @@ -0,0 +1,9 @@ + +#include "StdAfx.h" +#include "RpcCommand.h" + +CRhinoCommand::result CRpcCommandBase::RunCommand(const CRhinoCommandContext& context) +{ + const CRhinoCommand::result result = RunRpcCommand(context); + return result; +} diff --git a/RpcCommand.h b/RpcCommand.h new file mode 100644 index 0000000..c0dc505 --- /dev/null +++ b/RpcCommand.h @@ -0,0 +1,25 @@ +#pragma once + + +class CRpcCommandBase : public CRhinoScriptCommand +{ +protected: + CRpcCommandBase(bool bTest) : CRhinoScriptCommand(false, false, 0, bTest) {} +public: + virtual CRhinoCommand::result RunRpcCommand(const CRhinoCommandContext&) = 0; + +protected: + virtual CRhinoCommand::result RunCommand( const CRhinoCommandContext& ); +}; + +class CRpcTestCommand : public CRpcCommandBase +{ +protected: + CRpcTestCommand() : CRpcCommandBase(true) {} +}; + +class CRpcCommand : public CRpcCommandBase +{ +protected: + CRpcCommand() : CRpcCommandBase(false) {} +}; \ No newline at end of file diff --git a/RpcCrmProvider.cpp b/RpcCrmProvider.cpp new file mode 100644 index 0000000..ad683f1 --- /dev/null +++ b/RpcCrmProvider.cpp @@ -0,0 +1,102 @@ + +#include "stdafx.h" +#include "RpcCrmProvider.h" +#include "RpcObject.h" +#include "RpcMains.h" +#include "RpcInstance.h" +#include "RpcUtilities.h" +#include "RpcRenderMeshBuilder.h" +#include "RpcRdkPlugIn.h" + +CRpcCrmProvider::CRpcCrmProvider(void) +{ +} + +CRpcCrmProvider::~CRpcCrmProvider(void) +{ +} + +UUID CRpcCrmProvider::ProviderId(void) const +{ + // {70D82ED4-1CFC-4d06-8E20-01C76D262923} + static const GUID id = { 0x70d82ed4, 0x1cfc, 0x4d06, { 0x8e, 0x20, 0x1, 0xc7, 0x6d, 0x26, 0x29, 0x23 } }; + return id; +} + +UUID CRpcCrmProvider::PlugInId(void) const +{ + return Mains().RdkPlugIn().PlugInId(); +} + +ON_wString CRpcCrmProvider::Name(void) const +{ + return L"RPC"; +} + +bool CRpcCrmProvider::WillBuildCustomMesh(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& /*doc*/, + const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const +{ + if (nullptr != pAttributes) + return false; + + CRpcObject ro(pObject); + return ro.IsTagged(); +} + +ON_BoundingBox CRpcCrmProvider::BoundingBox(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const +{ + return ::RMPBoundingBoxImpl(*this, vp, pObject, doc, uuidRequestingPlugIn, pAttributes); +} + +bool CRpcCrmProvider::BuildCustomMeshes(const ON_Viewport& vp, const UUID& uuidRequestingPlugIn, const CRhinoDoc& doc, IRhRdkCustomRenderMeshes& crmInOut, const CDisplayPipelineAttributes* pAttributes, bool bWillBuildCustomMeshCheck) const +{ + if(bWillBuildCustomMeshCheck && !WillBuildCustomMesh(vp, crmInOut.Object(), doc, uuidRequestingPlugIn, pAttributes)) + return false; + + const CRhinoObject* pObject = crmInOut.Object(); + if (NULL == pObject) return false; + + const CRhinoInstanceObject* pBlock = CRhinoInstanceObject::Cast(pObject); + if (NULL == pBlock) return false; + + ON_Xform xformInstance = pBlock->InstanceXform(); + xformInstance.Invert(); + + ON_3dPoint ptCamera = vp.CameraLocation(); + + ptCamera.Transform(xformInstance); + + crmInOut.SetAutoDeleteMeshesOn(); + crmInOut.SetAutoDeleteMaterialsOn(); + + CRpcInstance rpc(doc, *pBlock); + if (rpc.IsValid()) + { + const RPCapi::Instance* pRpcInstance = rpc.Instance(); + if (NULL != pRpcInstance) + { + // DebugSaveTexturesToRoot(*pRpcInstance, ptCamera); + + ON_SimpleArray aMeshes; + ON_SimpleArray aMaterials; + + CRpcRenderMeshBuilder mb(doc, *pRpcInstance); + if (mb.Build(ptCamera, aMeshes, aMaterials)) + { + for (int i=0; iTransform(pBlock->InstanceXform()); + + CRhRdkBasicMaterial* pRdkMaterial = NULL; + + pRdkMaterial = aMaterials[i]; + + crmInOut.Add(pRhinoMesh, pRdkMaterial); + } + } + } + } + + return true; +} diff --git a/RpcCrmProvider.h b/RpcCrmProvider.h new file mode 100644 index 0000000..dc9503d --- /dev/null +++ b/RpcCrmProvider.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "RPCPlugIn.h" + +class CRpcCrmProvider : public CRhRdkCustomRenderMeshProvider +{ +public: + CRpcCrmProvider(void); + virtual ~CRpcCrmProvider(void); + +public: + virtual UUID ProviderId(void) const override; + virtual UUID PlugInId(void) const override; + virtual ON_wString Name(void) const override; + + virtual bool IsViewDependent(void) const override { return true; } + + virtual bool WillBuildCustomMesh(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, + const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const override; + + virtual ON_BoundingBox BoundingBox(const ON_Viewport& vp, const CRhinoObject* pObject, const CRhinoDoc& doc, + const UUID& uuidRequestingPlugIn, const CDisplayPipelineAttributes* pAttributes) const override; + + virtual bool BuildCustomMeshes(const ON_Viewport& vp, const UUID& uuidRequestingPlugIn, const CRhinoDoc& doc, + IRhRdkCustomRenderMeshes& crmInOut, const CDisplayPipelineAttributes* pAttributes, bool bWillBuildCustomMeshCheck) const override; + + virtual bool IsRequestingPlugInDependent(void) const override { return false; } + virtual bool IsPreviewAndStandardSameMesh(void) const override { return false; } + + virtual CRhRdkVariant GetParameter(const CRhinoObject& object, const wchar_t* wszParamName) const override { return CRhRdkVariant(); } + virtual void SetParameter(const CRhinoObject& object, const wchar_t* wszParamName, const CRhRdkVariant& value) override { } +}; diff --git a/RpcDefinitions.h b/RpcDefinitions.h new file mode 100644 index 0000000..be7566f --- /dev/null +++ b/RpcDefinitions.h @@ -0,0 +1,7 @@ +#pragma once + + +#define RPC_USER_DATA_VERSION 1 + +// Custom control IDs. +#define IDCC1 81 \ No newline at end of file diff --git a/RpcDocument.cpp b/RpcDocument.cpp new file mode 100644 index 0000000..d17514a --- /dev/null +++ b/RpcDocument.cpp @@ -0,0 +1,93 @@ +#include "StdAfx.h" +#include "RpcDocument.h" +#include "RpcMains.h" +#include "RpcEventMachine.h" +#include "RpcUtilities.h" + +CRpcDocument::CRpcDocument(void) +{ + Defaults(); +} + +CRpcDocument::~CRpcDocument(void) +{ +} + +void CRpcDocument::Defaults(void) +{ + m_iAnimationFrame = 0; +} + +int CRpcDocument::Version(void) +{ + return 1; +} + +int CRpcDocument::AnimationFrame(void) const +{ + return m_iAnimationFrame; +} + +void CRpcDocument::SetAnimationFrame(CRhinoDoc& doc, int iFrame) +{ + if (m_iAnimationFrame == iFrame) + return; + + const int iOldFrame = m_iAnimationFrame; + m_iAnimationFrame = iFrame; + Mains().EventMachine().OnAnimationFrameChanged(doc, iOldFrame, iFrame); +} + +bool CRpcDocument::ReadFromArchive(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileReadOptions& options) +{ + int major_version = 0; + int minor_version = 0; + if (!archive.BeginRead3dmChunk(TCODE_USER, &major_version, &minor_version)) + return false; + + int iVersion = 0; + if (!archive.ReadInt(&iVersion)) + { + archive.EndRead3dmChunk(); + return false; + } + + if (iVersion > Version()) + { + const CLBPString sMsg = _RhLocalizeString( L"This model was created with a more recent version of the RPC Plugin.", 36076); + ::MessageBox(RhinoApp().MainWnd(), sMsg, L"RPC", MB_OK|MB_ICONINFORMATION); + archive.EndRead3dmChunk(); + return false; + } + + bool bRet = true; + + if (bRet) {bRet = archive.ReadInt(&m_iAnimationFrame);} + + if (!archive.EndRead3dmChunk()) + return false; + + return bRet; +} + +bool CRpcDocument::WriteToArchive(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileWriteOptions& options) +{ + const int major_version = 1; + const int minor_version = 0; + archive.BeginWrite3dmChunk(TCODE_USER, major_version, minor_version); + + if (!archive.WriteInt(Version())) + { + archive.EndWrite3dmChunk(); + return false; + } + + bool bRet = true; + + if (bRet) {bRet = archive.WriteInt(m_iAnimationFrame);} + + if (!archive.EndWrite3dmChunk()) + return false; + + return bRet; +} diff --git a/RpcDocument.h b/RpcDocument.h new file mode 100644 index 0000000..129b293 --- /dev/null +++ b/RpcDocument.h @@ -0,0 +1,25 @@ +#pragma once + + +class CRpcDocument +{ +public: + CRpcDocument(void); + ~CRpcDocument(void); + +public: + int Version(void); + +public: + int AnimationFrame(void) const; + void SetAnimationFrame(CRhinoDoc& doc, int iFrame); + + void Defaults(void); + +public: + bool ReadFromArchive(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileReadOptions& options); + bool WriteToArchive(CRhinoDoc& doc, ON_BinaryArchive& archive, const CRhinoFileWriteOptions& options); + +private: + int m_iAnimationFrame; +}; diff --git a/RpcDragDropHandler.cpp b/RpcDragDropHandler.cpp new file mode 100644 index 0000000..d86f8e8 --- /dev/null +++ b/RpcDragDropHandler.cpp @@ -0,0 +1,111 @@ +#include "StdAfx.h" +#include "RpcDragDropHandler.h" +#include "RpcUtilities.h" +#include "RpcInstance.h" + + +template +void SDSFromZDS(const T* p, ON_ClassArray& string_array) +{ + CLBPString s(p); + + while (!s.IsEmpty()) + { + if (CRpcInstance::IsValidRpc(s)) + { + string_array.Append(s); + } + + while(*p != 0) + p++; + s = ++p; + } +} + + +CRpcDragDropHandler::CRpcDragDropHandler(void) +{ +} + +CRpcDragDropHandler::~CRpcDragDropHandler(void) +{ +} + +bool CRpcDragDropHandler::SupportDataObject( COleDataObject* pDataObject) +{ + ON_ClassArray aRpcs; + RpcPathFromOleData(pDataObject, aRpcs); + return (aRpcs.Count() > 0) ? true : false; +} + +bool CRpcDragDropHandler::OnDropOnRhinoView(CRhinoView* pRhinoView, COleDataObject* pDataObject, DROPEFFECT dropEffect, const ON_2iPoint& point) +{ + if (NULL == pRhinoView) + return false; + + CRhinoDoc* pDoc = pRhinoView->GetDocument(); + if (NULL == pDoc) return false; + + ON_ClassArray aRpcs; + RpcPathFromOleData(pDataObject, aRpcs); + if (aRpcs.Count () < 1) + return false; + + CPoint first_ptIn; + GetCursorPos(&first_ptIn); + + ON_2iPoint ptIn = P2P(first_ptIn); + pRhinoView->ScreenToClient(ptIn); + + ON_3dPoint pt3D(ptIn.x, ptIn.y, 0); + + ON_Line line; + pRhinoView->ActiveViewport().VP().GetFrustumLine(ptIn.x, ptIn.y, line); + + const ON_3dmConstructionPlane& plane = pRhinoView->ActiveViewport().ConstructionPlane(); + + double dParam = 0.0; + if (!ON_Intersect(line, plane.m_plane, &dParam)) + return false; + + pt3D = line.PointAt(dParam); + + for(int i=0; iRedraw(); + + return true; +} + +void CRpcDragDropHandler::RpcPathFromOleData(COleDataObject* pDataObject, ON_ClassArray& aRpcs) +{ + if (!pDataObject->IsDataAvailable(CF_HDROP)) + return; + + STGMEDIUM medium = { 0 }; + if (!pDataObject->GetData(CF_HDROP, &medium)) + return; + + if (TYMED_HGLOBAL != medium.tymed) + return; + + const DROPFILES* pDropFiles = reinterpret_cast(::GlobalLock(medium.hGlobal)); + if (NULL == pDropFiles) return; + + const BYTE* pText = reinterpret_cast(pDropFiles) + pDropFiles->pFiles; + + if (pDropFiles->fWide) + { + SDSFromZDS(reinterpret_cast(pText), aRpcs); + } + else + { + SDSFromZDS(reinterpret_cast(pText), aRpcs); + } + + ::GlobalUnlock(medium.hGlobal); +} diff --git a/RpcDragDropHandler.h b/RpcDragDropHandler.h new file mode 100644 index 0000000..19faf70 --- /dev/null +++ b/RpcDragDropHandler.h @@ -0,0 +1,17 @@ + +#pragma once + +class CRpcDragDropHandler : public CRhinoDropTarget +{ +public: + CRpcDragDropHandler(void); + ~CRpcDragDropHandler(void); + +public: + // CRhinoDropTarget overrides + virtual bool SupportDataObject(COleDataObject* pDataObject); + virtual bool OnDropOnRhinoView( CRhinoView* pRhinoView, COleDataObject* pDataObject, DROPEFFECT dropEffect, const ON_2iPoint& point) override; + +private: + void RpcPathFromOleData(COleDataObject* pDataObject, ON_ClassArray& aRpcs); +}; diff --git a/RpcEditCmd.cpp b/RpcEditCmd.cpp new file mode 100644 index 0000000..23ea48b --- /dev/null +++ b/RpcEditCmd.cpp @@ -0,0 +1,110 @@ +#include "StdAfx.h" +#include "RpcEditCmd.h" +#include "RpcObject.h" +#include "RpcInstance.h" +#include "RpcEditDlg.h" +#include "RpcUtilities.h" + + +const wchar_t * CRpcEditCmd::EnglishCommandName() +{ + return L"RPCEdit"; +} + +UUID CRpcEditCmd::CommandUUID() +{ + // {4386730C-5C6A-4427-B182-B31EB6F592B3} + static const GUID id = + { 0x4386730c, 0x5c6a, 0x4427, { 0xb1, 0x82, 0xb3, 0x1e, 0xb6, 0xf5, 0x92, 0xb3 } }; + return id; +} + +bool CRpcEditCmd::GetRpcBlock(const CRhinoDoc& doc, ON_SimpleArray& aBlock) +{ + CRhinoGetObject GetBlock; + GetBlock.AcceptNothing(); + GetBlock.SetGeometryFilter(CRhinoGetObject::instance_reference); + GetBlock.SetCommandPrompt(_RhLocalizeString( L"Select RPC(s)", 36077)); + GetBlock.EnableSubObjectSelect(false); + GetBlock.EnablePreSelect(TRUE); + GetBlock.GetObjects(1,0); + if(CRhinoGet::object != GetBlock.Result()) + return false; + + for(int i=0; i 0) ? true : false; +} + +CRhinoCommand::result CRpcEditCmd::RunRpcCommand(const CRhinoCommandContext& context) +{ + CRhinoDoc* pDoc = context.Document(); + if (NULL == pDoc) + return failure; + + ON_SimpleArray aBlock; + if (!GetRpcBlock(*pDoc, aBlock)) + return cancel; + + ON_SimpleArray aRpc; + ON_SimpleArray aBlockIndex; + for(int i=0; iIsValid()) + { + RPCapi::Mesh* pRpcMesh = pRpc->Instance()->getEditMesh(); + if (NULL != pRpcMesh) + { + delete pRpcMesh; + aRpc.Append(pRpc); + aBlockIndex.Append(i); + } + } + } + + if (aRpc.Count() <= 0) + { + RhinoApp().Print(_RhLocalizeString( L"No valid RPC(s) selected\n", 36078)); + return cancel; + } + + CRpcEditDlg dlg(*pDoc, aRpc); + if (!dlg.Edit()) + { + for (int i=0; iReplace(*pDoc); + if (NULL != pInstObj) + { + pInstObj->Select(true); + } + } + + for (int i=0; iRedraw(); + + return success; +} diff --git a/RpcEditCmd.h b/RpcEditCmd.h new file mode 100644 index 0000000..e6cdec0 --- /dev/null +++ b/RpcEditCmd.h @@ -0,0 +1,15 @@ + +#pragma once + +#include "RpcCommand.h" + +class CRpcEditCmd : public CRpcTestCommand +{ +public: + virtual CRhinoCommand::result RunRpcCommand(const CRhinoCommandContext& context); + virtual const wchar_t * EnglishCommandName(); + virtual UUID CommandUUID(); + +private: + bool GetRpcBlock(const CRhinoDoc& doc, ON_SimpleArray& aBlock); +}; diff --git a/RpcEditDlg.cpp b/RpcEditDlg.cpp new file mode 100644 index 0000000..ddb6b02 --- /dev/null +++ b/RpcEditDlg.cpp @@ -0,0 +1,57 @@ +#include "StdAfx.h" +#include "RpcEditDlg.h" +#include "RpcInstance.h" +#include "RpcClient.h" +#include "RpcMains.h" +#include "Resource.h" + + +CRpcEditDlg::CRpcEditDlg(const CRhinoDoc& doc, ON_SimpleArray& aRpc) +: m_aRpc(aRpc), + m_doc(doc) +{ + +} + +CRpcEditDlg::~CRpcEditDlg(void) +{ + +} + +ON_SimpleArray& CRpcEditDlg::RPC(void) +{ + return m_aRpc; +} + +bool CRpcEditDlg::Edit(void) +{ + const int iCount = m_aRpc.Count(); + + if (iCount <= 0) + { + return false; + } + else + { + RPCapi::MassEditInterface* pEditInterface = dynamic_cast + (Mains().RpcClient().RPCgetAPI()->newObject(RPCapi::ObjectCodes::MASS_EDIT_INTERFACE)); + + if (NULL == pEditInterface) + return false; + + for(int i=0; iaddInstance(m_aRpc[i]->Instance()); + } + + const double dScale = ON::UnitScale(ON::LengthUnitSystem::Inches, m_doc.ModelUnits()); + pEditInterface->setUnits(RPCapi::Units::LINEAR_UNITS, dScale); + + const int iRet = pEditInterface->show(RhinoApp().MainWnd(), 0); + + pEditInterface->hide(); + delete pEditInterface; + + return (IDOK == iRet) ? true : false; + } +} diff --git a/RpcEditDlg.h b/RpcEditDlg.h new file mode 100644 index 0000000..3f7ca73 --- /dev/null +++ b/RpcEditDlg.h @@ -0,0 +1,22 @@ +#pragma once + + +class CRpcInstance; + + +class CRpcEditDlg +{ +public: + CRpcEditDlg(const CRhinoDoc& doc, ON_SimpleArray& aRpc); + ~CRpcEditDlg(void); + +public: + bool Edit(void); + +public: + ON_SimpleArray& RPC(void); + +private: + ON_SimpleArray& m_aRpc; + const CRhinoDoc& m_doc; +}; diff --git a/RpcEventMachine.cpp b/RpcEventMachine.cpp new file mode 100644 index 0000000..f0c5d4a --- /dev/null +++ b/RpcEventMachine.cpp @@ -0,0 +1,73 @@ +#include "StdAfx.h" +#include "RpcEventMachine.h" + + +#define EVENT_BEGIN if (m_bEnabled) { for (int i = 0; i < m_aEventSinks.GetSize(); i++) { if (m_aEventSinks[i] != NULL) { +#define EVENT_CALL(f) m_aEventSinks[i]->f +#define EVENT_END } } } CleanUp(); + + +CRpcEventMachine::CRpcEventMachine() +{ + m_bEnabled = true; +} + +void CRpcEventMachine::RegisterEventSink(CRpcEventSinkBase* pEventSink) +{ + m_aEventSinks.Add(pEventSink); +} + +void CRpcEventMachine::UnregisterEventSink(CRpcEventSinkBase* pEventSink) +{ + const int iIndex = EventSinkIndex(pEventSink); + if (iIndex >= 0) + { + m_aEventSinks[iIndex] = NULL; + } +} + +void CRpcEventMachine::EnableEvents(bool bEnabled) +{ + m_bEnabled = bEnabled; +} + +int CRpcEventMachine::EventSinkIndex(const CRpcEventSinkBase* pEventSink) +{ + for (int i = 0; i < m_aEventSinks.GetSize(); i++) + { + if (m_aEventSinks[i] == pEventSink) + return i; + } + + return -1; +} + +void CRpcEventMachine::CleanUp(void) +{ + int iIndex = 0; + while (iIndex < m_aEventSinks.GetSize()) + { + if (NULL == m_aEventSinks[iIndex]) + { + m_aEventSinks.RemoveAt(iIndex); + } + else + { + iIndex++; + } + } +} + +void CRpcEventMachine::OnRpcParameterChanged(const RPCapi::ID* id) +{ + EVENT_BEGIN; + EVENT_CALL(OnRpcParameterChanged(id)); + EVENT_END; +} + +void CRpcEventMachine::OnAnimationFrameChanged(CRhinoDoc& doc, int iOldFrame, int iNewFrame) +{ + EVENT_BEGIN; + EVENT_CALL(OnAnimationFrameChanged(doc, iOldFrame, iNewFrame)); + EVENT_END; +} diff --git a/RpcEventMachine.h b/RpcEventMachine.h new file mode 100644 index 0000000..fc492b1 --- /dev/null +++ b/RpcEventMachine.h @@ -0,0 +1,33 @@ +#pragma once + + +#include "RpcEventSink.h" + + +class CRpcEventSinkBaseArray : public CArray +{ +}; + +class CRpcEventMachine +{ +public: + CRpcEventMachine(); + virtual ~CRpcEventMachine() { } + + void RegisterEventSink(CRpcEventSinkBase* pEventSink); + void UnregisterEventSink(CRpcEventSinkBase* pEventSink); + + void EnableEvents(bool bEnabled); + +public: + void OnRpcParameterChanged(const RPCapi::ID* id); + void OnAnimationFrameChanged(CRhinoDoc& doc, int iOldFrame, int iNewFrame); + +protected: + int EventSinkIndex(const CRpcEventSinkBase* pEventSink); + void CleanUp(void); + +private: + CRpcEventSinkBaseArray m_aEventSinks; + bool m_bEnabled; +}; diff --git a/RpcEventSink.cpp b/RpcEventSink.cpp new file mode 100644 index 0000000..6d70f67 --- /dev/null +++ b/RpcEventSink.cpp @@ -0,0 +1,27 @@ +#include "StdAfx.h" +#include "RpcEventSink.h" +#include "RPCPlugIn.h" +#include "RpcMains.h" +#include "RpcEventMachine.h" + + +CRpcEventSinkBase::CRpcEventSinkBase() +{ + +} + +CRpcEventSinkBase::~CRpcEventSinkBase() +{ + +} + + +CRpcEventSink::CRpcEventSink() +{ + Mains().EventMachine().RegisterEventSink(this); +} + +CRpcEventSink::~CRpcEventSink() +{ + Mains().EventMachine().UnregisterEventSink(this); +} diff --git a/RpcEventSink.h b/RpcEventSink.h new file mode 100644 index 0000000..22e7bb3 --- /dev/null +++ b/RpcEventSink.h @@ -0,0 +1,20 @@ + +#pragma once + +class CRpcEventSinkBase +{ +public: + CRpcEventSinkBase(); + virtual ~CRpcEventSinkBase(); + +public: + virtual void OnRpcParameterChanged(const RPCapi::ID* id) { } + virtual void OnAnimationFrameChanged(CRhinoDoc& doc, int iOldFrame, int iNewFrame) { } +}; + +class CRpcEventSink : protected CRpcEventSinkBase +{ +public: + CRpcEventSink(); + virtual ~CRpcEventSink(); +}; diff --git a/RpcEventWatcher.cpp b/RpcEventWatcher.cpp new file mode 100644 index 0000000..5371235 --- /dev/null +++ b/RpcEventWatcher.cpp @@ -0,0 +1,60 @@ +#include "StdAfx.h" +#include "RpcEventWatcher.h" +#include "RpcMains.h" +#include "RpcDocument.h" + + +CRpcEventWatcher::CRpcEventWatcher(void) +{ + m_bMergeDocument = false; + m_bReferenceDocument = false; + m_bOpeningDocument = false; + m_bReadRpcData = false; +} + +CRpcEventWatcher::~CRpcEventWatcher(void) +{ +} + +bool CRpcEventWatcher::IsMergingDocument(void) const +{ + return m_bMergeDocument; +} + +bool CRpcEventWatcher::IsReferenceDocument(void) const +{ + return m_bReferenceDocument; +} + +bool CRpcEventWatcher::IsOpeningDocument(void) const +{ + return m_bOpeningDocument; +} + +void CRpcEventWatcher::SetReadingRpcData(void) +{ + m_bReadRpcData = true; +} + +void CRpcEventWatcher::OnNewDocument(CRhinoDoc& doc) +{ + Mains().RpcDocument().Defaults(); +} + +void CRpcEventWatcher::OnBeginOpenDocument(CRhinoDoc& doc, const wchar_t* filename, BOOL bMerge, BOOL bReference) +{ + m_bMergeDocument = bMerge ? true : false; + m_bReferenceDocument = bReference ? true : false; + m_bOpeningDocument = true; + m_bReadRpcData = false; +} + +void CRpcEventWatcher::OnEndOpenDocument(CRhinoDoc& doc, const wchar_t* filename, BOOL bMerge, BOOL bReference) +{ + m_bOpeningDocument = false; + + if (!m_bReadRpcData) + { + Mains().RpcDocument().Defaults(); + } +} diff --git a/RpcEventWatcher.h b/RpcEventWatcher.h new file mode 100644 index 0000000..70b7ab6 --- /dev/null +++ b/RpcEventWatcher.h @@ -0,0 +1,26 @@ +#pragma once + +class CRpcEventWatcher : public CRhinoEventWatcher +{ +public: + CRpcEventWatcher(void); + ~CRpcEventWatcher(void); + +public: + bool IsMergingDocument(void) const; + bool IsReferenceDocument(void) const; + bool IsOpeningDocument(void) const; + + void SetReadingRpcData(void); + +public: + virtual void OnNewDocument(CRhinoDoc& doc); + virtual void OnBeginOpenDocument(CRhinoDoc& doc, const wchar_t* filename, BOOL bMerge, BOOL bReference); + virtual void OnEndOpenDocument(CRhinoDoc& doc, const wchar_t* filename, BOOL bMerge, BOOL bReference); + +private: + bool m_bMergeDocument; + bool m_bReferenceDocument; + bool m_bReadRpcData; + bool m_bOpeningDocument; +}; diff --git a/RpcFileDlg.cpp b/RpcFileDlg.cpp new file mode 100644 index 0000000..0b80d15 --- /dev/null +++ b/RpcFileDlg.cpp @@ -0,0 +1,109 @@ +#include "StdAfx.h" +#include "RpcFileDlg.h" +#include "Resource.h" +#include "RpcUtilities.h" + + +CRpcFileDlg::CRpcFileDlg(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, + DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) +: CLBPPreviewingFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd) +{ + +} + +CRpcFileDlg::~CRpcFileDlg(void) +{ +} + +BOOL CRpcFileDlg::OnInitDialog() +{ + CLBPPreviewingFileDialog::OnInitDialog(); + + PreviewCheck().SetWindowText(_RhLocalizeString( L"Preview", 32810)); // RR #83932. + + return TRUE; +} + +DWORD CRpcFileDlg::ResourceID(DWORD dwWhich) const +{ + switch (dwWhich) + { + case 1: return IDC_CHECK_PREVIEW; + case 2: return IDC_STATIC_PREVIEW; + case 3: return IDC_PROGRESS1; + } + + return IDD_FILE_DIALOG; +} + +HINSTANCE CRpcFileDlg::ResourceInstance(void) const +{ + return AfxGetInstanceHandle(); +} + + + +BEGIN_MESSAGE_MAP(CRpcAdvancedFileDialog, CRpcFileDlg) + ON_WM_SIZE() + ON_BN_CLICKED(IDCC1, OnAdvancedButtonClicked) +END_MESSAGE_MAP() + +CRpcAdvancedFileDialog::CRpcAdvancedFileDialog(CWnd* pParent) + : + m_bAdvanced(false), + CRpcFileDlg(true, L"*.rpc", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, L"RPC files (*.rpc)|*.rpc|All files (*.*)|*.*||", pParent) +{ +} + +BOOL CRpcAdvancedFileDialog::OnInitDialog() +{ + __super::OnInitDialog(); + + const CRect rectNil(0, 0, 0, 0); + + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + const DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP; + m_button_advanced.Create(_RhLocalizeString( L"Ad&vanced...", 32811), dwStyle, rectNil, this, IDCC1); + m_button_advanced.SetFont(GetFont()); + + const HICON hIcon = (HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ACM), IMAGE_ICON, 32, 32, LR_SHARED); + m_button_advanced.SetIcon(hIcon); + + SetAdvancedButtonPosition(); + + return TRUE; +} + +void CRpcAdvancedFileDialog::OnAdvancedButtonClicked() +{ + m_bAdvanced = true; + + ::EndDialog(GetParent()->GetSafeHwnd(), IDCANCEL); +} + +void CRpcAdvancedFileDialog::OnSize(UINT nType, int cx, int cy) +{ + __super::OnSize(nType, cx, cy); + + SetAdvancedButtonPosition(); +} + +void CRpcAdvancedFileDialog::SetAdvancedButtonPosition(void) +{ + CWnd* pWnd = GetDlgItem(ResourceID(1)); + if (NULL == pWnd) + return; + + CRect rectW; + pWnd->GetWindowRect(rectW); + ScreenToClient(rectW); + + CRect rect; + GetClientRect(rect); + rect.top = rectW.bottom + 20; + rect.bottom = rect.top + 60; + rect.left = rectW.left; + rect.right = rectW.right; + m_button_advanced.MoveWindow(rect); +} diff --git a/RpcFileDlg.h b/RpcFileDlg.h new file mode 100644 index 0000000..8be457d --- /dev/null +++ b/RpcFileDlg.h @@ -0,0 +1,43 @@ +#pragma once + +#include "LBPPreviewingFileDialog.h" + + +class CRpcFileDlg : public CLBPPreviewingFileDialog +{ +public: + CRpcFileDlg(BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, + CWnd* pParentWnd = NULL); + virtual ~CRpcFileDlg(void); + +protected: + virtual BOOL OnInitDialog(); + +public: + virtual HINSTANCE ResourceInstance() const; + virtual DWORD ResourceID(DWORD dwWhich) const; +}; + + +class CRpcAdvancedFileDialog : public CRpcFileDlg +{ +public: + CRpcAdvancedFileDialog(CWnd* pParent); + + bool Advanced(void) const { return m_bAdvanced; } + +protected: + virtual BOOL OnInitDialog(); + +protected: + afx_msg void OnAdvancedButtonClicked(); + afx_msg void OnSize(UINT nType, int cx, int cy); + DECLARE_MESSAGE_MAP() + + void SetAdvancedButtonPosition(void); + +private: + CRhinoUiBitmapButton m_button_advanced; + bool m_bAdvanced; +}; diff --git a/RpcInstance.cpp b/RpcInstance.cpp new file mode 100644 index 0000000..a5a5426 --- /dev/null +++ b/RpcInstance.cpp @@ -0,0 +1,409 @@ +#include "StdAfx.h" +#include "RpcInstance.h" +#include "RpcClient.h" +#include "Resource.h" +#include "RpcMains.h" +#include "Resource.h" +#include "RpcObject.h" +#include "RpcUtilities.h" +#include "RpcDocument.h" + + +CRpcInstance::CRpcInstance(const CRhinoDoc& doc, const CLBPString& sFullPath) +{ + Construct(doc.RuntimeSerialNumber(), NULL, sFullPath); +} + +CRpcInstance::CRpcInstance(const CRhinoDoc& doc, const CRhinoObject& obj) +{ + Construct(doc.RuntimeSerialNumber(), &obj, L""); +} + +CRpcInstance::~CRpcInstance() +{ + KillEditUi(); + delete m_pInstance; +} + +void CRpcInstance::Construct(UINT idDoc, const CRhinoObject* pObject, const CLBPString& sRpcPath) +{ + m_pEditInterface = NULL; + m_pEditDlgCallback = NULL; + m_idObject = ON_nil_uuid; + m_idDoc = idDoc; + + if (!sRpcPath.IsEmpty()) + { + Mains().RpcClient().OnRpcInstanceWillBeCreated(m_idDoc, sRpcPath); + } + + m_pInstance = dynamic_cast(Mains().RpcClient().RPCgetAPI()->newObject(RPCapi::ObjectCodes::INSTANCE)); + if (NULL == m_pInstance) return; + + m_pInstance->setClientInstance(this); + + if (!sRpcPath.IsEmpty()) + { + if (!m_pInstance->setRPCFileName(sRpcPath.T())) + { + RPCapi::ParamList* pRpcFile = Mains().RpcClient().RPCgetAPI()->openRPCFile(sRpcPath.T()); + if (NULL != pRpcFile) + { + RPCapi::ID* pRpcId = dynamic_cast(pRpcFile->get("/metadata/id")); + delete pRpcFile; + m_pInstance->setRpcId(pRpcId); + delete pRpcId; + } + } + } + else + if (NULL != pObject) + { + m_idObject = pObject->ModelObjectId(); + CopyToRpc(*pObject); + } + else + { + return; + } +} + +bool CRpcInstance::IsValidRpc(const CLBPString& s) // static +{ + RPCapi* pApi = Mains().RpcClient().RPCgetAPI(); + if (NULL == pApi) return false; + + RPCapi::ParamList* pRpcFile = pApi->openRPCFile(s.T()); + if (NULL == pRpcFile) return false; + + delete pRpcFile; + return true; +} + +bool CRpcInstance::IsValid(void) const +{ + RPCapi* pApi = Mains().RpcClient().RPCgetAPI(); + if (NULL == pApi) return false; + + CLBPString sPath = FileName(); + if (sPath.IsEmpty()) + { + pApi->updatePaths(); + sPath = FileName(); + } + + RPCapi::ParamList* pRpcFile = pApi->openRPCFile(sPath.T()); + if (NULL == pRpcFile) + { + return false; + } + + delete pRpcFile; + return true; +} + +void CRpcInstance::SetId(const RPCapi::ID *id) +{ + if (NULL == m_pInstance) + return; + + m_pInstance->setRpcId(id); +} + +const RPCapi::ID* CRpcInstance::Id(void) const +{ + if (NULL == m_pInstance) + return NULL; + + return m_pInstance->getID(); +} + +CLBPString CRpcInstance::FileName(void) const +{ + if (NULL == m_pInstance) + return L""; + + const TString & s = m_pInstance->getRPCFileName(); + + return s.getA(); +} + +CLBPString CRpcInstance::ObjectName(void) const +{ + const CRhinoObject* pObject = Object(); + + if (NULL == pObject) + { + m_sName = L""; + } + else + { + m_sName = pObject->Attributes().m_name; + } + + return m_sName; +} + +CLBPString CRpcInstance::RpcName(void) const +{ + if (NULL == m_pInstance) + return L""; + + TString* pName = m_pInstance->getName(); + if (NULL == pName) return L""; + + CLBPString s = pName->getA(); + delete pName; + + return s; +} + +RPCapi::Instance* CRpcInstance::Instance(void) +{ + return m_pInstance; +} + +TStringArg CRpcInstance::RPCgetName(void) +{ + ObjectName(); + return m_sName.AsMBCSString(); +} + +RPCapi::Instance* CRpcInstance::RPCgetInstance(void) +{ + return m_pInstance; +} + +void CRpcInstance::RPCgetPivot(double px, double py, double pz, double &distance, double &dx, double &dy, double &dz) +{ + +} + +int CRpcInstance::RPCgetTime(void) +{ + return Mains().RpcDocument().AnimationFrame(); +} + +bool CRpcInstance::RPCisSelected(void) +{ + const CRhinoObject* pObject = Object(); + if (NULL == pObject) + { + return false; + } + + return (0 == pObject->IsSelected()) ? false : true; +} + +void CRpcInstance::RPCparameterChangeNotification(bool newInstance, const TString **params, int num) +{ + if (NULL != m_pEditDlgCallback) + { + m_pEditDlgCallback->OnRpcParameterChanged(); + } +} + +void CRpcInstance::RPCselect(bool select) +{ + const CRhinoObject* pObject = Object(); + if (NULL != pObject) + { + pObject->Select(select); + } +} + +bool CRpcInstance::Data(CLBPBuffer& buf) const +{ + if (!IsValid()) + return false; + + const int iSize = m_pInstance->size(); + char* buffer = new char[iSize + 1]; + buffer[iSize] = 0; + + stringstream streamOut(buffer); + m_pInstance->toStream(streamOut, 0); + + string str = streamOut.str(); + const size_t strSize = str.size(); + + buf.Set(str.c_str(), strSize); + + delete []buffer; + + return true; +} + +void CRpcInstance::SetData(const CLBPBuffer& buf) +{ + if (NULL == m_pInstance) + return; + + const int iType = m_pInstance->typeCode(); + const size_t iSize = buf.NumBytes(); + const BYTE* pBytes = buf.Bytes(); + + const char* sz = (const char*)pBytes; + + string str(sz, iSize); + stringstream streamIn(str); + + m_pInstance->fromStream(streamIn, 0, iType); + + Mains().RpcClient().OnRpcInstanceWillBeCreated(m_idDoc, FileName()); +} + +bool CRpcInstance::CopyToRpc(const CRhinoObject& obj) +{ + CRpcObject ro(&obj); + if (!ro.IsTagged()) + return false; + + CLBPBuffer buf; + ro.RpcData(buf); + + SetData(buf); + + return true; +} + +bool CRpcInstance::CopyFromRpc(const CRhinoObject& obj) const +{ + const CLBPString sRpc = FileName(); + if (sRpc.IsEmpty()) + return false; + + CLBPBuffer buf; + Data(buf); + + CRpcObject ro(&obj); + ro.Tag(buf); + + return true; +} + +void CRpcInstance::KillEditUi(void) +{ + if (NULL != m_pEditInterface) + { + m_pEditInterface->hide(); + delete m_pEditInterface; + m_pEditInterface = NULL; + m_pEditDlgCallback = NULL; + } +} + +bool CRpcInstance::EditUi(HWND hWndParent, IEditDialogCallback* pCallback) +{ + if (NULL == m_pInstance) + return false; + + m_pEditDlgCallback = pCallback; + + KillEditUi(); + + m_pEditInterface = dynamic_cast(Mains().RpcClient().RPCgetAPI()->newObject(RPCapi::ObjectCodes::INSTANCE_INTERFACE)); + if (NULL == m_pEditInterface) return false; + + m_pEditInterface->setInstance(m_pInstance); + + CRhinoDoc* pDoc = CRhinoDoc::FromRuntimeSerialNumber(m_idDoc); + const double dScale = (NULL != pDoc) ? ON::UnitScale(ON::LengthUnitSystem::Inches, pDoc->ModelUnits()) : 1.0; + + m_pEditInterface->setUnits(RPCapi::Units::LINEAR_UNITS, dScale); + m_pEditInterface->show(hWndParent, RPCapi::InstanceInterface::Window::PARAMETERS, RPCapi::InstanceInterface::Window::MODE_CODE::MODELESS); + + return true; +} + +CRhinoInstanceObject* CRpcInstance::Replace(CRhinoDoc& doc) +{ + const CRhinoInstanceObject* pBlock = CRhinoInstanceObject::Cast(Object()); + if (NULL == pBlock) return NULL; + + const int iInstanceDefintionId = pBlock->InstanceDefinition()->Index(); + const ON_Xform xformInstance = pBlock->InstanceXform(); + const CRhinoObjectAttributes attr = pBlock->Attributes(); + const CLBPString sName = attr.m_name; + + if (!doc.DeleteObject(CRhinoObjRef(pBlock))) + return NULL; + + CRhinoInstanceDefinitionTable& def_table = doc.m_instance_definition_table; + def_table.DeleteInstanceDefinition(iInstanceDefintionId, false, true); + + CRhinoInstanceObject* pAddedObject = AddToDocument(doc, sName, xformInstance); + return pAddedObject; +} + +CRhinoInstanceObject* CRpcInstance::AddToDocument(CRhinoDoc& doc, const ON_3dPoint& pt) +{ + ON_Xform xform = ON_Xform::TranslationTransformation(pt - ON_origin); + + const CLBPString sName = UnusedRpcName(RpcName()); + + return AddToDocument(doc, sName, xform); +} + +const CRhinoObject* CRpcInstance::Object(void) const +{ + const CRhinoDoc* pDoc = CRhinoDoc::FromRuntimeSerialNumber(m_idDoc); + if (NULL != pDoc) + { + return pDoc->LookupObject(m_idObject); + } + return NULL; +} + +CRhinoInstanceObject* CRpcInstance::AddToDocument(CRhinoDoc& doc, const CLBPString& sName, + const ON_Xform& xform) +{ + if (NULL == m_pInstance) + return NULL; + + RPCapi::Mesh* pRpcMesh = m_pInstance->getEditMesh(); + if (NULL == pRpcMesh) + { + // const CLBPString sRpc = rpc.Name(); + // const CLBPString sMsg = L"RPC: " + sRpc + L" has no mesh. Invalid RPC.\n"; + RhinoApp().Print(_RhLocalizeString( L"RPC invalid: selected RPC has no mesh.\n", 36080)); + return NULL; + } + + const double dUnitsScale = ON::UnitScale(ON::LengthUnitSystem::Inches, doc.ModelUnits()); + ON_Xform xformUnitsScale = ON_Xform::DiagonalTransformation(dUnitsScale, dUnitsScale, dUnitsScale); + + ON_Mesh* pRhinoMesh = new ON_Mesh; + + RpcMesh2RhinoMesh(*pRpcMesh, *pRhinoMesh); + + delete pRpcMesh; + + pRhinoMesh->Transform(xformUnitsScale); + + CRhinoMeshObject* pMesh = new CRhinoMeshObject; + pMesh->SetMesh(pRhinoMesh); + + CRhinoInstanceDefinitionTable& def_table = doc.m_instance_definition_table; + + const ON_wString sDefName = UnusedInstanceDefinitionName(doc).Wide(); + + ON_InstanceDefinition idef; + idef.SetName(sDefName); + + const int iIndex = def_table.AddInstanceDefinition(idef, pMesh); + + CRhinoInstanceObject* pInstObj = doc.m_instance_definition_table.AddInstanceObject(iIndex, xform); + if (NULL != pInstObj) + { + CRhinoObjectAttributes attr = pInstObj->Attributes(); + attr.m_name = sName.Wide(); + pInstObj->ModifyAttributes(attr); + + CopyFromRpc(*pInstObj); + m_idObject = pInstObj->ModelObjectId(); + m_idDoc = doc.RuntimeSerialNumber(); + } + + return pInstObj; +} diff --git a/RpcInstance.h b/RpcInstance.h new file mode 100644 index 0000000..c418fce --- /dev/null +++ b/RpcInstance.h @@ -0,0 +1,70 @@ + +#pragma once + +class CRpcInstance : public RPCapi::ClientInstance +{ +public: + CRpcInstance(const CRhinoDoc& doc, const CLBPString& sFullPath); + CRpcInstance(const CRhinoDoc& doc, const CRhinoObject& obj); + + virtual ~CRpcInstance(); + +public: + bool IsValid(void) const; + + CLBPString FileName(void) const; + CLBPString ObjectName(void) const; + CLBPString RpcName(void) const; + + void SetId(const RPCapi::ID *id); + const RPCapi::ID* Id(void) const; + + bool Data(CLBPBuffer& buf) const; + void SetData(const CLBPBuffer& buf); + + unsigned int Document(void) const { return m_idDoc; } + + CRhinoInstanceObject* AddToDocument(CRhinoDoc& doc, const ON_3dPoint& pt); + CRhinoInstanceObject* Replace(CRhinoDoc& doc); + + bool CopyToRpc(const CRhinoObject& obj); + bool CopyFromRpc(const CRhinoObject& obj) const; + + class IEditDialogCallback + { + public: + virtual void OnRpcParameterChanged(void) = 0; + }; + + bool EditUi(HWND hWndParent, IEditDialogCallback* pCallback = NULL); + +public: + RPCapi::Instance* Instance(void); + + static bool IsValidRpc(const CLBPString& s); + +private: + // RPCapi::ClientInstance overrides + virtual RPCapi::Instance* RPCgetInstance(void); + virtual TStringArg RPCgetName(void); + virtual void RPCgetPivot(double px, double py, double pz, double &distance, double &dx, double &dy, double &dz); + virtual int RPCgetTime(void); + virtual bool RPCisSelected(void); + virtual void RPCparameterChangeNotification(bool newInstance, const TString **params, int num); + virtual void RPCselect(bool select); + +private: + void Construct(UINT idDoc, const CRhinoObject* pObject, const CLBPString& sRpcPath); + void KillEditUi(void); + + CRhinoInstanceObject* AddToDocument(CRhinoDoc& doc, const CLBPString& sName, const ON_Xform& xform); + const CRhinoObject* Object(void) const; + +private: + RPCapi::Instance* m_pInstance; + UUID m_idObject; + unsigned int m_idDoc; + mutable CLBPString m_sName; + RPCapi::InstanceInterface* m_pEditInterface; + IEditDialogCallback* m_pEditDlgCallback; +}; diff --git a/RpcMains.cpp b/RpcMains.cpp new file mode 100644 index 0000000..873ca13 --- /dev/null +++ b/RpcMains.cpp @@ -0,0 +1,204 @@ + +#include "stdafx.h" +#include "RpcMains.h" +#include "RPCApp.h" +#include "RpcCrmProvider.h" +#include "RpcRdkPlugIn.h" +#include "RpcClient.h" +#include "RpcUtilities.h" +#include "RpcEventMachine.h" +#include "RpcDragDropHandler.h" +#include "RpcDocument.h" +#include "RpcAnimationFrameRrp.h" +#include "RpcEventWatcher.h" +#include "RpcPropertiesDlg.h" +#include "RpcAddCmd.h" +#include "RpcEditCmd.h" +#include "RpcSetAnimationFrameCmd.h" + + +void CRpcMains::CreateCommands(void) +{ + static class CRpcAddCmd theRpcAddCmd; + static class CRpcEditCmd theRpcEditCmd; + static class CRpcSetAnimationFrameCmd theSetAnimationFrameCmd; +} + + +extern CRPCApp theApp; + + +CRpcMains& Mains(void) +{ + return PlugIn().Mains(); +} + + +CRpcMains::CRpcMains(const CRPCPlugIn& plug) +: m_PlugIn(plug) +{ + m_pRpcClient = NULL; + m_pRdkPlugIn = NULL; + m_pEventMachine = NULL; + m_pDragDropHandler = NULL; + m_pRpcDocument = NULL; + m_pEventWatcher = NULL; + m_pRpcPropDlg = NULL; +} + +CRpcMains::~CRpcMains(void) +{ + CleanUp(); +} + +void CRpcMains::CleanUp(void) +{ + if (NULL != m_pEventWatcher) + { + m_pEventWatcher->Enable(false); + m_pEventWatcher->UnRegister(); + delete m_pEventWatcher; + m_pEventWatcher = NULL; + } + + if (NULL != m_pRdkPlugIn) + { + m_pRdkPlugIn->Uninitialize(); + delete m_pRdkPlugIn; + m_pRdkPlugIn = NULL; + } + + if (NULL != m_pRpcClient) + { + m_pRpcClient->Uninitialize(); + delete m_pRpcClient; + m_pRpcClient = NULL; + } + + if(NULL != m_pEventMachine) + { + m_pEventMachine->EnableEvents(false); + } + + if (NULL != m_pRpcDocument) + { + delete m_pRpcDocument; + m_pRpcDocument = NULL; + } + + if (NULL != m_pRpcPropDlg) + { + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + delete m_pRpcPropDlg; + m_pRpcPropDlg = NULL; + } + + if (NULL != m_pEventMachine) + { + delete m_pEventMachine; + m_pEventMachine = NULL; + } + + if (NULL != m_pDragDropHandler) + { + delete m_pDragDropHandler; + m_pDragDropHandler = NULL; + } +} + +bool CRpcMains::Initialize(void) +{ + m_pRdkPlugIn = new CRpcRdkPlugIn; + if (!m_pRdkPlugIn->Initialize()) + { + RpcMessageBox(_RhLocalizeString( L"Failed to register RDK client.", 32814), MB_ICONERROR); + CleanUp(); + return false; + } + + RhRdkRegisteredPropertiesManager().Add(new CRpcAnimationFrameRrp); + + m_pRpcClient = new CRpcClient; + if (!m_pRpcClient->Initialize()) + { + RpcMessageBox(_RhLocalizeString( L"Failed to register RPC client.", 32815), MB_ICONERROR); + CleanUp(); + return false; + } + + EventWatcher(); + EventMachine(); + RpcDocument(); + + m_pDragDropHandler = new CRpcDragDropHandler; + m_pDragDropHandler->EnableRhinoDropTarget(true); + m_pDragDropHandler->EnableAllowDropOnObject(false); + m_pDragDropHandler->EnableAllowDropOnSubObject(false); + m_pDragDropHandler->EnableAllowDropOnRhinoView(true); + m_pDragDropHandler->EnableAllowDropOnRhinoLayerListControl(false); + m_pDragDropHandler->EnableDeselectAllOnDrag(false); + + CreateCommands(); + + return true; +} + +void CRpcMains::Uninitialize(void) +{ + CleanUp(); +} + +const CRhRdkPlugIn& CRpcMains::RdkPlugIn() +{ + return *m_pRdkPlugIn; +} + +CRpcClient& CRpcMains::RpcClient(void) +{ + return *m_pRpcClient; +} + +CRpcEventMachine& CRpcMains::EventMachine(void) const +{ + if (NULL == m_pEventMachine) + { + m_pEventMachine = new CRpcEventMachine; + m_pEventMachine->EnableEvents(true); + } + + return *m_pEventMachine; +} + +CRpcEventWatcher& CRpcMains::EventWatcher(void) const +{ + if (NULL == m_pEventWatcher) + { + m_pEventWatcher = new CRpcEventWatcher; + m_pEventWatcher->Register(); + m_pEventWatcher->Enable(); + } + + return *m_pEventWatcher; +} + +CRpcDocument& CRpcMains::RpcDocument(void) +{ + if (NULL == m_pRpcDocument) + { + m_pRpcDocument = new CRpcDocument; + } + + return *m_pRpcDocument; +} + +CRpcPropertiesDlg& CRpcMains::PropertiesDlg(void) +{ + AFX_MANAGE_STATE(AfxGetStaticModuleState()); + + if (NULL == m_pRpcPropDlg) + { + m_pRpcPropDlg = new CRpcPropertiesDlg; + } + + return *m_pRpcPropDlg; +} diff --git a/RpcMains.h b/RpcMains.h new file mode 100644 index 0000000..edc08c2 --- /dev/null +++ b/RpcMains.h @@ -0,0 +1,48 @@ +#pragma once + + +class CRpcRdkPlugIn; +class CRpcClient; +class CRpcEventMachine; +class CRpcDragDropHandler; +class CRpcDocument; +class CRpcEventWatcher; +class CRPCPlugIn; +class CRpcPropertiesDlg; + + +class CRpcMains +{ +public: + CRpcMains(const CRPCPlugIn& plug); + virtual ~CRpcMains(void); + +public: + bool Initialize(void); + void Uninitialize(void); + +public: + const CRhRdkPlugIn& RdkPlugIn(void); + CRpcClient& RpcClient(void); + CRpcEventMachine& EventMachine(void) const; + CRpcDocument& RpcDocument(void); + CRpcEventWatcher& EventWatcher(void) const; + CRpcPropertiesDlg& PropertiesDlg(void); + +private: + void CleanUp(void); + void CreateCommands(void); + +private: + CRpcRdkPlugIn* m_pRdkPlugIn; + CRpcClient* m_pRpcClient; + CRpcDragDropHandler* m_pDragDropHandler; + CRpcDocument* m_pRpcDocument; + mutable CRpcEventMachine* m_pEventMachine; + mutable CRpcEventWatcher* m_pEventWatcher; + const CRPCPlugIn& m_PlugIn; + CRpcPropertiesDlg* m_pRpcPropDlg; +}; + + +CRpcMains& Mains(void); diff --git a/RpcObject.cpp b/RpcObject.cpp new file mode 100644 index 0000000..d7aba64 --- /dev/null +++ b/RpcObject.cpp @@ -0,0 +1,118 @@ +#include "StdAfx.h" +#include "RpcObject.h" +#include "RpcObjectUserDataWrapper.h" + + +CLBPRhWrapperMap g_mapRpcObject; + +CRpcObject::CRpcObject(const CRhinoObject * pRhinoObject) +{ + Construct(pRhinoObject); +} + +CRpcObject::~CRpcObject(void) +{ + if (NULL != m_pWrapperDirect) + { + delete m_pWrapperDirect->OriginalObject(); + } + + delete m_pWrapper; + delete m_pWrapperDirect; + delete m_pDefaultObject; +} + +void CRpcObject::Construct(const CRhinoObject* pObject) +{ + m_pWrapperDirect = NULL; + + if (NULL == pObject) + { + m_pDefaultObject = new CRhinoPointObject; + m_pWrapper = new CRpcObjectUserDataWrapperRef(g_mapRpcObject, m_pDefaultObject, lwt_attributes); + } + else + { + m_pDefaultObject = NULL; + m_pWrapper = new CRpcObjectUserDataWrapperRef(g_mapRpcObject, pObject, lwt_attributes); + } +} + +CRpcObjectUserDataWrapper& CRpcObject::Wrapper() +{ + if(NULL != m_pWrapperDirect) + { + return *m_pWrapperDirect; + } + + return m_pWrapper->Wrapper(); +} + +const CRpcObjectUserDataWrapper& CRpcObject::Wrapper() const +{ + if(NULL != m_pWrapperDirect) + { + return *m_pWrapperDirect; + } + + return m_pWrapper->Wrapper(); +} + +UUID CRpcObject::ObjectId(void) const +{ + return Wrapper().OriginalUuid(); +} + +bool CRpcObject::IsValid() const +{ + return (Wrapper().OriginalObject() == NULL) ? false : true; +} + +bool CRpcObject::Tag(const CLBPBuffer& buf) +{ + if (IsTagged()) + { + Untag(); + } + + CRpcObjectUserData* pData = Wrapper().UserDataToModify(); + if (NULL == pData) return false; + + pData->SetRpcData(buf); + + return true; +} + +bool CRpcObject::Untag(void) +{ + return Wrapper().RemoveData(); +} + +bool CRpcObject::IsTagged(void) const +{ + return Wrapper().UserDataPresent(); +} + +bool CRpcObject::RpcData(CLBPBuffer& buf) const +{ + if (!IsTagged()) + return false; + + const CRpcObjectUserData* pData = Wrapper().UserData(); + if (NULL == pData) return false; + + pData->RpcData(buf); + + return true; +} + +void CRpcObject::SetRpcData(const CLBPBuffer& buf) +{ + if (!IsTagged()) + return; + + CRpcObjectUserData* pData = Wrapper().UserDataToModify(); + if (NULL == pData) return; + + pData->SetRpcData(buf); +} diff --git a/RpcObject.h b/RpcObject.h new file mode 100644 index 0000000..1ba5544 --- /dev/null +++ b/RpcObject.h @@ -0,0 +1,40 @@ +#pragma once + + +class CRpcObjectUserDataWrapper; +typedef CLBPRhObjectWrapperRef CRpcObjectUserDataWrapperRef; + + +class CRpcObject +{ +public: + CRpcObject(const CRhinoObject * pRhinoObject); + virtual ~CRpcObject(void); + +public: + UUID ObjectId(void) const; + + bool IsValid(void) const; + + bool Tag(const CLBPBuffer& buf); + bool Untag(void); + bool IsTagged(void) const; + + bool RpcData(CLBPBuffer& buf) const; + void SetRpcData(const CLBPBuffer& buf); + +private: + void Construct(const CRhinoObject* pObject); + + CRpcObjectUserDataWrapper& Wrapper(); + const CRpcObjectUserDataWrapper& Wrapper() const; + +private: + CRpcObject(const CRpcObject& src); + const CRpcObject& operator=(const CRpcObject& src); + +private: + CRpcObjectUserDataWrapper* m_pWrapperDirect; + CRpcObjectUserDataWrapperRef* m_pWrapper; + CRhinoPointObject* m_pDefaultObject; +}; diff --git a/RpcObjectStorageStrings.h b/RpcObjectStorageStrings.h new file mode 100644 index 0000000..8cd0f59 --- /dev/null +++ b/RpcObjectStorageStrings.h @@ -0,0 +1,12 @@ +#pragma once + +#define SLASH L"/" + +#define RPC_DATA L"rpc_data" + + #define RPC_VERSION L"version" + + #define RPC_PROPERTIES L"properties" + + #define RPC_NAME L"rpc_name" + #define RPC_DATA L"rpc_data" \ No newline at end of file diff --git a/RpcObjectUserData.cpp b/RpcObjectUserData.cpp new file mode 100644 index 0000000..8b18216 --- /dev/null +++ b/RpcObjectUserData.cpp @@ -0,0 +1,156 @@ +#include "StdAfx.h" +#include "RpcObjectUserData.h" +#include "RPCPlugIn.h" +#include "RpcObjectStorageStrings.h" +#include "RpcUtilities.h" + +ON_OBJECT_IMPLEMENT(CRpcObjectUserData, ON_UserData, "F3E7353B-3C7F-45bc-9F71-32025D6E6C4A"); + + +UUID CRpcObjectUserData::Id() +{ + // {F3E7353B-3C7F-45bc-9F71-32025D6E6C4A} + static const GUID id = + { 0xf3e7353b, 0x3c7f, 0x45bc, { 0x9f, 0x71, 0x32, 0x2, 0x5d, 0x6e, 0x6c, 0x4a } }; + return id; +} + +CRpcObjectUserData::CRpcObjectUserData(void) +{ + m_userdata_uuid = Id(); + m_application_uuid = PlugIn().PlugInID(); + m_userdata_copycount = 1; +} + +CRpcObjectUserData::CRpcObjectUserData(const CRpcObjectUserData& ud) +: CLBPRh_XMLUserData(ud) +{ + m_userdata_uuid = Id(); + m_application_uuid = PlugIn().PlugInID(); + *this = ud; +} + +CRpcObjectUserData::~CRpcObjectUserData(void) +{ + +} + +const CRpcObjectUserData& CRpcObjectUserData::operator = (const CRpcObjectUserData& ud) +{ + if(this != &ud) + { + CLBPRh_XMLUserData::operator = (ud); + } + + return *this; +} + +bool CRpcObjectUserData::operator==(const CRpcObjectUserData& src) +{ + if(this == &src) + return true; + + return (XMLRoot().CRC() == src.XMLRoot().CRC()) ? true : false; +} + +bool CRpcObjectUserData::operator!=(const CRpcObjectUserData& src) +{ + return (*this == src) ? false : true; +} + +int CRpcObjectUserData::VersionNumber() const +{ + return RPC_USER_DATA_VERSION; +} + +void CRpcObjectUserData::SetToDefaults(void) const +{ + Clear(); + + CLBP_XMLProperty prop; + prop.SetName(L""); + + // Main + CLBP_XMLNode* pNodeMain = XMLRoot().AddChildNode(new CLBP_XMLNode(RPC_DATA)); + + // Version + CLBP_XMLNode* pNodeVersion = pNodeMain->AddChildNode(new CLBP_XMLNode(RPC_VERSION)); + prop.SetValue(Version()); + pNodeVersion->AddProperty(&prop); + + // Properties + CLBP_XMLNode* pNodeProperties = pNodeMain->AddChildNode(new CLBP_XMLNode(RPC_PROPERTIES)); + + CLBP_XMLNode* pNode = pNodeProperties->AddChildNode(new CLBP_XMLNode(RPC_DATA)); + prop.SetValue(CLBPBuffer()); + pNode->AddProperty(&prop); +} + +void CRpcObjectUserData::ReportVersionError(void) const +{ + const CLBPString sMsg = _RhLocalizeString( L"This model was created with a more recent version of the RPC plugin.\nPlease upgrade your version of the RPC plugin.", 36081); + ::MessageBox(RhinoApp().MainWnd(), sMsg, L"RPC", MB_OK | MB_ICONINFORMATION); +} + +bool CRpcObjectUserData::Archive(void) const +{ + return true; +} + +bool CRpcObjectUserData::Write(ON_BinaryArchive& archive) const +{ + if(!archive.WriteInt(VersionNumber())) + return false; + + return CLBPRh_XMLUserData::Write(archive); +} + +bool CRpcObjectUserData::Read(ON_BinaryArchive& archive) +{ + int iVersion = 0; + if(!archive.ReadInt(&iVersion)) + return false; + + if(iVersion > VersionNumber()) + { + return false; + } + + SetToDefaults(); + + return CLBPRh_XMLUserData::Read(archive); +} + +void CRpcObjectUserData::RpcData(CLBPBuffer& buf) const +{ + CLBP_XMLNode* pNodeMain = XMLRoot().GetNamedChild(RPC_DATA); + ASSERT(NULL != pNodeMain); + + CLBP_XMLNode* pNodeProperties = pNodeMain->GetNamedChild(RPC_PROPERTIES); + ASSERT(NULL != pNodeProperties); + + CLBP_XMLNode* pNode = pNodeProperties->GetNamedChild(RPC_DATA); + ASSERT(NULL != pNode); + + CLBP_XMLProperty* pProp = pNode->GetNamedProperty(L""); + ASSERT(NULL != pProp); + + buf = pProp->GetValue().AsBuffer(); +} + +void CRpcObjectUserData::SetRpcData(const CLBPBuffer& buf) +{ + CLBP_XMLNode* pNodeMain = XMLRoot().GetNamedChild(RPC_DATA); + ASSERT(NULL != pNodeMain); + + CLBP_XMLNode* pNodeProperties = pNodeMain->GetNamedChild(RPC_PROPERTIES); + ASSERT(NULL != pNodeProperties); + + CLBP_XMLNode* pNode = pNodeProperties->GetNamedChild(RPC_DATA); + ASSERT(NULL != pNode); + + CLBP_XMLProperty* pProp = pNode->GetNamedProperty(L""); + ASSERT(NULL != pProp); + + return pProp->SetValue(buf); +} diff --git a/RpcObjectUserData.h b/RpcObjectUserData.h new file mode 100644 index 0000000..47fc9ca --- /dev/null +++ b/RpcObjectUserData.h @@ -0,0 +1,36 @@ +#pragma once + + +class CRpcObjectUserData : public CLBPRh_XMLUserData +{ +private: + ON_OBJECT_DECLARE(CRpcObjectUserData); + +public: + CRpcObjectUserData(void); + CRpcObjectUserData(const CRpcObjectUserData& ud); + virtual ~CRpcObjectUserData(void); + + const CRpcObjectUserData& operator = (const CRpcObjectUserData& ud); + +public: + bool operator==(const CRpcObjectUserData& src); + bool operator!=(const CRpcObjectUserData& src); + +public: + static UUID Id(void); + int VersionNumber() const; + +public: + void RpcData(CLBPBuffer& buf) const; + void SetRpcData(const CLBPBuffer& buf); + +private: + // CLBPRh_XMLUserData overrides + virtual void SetToDefaults() const; + virtual void ReportVersionError(void) const; + + bool Archive(void) const override; + bool Write(ON_BinaryArchive&) const override; + bool Read(ON_BinaryArchive&) override; +}; diff --git a/RpcObjectUserDataWrapper.cpp b/RpcObjectUserDataWrapper.cpp new file mode 100644 index 0000000..12df9cb --- /dev/null +++ b/RpcObjectUserDataWrapper.cpp @@ -0,0 +1,24 @@ + +#include "StdAfx.h" +#include "RpcObjectUserDataWrapper.h" + +CRpcObjectUserDataWrapper::CRpcObjectUserDataWrapper(const CRhinoObject * pRhinoObject, lbprh_wrapper_eType type) +:CLBPRhObjectWrapper(pRhinoObject, type) +{ + +} + +CRpcObjectUserDataWrapper::~CRpcObjectUserDataWrapper() +{ + +} + +const CRpcObjectUserData* CRpcObjectUserDataWrapper::UserData(eLBPRhWrapper_DefaultUse du) const +{ + return __super::UserData(du); +} + +CRpcObjectUserData* CRpcObjectUserDataWrapper::UserDataToModify(void) +{ + return __super::UserDataToModify(); +} diff --git a/RpcObjectUserDataWrapper.h b/RpcObjectUserDataWrapper.h new file mode 100644 index 0000000..dcd6f7b --- /dev/null +++ b/RpcObjectUserDataWrapper.h @@ -0,0 +1,16 @@ +#pragma once + + +#include "RpcObjectUserData.h" + + +class CRpcObjectUserDataWrapper : public CLBPRhObjectWrapper +{ +public: + CRpcObjectUserDataWrapper(const CRhinoObject * pRhinoObject, lbprh_wrapper_eType type = lwt_attributes); + ~CRpcObjectUserDataWrapper(void); + +public: + virtual const CRpcObjectUserData* UserData(eLBPRhWrapper_DefaultUse du = lbprh_dont_use_defaults) const; + virtual CRpcObjectUserData* UserDataToModify(void); +}; diff --git a/RpcPropertiesDlg.cpp b/RpcPropertiesDlg.cpp new file mode 100644 index 0000000..dca2040 --- /dev/null +++ b/RpcPropertiesDlg.cpp @@ -0,0 +1,283 @@ + +#include "stdafx.h" +#include "RpcPropertiesDlg.h" +#include "RpcObject.h" +#include "RpcInstance.h" +#include "RpcClient.h" +#include "RPCPlugIn.h" +#include "RpcUtilities.h" +#include "LBPRhObjectSelection.h" + +#define RPC_PARAM_CHANGED 005 +#define UPDATE_UI 006 + +#pragma warning(disable : 4355) // 'this' used in base member initialization list. + +CRpcPropertiesDlg::CRpcPropertiesDlg(CWnd*) + : + m_Resize(this), + TRhinoPropertiesPanelPage(IDD, IDI_PROP_RPC, false) +{ + m_pRpcInstance = nullptr; + m_iRpcParamChangedTimer = 0; + m_iUpdateUiTimer = 0; + m_bSelectionChangeByUi = false; + m_rcRpcUiWnd.SetRectEmpty(); +} + +void CRpcPropertiesDlg::KillUI(void) +{ + delete m_pRpcInstance; + m_pRpcInstance = nullptr; + + m_btMassEditButton.ShowWindow(SW_HIDE); + + m_rcRpcUiWnd.SetRectEmpty(); +} + +void CRpcPropertiesDlg::OnDestroy() +{ + Enable(false); + UnRegister(); + KillUI(); + __base_class::OnDestroy(); +} + +const wchar_t* CRpcPropertiesDlg::EnglishTitle() const +{ + return L"RPC"; +} + +const wchar_t* CRpcPropertiesDlg::LocalTitle() const +{ + return _RhLocalizeString( L"RPC", 35155); +} + +BEGIN_MESSAGE_MAP(CRpcPropertiesDlg, __base_class) + ON_WM_DESTROY() + ON_WM_TIMER() + ON_WM_SIZE() + ON_BN_CLICKED(ID_BUTTON_EDIT, OnButtonClickedEditMass) +END_MESSAGE_MAP() + +void CRpcPropertiesDlg::DoDataExchange(CDataExchange* pDX) +{ + __base_class::DoDataExchange(pDX); + DDX_Control(pDX, ID_BUTTON_EDIT, m_btMassEditButton); +} + +BOOL CRpcPropertiesDlg::OnInitDialog() +{ + __super::OnInitDialog(); + + Register(); + Enable(); + + m_Resize.Add(&m_btMassEditButton, CRhinoUiDialogItemResizer::resize_locktopleft | CRhinoUiDialogItemResizer::resize_locktopright); + + return TRUE; +} + +void CRpcPropertiesDlg::UpdatePage(IRhinoPropertiesPanelPageEventArgs&) +{ + if (m_bSelectionChangeByUi) + return; + + if (0 == m_iUpdateUiTimer) + { + m_iUpdateUiTimer = SetTimer(UPDATE_UI, 100, NULL); + } +} + +bool CRpcPropertiesDlg::IncludeInNavigationControl(IRhinoPropertiesPanelPageEventArgs&) const +{ + CLBPRhObjectSelection sel(this, false); + if (sel.Count() < 1) + return false; + + for (auto pObject = sel.First(); nullptr != pObject; pObject = sel.Next()) + { + CRpcObject ro(pObject); + if (ro.IsTagged()) + return true; + } + + return false; +} + +CRhinoCommand::result CRpcPropertiesDlg::RunScript(IRhinoPropertiesPanelPageEventArgs&) +{ + return CRhinoCommand::success; +} + +void CRpcPropertiesDlg::UpdateParameterEditor(void) +{ + KillUI(); + + CLBPRhObjectSelection sel(this, ON::instance_reference); + if (sel.Count() < 1) + return; + + ON_SimpleArray aSelectedRpcs; + + for (auto pObject = sel.First(); nullptr != pObject; pObject = sel.Next()) + { + CRpcObject ro(pObject); + if (ro.IsTagged()) + { + aSelectedRpcs.Append(pObject); + } + } + + const int iSelected = aSelectedRpcs.Count(); + if (0 == iSelected) + { + return; + } + else + if (1 == iSelected) + { + const auto pDoc = aSelectedRpcs[0]->Document(); + if (nullptr == pDoc) + return; + + m_pRpcInstance = new CRpcInstance(*pDoc, *aSelectedRpcs[0]); + } + + CreateRpcUI((iSelected > 1) ? true : false); + + SetControlPositionAndSize(); +} + +void CRpcPropertiesDlg::OnSize(UINT nType, int cx, int cy) +{ + __super::OnSize(nType, cx, cy); + + if (!::IsWindow(GetSafeHwnd())) + return; + + SetControlPositionAndSize(); +} + +static const int iBorder = 15; + +CRect CRpcPropertiesDlg::HackRpcUiRect(void) +{ + CRect rcOut; + rcOut.SetRectEmpty(); + + HWND hWnd = ::FindWindowEx(GetSafeHwnd(), NULL, L"#32770", NULL); + CWnd* pWndUI = CWnd::FromHandle(hWnd); + if (nullptr != pWndUI) + { + CRect rc; + pWndUI->GetClientRect(rc); + + rcOut.SetRect(0, 0, rc.Width(), rc.Height()+iBorder); + } + + return rcOut; +} + +void CRpcPropertiesDlg::SetControlPositionAndSize(void) +{ + if (!m_rcRpcUiWnd.IsRectEmpty() && !m_rcRpcUiWnd.IsRectNull()) + { + CRect rcHolderUI; + GetClientRect(rcHolderUI); + + if (rcHolderUI.Height() < m_rcRpcUiWnd.Height()) + { + rcHolderUI.SetRect(0, 0, rcHolderUI.Width(), m_rcRpcUiWnd.Height()); + MoveWindow(rcHolderUI); + //RecalcStackedDialogScrollBars(); + } + } + + m_Resize.Resize(this); +} + +void CRpcPropertiesDlg::CreateRpcUI(bool bMultipleSelection) +{ + if (bMultipleSelection) + { + m_btMassEditButton.ShowWindow(SW_SHOW); + } + else + { + if (m_pRpcInstance->EditUi(GetSafeHwnd(), this)) + m_rcRpcUiWnd = HackRpcUiRect(); + } +} + +void CRpcPropertiesDlg::OnTimer(UINT_PTR nIDEvent) +{ + if (RPC_PARAM_CHANGED == nIDEvent) + { + KillTimer(m_iRpcParamChangedTimer); + m_iRpcParamChangedTimer = 0; + } + else + if (UPDATE_UI == nIDEvent) + { + KillTimer(m_iUpdateUiTimer); + m_iUpdateUiTimer = 0; + UpdateParameterEditor(); + } + + __super::OnTimer(nIDEvent); +} + +void CRpcPropertiesDlg::OnRpcParameterChanged() +{ + if (!IsWindow(GetSafeHwnd())) + return; + + if (!IsWindowVisible()) + return; + + if (nullptr == m_pRpcInstance) + return; + + const auto pRhinoDoc = CRhinoDoc::FromRuntimeSerialNumber(m_pRpcInstance->Document()); + if (nullptr == pRhinoDoc) + return; + + m_bSelectionChangeByUi = true; + + CRhinoInstanceObject* pObject = m_pRpcInstance->Replace(*pRhinoDoc); + if (nullptr != pObject) + { + pObject->Select(); + pRhinoDoc->Redraw(); + } + + m_bSelectionChangeByUi = false; +} + +void CRpcPropertiesDlg::OnButtonClickedEditMass() +{ + RhinoApp().RunScript( CRhinoDoc::NullRuntimeSerialNumber, L"_RpcEdit"); + UpdateParameterEditor(); +} + +BOOL CRpcPropertiesDlg::PreTranslateMessage(MSG* pMsg) +{ + if ((nullptr != pMsg) && (WM_KEYDOWN == pMsg->message)) + { + if (VK_RETURN == pMsg->wParam) + { + ::TranslateMessage(pMsg); + ::DispatchMessage(pMsg); + + return TRUE; + } + } + + return __super::PreTranslateMessage(pMsg); +} + +void CRpcPropertiesDlg::OnBeginCommand(const CRhinoCommand& command, const CRhinoCommandContext& context) +{ + UpdateParameterEditor(); +} diff --git a/RpcPropertiesDlg.h b/RpcPropertiesDlg.h new file mode 100644 index 0000000..f7fb6a3 --- /dev/null +++ b/RpcPropertiesDlg.h @@ -0,0 +1,55 @@ + +#pragma once + +#include "resource.h" +#include "RpcEventSink.h" +#include "RpcInstance.h" + +class CRpcPropertiesDlg : public TRhinoPropertiesPanelPage, + public CRpcInstance::IEditDialogCallback, + public CRhinoEventWatcher +{ +public: + CRpcPropertiesDlg(CWnd* pParent = nullptr); + +private: + enum { IDD = IDD_EDIT}; + +private: + void SetControlPositionAndSize(void); + void UpdateParameterEditor(void); + void KillUI(void); + void CreateRpcUI(bool bMultipleSelection); + CRect HackRpcUiRect(void); + +private: + virtual void OnRpcParameterChanged(void); + +protected: + virtual BOOL OnInitDialog() override; + virtual void DoDataExchange(CDataExchange* pDX) override; + virtual BOOL PreTranslateMessage(MSG* pMsg) override; + + RHINO_PAGE_DECLARE + RHINO_PROPERTIES_PANEL_PAGE_DECLARE + + virtual int Index(void) const override { return 7176; } + virtual RhinoPropertiesPanelPageType PageType() const override { return RhinoPropertiesPanelPageType::Custom; } + virtual void OnBeginCommand(const CRhinoCommand& command, const CRhinoCommandContext& context) override; + +protected: + DECLARE_MESSAGE_MAP() + afx_msg void OnTimer(UINT_PTR nIDEvent); + afx_msg void OnDestroy(); + afx_msg void OnButtonClickedEditMass(); + afx_msg void OnSize(UINT nType, int cx, int cy); + +private: + CRpcInstance* m_pRpcInstance; + UINT_PTR m_iRpcParamChangedTimer; + UINT_PTR m_iUpdateUiTimer; + CButton m_btMassEditButton; + CRhinoUiDialogItemResizer m_Resize; + bool m_bSelectionChangeByUi; + CRect m_rcRpcUiWnd; +}; diff --git a/RpcRdkPlugIn.cpp b/RpcRdkPlugIn.cpp new file mode 100644 index 0000000..3211bde --- /dev/null +++ b/RpcRdkPlugIn.cpp @@ -0,0 +1,59 @@ +#include "StdAfx.h" +#include "RpcRdkPlugIn.h" +#include "RpcCrmProvider.h" +#include "RpcAnimationFrameRrp.h" + + +CRpcRdkPlugIn::CRpcRdkPlugIn(void) +{ +} + +CRpcRdkPlugIn::~CRpcRdkPlugIn(void) +{ +} + +void CRpcRdkPlugIn::RegisterExtensions(void) const +{ + AddExtension(new CRpcCrmProvider); +} + +UUID CRpcRdkPlugIn::PlugInId(void) const +{ + return PlugIn().PlugInID(); +} + +CRhinoPlugIn& CRpcRdkPlugIn::RhinoPlugIn(void) const +{ + return PlugIn(); +} + +bool CRpcRdkPlugIn::SupportsFeature(const UUID&) const +{ + return false; +} + +/*void CRpcRdkPlugIn::PlugInRdkVersion(int& iMajorVersion, int& iMinorVersion, int& iBetaRelease) const +{ + iMajorVersion = RDK_MAJOR_VERSION; + iMinorVersion = RDK_MINOR_VERSION; + iBetaRelease = RDK_BETA_RELEASE; +}*/ + +void CRpcRdkPlugIn::OnAnimationFrameChanged(CRhinoDoc& doc, int iOldFrame, int iNewFrame) +{ + static bool bWorking = false; + + if (bWorking) + return; + + IRhRdkRegisteredProperty* pProp = RhRdkRegisteredPropertiesManager().FindProperty(idAF); + ASSERT(NULL != pProp); + if (NULL == pProp) return; + + bWorking = true; + + CRpcAnimationFrameRrp* pRRP = static_cast(pProp); + pRRP->CallPropertyChangedEvent(doc, iOldFrame); + + bWorking = false; +} diff --git a/RpcRdkPlugIn.h b/RpcRdkPlugIn.h new file mode 100644 index 0000000..7246b0c --- /dev/null +++ b/RpcRdkPlugIn.h @@ -0,0 +1,20 @@ + +#pragma once + +#include "RpcEventSink.h" + +class CRpcRdkPlugIn : public CRhRdkPlugIn, public CRpcEventSink +{ +public: + CRpcRdkPlugIn(); + virtual ~CRpcRdkPlugIn(); + +public: + virtual void RegisterExtensions(void) const override; + virtual UUID PlugInId(void) const override; + virtual CRhinoPlugIn& RhinoPlugIn(void) const override; + virtual bool SupportsFeature(const UUID&) const; + +public: + virtual void OnAnimationFrameChanged(CRhinoDoc& doc, int iOldFrame, int iNewFrame) override; +}; diff --git a/RpcRenderMeshBuilder.cpp b/RpcRenderMeshBuilder.cpp new file mode 100644 index 0000000..2276b37 --- /dev/null +++ b/RpcRenderMeshBuilder.cpp @@ -0,0 +1,448 @@ + +#include "StdAfx.h" +#include "RpcRenderMeshBuilder.h" +#include "RpcUtilities.h" + +static class CTestRpcGamma : public CRhinoTestCommand +{ + static double m_dGamma; + + virtual const wchar_t * EnglishCommandName() { return L"TestRPCGamma"; } + virtual UUID CommandUUID() + { + // {F70A388B-7298-4506-ACFE-1FACD6106B1C} + static const GUID uuid = { 0xf70a388b, 0x7298, 0x4506, { 0xac, 0xfe, 0x1f, 0xac, 0xd6, 0x10, 0x6b, 0x1c } }; + return uuid; + } + + virtual CRhinoCommand::result RunCommand(const CRhinoCommandContext& context) + { + CRhinoGetNumber gi; + gi.SetLowerLimit(0.0); + gi.SetUpperLimit(5.0); + gi.SetDefaultNumber(m_dGamma); + + gi.SetCommandPrompt(L"RPC texture gamma:"); + if (CRhinoGet::number != gi.GetNumber()) + return cancel; + + m_dGamma = gi.Number(); + + return success; + } + + friend class CRpcRenderMeshBuilder; +} +theTestRPCCommand; + +double CTestRpcGamma::m_dGamma = 1.0; + +CRpcRenderMeshBuilder::CRpcRenderMeshBuilder(const CRhinoDoc& doc, const RPCapi::Instance& rpc) +: m_Rpc(rpc), + m_doc(doc) +{ +} + +CRpcRenderMeshBuilder::~CRpcRenderMeshBuilder(void) +{ +} + +void CRpcRenderMeshBuilder::Flush(ON_SimpleArray& aMeshes, + ON_SimpleArray& aMaterials) +{ + for(int i=0; i& aMeshes, + ON_SimpleArray& aMaterials) +{ + const RPCapi::Mesh* pRpcMesh = m_Rpc.getMesh(NULL, ptCamera.x, ptCamera.y, ptCamera.z); + if (NULL == pRpcMesh) return false; + + const double dUnitsScale = ON::UnitScale(ON::LengthUnitSystem::Inches, m_doc.ModelUnits()); + ON_Xform xformUnitsScale = ON_Xform::DiagonalTransformation(dUnitsScale, dUnitsScale, dUnitsScale); + + RPCapi::Texture** Texture = NULL; + RPCapi::TextureMesh* pTextureMesh = NULL; + int iTextures = 0; + + if (!m_Rpc.getTextures(ptCamera.x, ptCamera.y, ptCamera.z, iTextures, Texture, pTextureMesh)) + { + ON_Mesh* pRhinoMesh = new ON_Mesh; + RpcMesh2RhinoMesh(*pRpcMesh, *pRhinoMesh); + + pRhinoMesh->Transform(xformUnitsScale); + + aMeshes.Append(pRhinoMesh); + aMaterials.Append(NULL); + + delete pRpcMesh; + + return true; + } + + ON_SimpleArray aTextures; + + for (int i=0; iTransform(xformUnitsScale); + } + + if (NULL != Texture) + { + for (int i=0; i& map, + ON_SimpleArray& sourceVertexIndexList, + //ON_SimpleMap& tc_map, + ON_MeshFace& on_face, + ON_Mesh& on_mesh, + const RPCapi::Mesh::Vertex* pVerts, + const RPCapi::TextureMesh::Vertex* pTextureVerts + ) +{ + int iONMeshVertexIndex = -1; + on_face.vi[i] = -1; + + if (map.Lookup(iMeshFaceVertexIndex, iONMeshVertexIndex)) + { + //ASSERT(tc_map.Lookup(iTextureFaceVertexIndex, iONMeshTCIndex))); + //ASSERT(iONMeshVertexIndex == iONMeshTCIndex); + + const ON_2fPoint& t = on_mesh.m_T[iONMeshVertexIndex]; + const RPCapi::TextureMesh::Vertex& tv = pTextureVerts[iTextureFaceVertexIndex]; + + if (LBPIsFloatEqual(t.x,(float)tv.x) && LBPIsFloatEqual(t.y,(float)tv.y)) + { + on_face.vi[i] = iONMeshVertexIndex; + } + } + + if (on_face.vi[i] == -1) + { + const int iIndex = on_mesh.m_V.Count(); + on_face.vi[i] = iIndex; + + ON_3fPoint& v = on_mesh.m_V.AppendNew(); + v.x = float(pVerts[iMeshFaceVertexIndex].x); + v.y = float(pVerts[iMeshFaceVertexIndex].y); + v.z = float(pVerts[iMeshFaceVertexIndex].z); + + ON_2fPoint& t = on_mesh.m_T.AppendNew(); + t.x = float(pTextureVerts[iTextureFaceVertexIndex].x); + t.y = float(pTextureVerts[iTextureFaceVertexIndex].y); + + map.SetAt(iMeshFaceVertexIndex, iIndex); + + sourceVertexIndexList.Append(iMeshFaceVertexIndex); + } +} + +template +static bool IsMeshFaceValid(const FACE& face) +{ + return face.v0 != face.v1 && face.v1 != face.v2 && face.v2 != face.v0; +} + +static bool IsDegenerateTriangle(const ON_Mesh& mesh, const ON_MeshFace& face) +{ + if (face.IsQuad()) return true; + + if (!face.IsValid(mesh.m_V.Count())) + return true; + + const float fSmall = 0.000001f; + + if ((mesh.m_V[face.vi[0] ] - mesh.m_V[face.vi[1] ]).LengthSquared() < fSmall) + return true; + + if ((mesh.m_V[face.vi[1] ] - mesh.m_V[face.vi[2] ]).LengthSquared() < fSmall) + return true; + + if ((mesh.m_V[face.vi[2] ] - mesh.m_V[face.vi[0] ]).LengthSquared() < fSmall) + return true; + + return false; +} + +void CRpcRenderMeshBuilder::RpcMesh2RhinoMeshes(const RPCapi::Mesh& RpcMesh, + const RPCapi::TextureMesh& RpcTextureMesh, + ON_SimpleArray& aMeshes) +{ + ON_SimpleMap* pMaps = new ON_SimpleMap[aMeshes.Count()]; + const int iFaceCount = RpcMesh.getNumFaces(); + const RPCapi::Mesh::Face* pFaces = RpcMesh.getConstFaces(); + + ON_SimpleArray* pSourceVertexIndexLists = new ON_SimpleArray [aMeshes.Count()]; + + const int iVertexCount = RpcMesh.getNumVerts(); + const RPCapi::Mesh::Vertex* pVerts = RpcMesh.getConstVertices(); + + const RPCapi::TextureMesh::Face* pTectureFaces = RpcTextureMesh.getConstFaces(); + const int iTextureFaceCount = RpcTextureMesh.getNumFaces(); + + const RPCapi::TextureMesh::Vertex* pTextureVerts = RpcTextureMesh.getConstVertices(); + + ASSERT(iFaceCount == iTextureFaceCount); + int iBadFaces = 0; + + for (int i=0; iu.textureIndex; + + const RPCapi::Mesh::Face* pFace = pFaces+i; + + if (IsMeshFaceValid(*pFace) && IsMeshFaceValid(*pTextureFace)) + { + ON_Mesh* pRhinoMesh = aMeshes[iTextureIndex]; + ON_SimpleMap& map = pMaps[iTextureIndex]; + + ON_SimpleArray & sourceVertexIndexList = pSourceVertexIndexLists[iTextureIndex]; + + ON_MeshFace face;// = pRhinoMesh->m_F.AppendNew(); + + const int iVertexCount = pRhinoMesh->m_V.Count(); + + AddVertex(0, pFace->v0, pTextureFace->v0, map, sourceVertexIndexList, face, *pRhinoMesh, pVerts, pTextureVerts); + AddVertex(1, pFace->v1, pTextureFace->v1, map, sourceVertexIndexList, face, *pRhinoMesh, pVerts, pTextureVerts); + AddVertex(2, pFace->v2, pTextureFace->v2, map, sourceVertexIndexList, face, *pRhinoMesh, pVerts, pTextureVerts); + face.vi[3] = face.vi[2]; + + if (!IsDegenerateTriangle(*pRhinoMesh, face)) + { + pRhinoMesh->m_F.Append(face); + ASSERT(pRhinoMesh->IsValid()); + } + else + { + iBadFaces++; + pRhinoMesh->m_V.SetCount(iVertexCount); + pRhinoMesh->m_T.SetCount(iVertexCount); + } + } + else + { + iBadFaces++; + } + } + + if (iBadFaces > 0) + { + RhinoApp().Print(_RhLocalizeString( L"Bad face count = %d\n", 36082), iBadFaces); + } + + delete [] pMaps; + + for(int i=0; iComputeVertexNormals(); + if (aMeshes[i]->m_N.Count() == aMeshes[i]->m_V.Count()) + { + UniteVertexNormals(*aMeshes[i], pSourceVertexIndexLists[i], iVertexCount); + } + } + + delete [] pSourceVertexIndexLists; +} + +bool CRpcRenderMeshBuilder::Rgb2Material(RPCapi::Texture& RpcTexture, CRhRdkBasicMaterial& Material) +{ + int iWidth = 0; + int iHeight = 0; + int iBytes; + unsigned char* pRGB = NULL; + + iBytes = RpcTexture.data(pRGB, false, RPCapi::Texture::Channel::RGB, RPCapi::Texture::Scale::SLOW, iWidth, iHeight); + + if (iBytes > 0) + { + pRGB = new BYTE[iBytes]; + + VERIFY(iBytes = RpcTexture.data(pRGB, false, RPCapi::Texture::Channel::RGB, RPCapi::Texture::Scale::SLOW, iWidth, iHeight)); + } + else + { + return false; + } + + BYTE* rgb = pRGB; + + CRhinoDib rdRGB(iWidth, iHeight, 32); + + for(int y=0; ySetAdjustmentGamma(CTestRpcGamma::m_dGamma); + } + + CRhRdkBasicMaterial::CTextureSlot slot = Material.TextureSlot(CRhRdkMaterial::ChildSlotUsage::Diffuse); + slot.m_bOn = true; + slot.m_dAmount = 1.0; + slot.m_bFilterOn = true; + Material.SetTextureSlot(CRhRdkMaterial::ChildSlotUsage::Diffuse, slot); + + VERIFY(Material.SetChild(pRdkTexture, RDK_BASIC_MAT_BITMAP_TEXTURE)); + + delete[] pRGB; + + return true; +} + +bool CRpcRenderMeshBuilder::Alpha2Material(RPCapi::Texture& RpcTexture, CRhRdkBasicMaterial& Material) +{ + int iAlphaWidth = 0; + int iAlphaHeight = 0; + int iAlphaBytes; + unsigned char* pAlpha = NULL; + + iAlphaBytes = RpcTexture.data(pAlpha, false, RPCapi::Texture::Channel::ALPHA,RPCapi::Texture::Scale::SLOW, iAlphaWidth, iAlphaHeight); + + if (iAlphaBytes > 0) + { + pAlpha = new BYTE[iAlphaBytes]; + VERIFY(iAlphaBytes == RpcTexture.data(pAlpha, false, RPCapi::Texture::Channel::ALPHA,RPCapi::Texture::Scale::SLOW, iAlphaWidth, iAlphaHeight)); + } + else + { + return false; + } + + BYTE* alp = pAlpha; + + CRhinoDib rdAlpha(iAlphaWidth, iAlphaHeight, 32); + + for(int y=0; y& aTextures, + ON_SimpleArray& aMaterials) +{ + for(int i=0; ihasChannel(RPCapi::Texture::Channel::RGB)) + { + pRdkMaterial = CreateNewBasicMaterial(); + if (NULL == pRdkMaterial) continue; + + pRdkMaterial->SetInstanceName(L"RpcSpecialMaterial"); + + if (!Rgb2Material(*pRpcTexture, *pRdkMaterial)) + { + pRdkMaterial->Uninitialize(); + delete pRdkMaterial; + pRdkMaterial = NULL; + } + } + + if (pRpcTexture->hasChannel(RPCapi::Texture::Channel::ALPHA)) + { + bool bRgbChannel; + + if (NULL == pRdkMaterial) + { + pRdkMaterial = CreateNewBasicMaterial(); + if (NULL == pRdkMaterial) continue; + + pRdkMaterial->SetInstanceName(L"RpcSpecialMaterial"); + bRgbChannel = false; + } + else + { + bRgbChannel = true; + } + + if (!Alpha2Material(*pRpcTexture, *pRdkMaterial)) + { + if (!bRgbChannel) + { + pRdkMaterial->Uninitialize(); + delete pRdkMaterial; + pRdkMaterial = NULL; + } + } + } + + aMaterials.Append(pRdkMaterial); + } +} diff --git a/RpcRenderMeshBuilder.h b/RpcRenderMeshBuilder.h new file mode 100644 index 0000000..7c3de95 --- /dev/null +++ b/RpcRenderMeshBuilder.h @@ -0,0 +1,27 @@ +#pragma once + + +class CRpcRenderMeshBuilder +{ +public: + CRpcRenderMeshBuilder(const CRhinoDoc& doc, const RPCapi::Instance& rpc); + ~CRpcRenderMeshBuilder(void); + +public: + bool Build(const ON_3dPoint& ptCamera, ON_SimpleArray& aMeshes, + ON_SimpleArray& aMaterials); + +private: + void Flush(ON_SimpleArray& aMeshes, ON_SimpleArray& aMaterials); + + void RpcMesh2RhinoMeshes(const RPCapi::Mesh& RpcMesh, const RPCapi::TextureMesh& RpcTextureMesh, ON_SimpleArray& aMeshes); + + void RpcTexture2RhinoMaterial(const ON_SimpleArray& aTextures, ON_SimpleArray& aMaterials); + + bool Rgb2Material(RPCapi::Texture& RpcTexture, CRhRdkBasicMaterial& Material); + bool Alpha2Material(RPCapi::Texture& RpcTexture, CRhRdkBasicMaterial& Material); + +private: + const RPCapi::Instance& m_Rpc; + const CRhinoDoc& m_doc; +}; diff --git a/RpcSelectDlg.cpp b/RpcSelectDlg.cpp new file mode 100644 index 0000000..526e76d --- /dev/null +++ b/RpcSelectDlg.cpp @@ -0,0 +1,57 @@ + +#include "StdAfx.h" +#include "RpcSelectDlg.h" +#include "Resource.h" +#include "RpcInstance.h" +#include "RpcClient.h" +#include "RpcMains.h" + +CRpcSelectDlg::CRpcSelectDlg(CRhinoDoc& doc, const CLBPString& sRpc) +{ + m_pSelInterface = NULL; + m_pRpcInstance = new CRpcInstance(doc, sRpc); + m_ret = IDCANCEL; +} + +CRpcSelectDlg::~CRpcSelectDlg(void) +{ + if (nullptr != m_pSelInterface) + { + m_pSelInterface->registerCallback(this, false); + } + delete m_pSelInterface; + delete m_pRpcInstance; +} + +INT_PTR CRpcSelectDlg::DoModal(void) +{ + m_pSelInterface = dynamic_cast(Mains().RpcClient().RPCgetAPI()->newObject(RPCapiSI::SelectionInterface::OBJECT_CODE)); + if (nullptr == m_pSelInterface) return IDCLOSE; + + m_pSelInterface->registerCallback(this, true); + m_pSelInterface->idSet(m_pRpcInstance->Id()); + m_pSelInterface->show(RhinoApp().MainWnd(), RPCapiSI::SelectionInterface::THUMBS_PLUS, RPCapiSI::SelectionInterface::MODAL); + return m_ret; +} + +void CRpcSelectDlg::RPCselectionInterfaceCallbackProc(const RPCapiSI::SelectionInterface *sw, int action, va_list args) +{ + if (RPCapiSI::SelectionInterface::SIcallback::ACTION_CODE_TYPE::WINDOW_CLOSING == action) + { + const int arg = va_arg(args, int); + m_ret = (1==arg) ? IDOK : IDCANCEL; + } +} + +CLBPString CRpcSelectDlg::Selection(void) const +{ + if (NULL == m_pSelInterface) + return L""; + + const RPCapi::ID* pId = m_pSelInterface->idGet(); + if (NULL == pId) return L""; + + m_pRpcInstance->SetId(pId); + + return m_pRpcInstance->FileName(); +} diff --git a/RpcSelectDlg.h b/RpcSelectDlg.h new file mode 100644 index 0000000..9413ef3 --- /dev/null +++ b/RpcSelectDlg.h @@ -0,0 +1,24 @@ + +#pragma once + +class CRpcInstance; + +class CRpcSelectDlg : public RPCapiSI::SelectionInterface::SIcallback +{ +public: + CRpcSelectDlg(CRhinoDoc& doc, const CLBPString& sRpc); + virtual ~CRpcSelectDlg(void); + +public: + INT_PTR DoModal(void); + CLBPString Selection(void) const; + +private: + void CreateRpcUI(void); + virtual void RPCselectionInterfaceCallbackProc(const RPCapiSI::SelectionInterface *sw, int action, va_list args); + +private: + CRpcInstance* m_pRpcInstance; + RPCapiSI::SelectionInterface* m_pSelInterface; + INT_PTR m_ret; +}; diff --git a/RpcSetAnimationFrameCmd.cpp b/RpcSetAnimationFrameCmd.cpp new file mode 100644 index 0000000..8efc2f4 --- /dev/null +++ b/RpcSetAnimationFrameCmd.cpp @@ -0,0 +1,41 @@ +#include "StdAfx.h" +#include "RpcSetAnimationFrameCmd.h" +#include "RPCPlugIn.h" +#include "RpcDocument.h" +#include "RpcMains.h" +#include "RpcUtilities.h" + + +const wchar_t * CRpcSetAnimationFrameCmd::EnglishCommandName() +{ + return L"RPCSetAnimationFrame"; +} + +UUID CRpcSetAnimationFrameCmd::CommandUUID() +{ + // {DEF313CB-2713-461d-9ED9-F5E021ADC2A1} + static const GUID id = + { 0xdef313cb, 0x2713, 0x461d, { 0x9e, 0xd9, 0xf5, 0xe0, 0x21, 0xad, 0xc2, 0xa1 } }; + return id; +} + +CRhinoCommand::result CRpcSetAnimationFrameCmd::RunRpcCommand(const CRhinoCommandContext& context) +{ + const int iOldFrame = Mains().RpcDocument().AnimationFrame(); + + CRhinoGetInteger gi; + gi.SetLowerLimit(0); + gi.SetDefaultInteger(iOldFrame); + gi.SetCommandPrompt(_RhLocalizeString( L"Animation Frame", 36083)); + if (CRhinoGet::number != gi.GetInteger()) + return cancel; + + const int iNewFrame = gi.Number(); + + if (iNewFrame == iOldFrame) + return success; + + Mains().RpcDocument().SetAnimationFrame(context.m_doc, iNewFrame); + + return success; +} diff --git a/RpcSetAnimationFrameCmd.h b/RpcSetAnimationFrameCmd.h new file mode 100644 index 0000000..dea3df2 --- /dev/null +++ b/RpcSetAnimationFrameCmd.h @@ -0,0 +1,13 @@ +#pragma once + + +#include "RpcCommand.h" + + +class CRpcSetAnimationFrameCmd : public CRpcCommand +{ +public: + virtual CRhinoCommand::result RunRpcCommand(const CRhinoCommandContext& context); + virtual const wchar_t * EnglishCommandName(); + virtual UUID CommandUUID(); +}; diff --git a/RpcUtilities.cpp b/RpcUtilities.cpp new file mode 100644 index 0000000..af84cb7 --- /dev/null +++ b/RpcUtilities.cpp @@ -0,0 +1,288 @@ +#include "StdAfx.h" +#include "RpcUtilities.h" +#include "RpcInstance.h" + + +int RpcMessageBox(const CLBPString& sMessage, UINT uType) +{ + CRhRdkModalizer m; + + uType |= MB_SETFOREGROUND; + + return ::MessageBox(RhinoApp().MainWnd(), sMessage, L"RPC PlugIn", uType); +} + +void RpcMesh2RhinoMesh(const RPCapi::Mesh& RpcMesh, ON_Mesh& RhinoMesh) +{ + const int iVertexCount = RpcMesh.getNumVerts(); + + const RPCapi::Mesh::Vertex* pVerts = RpcMesh.getConstVertices(); + ASSERT(pVerts); + + RhinoMesh.m_V.SetCapacity(iVertexCount); + for (int i=0;ix), float(pVert->y), float(pVert->z)); + RhinoMesh.m_V.Append(v); + } + + const int iFaceCount = RpcMesh.getNumFaces(); + const RPCapi::Mesh::Face* pFaces = RpcMesh.getConstFaces(); + + RhinoMesh.m_F.SetCapacity(iFaceCount); + for (int i=0;iv0; + face.vi[1] = pFace->v1; + face.vi[2] = pFace->v2; + face.vi[3] = pFace->v2; + + RhinoMesh.m_F.Append(face); + } + + RhinoMesh.ComputeVertexNormals(); +} + +bool IsRpcNameInUse(const ON_ClassArray& aNames, const CLBPString& sRpcName) +{ + for(int i=0; i aRpcNames; + + CRhinoObjectIterator oi; + oi.SetObjectFilter(ON::instance_reference); + for(const CRhinoObject* pObject = oi.First(); + NULL != pObject; + pObject = oi.Next()) + { + CLBPString& s = aRpcNames.AppendNew(); + s = pObject->Attributes().m_name; + } + + int iIndex = 0; + + while(true) + { + CLBPString s; + s.Format(L"%S_%d",sRpcName.Wide(), iIndex); + + if (!IsRpcNameInUse(aRpcNames, s)) + return s; + + iIndex++; + } +} + +CLBPString UnusedInstanceDefinitionName(CRhinoDoc& doc) +{ + CRhinoInstanceDefinitionTable& def_table = doc.m_instance_definition_table; + ON_wString unused_idef_name; + def_table.GetUnusedInstanceDefinitionName(L"*_RPC_",unused_idef_name); + const CLBPString sDef = unused_idef_name; + return sDef; +} + +void DebugSaveTexturesToRoot(const RPCapi::Instance& rpc, const ON_3dPoint& ptCamera) +{ + RPCapi::Texture** Texture; + RPCapi::TextureMesh* TextureMesh; + int iTextures; + if (!rpc.getTextures(ptCamera.x, ptCamera.y, ptCamera.z, iTextures, Texture, TextureMesh)) + return; + + for(int i = 0; ihasChannel(RPCapi::Texture::Channel::RGB); + bool bAlpha = Texture[i]->hasChannel(RPCapi::Texture::Channel::ALPHA); + + int iWidth = 0; + int iHeight = 0; + int iBytes; + unsigned char* pRGB = NULL; + + if (bRgb) + { + iBytes = Texture[i]->data(pRGB, false, RPCapi::Texture::Channel::RGB, RPCapi::Texture::Scale::SLOW, iWidth, iHeight); + + if (iBytes > 0) + { + pRGB = new BYTE[iBytes]; + + VERIFY(iBytes = Texture[i]->data(pRGB, false, RPCapi::Texture::Channel::RGB, RPCapi::Texture::Scale::SLOW, iWidth, iHeight)); + } + else + { + bRgb = false; + } + } + + int iAlphaWidth = 0; + int iAlphaHeight = 0; + int iAlphaBytes; + unsigned char* pAlpha = NULL; + + if (bAlpha) + { + iAlphaBytes = Texture[i]->data(pAlpha, false, RPCapi::Texture::Channel::ALPHA,RPCapi::Texture::Scale::SLOW, iAlphaWidth, iAlphaHeight); + + if (iAlphaBytes > 0) + { + pAlpha = new BYTE[iAlphaBytes]; + VERIFY(iAlphaBytes == Texture[i]->data(pAlpha, false, RPCapi::Texture::Channel::ALPHA,RPCapi::Texture::Scale::SLOW, iAlphaWidth, iAlphaHeight)); + } + else + { + bAlpha = false; + } + } + + BYTE* rgb = pRGB; + BYTE* alp = pAlpha; + + CRhinoDib rdRGB(iWidth, iHeight, 32); + CRhinoDib rdAlpha(iWidth, iHeight, 32); + + for(int y=0; y & aSourceVertexIndexList, int iSourceVertexCount) +{ + ASSERT(mesh.QuadCount() == 0); + ASSERT(mesh.HasVertexNormals()); + + ON_3fVectorArray aSourceVertexNormals(iSourceVertexCount); + aSourceVertexNormals.SetCount(iSourceVertexCount); + aSourceVertexNormals.MemSet(0); + + for (int f = 0; f < mesh.FaceCount(); f++) + { + const ON_MeshFace & face = mesh.m_F[f]; + + const int iAlpha = face.vi[0]; + const int iBeta = face.vi[1]; + const int iGamma = face.vi[2]; + + const ON_3fVector vtA = mesh.m_V[iGamma] - mesh.m_V[iBeta]; + const double aSq = vtA.LengthSquared(); + const ON_3fVector vtB = mesh.m_V[iGamma] - mesh.m_V[iAlpha]; + const double bSq = vtB.LengthSquared(); + const ON_3fVector vtC = mesh.m_V[iBeta] - mesh.m_V[iAlpha]; + const double cSq = vtC.LengthSquared(); + const double a = sqrt(aSq); + const double b = sqrt(bSq); + const double c = sqrt(cSq); + + const double d2bc = 2.0 * b * c; + const double d2ac = 2.0 * a * c; + const double d2ab = 2.0 * a * b; + + if (d2bc <= 0.0 || d2ac <= 0.0 || d2ab <= 0.0) + continue; + + const double cosAlpha = (bSq + cSq - aSq) / d2bc; + const double cosBeta = (aSq + cSq - bSq) / d2ac; + const double cosGamma = (aSq + bSq - cSq) / d2ab; + const float alpha = (float)acos(min(1.0, max(-1.0, cosAlpha))); + const float beta = (float)acos(min(1.0, max(-1.0, cosBeta))); + const float gamma = (float)acos(min(1.0, max(-1.0, cosGamma))); + + ON_3fVector vtFaceNormal = ON_CrossProduct(vtC, vtB); + if (false == vtFaceNormal.Unitize()) + return false; + + aSourceVertexNormals[aSourceVertexIndexList[iAlpha]] += alpha * vtFaceNormal; + aSourceVertexNormals[aSourceVertexIndexList[iBeta]] += beta * vtFaceNormal; + aSourceVertexNormals[aSourceVertexIndexList[iGamma]] += gamma * vtFaceNormal; + } + + for (int n = 0; n < aSourceVertexNormals.Count(); n++) + { + if (false == aSourceVertexNormals[n].Unitize()) + aSourceVertexNormals[n].Set(0.0f, 0.0f, 0.0f); + } + + for (int v = 0; v < mesh.VertexCount(); v++) + { + const ON_3fVector & vtNormal = aSourceVertexNormals[aSourceVertexIndexList[v]]; + + if (!vtNormal.IsZero()) + mesh.m_N[v] = aSourceVertexNormals[aSourceVertexIndexList[v]]; + } + + return true; +} + +CRhRdkBasicMaterial* CreateNewBasicMaterial(void) +{ + const CRhRdkContentFactory* pFactory = RhRdkContentFactoriesEx().FindFactory(uuidBasicMaterialType); + if (NULL == pFactory) + return NULL; + + CRhRdkContent* pContent = pFactory->NewContent(); + if (NULL == pContent) + return NULL; + + if (!pContent->Initialize()) + return NULL; + + CRhRdkBasicMaterial* pMaterial = dynamic_cast(pContent); + if (NULL == pMaterial) + { + pContent->Uninitialize(); + delete pContent; + return NULL; + } + + return pMaterial; +} + +const wchar_t* _RhLocalizeString( const wchar_t* wsz, int nContext) +{ + return wsz; +} diff --git a/RpcUtilities.h b/RpcUtilities.h new file mode 100644 index 0000000..fcc42c3 --- /dev/null +++ b/RpcUtilities.h @@ -0,0 +1,20 @@ +#pragma once + + +int RpcMessageBox(const CLBPString& sMessage, UINT uType); + +void RpcMesh2RhinoMesh(const RPCapi::Mesh& RpcMesh, ON_Mesh& RhinoMesh); + +CLBPString UnusedInstanceDefinitionName(CRhinoDoc& doc); + +CLBPString UnusedRpcName(const CLBPString& sRpcName); + +void DebugSaveTexturesToRoot(const RPCapi::Instance& rpc, const ON_3dPoint& ptCamera); + +bool UniteVertexNormals(ON_Mesh & mesh, ON_SimpleArray & aSourceVertexIndexList, int iSourceVertexCount); + +CRhRdkBasicMaterial* CreateNewBasicMaterial(void); + +inline const ON_2iPoint P2P(const CPoint& p) { return ON_2iPoint(p.x, p.y);} + +const wchar_t* _RhLocalizeString( const wchar_t* wsz, int nContext); diff --git a/SDK/inc/RPCapi.h b/SDK/inc/RPCapi.h new file mode 100644 index 0000000..bb456f1 --- /dev/null +++ b/SDK/inc/RPCapi.h @@ -0,0 +1,3177 @@ +// This is the RPC interface. This header file (and its associated +// .lib and .dll files) are used by applications / plugins to +// read data from RPC files. +// The RPC API definitions are below; search for "class RPCapi" +// NOTE: Be sure to correctly define which PORT this file is being +// compiled in. This is particularly important in distinguishing +// Windows static compiles from Windows DLL imports +// NOTE: If you are using RPCapi as a static library rather than +// as a dynamic library, be sure to define RPC_STATIC_LIB +// before including RPCapi.h. + + +// ///////////////////////////////////////////////////////////////// // +// NOTE NOTE NOTE NOTE NOTE // +// // +// -To properly use the RPCapi dll, your application MUST use // +// the Multithreaded run-time DLL. // +// In Visual C++, go to Project->Settings // +// On C/C++ tab, in the Category tab box, select Code Generation. // +// In the User run-time library tab box, select Multithreaded DLL // +// or Debug Multithreaded DLL (if the latter is chosen, be sure // +// you are linking with a DEBUG version of the RPCapi dll // +// // +// -Your application must also support Run-Time Type Information // +// or (RTTI). // +// To enable this in Visual C++ go to Project->Settings // +// On C/C++ tab, in the Category tab box, select C++ Language. // +// Select Enable Run-Time Type Information(RTTI). // +// ///////////////////////////////////////////////////////////////// // + +#ifndef RPCAPI_H +#define RPCAPI_H + + +// This tells RPCapiInternal to get its content from a CLS +#define RPC_CONTENT_CLIENT +#define IMPLEMENT_THUMBNAIL_BROWSER +#define RPC_API_ML // Multilanguage support +#define RPC_ACM_BULLETIN +#define DASHBOARD_LINK //include dashboard link beside project combo box + +// *********************************************************************** // +// BEGIN PLATFORM SPECIFIC DEFINITIONS SECTION // +// *********************************************************************** // + +// Define the OS and build here. +#ifdef WIN32 +#define PORT_WINDOWS +#endif // ifdef WIN32 +#ifdef linux +#define PORT_LINUX +#define PORT_UNIX +#define PORT_MAC_OR_UNIX +#endif // ifdef linux +#ifdef sgi +#define PORT_IRIX +#define PORT_UNIX +#define PORT_MAC_OR_UNIX +#endif // ifdef sgi +#ifdef macintosh +#define PORT_MAC +#define PORT_MAC_OR_UNIX +#endif // ifdef macintosh + + +// Select the supported and default character widths here. +#ifdef PORT_WINDOWS +#ifndef _UNICODE +#define _UNICODE +#endif // ifndef _UNICODE +#ifdef RPCAPI_SRC +#define UNICODE +#endif // ifdef RPCAPI_SRC +#endif // ifdef PORT_WINDOWS +#ifdef _UNICODE +#include +#endif // ifdef _UNICODE + + +#ifdef PORT_WINDOWS +#ifdef RPCAPI_SRC +#define _WINSOCKAPI_ +#endif // ifdef RPCAPI_SRC +#ifndef USE_MF +#include "windows.h" +#endif // ifndef USE_MFC +#endif // ifdef PORT_WINDOWS + + +// We must select stdlibc++ include files based on our compiler version: +#if (__GNUC__ >= 3) +#include +#define RPC_STL3 +// Until the stream problems can be worked out with the STL3 build use +// the old STL libraries +#elif (defined (_MSC_VER) && _MSC_VER >= 1310) // Visual Studio .NET 2003 or later +#include +#define RPC_STL3 +#else +#include +#endif // if (__GNUC__ >= 3) + + +#ifdef PORT_MAC +#include +// CodeWarrior / Mac made verify a keyword. Weird. +#undef verify +#define RPC_STL3 +#endif // ifdef PORT_MAC + + +// *********************************************************************** // +// BEGIN RPC API SECTION // +// *********************************************************************** // + + +/*! \class RPCapi +* \brief This class represents the entire RPC API +* +* This class represents the entire RPC API. Get a single instance of +* this class using the RPCgetAPI function (defined below), store it statically, +* and let it live for the duration of an execution of your program. +*/ +class RPCapi { +public: + + // These are the interfaces defined by the RPCapi. It is the + // job of the client code developer to implement these interfaces. + class Client; // The client-code counterpart to class RPCapi. + // The client code should define this class and + // create a single instantiation of it, and pass + // a pointer of that instantiation to the global + // RPCapi object. + class ClientInstance; // The client-code counterpart to RPCapi::Instance + // (see class Instance, below). + + // These are the classes exported by the RPCapi: + class Param; // Base class of all parameters of an RPC. + class Checksum; // A checksum exists in every RPC file. + class Content; // Wrapper class for the content type codes + class ContentMgr; // manages the content for this RPCapi.dll + class Creator; // Used to create an RPC. Only usable if + // the API has a Creator License. + class Error; // Wrapper class for the error codes in RPCapi + class ID; // Unique RPC identifier. All Instances of + // an RPC share the same ID, but different + // RPC files have different IDs. + class Instance; // Single instance of an RPC in a scene. + class InstanceInterface; // Used to edit a single Instance + class License; // Represents the licenses of the RPCs which + // the client app and this API can read + class MassEditInterface; // Used to edit many Instances at once + class Mesh; // Geometric mesh. Contains vertices and faces. + template + class Prim; // Class for primitives (int,char,double, ...) + class ParamList; // All RPC files are are ParamLists + // and contain sub ParamLists. + class PrintOptions; // Used to store options for printing when + // print is called. + class String; // Encapsulating class for strings. + class Texture; // The class which encapsulates all image + // data retrieved from an Instance. + class TextureMesh; // Texture mesh. Contains texture vertices + // and texture faces. + class Vector; // A 3D vector, with x, y, and z components. + + + + /*! \class ObjectCodes + * \brief Param codes for classes in RPCapi + * + * These are the param codes for all classes in the RPCapi which + * can be retrieved by the client code using RPCapi::newObject: + * NOTE: These are NOT necessarily the same as the type codes + * returned by RPCapi::Param::typeCode(). + * + * To create a new, empty Instance, for example, use + * RPCapi::Instance *newInstance = + * RPCapi::newObject(RPCapi::ObjectCodes::INSTANCE); + */ + class ObjectCodes { + public: + typedef enum OBJECT_CODE { + PRIM_BOOL = 1, + PRIM_CHAR = 2, + PRIM_UCHAR = 3, + PRIM_SHORT = 4, + PRIM_USHORT = 5, + PRIM_INT = 6, + PRIM_UINT = 7, + PRIM_FLOAT = 8, + PRIM_DOUBLE = 9, + CHECKSUM = 10, + CREATOR = 11, // only good in Creator-licensed API + ID = 12, + INSTANCE = 13, + INSTANCE_INTERFACE = 14, + LICENSE = 15, + MASS_EDIT_INTERFACE = 16, + PARAM_LIST = 17, // stream-accessing param list + PRINT_OPTIONS = 18, + STRING = 19, + VECTOR = 20, + CLIENT_INTERFACE = 21 + } OBJECT_CODE_T; // end typedef enum OBJECT_CODE + }; // end class ObjectCodes + + + /*! \class BaseObject + * \brief Base class for all classes of the RPC API + * + * + * This is the base class for all classes of the RPC API. + * + */ + + class BaseObject { + public: + + /*! \function ~BaseObject(void). + * \brief Destructor + * + * This is a dummy destructor; C++ compilers like to have + * destructors for all classes, even abstract classes. + */ + virtual ~BaseObject(void) {} + }; // end class BaseObject + + + + + + class TString; + + /*! \class Param + * \brief Param of BaseObject + * + * An RPC contains many different objects, including its ID, + * geometric meshes, textures, and primitives. All of these objects + * are subclasses of Param. When a Param is retrieved, you can use + * either its typecode or the C++ dynamic_cast operator to determine + * if it is of the desired type. + * + */ + class Param : public BaseObject { + public: + /*! \function virtual ~Param(void) + * \brief Destructor. + * + * This is a dummy destructor; C++ compilers like to have + * destructors for all classes, even abstract classes. + */ + virtual ~Param(void) {} + + /*! \function virtual const TString &className(void) + * \brief Gets class name of object. + * + * This routine returns the class name of this object as a string. + * Returns: + * The class name of this object. + */ + virtual const TString &className(void) const = 0; + + /*! \function virtual Param *copy(void) + * \brief + * This routine copies this Param object into a new object with + * the same value. + * Return: + * A pointer to the new Param object. + */ + virtual Param *copy(void) const = 0; + + /*! \function virtual bool fromCreator(Creator *creator) + * \brief Reads params from a creator. + * + * This routine reads this Param's value from the stream provided by + * the input creator. That stream is in a .fmt format, rather than a + * binary format. + * Parameters: + * creator: The Creator object to use to create this Param. + * Returns: + * true on success, false on failure. + */ + virtual bool fromCreator(Creator *creator) = 0; + + /*! \function virtual int fromStream(istream &stream, int offset, int typeCode) + * \brief Converts stream to object data. + * + * This routine converts the input stream to the data of this object. + * Parameters: + * stream: stream from which to extract this object. + * offset: offset in stream to begin reading this object. + * typeCode: the typeCode that was read for this object. + * This dictates how this object unserializes itself. + * If typeCode is negative, this object unserializes + * itself in a default fashion. + * Return: + * The number of bytes read, or an error code (which is < 0). + */ +#ifndef RPC_STL3 + virtual int fromStream(istream &stream, int offset, int typeCode) = 0; +#else + virtual int fromStream(std::istream &stream, int offset, int typeCode) = 0; +#endif + + /*! \function virtual void print(ostream &os = cout, int tab = 0, PrintOptions *options = NULL) + * \brief Prints Param + * + * This routine outputs this Param in a human-readable format. + * For Param's which have no simple human-readable format (such as + * Textures), the class name of the object is printed. + * Parameters: + * os: The stream to print to. + * tab: The number of times to indent. + * options: The options for printing. + */ +#ifndef RPC_STL3 + virtual void print(ostream &os = cout, int tab = 0, + PrintOptions *options = NULL) const = 0; +#else + virtual void print(std::ostream &os = std::cout, int tab = 0, + PrintOptions *options = NULL) const = 0; +#endif + + /*! \function virtual int size(void) + * \brief Gets size of serial object. + * + * This routine determines how many bytes this object will + * require to serialize itself. + * Return: + * The number of bytes required to serialize this object, + * or an error code (which is < 0). + */ + virtual int size(void) const = 0; + + /*! \function virtual int toStream(ostream &stream, int offset) + * \brief Writes object to a stream. + * + * This routine writes this object's bytes to a stream. + * Parameters: + * stream: The stream to which this object should be written. + * offset: offset in stream to begin writing this object + * Return: + * The number of bytes written, or an error code (which is < 0). + */ +#ifndef RPC_STL3 + virtual int toStream(ostream &stream, int offset) const = 0; +#else + virtual int toStream(std::ostream &stream, int offset) const = 0; +#endif + + /*! \function virtual int typeCode(void) + * \brief Gets type code of the object. + * + * This routine returns the type code of this object. + * Return: + * The unique integer type code of this object's class. + */ + virtual int typeCode(void) const = 0; + + }; // end class RPCapi::Param + + + /*! \class TString + * \brief Wraps string arguments that are passed to/from RPCapi functions/ + * + * This class exists to wrap string arguments that are passed + * to / from RPCapi functions. It provides functionality for + * multiple character widths. + * + * Client programs are responsible for deallocating any non-const + * TStrings that the RPCapi returns to them. + * + * RPCapi functions take as input TStringArgs that can be constructed + * from character strings. + * + * NOTE on ellipsis (...) operator: + * When passing a TLabel or TString reference to an ellipsis operator, + * use the TSA macro. + * When retrieving a TLabel or TString argument, use the TSAU macro. + * This avoids nasty type problems (like the fact that a TString reference + * is not the same size as a TLabel). + * + */ +#ifdef _UNICODE +#define RPC_WIDE_STRINGS +#ifdef UNICODE +#define RPC_WIDE_DISPLAY +#endif // ifdef UNICODE +#endif // ifdef _UNICODE + class TStringArg; + class TString : public Param { + public: + // These are the possible character types for this class. + // Ensure that a character width can represent all lower + // character widths. + typedef enum _CHAR_TYPE_T { + ACHAR = 1, // ANSI, single-byte character + WCHAR = 2 // wide character (2 bytes on Windows, 4 on Linux). + } _CHAR_TYPE; + + public: + // Destructor. + virtual ~TString(void) { } + + /*! \function virtual int compareS(TStringArg str, bool icase = false, int num = -1, int start = -1, int sstart = -1) + * \brief Determines ordering relative to input string. + * + * This routine determines the ordering of this TString + * relative to the input string. + * Parameters: + * str: The string to compare this to. + * icase: If true, do case-insensitive comparison. + * num: If non-negative, the number of characters to compare. + * start: If non-negative, the offset to start comparing + * in this TString. + * sstart: If non-negative, the offset to start comparing + * in the input string. + * Returns: + * -1 if this < str + * 0 if this == str + * +1 if this > str + */ + virtual int compareS(TStringArg str, bool icase = false, + int num = -1, int start = -1, int sstart = -1) const = 0; + + /*! \function virtual void *extractV(void) + * \brief + * + * These routines extract the string pointer from this TString. + * These routines either convert this TString's string pointer + * to the desired type and delete it, or copy the pointer and + * then clear it. Either way, the caller assumes deallocation + * responsibility for the return, and this TString has a NULL str + * at the end of the call. + */ + virtual void *extractV(void) = 0; + virtual char *extractA(void) = 0; +#ifdef RPC_WIDE_STRINGS + virtual wchar_t *extractW(void) = 0; +#endif // ifdef RPC_WIDE_STRINGS + + /*! \function virtual const void *getV(void) + * \brief Get a const pointer to the string. + * + * These routines return a constant pointer to + * this TString's string. + * Parameters: + * force: If true, and this TString has a NULL pointer, + * a pointer to an empty string will be returned. + * NOTE: Even though these functions are const, if this TString's + * str is of a different character width from that requested, + * this TString converts itself in the call. + */ + virtual const void *getV(void) const = 0; + virtual const char *getA(bool force = false) const = 0; +#ifdef RPC_WIDE_STRINGS + virtual const wchar_t *getW(bool force = false) const = 0; +#endif // ifdef RPC_WIDE_STRINGS + virtual char *getPtrA(void) = 0; +#ifdef RPC_WIDE_STRINGS + virtual wchar_t *getPtrW(void) = 0; +#endif // ifdef RPC_WIDE_STRINGS + + /*! \function virtual char *getCopyA(void) + * \brief Returns a copy of the string. + * + * These routines return a copy of this TString's string. + * NOTE: The caller assumes deallocation responsibility + * for the returned string. + */ + virtual char *getCopyA(void) const = 0; +#ifdef RPC_WIDE_STRINGS + virtual wchar_t *getCopyW(void) const = 0; +#endif // ifdef RPC_WIDE_STRINGS + + /*! \function virtual char *getGAllocA(void) const = 0; + * \brief Returns a copy of the string allocated on the global heap + * + * Note: The Caller assumes deallocation responsibility + * for the returned string. + */ + virtual char *getGlobalAllocA(void) const = 0; +#ifdef RPC_WIDE_STRINGS + virtual wchar_t *getGlobalAllocW(void) const = 0; +#endif + + /*! \function virtual int length(void) + * \brief Get length of the TLabel string. + * + * This routine returns the length of this TLabel's string, in characters. + * The return does not include the null-terminator. + */ + virtual int length(void) const = 0; + + /*! \function virtual void own(bool ownit) + * \brief Sets ownership. + * + * These routines get /set whether this object owns + * its string. If it is changed to own, it will make a + * copy of its current unowned string. + */ + virtual void own(bool ownit) = 0; + virtual bool own(void) const = 0; + + /*! \function virtual void setTSA(const TStringArg &str) + * \brief Set the string value. + * + * These routines set this string's value. + */ + virtual void setTSA(const TStringArg &str) = 0; + virtual void setPtrTS(TString &str) = 0; + virtual void setConstPtrTS(const TString &str) = 0; + virtual void setA(const char *str) = 0; + virtual void setPtrA(char *str) = 0; +#ifdef RPC_WIDE_STRINGS + virtual void setW(const wchar_t *str) = 0; + virtual void setPtrW(wchar_t *str) = 0; +#endif // ifdef RPC_WIDE_STRINGS + + /*! \function virtual int type(void) + * \brief Get the type of string. + * + * These routines get /set the current type of this string. + * (ACHAR, WCHAR, etc). + * Parameters: + * stype: The type to switch to. + * p: If true, preserve the current contents. + */ + virtual int type(void) const = 0; + virtual void type(int stype, bool p = false) = 0; + + }; // class TString + + + + /*! \class TStringArg + * \brief Used to pass variable width strings + * + * This inlined class is intended to make it easy to pass + * in strings of various character widths. + */ + class TStringArg { + public: + const void *strptr; // The string contained by this object. + unsigned char strtype; // Type of string in strptr (see TString::CHAR_TYPE). + + /*! \function inline void init(unsigned char itype, const void *iptr) + * \brief Initializes. + * + * This routine initializes this object. + * Call it in every constructor. + */ + inline void init(unsigned char itype, const void *iptr) { + strtype = itype; + strptr = iptr; + } + + // Constructors. + inline TStringArg(void) { + init(TString::ACHAR, NULL); + } + inline TStringArg(const int istr) { // Always means NULL. + init(TString::ACHAR, NULL); + } + inline TStringArg(const char *istr) { + init(TString::ACHAR, (const void *)istr); + } +#ifdef RPC_WIDE_STRINGS + inline TStringArg(const wchar_t *istr) { + init(TString::WCHAR, (const void *)istr); + } +#endif // ifdef RPC_WIDE_STRINGS + inline TStringArg(const TStringArg &istr) { + init(istr.strtype, istr.strptr); + } + inline TStringArg(const TString &istr) { + init(istr.type(), istr.getV()); + } + + // Assignment operators. + inline TStringArg &operator =(const int istr) { // Always means NULL. + init(TString::ACHAR, NULL); + return *this; + } + inline TStringArg &operator =(const char *istr) { + init(TString::ACHAR, (const void *)istr); + return *this; + } +#ifdef RPC_WIDE_STRINGS + inline TStringArg &operator =(const wchar_t *istr) { + init(TString::WCHAR, (const void *)istr); + return *this; + } +#endif // ifdef RPC_WIDE_STRINGS + inline TStringArg &operator =(const TStringArg &istr) { + init(istr.strtype, istr.strptr); + return *this; + } + inline TStringArg &operator =(const TString &istr) { + init(istr.type(), istr.getV()); + return *this; + } + + }; // class TStringArg + + + /*! \class Checksum + * \brief This is the interface for the Checksum class + * + * This is the interface for the Checksum class + */ + class Checksum : public Param { + public: + /* \function virtual ~Checksum(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface + */ + virtual ~Checksum(void) {} + + /* \function virtual void calculate(const ParamList &list, const License &license) + * \brief Calculates the Checksum + * + * This routine calculates this Checksum over the input ParamList. + * Parameters: + * list: The List over which this checksum should be + * calculated. + * license: The license of the content, which determines which + * method of calculating the checksum to use. + */ + virtual void calculate(const ParamList &list, + const License &license) = 0; + + /* \function virtual bool verify(const ParamList &list, const License &license) + * \brief Checks input params against the Checksum + * + * This routine checks the input ParamList against this checksum. + * Parameters: + * list: The List over which this checksum should be + * calculated. + * license: The license of the content, which determines which + * method of calculating the checksum to use. + * Returns: + * true iff the input ParamList is verified by this checksum. + */ + virtual bool verify(const ParamList &list, + const License &license) const = 0; + + }; // end class Checksum + + + /*! \class Client + * \brief Interface between global API and client code. + * + * This is the interface that the client code must implement + * and pair with the global API object obtainable by calling + * the function RPCgetAPI(). + */ + + class Client { + public: + /*! \class Mode + * \brief Mode of the host program. + * + * The RPCapi::Client::Mode represents the mode of the host + * program. If the host program is in a normal mode that allows + * editing of the scene the the state should be + * RPCapi::Client::Mode::EDIT. If the host program is in a render + * slave mode that does not allow the scene to be edited then it + * should return RPCapi::Client::Mode::NO_EDIT. + */ + class Mode { + public: + typedef enum MODE_TYPE { + NO_EDIT = 0, + EDIT = 1 + } MODE_TYPE_T; // end typedef enum MODE_TYPE + + }; // end class RPCapi::Mode + + + /*! \function + * \brief + * + * This is the dummy destructor for this interface. + */ + virtual ~Client(void) {} + + /*! \function + * \brief + * + * This routine returns the string for the client code which should + * be displayed in the RPC api's About Dialog. + * Returns: + * A string containing the client code's version information. + */ + virtual TStringArg RPCgetAboutString(void) = 0; + + /*! \function + * \brief + * + * This routine retrieves a pointer to the API object paired + * with this Client object. + * Returns: + * A pointer to the API object paired with this Client object. + */ + virtual RPCapi *RPCgetAPI(void) = 0; + +#ifndef RPC_CONTENT_CLIENT + /*! \function + * \brief + * + * This routine retrieves the license of this Client object. + * Returns: + * A pointer to this Client object's license. + */ + virtual const License *RPCgetLicense(void) = 0; +#endif // RPC_CONTENT_CLIENT + + /*! \function + * \brief + * + * This routine returns the mode of the host program. + * If the host program is in a render slave mode that + * does not allow editing of a scene then this method + * should return "RPCapi::Client::State::NO_EDIT". + * If the host is in a normal mode that allows editing + * of the scene then this method should return + * "RPCapi::Client::Mode::EDIT". + */ + virtual int RPCgetMode() = 0; + + /*! \function + * \brief + * + * This routine returns the list of paths that should be + * searched for RPC content. This method will be called when + * the API needs to know where to look for RPC content. + * Parameters: + * start: If true, this call means that the API is + * going to scane the paths it retrieves from + * this function. This process could take + * a while, so the client code may want to + * take appropriate action (like displaying + * a dialog or making the mouse cursor an hour glass). + * If false, this call means that the API + * is done scanning the paths, and that this + * function does not need to return any paths. + * numPaths: The number of paths returned. + * Returns: + * A pointer to an array of strings, each of which + * is the name of a directory. + */ + virtual const TStringArg *RPCgetPaths(bool start, int &numPaths) = 0; + +#ifdef RPC_CONTENT_CLIENT + /* + * This routine returns the path to the RPCapi.ini file. + * If this routine returns NULL, the RPCapi.dll will + * look for the INI file in the path that was current + * when the RPCapi object was created. + * Returns: + * The path to the INI file. + */ + virtual TStringArg RPCiniPath(void) = 0; + + /*! \function + * \brief + * + * This routine is called whenever the license provided + * by the content client for this plugin has changed. + * NOTE: This routine will be called anytime the licenses + * are updated for this client from the ACM. This + * occurs when RPCapi::updateACM() is called, when the + * first RPCapi::Instance is created, or asynchronously + * when the client loses its connection with the ACM. + * Parameters: + * licensed: true if the plugin is considered licensed. + * false otherwise. + * acm: true iff this client is connected to the ACM. + */ + virtual void RPClicenseChange(bool licensed, bool acm) = 0; + + /*! \function + * \brief + * + * This routine returns the globally-unique identifier for + * the Client class. This id, used with RPCpluginMetadata, + * should uniquely determine any program or plugin that uses the + * RPCapi. It should be of the format S-XXXX-XXXX-XXXX..., + * where S is the number of bytes in the ID, and each + * hyphen-separated word (XXXX) represents 20 bits, where X can + * range from 0 to 9 and from A to Z (excluding I, O, and Z). + * 8 words (20 bytes) is a good size. + * Returns: + * The human-readable GUID of this client. + */ + virtual TStringArg RPCpluginId(void) = 0; + + /*! \function + * \brief + * + * This routine gets the metadata for the Client class. + * This metadata, usedwith RPClicenseID, identifies and + * describes any program or plugin that uses the RPCapi. + * Metadata should include the pair "author", "". + * Parameters: + * num: The number of metadata items, returned by refernece. + * keys: The metadata keys, returned by reference. + * values: The metadata values, returned by reference. + */ + virtual void RPCpluginMetadata(int &num, const TStringArg *&keys, + const Param **&values) = 0; + + /*! \function + * \brief + * + * This routine determines what kinds of content this plugin + * supports. Each element of setsize / keys / values represents + * a set of metadata. A piece of content must match all key / value + * pairs in at least one set in order to match. If no + * sets are returned, all content matches. + * NOTE: The caller will not deallocate the returned values. + * Parameters: + * req: Request type. + * 1 == metadata of content the plugin supports. + * If a piece of content does not match, it is + * not retrieved and made available. + * NOTE: The return values for this request type + * should always be the same for a give plugin. + * 2 == metadata of content to get licenses for. + * numsets: Number of sets of permissions. + * setsize: Array containing size of each set. + * keys: Array of arrays of keys. + * values: Array of arrays of values. + */ + virtual void RPCcontentMetadata(int req, int &numsets, + const int *&setsize, const TStringArg **&keys, + const Param ***&values) = 0; +#endif // ifdef RPC_CONTENT_CLIENT + +#ifdef RPC_CONTENT_CLIENT + /*! \function + * \brief + * + * The RPC API calls this routine when it has some message + * that it wishes to display to the user. + * Parameters: + * msgType: The type of this message. + * 1: generic information. + * 2: warning + * 3: error. + * 4: catastrophic error (we're going to die). + * 5: a time-consuming task is about to begin. + * no window needs to be displayed, but + * the API will be unresponsive + * (an hour-glass would be good). + * 6: a time-consuming task is done. + * 7: more verbose warning or error. + * ret: If true, we need a yes-no response. + * title: If a window will display the message, use this title. + * msg: The message to display. + * Returns: + * If ret is true, true or false (depending on the user's response). + * If the message will not be displayed, or if ret is false, + * return false. + */ + virtual bool RPCuserMessage(int msgType, bool ret, + const TString &title, const TString &msg) = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + }; // end class Client + + + /*! \class ClientInstance + * \brief Interface between client code and each RPCapi instance + * + * This is the interface that the client code must implement + * and pair with each RPCapi::Instance object it uses. + */ + class ClientInstance { + public: + /*! \function virtual ~ClientInstance(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface. + */ + virtual ~ClientInstance(void) {} + + /*! \function virtual Instance *RPCgetInstance(void) + * \brief Gets corresponding RPCapi instance. + * + * This method is called when a procedure needs access to + * the RPCapi::Instance that corresponds to this ClientInstance object. + * Returns: + * a pointer to the corresponding RPCapi::Instance + */ + virtual Instance *RPCgetInstance(void) = 0; + + + /*! \function virtual TStringArg RPCgetName(void) + * \brief Gets the name of the ClientInstnace. + * + * This routine returns the name of the ClientInstance. + * Returns: + * The human-readable string the ClientInstance is + * using to identify itself. + */ + virtual TStringArg RPCgetName(void) = 0; + + /*! \function virtual void RPCgetPivot(double px, double py, double pz, + double &distance, double &dx, double &dy, double &dz) + * \brief Converts the pivot into new pivot/direction. + * + * This routine converts the input pivot position + * into a new pivot position and a direction. + * Parameters: + * px, py, pz: The current pivot position. + * position: The world-coordinates position of the + * indicated pivot, returned by reference. + * distance: The distance in inches the pivot is + * from the Instance's pivot at time 0. + * This may be an arbitrary value, since + * pivot may have followed a curved path + * (the distance is measured along the curve). + * dx, dy, dz: The world-coordinates direction of motion + * of the indicated pivot, returned by reference. + */ + virtual void RPCgetPivot(double px, double py, double pz, + double &distance, double &dx, double &dy, double &dz) = 0; + + /*! \function virtual int RPCgetTime(void) + * \brief Gets the current time in frames. + * + * This routine returns the current time, + * in frames (1/30th of a second). This routine is used + * primarily at render time during animations. + * Returns: + * The current time, in frames. + */ + virtual int RPCgetTime(void) = 0; + + /*! \function virtual bool RPCisSelected(void) + * \brief Is the ClientInstance selected? + * + * This routine determines whether or not the ClientInstance + * is currently selected in the scene. + * Returns: + * true iff the ClientInstance is currently selected. + */ + virtual bool RPCisSelected(void) = 0; + + /*! \function virtual void RPCparameterChangeNotification(bool newInstance, + const TString **params, int num) + * \brief Called when parameters are changed by user. + * + * This is a method that the plug in developer implements. This method + * will be called when a set of parameters is changed via the UserInterface + * Parameters: + * newInstance : if true then the old data for the rpc needs to be + * removed and replaced with the data for this instance. + * true will usually appear during the creation process + * when the user changes which RPC they want to add to + * a scene. It can also appear if the user switches the + * RPC after placement. + * params: These are the keys of the transforms + * (retrievable through Instance::getTransform()) + * that have changed. + * num: The number of params. + */ + virtual void RPCparameterChangeNotification(bool newInstance, + const TString **params, int num) = 0; + + /*! \function virtual void RPCselect(bool select) + * \brief Selects/deselects client instance. + * + * This routine causes this client instance to become + * selected or deselected. + * Parameters: + * select: true if the client instance is to be selected. + * false if the client instance is to be unselected. + */ + virtual void RPCselect(bool select) = 0; + + }; // end class ClientInstance + + + /*! \class Content + * \brief Type of content of an RPC + * + * + * The RPCapi::Content represents the type of content + * of an RPC. The current types of content are + * 2D, 2.5D, 3D, 3.5D, and Smart. + * The content type of an RPC is stored in /metadata/content type + * + */ + class Content { + public: + typedef enum CONTENT_TYPE { + _2D = 1, + _2_5D = 2, + _3D = 3, + _3_5D = 4, + SMART = 5 + } CONTENT_TYPE_T; // end typedef enum CONTENT_TYPE + + }; // end class RPCapi::Content + + /*! \class Creator + * \brief Base interface for Creator + * + * + * This is the base interface for a Creator object which recognizes + * a format file of a particular style. + * NOTE: This class is used in the creation of RPC files. + * This class is only usable if the RPCapi.dll file you are + * using has the Creator license enabled. + */ + class Creator : public BaseObject { + public: + /*! \function virtual ~Creator(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface + */ + virtual ~Creator(void) {} + + /*! \function virtual bool createRPC(int argc, char **argv) + * \brief Creates the RPC. + * + * This routine builds an RPC from this Creator + * Parameters: + * argc, argv + * Returns: + * true iff rpc was created. + */ + virtual bool createRPC(int argc, char **argv) = 0; + + /*! \function virtual bool readTString(TString *rs) + * \brief Reads a TString from the Creator. + * + * This routine reads a TString from this Creator. + */ + virtual bool readTString(TString *rs) = 0; + + }; // end class Creator + + + /*! \class Error + * \brief All error codes by any function. + * + * + * The RPCapi::Error class encapsulates all of the error + * codes which may be returned by any RPCapi function. + * All error codes are < 0. + * To use in your code, say "RPCapi::Error::NONE", or + * whatever error code is appropriate. + * + */ + class Error { + public: + typedef enum ERROR_CODE { + NONE = 0, // no error + UNKNOWN = -1, // error of unknown type + STREAM_OVERFLOW = -10, // tried to read/write past end of stream + PARSING = -11, // format error in a stream or object + READING_STREAM = -12, // could not read from a stream + WRITING_STREAM = -13, // could not write to a stream + MEMORY_ALLOC = -14, // could not allocate memory + NO_READ_ACCESS = -15, // did not have read access on stream + NO_WRITE_ACCESS = -16, // did not have write access on stream + NO_STREAM = -17, // there was no stream to access + INVALID_SCALING = -18, // unsupported scaling mode used + NETWORK = -19, // network error + UNKNOWN_TYPE_CODE = -20,// unkown type code + CRYPTO_CHECK = -21, // cryptographic checksum failed + + // The following errors can be returned by acmSendRequest(). + ACM_REQ_CONNECTION = -22, // unable to connect to an ACM + ACM_REQ_NETWORK = -23, // error in net communication w/ ACM + ACM_REQ_BAD_SERVER_ID = -24,// unable to dereference source server ID + ACM_REQ_SERVER_ERROR = -25 // source server reported an error + + } ERROR_CODE_T; // end typedef enum ERROR_CODE + + }; // end class RPCapi::Error + + + /*! \class ID + * \brief This is the interface for the unique IDs assigned to each RPC file. + * + * This is the interface for the unique IDs assigned to each RPC file. + */ + class ID : public Param { + public: + // This is the dummy destructor for this interface + virtual ~ID(void) {} + + /*! \function + * \brief + * + * This routine returns a constant pointer to the bytes of this ID. + * Returns: + * The bytes of this ID. + */ + virtual const unsigned char *bytes(void) const = 0; + + /*! \function virtual int compare(const ID &inputID) + * \brief Compares IDs. + * + * This routine compares two IDs. + * Parameters: + * inputID: The ID with which to compare this ID. + * Returns: + * -1 if this ID < inputID; + * 0 if this ID == input ID; + * +1 if this ID > input ID + */ + virtual int compare(const ID &inputID) const = 0; + + /*! \function virtual int numBytes(void) + * \brief Gets size of ID. + * + * This routine returns the number of bytes in this ID. + * Returns: + * number of bytes in this ID's representation. + */ + virtual int numBytes(void) const = 0; + + /*! \function virtual void setBytes(const unsigned char *inBytes, int numBytes) + * \brief Set the data. + * + * Set the data of this ID to be that contained in the + * input byte array. + * Parameters: + * inBytes: bytes to set this ID to + * numBytes: number of bytes in inBytes + */ + virtual void setBytes(const unsigned char *inBytes, int numBytes) = 0; + + }; // end class ID + + + /*! \class Image + * \brief The Image interface encapsulates any texture image retrieved from an RPC. + * + * + * The Image interface encapsulates any texture image retrieved from an RPC. + * NOTE: Unless you are using ParamLists, you should NOT + * use this class. See class Texture. + * + */ + class Image : public Param { + public: + /*! \function virtual ~Image(void) + * \brief Destructor.; + * + * This is the dummy destructor for this interface + */ + virtual ~Image(void) {} + + /*! \function virtual int data(unsigned char *&buffer, + bool allocBuffer, + unsigned char channels, + int scaleMode, + int &width, + int &height, + int x1 = 0, + int y1 = 0, + int x2 = 0, + int y2 = 0 + ) + * \brief Retrieves image data. + * + * This routine retrieves data from this Image. + * This routine allocates the data array used to store + * this Image, fills it in with the appropriate data, + * and returns a pointer to the array. + * It is the client application's responsibility to + * deallocate the data array when finished with it. + * This routine returns the size of the buffer array. + * Parameters: + * buffer: buffer in which the image data is placed. + * This buffer may be allocated inside this + * function, and a pointer to it is returned by + * reference. + * alloc: if true, this routine allocates buffer. + * if false, this routine fills in the buffer passed + * to it, or returns the size needed if that buffer + * is NULL. + * channels: specifies which channels to include in the + * texture data returned (see RPCapi::Channels). + * scaleMode: specifies which scale mode to use. + * (see RPCapi::Scale). + * width: The width to which the texture is to be scaled. + * If ScaleMode is set to SCALE_FAST, the returned + * texture's width will be scaled to the nearest + * power of 2. Set to 0 if you do not want the + * texture to be scaled. + * height: The height to which the texture is to be scaled. + * If ScaleMode is set to SCALE_FAST, the returned + * texture's height will be scaled to the nearest + * power of 2. Set to 0 if you do not want the + * texture to be scaled. + * x1: x coordinate of the top left corner of the + * rectangle of the texture you want returned. Set + * to 0 if you want the entire texture. + * y1: y coordinate of the top left corner of the + * rectangle of the texture you want returned. Set + * to 0 if you want the entire texture. + * x2: x coordinate of the bottom right corner of the + * rectangle of the texture you want returned. Set + * to 0 if you want the entire texture. + * y2: y coordinate of the bottom right corner of the + * rectangle of the texture you want returned. Set + * to 0 if you want the entire texture. + * Returns: + * The number of bytes used or required to store the requested, + * image, or < 0 on an error. + */ + virtual int data(unsigned char *&buffer, + bool allocBuffer, + unsigned char channels, + int scaleMode, + int &width, + int &height, + int x1 = 0, + int y1 = 0, + int x2 = 0, + int y2 = 0 + ) = 0; + +#ifdef PORT_WINDOWS + /*! \function virtual HBITMAP getHBitmap(void) + * \brief Create the HBITMAP from the image. + * + * This routine creates a screen compatible HBITMAP from + * the data in this image accessor + * Return: + * HBITMAP of this image accessor + */ + virtual HBITMAP getHBitmap(void) = 0; +#endif // ifdef PORT_WINDOWS + + /*! \function virtual int horizontalResolution(void) + * \brief Get the image width. + * + * This routine returns the width of this Image in pixels. + * Returns: + * The pixel-width of this Image. + */ + virtual int horizontalResolution(void) const = 0; + + /*! \function virtual int verticalResolution(void) + * \brief Get the image height. + * + * This routine returns the height of this Image in pixels. + * Returns: + * The pixel-height of this Image. + */ + virtual int verticalResolution(void) const = 0; + + }; // end class Image + + + + /*! \class Instance + * \brief Single instance of an RPC in a scene. + * + * + * An Instance object exists for every instance of an RPC in a scene. + * For example, if you are using "bmw.rpc", and have created + * two BMWs in your scene, then two Instance objects exist, + * one for each BMW to be rendered. + */ + class Instance : public Param { + public: + /*! \function virtual ~Instance(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface. + */ + virtual ~Instance(void) {} + + /*! \function virtual bool calculatePivots(bool editMesh) + * \brief Recaulculates pivots. + * + * Calling this routine tells the Instance that it should + * recalculate its pivots. This routine uses the RPCObject::getPivot() + * function to aid in the calculation. + * editMesh: true if the pivots are being calculated for + * an edit mesh, false if they are being + * calculated for a render mesh. + * Returns: + * true iff any pivot information in the Instance which + * affects the geometry has changed. + */ + virtual bool calculatePivots(bool editMesh) = 0; + + /*! \function virtual int contentType(void) + * \brief Returns the instance's content type. + * + * This routine calculates the content type of this Instance. + * Returns: + * The content type of this Instance, or 0 if it cannot be determined. + * (see typedef enum RPCapi::Content::CONTENT_TYPE). + */ + virtual int contentType(void) const = 0; + + /*! \function virtual bool editMeshHasChanged(bool value) + * \brief Checks if the mesh has changed. + * + * This routine returns true iff the edit mesh for this Instance + * has changed since the last time this function was called + * with a value of false. + * Parameters: + * value: The new value of the edit mesh change flag. + * Returns: + * true iff the edit mesh has changed. + */ + virtual bool editMeshHasChanged(bool value) = 0; + + /*! \function virtual Mesh *getEditMesh(void) + * \brief Retrieves the instance's edit mesh. + * + * This routine retrieves the edit mesh for this Instance. + * Returns: + * A pointer to a newly allocated copy of the edit mesh of + * this Instance, or NULL if an error occurred. + */ + virtual Mesh *getEditMesh(void) const = 0; + + /*! \function virtual const ID *getID(void) const + * \brief Retrieves the RPC's ID. + * + * This routine retrieves the unique ID of this Instance's RPC file. + * Returns: + * This Instance's RPC file's unique identifier, + * or NULL on an error. + */ + virtual const ID *getID(void) const = 0; + + + /*! \function virtual Mesh *getMesh(Mesh *mesh, + double cx = 0, double cy = 0, double cz = 0) + * \brief Gets the adjusted render mesh. + * + * This routine retrieves the render mesh for this + * Instance, adjusted for its current transforms. + * Parameters: + * mesh: A pointer to a render mesh. If this is NULL, + * this routine will allocate a new render mesh. + * If it is not NULL, this routine will modify the + * input mesh and return a pointer to it. + * NOTE: The next parameter is only required by + * RPCs whose render meshes are view-dependent. + * cx, cy, cz: The location of the camer in + * the RPC's mesh space. This location + * can be derived from the camera's location + * in world space, the RPC's location in + * world space, adn the RPC's orientation + * in world space. + * Returns: + * A pointer to the render mesh of this Instance, or NULL + * if an error occurred. + */ + virtual Mesh *getMesh(Mesh *mesh, + double cx = 0, double cy = 0, double cz = 0) const = 0; + virtual Mesh *getMesh(Mesh *mesh, + const Vector *cameraLocation = NULL) const = 0; + + + /*! \function virtual TString *getName(void) + * \brief Returns the Instance' RPC name. + * + * This routine returns the name of this Instance's RPC. + * Returns: + * A pointer to a newly-allocated string containing + * this Instance's RPC's name, or NULL if the name + * could not be retrieved. + */ + virtual TString *getName(void) const = 0; + + /*! \function virtual Texture *getPreviewTexture(void) + * \brief Gets a preview texture. + * + * This routine returns a preview texture. This Texture is not intended + * for painting, but will "look" like those Textures which are + * used for painting. + * Returns: + * A pointer to the newly-allocated preview texture, or + * NULL if an error occurred. + */ + virtual Texture *getPreviewTexture(void) const = 0; + + /*! \function virtual RPCapi *getRPCapi(void) + * \brief Gets the associated RPCapi object. + * + * This routine returns the RPCapi object associated + * with this Instance. This will be the object used to + * generate this Instance, or the object tied to the Client + * associated with this Instance's ClientInstance. + * Returns: + * A pointer to this Instance's RPCapi object. + */ + virtual RPCapi *getRPCapi(void) const = 0; + + /*! \function virtual const TString &getRPCFileName(void) + * \brief Gets the RPC file name. + * + * This routine retrieves the name of the RPC file + * for this RPC Instance. + * Returns: + * The name of this Instance's RPC file. + */ + virtual const TString &getRPCFileName(void) const = 0; + + /*! \function virtual ClientInstance *getClientInstance(void) + * \brief Gets the ClientInstance pointer. + * + * This routine returns a pointer to this Instance's + * Client Instance pointer. + * Returns: + * A pointer to this Instance's Client Instance. + */ + virtual ClientInstance *getClientInstance(void) const = 0; + + /*! \function virtual ParamList *getRPCFile(void) + * \brief Gets the ParalList for the RPC file. + * + * This routine opens the RPC file that this Instance points to + * and returns the ParamList pointing to the file. For + * more details, see class ParamList. + * Returns: + * A pointer to the newly-allocated ParamList poiting + * to the RPC file of this Instance. + */ + virtual ParamList *getRPCFile(void) const = 0; + + /*! \function virtual bool getTextures(double cx, double cy, double cz, + int &numTextures, Texture **&textures, + TextureMesh *&textureMesh) + * \brief Gets current trequired extures and meshes for the instance. + * + * This routine retrieves the Textures and TextureMeshes required to + * render this Instance given the current camera location. + * All of the TextureMesh faces' indices index into the returned array + * of Textures, and all TextureMesh vertices' indices index into the + * global uber-mesh returned earlier by this Instance, so no further + * index swizelling is required after this routine returns. + * Parameters: + * cx, cy, cz: The location of the camera in this + * Instance's space. + * numTextures: The number of Textures in the array. + * textures: The array of Textures, newly allocated, + * and returned by reference. + * textureMesh: The global TextureMesh, newly allocated + * and returned by reference. + * Returns: + * true iff the Textures and TextureMeshes + * were successfully retrieved + */ + virtual bool getTextures(double cx, double cy, double cz, + int &numTextures, Texture **&textures, + TextureMesh *&textureMesh) const = 0; + virtual bool getTextures(const Vector &cameraLocation, + int &numTextures, Texture **&textures, + TextureMesh *&textureMesh) const = 0; + + /*! \function virtual const Param *getTransform(TStringArg key) + * \brief Gets a single transform pointer. + * + * This routine gets a Transform from this Instance. + * Parameters: + * key: The key of the Transform to get. + * Returns: + * A constant pointer to the associated value of the Transform, + * or NULL if it does not exist. + */ + virtual const Param *getTransform(TStringArg key) const = 0; + + /*! \function virtual int getTransformKeys(const TString **&keys) + * \brief Gets an array of transform pointers. + * + * This routine allocates an array of strings, + * fills in the array with pointers to the keys of + * this Instance's Transforms, and returns a pointer + * to the array. + * NOTE: Deallocate the returned array, but NOT the returned TStrings. + * NOTE: On an error, or in the event that there are no keys, + * the returned keys pointer is NULL. + * Parameters: + * keys: Reference pointer through which is + * returned the array of keys. + * Returns: + * The number of keys, or < 0 if an error occurred. + */ + virtual int getTransformKeys(const TString **&keys) const = 0; + + /*! \function virtual int getTransformUnitType(TStringArg key) + * \brief Gets the unit type for the specified transform. + * + * This routine returns the unit type for a particular transform + * (eg LINEAR_UNITS). + * Parameters: + * key: The name of the transform in question. + * Returns: + * The unit type of the transform, or Units::NO_UNITS + * if the Transform does not exist. + */ + virtual int getTransformUnitType(TStringArg key) const = 0; + + /*! \function virtual int has(int prop) + * \brief Does the RPC have the given property. + * + * This routine returns true iff this RPC Instance has + * the indicated property. Note that these properties include + * geometry and speed, so this function with those properties + * is interchangable with hasGeometry and hasSpeed. + * NOTE: A return of true indicates only that this Instance + * has the potential to use an indicated property, and not + * that it absolutely will. + * NOTE: None of these properties are mutually exclusive. + * Parameters: + * prop: The property being queried. + * Returns: + * non-zero if this Instance has the input property. + */ + typedef enum PROPERTY { + GEOMETRY = 1, + SPEED = 2, + VIEW_DEPENDENT_MESH = 3, + VIEW_INDEPENDENT_MESH = 4, + VIEW_DEPENDENT_TEXTURE = 5, + VIEW_INDEPENDENT_TEXTURE = 6, + TIME_DEPENDENT_MESH = 7, + TIME_INDEPENDENT_MESH = 8, + TIME_DEPENDENT_TEXTURE = 9, + TIME_INDEPENDENT_TEXTURE = 10 + } PROPERTY_T; + virtual int has(int prop) const = 0; + + /*! \function virtual bool hasGeometry(void) + * \brief Does the instance have geometry. + * + * This routine retuns true iff this RPC Instance has geometry. + * Old RPCs do not have geometry; they create view-dependent + * planes at render time and paint textures on them. + * New RPCs may have true meshes. + * Returns: + * true iff this Instance has a geometric mesh. + */ + virtual bool hasGeometry(void) const = 0; + + /*! \function virtual bool hasSpeed(void) + * \brief Does the instance move or not. + * + * This routine returns true iff this RPC Instance has + * a speed; that is, the RPC object will move along a + * client-defined path during animation. + * Returns: + * true iff this Instance has speed. + */ + virtual bool hasSpeed(void) const = 0; + + /*! \function virtual bool setRpcId(const ID *id) + * \brief Sets the RPD's ID. + * + * This routine sets this Instance's RPC Id to the input bytes + * Parameters: + * id: The ID of the RPC file to point this Instance to. + * Returns: + * true iff the ID was successfully set. Note that if the + * license of the plugin or the API do not match that of + * the RPC with the input ID, this function will fail. + */ + virtual bool setRpcId(const ID *id) = 0; + + /*! \function virtual bool setRPCFileName(TStringArg rpcFileName) + * \brief Sets the file name. + * + * This routine sets this Instance's RPC File. + * It causes the global table of RPC paths to be updated so that + * it contains the directory containing the input RPC file + * (if the file exists and can be read). + * Parameters: + * rpcFileName: The name of the RPC file. + * Returns: + * true iff the ID of this RPC was successfully set to + * the ID contained in the input file. + */ + virtual bool setRPCFileName(TStringArg rpcFileName) = 0; + + /*! \function virtual void setClientInstance(ClientInstance *CI) + * \brief Sets the pointer to the instance. + * + * This routine sets the pointer to this Instance's ClientInstance. + * Parameters: + * CI: A pointer to the client code Instance with which this + * Instance should be paired. + */ + virtual void setClientInstance(ClientInstance *CI) = 0; + + + /*! \function virtual bool setTransform(TStringArg key, const Param *value) + * \brief Set a Transform on the instance. + * + * This routine sets a Transform of this Instance. + * Parameters: + * key: The key of the Transform to set. + * value: The new value of the Transform. + * Returns: + * true iff the Transform's value was successfully set. + */ + virtual bool setTransform(TStringArg key, const Param *value) = 0; + + /*! \function virtual double speed(void) + * \brief Get the instance speed. + * + * This routine returns the speed with which this Instance + * moves along the user-specified path. If this Instance + * does not move, this routine returns 0. + * Returns: + * The speed of this Instance, in inches per frame. + */ + virtual double speed(void) const = 0; + + }; // end class Instance + + + /*! \class Units + * \brief Types of units. + * + * These are the types of units which can be set + * in the setUnits routine. + * + */ + class Units { + public: + typedef enum UNIT_TYPES { + NO_UNITS = 0, + LINEAR_UNITS = 1, // base inches + ANGULAR_UNITS = 2, // base angles + VELOCITY_UNITS = 3 // base inches per frame + } UNIT_TYPES_T; // end typedef enum UNIT_TYPES + }; // end class Units + + + /*! \class InstanceInterface + * \brief Allows user to edit options of an Instance. + * + * + * The InstanceInterface can be displayed, allowing the user + * to edit options of an RPC Instance. + * + */ + class InstanceInterface : public BaseObject { + public: + /*! \function virtual ~InstanceInterface(void) + * \brief Destructor. + * This is the dummy destructor for this interface. + */ + virtual ~InstanceInterface(void) {} + + /*! \class Window + * \brief Contains window type codes. + * + * The RPCapi::InstanceInterface::Window class encapsulates all of the + * different window type codes. + * To use in your code, say + * "RPCapi::InstanceInterface::Window::SELECTION", + * or whatever window type is appropriate. + */ + class Window { + public: + typedef enum WINDOW_CODE { + NONE = -1, + SELECTION = 1, // The dialog that allows the user to select + // the RPC file used by this Interface's + // Instance + PARAMETERS = 2, // The dialog that allows the user to change + // the transforms of the currently selected + // Instance. + SELECTION_PARAMETERS = 3, // This dialog displays both the + // SELECTION and PARAMETERS windows. + // It is not yet supported +#ifdef IMPLEMENT_THUMBNAIL_BROWSER + SELECTION_THUMBS = 4 // Selection Window that displays + // all previews in selected category. +#endif // ifdef IMPLEMENT_THUMBNAIL_BROWSER + } WINDOW_CODE_T; // end typedef enum WINDOW_CODE + // OR these w/ the window code: + typedef enum WINDOW_OPTIONS { + // By default, set initial selection + // from the RPC Instance. + NO_SELECTION = 0x00010000, // Do not use global selection or + // instance to set initial selection. + GLOBAL_SELECTION = 0x00020000 // Use globally stored + // last selection to set + // initial selection + } WINDOW_OPTIONS_T; // end typedef enum WINDOW_OPTIONS + + typedef enum MODE_CODE { + MODELESS = 1, // Shows window inside parent + FLOATING = 2, // Shows window in its own border + MODAL = 3 // Modal dialog + } MODE_CODE_T; + }; // end class RPCapi::InstanceInterface::Window + +#ifdef PORT_WINDOWS + /*! \function virtual int show(HWND parentWindow, int windowToDisplay, int mode, + int x = 0, int y = 0, int w = 0, int h = 0, HWND z = NULL) + * \brief Displays the dialog. + * + * This routine displays the specified dialog to the screen. + * Parameters: + * parentWindow: handle to the parent window. + * windowToDisplay: the indicator of which window to display + * see Window::WINDOE_CODE for options + * mode: the mode of the window to display + & see Window::MODE_CODE + */ + virtual int show(HWND parentWindow, int windowToDisplay, int mode, + int x = 0, int y = 0, int w = 0, int h = 0, HWND z = NULL) = 0; +#endif // ifdef PORT_WINDOWS + +#ifdef PORT_MAC + /*! \function virtual int show(WindowRef parentWindow, int windowToDisplay) + * \brief Display the dialog for Mac. + * This routine displays the specified dialog to the screen. + * Parameters: + * parentWindow: handle to the parent window. + * windowToDisplay: the indicator of which window to display + * see UserInterface::Window for options + */ + virtual int show(WindowRef parentWindow, int windowToDisplay) = 0; +#endif // ifdef PORT_MAC + + /*! \function + * \brief + * + * This routine hides this dialog. + */ + virtual int hide(void) = 0; + + /*! \function + * \brief + * + * This routine sets the Instance which this InstanceInterface edits. + * Parameters: + * instance: The new Instance for this Interface. + */ + virtual void setInstance(Instance *instance) = 0; + + /*! \function + * \brief + * + * This routine sets the units used to display this Interface's + * unit-possessing Controls. + * Parameters: + * unitType: The type of units to be changed + * scale: The multiplicative factor to apply to + * the displayed values of controls + * with the input unitType. + * The scale is relative to the default units + * for the indicated unitType. + * The default unit for linear is inches, + * and the default unit for angular is degrees. + * unitSuffix: A NULL terminiated character string that can + * be appended after a number to indicate the + * displayed units + */ + virtual void setUnits(int unitType, double scale, TStringArg unitSuffix = NULL) = 0; + + /*! \function virtual int setBackgroundColor(int color) + * \brief Set interface attribute. + * + * The following methods will be used to set user interface attributes + * however they are not currently supported + */ + virtual int setBackgroundColor(int color) = 0; + virtual int setForegroundColor(int color) = 0; + virtual int setWidth(int newWidth) = 0; + virtual int setHeight(int newHeight) = 0; + + }; // end class InstanceInterface + + + /*! \class License + * \brief Represents the licenses of the RPCs which the client app and this API can read. + * + * The License class encapsulates all of the different license type codes. + * A License object can represent one or more license types simultaneously. + * For example, you might be writing a commercial plugin, in which + * case you should create a License object and set it to + * types COMMERCIAL and FREE. (All licenses should be set to FREE, + * indicating that the software can read RPC content which is freely + * available). + * + */ + class License : public Param { + public: + // These are the supported license types + typedef enum LICENSE_CODE { + NONE = 0, // used to indicate no license type + COMMERCIAL = 1, + EDUCATIONAL = 2, + FREE = 3, + REAL_TIME = 5, + CREATOR = 32 + } LICENSE_CODE_T; // end typedef enum LICENSE_CODE + + /*! \function virtual ~License(void) + * \brief Destructor. + * + * This is the default destructor + */ + virtual ~License(void) {} + +#ifdef RPC_CONTENT_CLIENT + /*! \function virtual unsigned int hash(void) + * \brief Hashes the object. + * + * This routine returns a simple hash over this object. + * Returns: + * A simple hash over this object. + */ + virtual unsigned int hash(void) const = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + /*! \function virtual bool is(int license) + * \brief Check if the passed license type is included. + * + * This routine determines if this License type includes + * the input license. A License object can represent + * multiple license types (for example, Commercial AND Free). + * Parameters: + * license: the type of License to be queried for. + * Returns: + * true iff this License includes the input license type. + */ + virtual bool is(int license) const = 0; + + /*! \function virtual void mask(const License &license) + * \brief Masks existing with input license. + * + * This routine masks this License with the input License. + * This means that any licenses supported by this License but + * not supported by the input License are no longer supported + * by this License. + * Parameters: + * license: The License to mask this one with. + */ + virtual void mask(const License &license) = 0; + + /*! \function virtual bool match(const License &license) + * \brief Does the passed license match the existing. + * + * This routine determines whether or not the input License + * matches this License. + * Paramters: + * license: The license to match with this one. + * mode: If 0, the input license matches this one + * if they share at least one license. + * If 1, the licenses match iff they + * are identical. + * If 2, the input license matches this one + * if this has at least all the licenses in license. + * Returns: + * true iff the input license matches. + */ + virtual bool match(const License &license +#ifdef RPC_CONTENT_CLIENT + , int mode = 0 +#endif // ifdef RPC_CONTENT_CLIENT + ) const = 0; + + /*! \function virtual void set(bool has, int num, ...) + * \brief Checks to see if license type is included + * + * This routine sets whether or not this License object includes + * the input license types. + * Parameters: + * has: if true, this function causes this License to + * include the input license types. If false, + * it causes this License object to not include them. + * num: The number of license types input to this function. + * ...: The license types. If this number is -1, it + * sets ALL of the license types to the value of has. + * Example call: + * set(true, 2, + * RPCapi::License::COMMERCIAL, RPCapi::License::FREE) + */ + virtual void set(bool has, int num, ...) = 0; + + }; // end class RPCapi::License + + + /*! \class MassEditInterface + * \brief Edit parameters of many instances at once. + * + * + * A MassEditInterface is used to edit the parameters of + * many Instances at once. + * + */ + class MassEditInterface : public BaseObject { + public: + /*! \function virtual ~MassEditInterface(void) + * \brief Destructor. + * This is the dummy destructor for this interface. + */ + virtual ~MassEditInterface(void) { } + + /*! \class Window + * \brief Contains window type codes. + * + * The RPCapi::MassEditInterface::Window class encapsulates all of the + * different window type codes. + * To use in your code, say + * "RPCapi::MassEditInterface::Window::EDIT", + * or whatever window type is appropriate. + */ + class Window { + public: + typedef enum WINDOW_CODE { + NONE = -1, + EDIT = 0, // The dialog that allows the user to edit the + // properties of many RPCapi::Instance objects + // at once + SELECTION = 1 // The dialog that allows the user to select + // many RPC files to be used + } WINDOW_CODE_T; // end typedef enum WINDOW_CODE + // OR these w/ the window code: + typedef enum WINDOW_OPTIONS { + // By default, set initial selection + // from the RPC Instance. + NO_SELECTION = 0x00010000, // Do not use global selection or + // instance to set initial selection. + GLOBAL_SELECTION = 0x00020000 // Use globally stored + // last selection to set + // initial selection + } WINDOW_OPTIONS_T; // end typedef enum WINDOW_OPTIONS + }; // end class RPCapi::MassEditInterface::Window + +#ifdef PORT_WINDOWS + /*! \function virtual int show(HWND hWndParent, int windowToDisplay) + * \brief Displays dialog. + * + * This routine displays the specified dialog to the screen. + * Parameters: + * hWndParent: handle to the parent window. + * windowToDisplay: the indicator of which window to display + * see MassEditInterface::Window for options + */ + virtual int show(HWND hWndParent, int windowToDisplay) = 0; + + /*! \function virtual int hide(void) + * \brief Hides the dialog. + * + * This routine hides this dialog. + */ + virtual int hide(void) = 0; +#endif // ifdef PORT_WINDOWS + + /*! \function virtual void addInstance(Instance *instance) + * \brief Adds an instances to the interface. + * + * This routine adds an Instance to be edited by this interface. + * Parameters: + * instance: The Instance to be added. + */ + virtual void addInstance(Instance *instance) = 0; + + /*! \function virtual void removeInstance(Instance *instance) + * \brief Removes one instance from the list. + * + * This routine removes an Instance that is currently being edited + * by this interface. It does not delete the Instance; it just + * removed it from this interface's list of Instances. + * Parameters: + * instance: The instance to be removed. + */ + virtual void removeInstance(Instance *instance) = 0; + + /*! \function virtual void removeAllInstances(void) + * \brief Removes all instances from the list. + * + * This routine removes all of the Instances currently being + * editted by this interface. It does not delete them; it just + * removes them from this interface's list of Instances. + */ + virtual void removeAllInstances(void) = 0; + + /*! \function virtual void setUnits(int unitType, double scale, TStringArg unitSuffix = NULL + * \brief Sets the display units. + * + * This routine sets the units used to display this Interface's + * unit-possessing Controls. + * Parameters: + * unitType: The type of units to be changed + * scale: The multiplicative factor to apply to + * the displayed values of controls + * with the input unitType. + * The scale is relative to the default units + * for the indicated unitType. + * The default unit for linear is inches, + * and the default unit for angular is degrees. + * unitSuffix: A NULL terminated character string that can + * be appended after a number to indicate the + * displayed units + */ + virtual void setUnits(int unitType, double scale, TStringArg unitSuffix = NULL) = 0; + + /*! \function virtual int setBackgroundColor(int color) + * \brief Set the interface attribute. + * + * The following methods will be used to set user interface attributes + * however they are not currently supported + */ + virtual int setBackgroundColor(int color) = 0; + virtual int setForegroundColor(int color) = 0; + virtual int setWidth(int newWidth) = 0; + virtual int setHeight(int newHeight) = 0; + }; // end class MassEditInterface + + + /*! \class Mesh + * \brief Geometric mesh. Contains vertices and faces. + * + * + * This is the base interface for any Mesh contained in an RPC. + * A Mesh is composed of an array of 3D Vertices and an array + * of Faces. + * + */ + class Mesh : public Param { + public: + + /*! \class Face + * \brief Vertices and visibilities of a face. + * + * A Face object contains six integers. The first three integers + * are the indices of this Face's vertices in the Vertex + * array returned by this Face's parent Mesh. + * The second three integers determine the visibility of + * the edges of this face. e0 == visibility of edge v0-v1, + * e1 == visibility of edge v1-v2, and e2 == visibility of edge v2-v0 + */ + class Face { + public: + int v0, v1, v2; + int e0, e1, e2; + + }; // end class Mesh::Face + + /*! \class Vertex + * \brief 3-D Coordinate. + * + * A Vertex object contains the 3-D coordinates of a vertex. + */ + class Vertex { + public: + double x, y, z; + + }; // end class Mesh::Vertex + + + /*! \function virtual ~Mesh(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface. + */ + virtual ~Mesh(void) {} + + /*! \function virtual double smoothingAngle(void) + * \brief Get the smooth angle. + * + * This routine gets the angle, in degrees, + * to use when smoothing this mesh. + * If the return is negative, no smoothing should be performed. + */ + virtual double smoothingAngle(void) const = 0; + + /*! \function virtual void smoothingAngle(double a) + * \brief Set the smooth angle. + * + * This routine sets the angle, in degrees, + * to use when smoothing this mesh. + */ + virtual void smoothingAngle(double a) = 0; + + /*! \function virtual const Face *getConstFaces(void) + * \brief Gets the array of faces (const). + * + * This routine gets this Mesh's array of faces. + * Returns: + * The pointer to this Mesh's Faces array. Note that if + * this Mesh is a Stream Accessor, this routine can + * only return NULL. + */ + virtual const Face *getConstFaces(void) const = 0; + + /*! \function virtual const Vertex *getConstVertices(void) + * \brief Get the array of vertices. + * + * This routine gets this Mesh's array of vertices. + * Returns: + * The pointer to this Mesh's Vertices array. Note that if + * this Mesh is a Stream Accessor, this routine can + * only return NULL. + */ + virtual const Vertex *getConstVertices(void) const = 0; + + /*! \function virtual Face *getFaces(void) + * \brief Get the array of faces. + * + * This routine gets this Mesh's array of faces. + * Returns: + * A newly allocated array of faces, which the caller is + * responsible for deallocating, or NULL if an error occurred. + */ + virtual Face *getFaces(void) const = 0; + + /*! \function virtual int getNumFaces(void) + * \brief Get the number of faces. + * + * This routine gets the number of faces in this Mesh + * Returns: + * The number of faces in this Mesh, + * or < 0 if an error occurred. + */ + virtual int getNumFaces(void) const = 0; + + /*! \function virtual int getNumVerts(void) + * \brief Gets the number of vertices. + * + * This routine gets the number of vertices in this Mesh + * Returns: + * The number of vertices in this Mesh, + * or < 0 if an error occurred. + */ + virtual int getNumVerts(void) const = 0; + + /*! \function virtual Vertex *getVerts(void) + * \brief Gets the vertices. + * + * This routine gets this Mesh's array of vertices. + * Returns: + * A newly allocated array of vertices, which the caller is + * responsible for deallocating, or NULL if an error occurred. + */ + virtual Vertex *getVerts(void) const = 0; + + /*! \function virtual bool setFaces(Face *faces, int numFaces) + * \brief Sets the faces. + * + * This routine sets the faces in this Mesh + * Note that if this Mesh is a Stream Accessor, + * this routine will do nothing. + * Parameters: + * faces: The faces to store in this Mesh + * numFaces: The number of faces in the input faces array + * Returns: + * true iff the faces were successfully stored. + */ + virtual bool setFaces(Face *faces, int numFaces) = 0; + + /*! \function virtual bool setVerts(Vertex *verts, int numVerts) + * \brief Sets the vertices. + * + * This routine sets the vertices in this Mesh + * Note that if this Mesh is a Stream Accessor, + * this routine will do nothing. + * Parameters: + * verts: The vertices to store in this Mesh + * numVerts: The number of vertices in the input faces array + * Returns: + * true iff the vertices were successfully stored. + */ + virtual bool setVerts(Vertex *verts, int numVerts) = 0; + + }; // end class Mesh + + + /*! \class ParamList + * \brief List of Params keyed by strings. + * + * + * An RPC file, after its version number and the typecode, + * is composed entirely of ParamLists. + * ParamLists contain Params, which are keyed by strings. + * This is the base class for all RPCapi ParamLists. + * There are two basic implementations + * of ParamList: one which accesses a ParamList in memory, + * and one which accesses a ParamList in a stream (like a byte + * array or a file). The only difference is that the latter + * implementation will always return NULL when getConst() is called. + * + */ + class ParamList : public Param { + public: + /*! \function virtual ~ParamList(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface. + */ + virtual ~ParamList(void) {} + + /*! \function virtual bool contains(TStringArg key, ...) + * \brief Does the list contain the parameter? + * + * This routine determines if this List contains a Parameter + * matching the input key. + * Parameters: + * key: The key of the Parameter to be checked. + * ...: Additional arguments used in creating the key string. + * key and ... are of the same format as arguments + * to printf(). + * NOTE: Use %g when specifying floating point + * or double numbers! + * Returns: + * true iff this List contains the input key. + */ + virtual bool contains(TStringArg key, ...) const = 0; + + /*! \function virtual int count(void) + * \brief Gets the number of params in the list. + * + * This routine returns the number of parameters in this ParamList. + * Returns: + * number of parameters in this List. + */ + virtual int count(void) const = 0; + + /*! \function virtual Param *get(TStringArg key, ...) + * \brief Gets a Param matching the input key. + * + * This routine retrieves a Param matching the input Key. + * It allocates a new copy of the parameter and returns a pointer to it. + * Parameters: + * key: The key of the Parameter to be retrieved. + * ...: Additional arguments used in creating the key string. + * key and ... are of the same format as arguments + * to printf(). + * NOTE: Use %g when specifying floating point or + * double numbers! + * Returns: + * A pointer to a newly created copy of the desired Parameter, + * or NULL if it is not found or an error occurs. + */ + virtual Param *get(TStringArg key, ...) const = 0; + + /*! \function virtual const Param *getConst(TStringArg key, ...) + * \brief Gets the const pointer to the param of the passed key. + * + * This routine retrieves a constant pointer to the Param matching + * the input Key. This routine will return NULL if this ParamList + * is a stream accessor (since no Param of this List exists in + * memory). + * Parameters: + * key: The key of the Parameter to be retrieved. + * ...: Additional arguments used in creating the key string. + * key and ... are of the same format as arguments + * to printf(). + * NOTE: Use %g when specifying floating point + * or double numbers! + * Returns: + * A pointer to the desired Parameter, or NULL if it is not + * found, or if this List is a StreamAccessor. + */ + virtual const Param *getConst(TStringArg key, ...) const = 0; + + /*! \function virtual int getKeys(const TString **&keys, int reserved = 0) + * \brief Gets an array of keys. + * + * This routine allocates an array of character strings, + * fills in the array with new strings which are copies + * of the keys of this List, and returns a pointer to the array. + * NOTE: On an error, or in the event that there are no keys, + * the returned keys pointer is NULL. + * Parameters: + * keys: Reference pointer through which is + * returned the array of keys. + * Delete the array. Only delete keys from getKeyPtrs. + * reserved: Reserved internal parameter. + * Returns: + * The number of keys, or < 0 if an error occurred. + */ + virtual int getKeys(const TString **&keys, int reserved = 0) const = 0; + virtual int getKeyCopies(TString **&keys, int reserved = 0) const = 0; + + /*! \function virtual const TString &getPath(void) + * \brief Get the path to the ParamList. + * + * This routine gets the path of this ParamList. + * Returns: + * The path of this ParamList. + */ + virtual const TString &getPath(void) const = 0; + + /*! \function virtual bool insert(TStringArg key, Param &data, bool insertCopy = true) + * \brief Insets key and data to the list. + * + * This routine uniquely inserts a key-data pair into this List. + * It creates copies of the key and the Param, and inserts + * these copies. + * Parameters: + * key: The key of the Parameter to be inserted. + * data: The Parameter to be inserted + * insertCopy: create a copy of the data, and insert + * the copy, if this flag is true. + * Returns: + * true iff the key and data were successfully inserted. + */ + virtual bool insert(TStringArg key, Param &data, + bool insertCopy = true) = 0; + + /*! \function virtual bool remove(TStringArg key) + * \brief Deletes the key and data. + * + * This routine deletes the indicated key and its corresponding data. + * Note that this function will deallocate the memory associated + * with the key and its data. + * Parameters: + * key: The key of the Parameter to be removed. + * Returns: + * true iff the key was found and the pair was successfully removed. + */ + virtual bool remove(TStringArg key) = 0; + + /*! \function virtual void setPath(TStringArg newPath) + * \brief Sets the paramList path. + * + * This routine sets the path of this ParamList. + * Parameters: + * inPath: The new path of this ParamList. + */ + virtual void setPath(TStringArg newPath) = 0; + + /*! \function virtual bool update(TStringArg key, Param &data, bool insertCopy = true) + * \brief Update the input key. + * + * This routine updates the value of the input key. + * Note that this routine is semantically equivalent to calling + * remove(); if (get() != NULL) insert() + * Parameters: + * key: The key of the Parameter to be updated. + * data: The new value the Param should take + * insertCopy: create a copy of the data, and insert + * the copy, if this flag is true. + * Returns: + * true iff the key's Param was found and updated + */ + virtual bool update(TStringArg key, Param &data, + bool insertCopy = true) = 0; + + }; // end class ParamList + + + /*! \class PrimP + * \brief Template for every primitive type in an RPC. + * + * This template class is used to represent every primitive type + * stored in an RPC (int, char, double, ....) + * I apologize for not calling this class Prim, but Visual C++ + * won't compile if I use that name. + * + */ + template + class PrimP : public Param { + public: + /*! \function virtual ~PrimP(void) + * \brief Destructor. + * + * This is the dummy destructor for this class + */ + virtual ~PrimP(void) { } + + /*! \function virtual operator Data () + * \brief Casts as Data. + * + * This routine casts this object as a primitive of type Data. + * This routine allows functions to treat this object + * as if it were a primitive of type data. + * Return: + * primitive value of this object. + */ + virtual operator Data () = 0; + + /*! \function virtual Data getValue(void) + * \brief Gets the value. + * + * This routine returns this Prim object's primitive value. + * This function is useful when + * C++ won't let us cast a Prim as a Data primitive. + * This can happen when we dereference a pointer to a + * Prim object. + * Returns: + * The primitive value of this Prim. + */ + virtual Data getValue(void) const = 0; + + /*! \function virtual void setValue(Data inValue) + * \brief Sets the value. + * + * This routine sets the value. + * Parameters: + * inValue: the value to set this PrimParam to. + */ + virtual void setValue(Data inValue) = 0; + + }; // end class PrimP + + + /*! \class PrintOptions + * \brief Print options for an RPC + * + * + * + * The PrintOptions class encapsulates several different test options. + * At most one of these objects should exist in a program execution; + * It will be pointed to by the static PrintOptions pointer. + * Param classes' print() functions reference the PrintOptions + * to see how to print themselves. + * + */ + class PrintOptions : public BaseObject { + public: + /*! \function virtual ~PrintOptions(void) + * \brief Destructor. + * + * This is the destructor. + */ + virtual ~PrintOptions(void) {} + + /*! \function virtual const TString &getUseString(void) + * \brief Gets the print options available. + * + * This routine gets a string specifying the options available + * to this PrintOptions implementation. These are the options + * which can be passed to the setOptions routine. + * Returns: + * A pointer to the string specifying this PrintOption's + * possible settings. + */ + virtual const TString &getUseString(void) const = 0; + + /*! \function virtual void reset(void) + * \brief Reset PrintOptions. + * + * This routine resets the options in this PrintOptions object. + */ + virtual void reset(void) = 0; + + /*! \function virtual bool setOptions(int argc, const char **argv) + * \brief Sets the options from the passed array. + * + * This routine sets the options in this PrintOptions object. + * It parses the input arg array, ignoring arguments it does + * not understand. The arguments possible are: + * -vm == print Meshes' vertices and faces + * -vtm == print TextureMeshes' vertices and faces + * -op name == prefix for output preview files + * -na # == Number of angles from which to take test textures + * -aa # == Specific angle from which to take test texture + * -nt # == Number of time steps from which to take test textures + * -ot name == prefix for output texture files + * -c # == maximum number of entries per list to print + * -ex name == list entries with this name will not be printed + * Parameters: + * argc: The number of arguments in the array + * argv: The arguments to parse. + * Returns: + * true iff the argument array was successfully parsed. + */ + virtual bool setOptions(int argc, const char **argv) = 0; + + /*! \function virtual void setRPC(ParamList *rpc, TStringArg fileName) + * \brief Sets the main RPC list and file name. + * + * This routine sets the main RPC list and the file name + * of the RPC which will be printed. + * Parameters: + * rpc: The main ParamList of the RPC. + * fileName: The name of the RPC file. + */ + virtual void setRPC(ParamList *rpc, TStringArg fileName) = 0; + + }; // end class PrintOptions + + + + /*! \class Texture + * \brief bitmap image of an instance + * + * + * A Texture represents a bitmap image retrieved from an Instance. + * + * + */ + class Texture : public Param { + public: + // The Scale class wraps all of the scale modes + // which may be used when retrieving texture images from an RPC. + // FAST indicates that the image should be scaled by a power of two + // which most closely matches the user's input dimensions. + // SLOW indicates that the user's exact dimensions should be used. + // To use in your code, say "RPCapi::Texture::Scale::FAST", or + // whatever scale mode is appropriate. + class Scale { + public: + typedef enum SCALE_CODE { + FAST = 1, + SLOW = 2 + } SCALE_CODE_T; // end typedef enum SCALE_CODE + + }; // end class Scale + + // These are the possible channels which can be retrieved. + // To use in your code, say "RPCapi::Texture::Channel::RGB" + class Channel { + public: + typedef enum CHANNEL_CODE { + RGB = 0, + ALPHA = 1 + } CHANNEL_CODE_T; // end typedef enum CHANNEL_CODE + }; // end class Channel + + // These are the possible values for the FIELD parameter + // of the property routine below: + typedef enum PROPERTY_NAMES { + SHININESS = 0, + SHININESS_STRENGTH = 1, + SELF_ILLUMINATION = 2, + TWO_SIDED = 3 + } PROPERTY_NAMES_T; // end typedef enum PROPERTY_NAMES + + + /*! \function virtual ~Texture(void) + * \brief Destructor. + * + * This is the dummy destructor for this interface + */ + virtual ~Texture(void) {} + + /*! \function virtual Texture *backFaceTexture(void) + * \brief Gets the texture for backfaces. + * + * This routine returns a pointer to the texture for backfaces, + * if this Texture possesses such. If not, NULL is returned. + * Returns: + * A pointer to the back-face Texture, or NULL + * if there is no back-face Texture. + */ + virtual Texture *backFaceTexture(void) = 0; + + /*! \function virtual int data(unsigned char *&buffer, bool allocBuffer, int channel, int scaleMode, int &width, int &height) + * \brief Retrieves texture data. + * + * This routine retrieves data from this Texture. + * This routine allocates the data array used to store + * this Texture, fills it in with the appropriate data, + * and returns a pointer to the array. + * It is the client application's responsibility to + * deallocate the data array when finished with it. + * This routine returns the size of the buffer array. + * Parameters: + * buffer: buffer in which the image data is placed. + * This buffer is allocated inside this function, + * and a pointer to it is returned by reference. + * allocBuffer:if true, this routine allocates buffer on its own; + * if false, this routine fills in the buffer passed + * to it, or returns the size needed if that buffer + * is NULL. + * channels: specifies which channels to include in the + * texture data returned (see typedef Channels above). + * scaleMode: specifies which scale mode to use. + * (see typedef ScaleMode above). + * width: The width to which the texture is to be scaled. + * If ScaleMode is set to SCALE_FAST, the returned + * texture's width will be scaled to the nearest power + * of 2. Set to 0 if you do not want the texture + * to be scaled. + * height: The height to which the texture is to be scaled. + * If ScaleMode is set to SCALE_FAST, the returned + * texture's height will be scaled to the nearest power + * of 2. Set to 0 if you do not want the texture + * to be scaled. + * Returns: + * The number of bytes used or required to store the requested, + * image, or < 0 on an error. + */ + virtual int data(unsigned char *&buffer, + bool allocBuffer, + int channel, + int scaleMode, + int &width, + int &height + ) = 0; + + + /*! \function virtual const TString &getID(void) + * \brief Gets the texture identifier. + * + * This routine retrieves the unique identifier for this Texture. + * This identifier includes all information used in producing + * the image data of this Texture. + * Returns: + * The unique identifier string for this Texture. + */ + virtual const TString &getID(void) const = 0; + + /*! \function virtual bool hasChannel(int channel) + * \brief Does the texture have an alpha channel? + * + * This routine determines whether this Texture has an Alpha channel. + * Parameters: + * channel: The channel to check for (can be RGB or ALPHA). + * Returns:S + * true iff this Texture contains the specified channel. + */ + virtual bool hasChannel(int channel) const = 0; + + /*! \function virtual bool property(int FIELD, double &value) + * \brief is the field specified by this texture? + * + * This routine returns true if the indicated field is specified + * by this Texture, and false if it is not. If the field is + * specified, its value is cast to a double and returned by + * reference, and should be applied by the client application. + * Parameters: + * FIELD: specifies which field to check + * The valid values for field are specified above. + * value: If this Texture contains the indicated property, + * its value is returned as a double through value. + * Returns: + * true iff this Texture specifies the indicated property. + */ + virtual bool property(int FIELD, double &value) const = 0; + + }; // end class Texture + + + /*! \class TextureMesh + * \brief Array of 2D vertecies and faces + * + * This is the base interface for the Texture Meshes contained in an RPC. + * A TextureMesh is composed of an array of 2D texture vertices + * (that contain an index into a Mesh that refers to a specific + * 3D Vertex) and 2D texture faces. Each face is composed of three + * texture vertices and a texture index. + * + * + */ + class TextureMesh : public Param { + public: + + /*! \class Face + * \brief Contains vertices and identifier. + * + * A Face object contains three integers, which are the indices + * of the texture vertices composing the face, and the identifier + * for the texture which should be used to paint the face. + * If the TextureMesh is retrieved directly from an RPC file, + * this identifier will be a float (corresponding to the + * horizontal viewing angle from which the texture was taken). + * If the TextureMesh is retrieved from an RPCapi::Instance using + * getTextures(), the identifier is an int, corresponding to + * the index in the array of Textures that points to the Texture + * to use to paint the face. + */ + class Face { + public: + int v0, v1, v2; + union { + int textureIndex; + float textureAngle; + } u; + + }; // end class TextureMesh::Face + + /*! \class Vertex + * \brief 2D coordinate of the vertex. + * A Vertex object contains the 2-D coordinates of a texture vertex + * and the index of the vertex to which the texture Vertex corresponds. + */ + class Vertex { + public: + double x, y; + + }; // end class TextureMesh::Vertex + + + /*! \function virtual ~TextureMesh(void) + * \brief Destructor. + * + * This is the dummy destructor for this class. + */ + virtual ~TextureMesh(void) {} + + /*! \function virtual const Face *getConstFaces(void) + * \brief Gets the faces (const) + * + * This routine gets this TextureMesh's array of faces. + * Returns: + * The pointer to this TextureMesh's Faces array. Note that if + * this TextureMesh is an Accessor, this routine + * can only return NULL. + */ + virtual const Face *getConstFaces(void) const = 0; + + /*! \function virtual const Vertex *getConstVertices(void) + * \brief Gets the vertices (const). + * + * This routine gets this TextureMesh's array of vertices. + * Returns: + * The pointer to this TextureMesh's Vertices array. Note that if + * this TextureMesh is an Accessor, this routine + * can only return NULL. + */ + virtual const Vertex *getConstVertices(void) const = 0; + + /*! \function virtual Face *getFaces(void) + * \brief Gets the faces. + * + * This routine gets this TextureMesh's array of faces. + * Returns: + * A newly allocated array of faces, which the caller is + * responsible for deallocating, or NULL if an error occurred. + */ + virtual Face *getFaces(void) const = 0; + + /*! \function virtual int getNumFaces(void) + * \brief Gets the number of faces. + * + * This routine gets the number of faces in this TextureMesh + * Returns: + * The number of faces in this TextureMesh, + * or < 0 if an error occurred. + */ + virtual int getNumFaces(void) const = 0; + + /*! \function virtual int getNumVerts(void) + * \brief Returns the number of vertices. + * + * This routine gets the number of vertices in this TextureMesh + * Returns: + * The number of vertices in this TextureMesh, + * or < 0 if an error occurred. + */ + virtual int getNumVerts(void) const = 0; + + /*! \function virtual Vertex *getVerts(void) + * \brief Gets the vertices. + * + * This routine gets this TextureMesh's array of vertices. + * Returns: + * A newly allocated array of vertices, which the caller is + * responsible for deallocating, or NULL if an error occurred. + */ + virtual Vertex *getVerts(void) const = 0; + + /*! \function virtual bool setFaces(Face *faces, int numFaces) + * \brief Sets the faces. + * + * This routine sets the faces in this TextureMesh + * Note that if this TextureMesh is a Stream Accessor, + * this routine will do nothing. + * Parameters: + * faces: The faces to store in this TextureMesh + * numFaces: The number of faces in the input faces array + * Returns: + * true iff the faces were successfully stored. + */ + virtual bool setFaces(Face *faces, int numFaces) = 0; + + /*! \function virtual bool setVerts(Vertex *verts, int numVerts) + * \brief Sets the verticies. + * + * This routine sets the vertices in this TextureMesh + * Note that if this TextureMesh is a Stream Accessor, + * this routine will do nothing. + * Parameters: + * verts: The vertices to store in this TextureMesh + * numVerts: The number of vertices in the input faces array + * Returns: + * true iff the vertices were successfully stored. + */ + virtual bool setVerts(Vertex *verts, int numVerts) = 0; + + }; // end class TextureMesh + + + /*! \class Vector + * \brief 3D vector + * + * + * 3D vector, with x, y, and z components. + * + */ + class Vector : public Param { + public: + // Destructor + virtual ~Vector(void) {} + + // These routines get / set the components of the vector. + virtual void get(double &ox, double &oy, double &oz) const = 0; + virtual void set(double ix, double iy, double iz) = 0; + virtual double xget(void) const = 0; + virtual void xset(double n) = 0; + virtual double yget(void) const = 0; + virtual void yset(double n) = 0; + virtual double zget(void) const = 0; + virtual void zset(double n) = 0; + + }; // end class Vector + + + // This is the destructor + virtual ~RPCapi(void) {} + +#ifdef RPC_CONTENT_CLIENT + /*! \function virtual void about(TString *product, TString *version, TString *copyright, TString *etc) + * \brief Retrieves info from the module. + * + * This routine retrieves about info from this module. + * If a requested component (product, version, etc...) is + * NULL, then it is ignored. + * Parameters: + * product: Product name / description. + * version: Version number. + * copyright: Copyright info. + * etc: Additional info. + * + */ + virtual void about(TString *product, TString *version, + TString *copyright, TString *etc) const = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + + /*! \function bitmapToMSBitmap + * + * \brief Converts RGB bitmap into MS bitmap. + * + * + * This routine converts an RGB bitmap into an MS-format bitmap. + * It allocates the new bitmap memory, so the caller is responsible + * for deallocation. + * NOTE: The data returned can be written directly to a file, + * which is then viewable as a MicroSoft bitmap. + * This routine returns the size of the bitmap array. + * Parameters: + * bitmap: The bitmap bytes. + * width: The horizontal resolution of the bitmap. + * height: The vertical resolution of the bitmap. + * msBitmap: The new MS-format bitmap is allocated + * by this routine and returned through this + * reference parameter. + * Returns: + * The size in bytes of msBitmap. + * + */ + virtual int bitmapToMSBitmap(unsigned char *bitmap, int width, int height, + unsigned char *&msBitmap) const = 0; + + /*! \function clearErrorLog(void) + * \brief Clears the error log. + * + * This routine clears the error log for this RPCapi object. + */ + virtual void clearErrorLog(void) = 0; + + /*! \function virtualClient *getClient(void) + * \brief Gets the pointer to the client object. + * + * This routine returns a pointer to the client of this API object. + * Returns: + * A pointer to this API's client object. + */ + virtual Client *getClient(void) const = 0; + + /*! \function virtual const TString &getErrorLog(void) + * \brief Returns error log for an RPCapi object. + * + * This routine returns the error log for this RPCapi object. + * The error log may contain useful debugging information should + * the RPCapi return an unexpected error. + * Returns: + * The current error log, as a string. + */ + virtual const TString &getErrorLog(void) const = 0; + + /*! \function virtual const License *getLicense(void) + * \brief Gets a pointer to a license. + * + * This routine returns a pointer to the License of the current DLL. + * The returned License contains the license information about this DLL, + * specifying COMMERCIAL, EDUCATIONAL, etc. See class RPCapi::License + * for details. + * Returns: + * A pointer to this DLL's License. + */ + virtual const License *getLicense(void) const = 0; + + /*! \function virtual BaseObject *newObject(int objectCode) + * \brief Makes a new object. + * + * This routine allocates a new object of the input object code + * and returns a pointer to it. The param codes for implementations + * of each of the available classes are listed near the top + * of this file, just after the class declarations. + * Parameters: + * objectCode: The object code of the desired object. + * See class ObjectCode, above. + * Returns: + * A pointer to the newly-allocated object, or NULL if + * an error occured (such as an unrecognized object code). + */ + virtual BaseObject *newObject(int objectCode) const = 0; + + /*! \function virtual Param *newParam(int typeCode) + * \brief Makes a new Param object. + * + * This routine allocates a new object of the input type code and + * returns a pointer to it. + * Parameters: + * typeCode: The type code of the desired object. + * Returns: + * A pointer to the newly-allocated object, or NULL if + * an error occured (such as an unrecognized typeCode). + */ + virtual Param *newParam(int typeCode) const = 0; + + /*! \function virtual TString *newTString(void) + * \brief Makes a new TString object. + * + * These routines allocate a new TString object. + * Parameters: + * str: The constant string value to assign the TString. + * strp: The string pointer for the new TString. + */ + virtual TString *newTString(void) const = 0; + virtual TString *newTString(const char *str) const = 0; + virtual TString *newTStringPtr(char *str) const = 0; +#ifdef RPC_WIDE_STRINGS + virtual TString *newTString(const wchar_t *str) const = 0; + virtual TString *newTStringPtr(wchar_t *str) const = 0; +#endif // ifdef RPC_WIDE_STRINGS + +#ifdef RPC_CONTENT_CLIENT + /*! \function virtual void *notify(int event, ...) + * \brief + * + * This is a catch-all routine that allows the client to + * inform the API about various events. Any events are purely optional. + * Parameters: + * event: The event that occurred. Possible values: + * 0: no event. + * 1: a scene is beginning to load from a save file. + * 2: a scene has finished loading from a save file. + * 3: suppress initial updateACM call. + * 4: set this API to connect to an ACM + * if Texture::data() cannot open a file from a + * deserialized Texture. + * 5: unset the flag set in notify(4). + * 6: Flag RPCapi to initialize its ACM settings + * according to this flow when it first attempts + * to read its INI file: + * If an INI file is present, read it. Otherwise, ... + * If a local ACM is found, use it. Otherwise, ... + * Scan for ACMs. + * If exactly 1 is found, use it. + * If > 1 is found, display ACM selection dialog. + * If < 1 is found, display content settings dialog. + * 7: launch a dialog to configure ACM location + * w/out contacting the ACM. + * args: phwnd (parent window) + * ...: Additional arguments, dependent on event, + * as documented above. + * Returns: + * Dependent on the event, above. + * + */ + virtual void *notify(int event, ...) = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + /*! \function virtual RPCapi::ParamList *openRPCFile(TStringArg fileName) + * \brief Returns Opens an RPC and gets Params. + * + * This routine opens an RPC file and returns the ParamList associated + * with the open stream. + * Parameters: + * fileName: The name of the RPC file. + * Returns: + * A pointer to the newly allocated ParamList, or NULL on an error. + */ + virtual RPCapi::ParamList *openRPCFile(TStringArg fileName) const = 0; + + /*! \function virtual void setClient(Client *CI) + * \brief Sets pointer to ClientInstance. + * + * This routine sets the pointer to this Instance's ClientInstance. + * Parameters: + * client: A pointer to the client code with which this + * API object should be paired. + */ + virtual void setClient(Client *CI) = 0; + + /*! \function virtual void setCreator(Creator *creator) + * \brief Sets creator being used. + * + * This routine sets the Creator currently being used to create an RPC. + * Note that this routine can only be used if the license of the + * RPCapi.dll includes the Creator license. + * Parameters: + * creator: A pointer to the Creator being used. + */ + virtual void setCreator(Creator *creator) = 0; + +#ifdef RPC_CONTENT_CLIENT + /*! \function virtual void updateACM(int acmType = 0, TStringArg acmExe = NULL, TStringArg acmDNS = NULL, short acmPort = 0) + * \brief Reconnects to ACM to get updates. + * + * This function tells the global RPCapi object that it should + * reconnect to the ACM and request content. + * NOTE: This function will cause Client::RPCgetPaths() to be called. + * Parameters: + * acmType: Type of the ACM. + * 0 == unspecified. + * 1 == locally started by client. + * 2 == network. + * 3 == none. + * 4 == locally started by client if not already specified. + * 5 == network if not already specified. + * 6 == none if not already specified. + * acmExe: If not NULL, exe of ACM to launch locally. + * acmDNS: If not NULL, the network location of the ACM. + * acmPort: If not 0, the port of the ACM. + */ + virtual void updateACM(int acmType = 0, TStringArg acmExe = NULL, + TStringArg acmDNS = NULL, short acmPort = 0) = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + /*! \function virtual void updatePaths(void) + * \brief Updates path list to get new RPC files. + * + * This function tells the global RPCapi object that it should update + * its paths containing RPC files. The client code should call + * this function whenever it desires the RPCapi to look in new directories + * for RPC files. + * NOTE: This function will cause Client::RPCgetPaths() to be called. + */ + virtual void updatePaths(void) = 0; + +#ifdef RPC_CONTENT_CLIENT + /*! \function virtual bool userMessage(int msgType, bool ret, TStringArg title, TStringArg msg, ...) + * \brief Display a message to the user. + * + * The RPC API calls this routine when it has some message + * that it wishes to display to the user. + * Parameters: + * msgType: The type of this message. + * 1: generic information. + * 2: warning + * 3: error. + * 4: catastrophic error (we're going to die). + * ret: If true, we need a yes-no response. + * title: If a window will display the message, use this title. + * msg: The message to display. + * .../args: Additional message args, printf style. + * Returns: + * If ret is true, true or false (depending on the user's response). + * If the message will not be displayed, or if ret is false, + * return false. + */ + virtual bool userMessage(int msgType, bool ret, + TStringArg title, TStringArg msg, ...) const = 0; + virtual bool userMessageV(int msgType, bool ret, + TStringArg title, TStringArg msg, va_list args) const = 0; +#endif // ifdef RPC_CONTENT_CLIENT + + // This is the typedef describing the function exported by the + // RPCapi dynmamically-linked library. +#if (defined (_MSC_VER) && _MSC_VER >= 1310) + typedef RPCapi *(*RPCgetAPIFuncType)(void *rpcCLS); +#else + typedef RPCapi *(*RPCgetAPIFuncType)(void *rpcCLS = NULL); +#endif + +#ifdef PORT_WINDOWS + /*! \function static RPCapi *RPCgetAPI(void *rpcCLS = NULL) + * \brief Makes a new RPCapi object. + * + * This function allocates a new RPCapi object and returns + * a pointer to it. Be sure to delete this pointer on + * program termination. + * To use this function, use code similar to the following + * (in Windows): + * HMODULE rpcDLL = LoadLibrary("RPCapi.dll"); + * RPCapi::RPCgetAPIFuncType RPCgetAPIFilePtr = + * (RPCapi::RPCgetAPIFuncType) + * GetProcAddress(rpcDLL, "RPCgetAPI"); + * ... + * FreeLibrary(rpcDLL); + * Parameters: + * rpcCLS: FOR INTERNAL USE ONLY. + * Pointer to the ContentClient associated with + * this RPCapi. If NULL, the returned RPCapi will + * spawn an RPCcls with which it will obtain content. + * Returns: + * A pointer to the global API object. + */ + static RPCapi *RPCgetAPI(void *rpcCLS = NULL); + +#endif // ifdef PORT_WINDOWS + +}; // end class RPCapi + + +/*! \function extern "C" RPCapi *RPCgetAPI(void *rpcCLS = NULL) +* \brief Makes a new RPCapi object. +* +* This function allocates a new RPCapi object and returns +* a pointer to it. Be sure to delete this pointer on +* program termination. +* To use this function, use code similar to the following +* (in Unix): +* void *rpcDLL = dlopen("libRPCapi.so", RTLD_LAZY); +* RPCapi::RPCgetAPIFuncType RPCgetAPIFilePtr = +* (RPCapi::RPCgetAPIFuncType) +* dlsym(rpcDLL, "RPCgetAPI"); +* ... +* dlclose(rpcDLL); +* Parameters: +* Parameters: +* rpcCLS: FOR INTERNAL USE ONLY. +* Pointer to the ContentClient associated with +* this RPCapi. If NULL, the returned RPCapi will +* spawn an RPCcls with which it will obtain content. +* Returns: +* A pointer to the global API object. +*/ +#ifdef PORT_MAC_OR_UNIX +#ifdef PORT_MAC +#pragma export on +#endif // ifdef PORT_MAC +extern "C" RPCapi *RPCgetAPI(void *rpcCLS = NULL); +#ifdef PORT_MAC +#pragma export off +#endif // ifdef PORT_MAC +#endif // ifdef PORT_MAC_OR_UNIX + + +#ifdef PORT_WINDOWS +const char BeginRPCapiKeyboardControl[] = +"RPCapi Begin Keyboard Control"; +const char EndRPCapiKeyboardControl[] = +"RPCapi End Keyboard Control"; +#ifdef IMPLEMENT_THUMBNAIL_BROWSER +const char RegisterModelessDialogWithClient[] = +"RPCapi Register Modeless Dialog With Client"; +#endif // ifdef IMPLEMENT_THUMBNAIL_BROWSER +#endif // ifdef PORT_WINDOWS + + +typedef RPCapi::TString TString; +typedef RPCapi::TStringArg TStringArg; + +// These macros encode and extract a TString from an ellipsis argument. +// str is a TString reference. +inline const TString *TSA(const TString &ts) { return &ts; } +inline TString *TSAU(va_list &args) { return va_arg(args, TString *); } + +#endif // RPCAPI_H \ No newline at end of file diff --git a/SDK/inc/RPCapiEx.h b/SDK/inc/RPCapiEx.h new file mode 100644 index 0000000..4c421c6 --- /dev/null +++ b/SDK/inc/RPCapiEx.h @@ -0,0 +1,203 @@ +// This header file exists to provide multilanguage functionality +// for the RPC API. An RPCapi object (from RPCapi.h) can be +// nwm Create a new wrapper class RPCapi2 + +// dynamically cast to an RPCapiML object in order to +// access this functionality. + +#ifndef RPC_API_CLIENT_INTERFACE_H +#define RPC_API_CLIENT_INTERFACE_H + +#include "RPCapi.h" + + +class RPCapiEx { +public: + class ClientEx { + public: + virtual ~ClientEx(void) {} + // This is a catch-all routine that allows the API to + // inform the client about various events. + // Parameters: + // event: The event that occurred. Possible values: + // 6: a new RPC ID was added to the global + // selection table as a result of automatic + // repair during RPCapi::Instance::fromStream + // or RPCapi::Instance::setRPCFilename. + // args: const RPCapiEx::IDex *newId + // 7: RPCapi wants to display a modal window, + // and needs a parent HWND. + // args: HWND *phwnd + // ...: Additional arguments, dependent on event, + // as documented above. + // Returns: + // Dependent on the event, above. + virtual void *RPCnotify(int event, ...) = 0; + }; // class ClientEx + + + #ifdef RPC_CONTENT_CLIENT + class ClientInterface; + class ClientInterface : public RPCapi::BaseObject + { + public: + // This is the dummy destructor for this interface. + virtual ~ClientInterface(void) {} + + // The RpcApiClientInterface::Window class encapsulates all of the + // different window type codes. + // To use in your code, say + // "RpcApiClientInterface::Window::ACM_CONFIGURATION", + // or whatever window type is appropriate. + class Window { + public: + typedef enum WINDOW_CODE { + NONE = -1, + ACM_CONFIGURATION = 1, // The dialog that allows the user to select + // the ACM that this client points to + } WINDOW_CODE_T; // end typedef enum WINDOW_CODE + }; // end class RPCapi::InstanceInterface::Window + + #ifdef PORT_WINDOWS + // This routine displays the specified dialog to the screen. + // Parameters: + // parentWindow: handle to the parent window. + // windowToDisplay: the indicator of which window to display + // see UserInterface::Window for options + virtual int show(HWND parentWindow, int windowToDisplay) = 0; + #endif // ifdef PORT_WINDOWS + + #ifdef PORT_MAC + // This routine displays the specified dialog to the screen. + // Parameters: + // parentWindow: handle to the parent window. + // windowToDisplay: the indicator of which window to display + // see UserInterface::Window for options + virtual int show(WindowRef parentWindow, int windowToDisplay) = 0; + #endif // ifdef PORT_MAC + + // This routine hides this dialog. + virtual int hide(void) = 0; + + // This routine sets the Client which this ClientInterface edits. + // Parameters: + // client: The RPCapi::Client for this Interface. + virtual void setClient(RPCapi::Client *client) = 0; + + // The following methods will be used to set user interface attributes + // however they are not currently supported + virtual int setBackgroundColor(int color) = 0; + virtual int setForegroundColor(int color) = 0; + virtual int setWidth(int newWidth) = 0; + virtual int setHeight(int newHeight) = 0; + + }; // end class ClientInterface + #endif // ifdef RPC_CONTENT_CLIENT + + + // This extends the interface for the unique IDs assigned to each RPC file + // by introducing directy string conversion routines. + class IDex : public RPCapi::ID { + public: + typedef enum TYPE_CODE_TYPE { + TYPE_CODE = 18 + } TYPE_CODE_T; + virtual ~IDex(void) {} + + // This routine reads this from a human-readable string. + // Parameters: + // str: The string to read. + virtual void fromString(RPCapi::TStringArg str) = 0; + + // This routine converts this into a newly-allocated string. + // Parameters: + // buffer: The buffer to copy this to (not deleted). + // offset: Offset to write to in buffer, + // returned as offset after end of written portion. + // Returns: + // A string representing this, or the number of bytes + // printed to buffer. + virtual RPCapi::TString *toString(void) const = 0; + + }; // class IDex + + + // This routine determines if the API will + // automatically attempt to acquire content + // when the first RPCapi::Instance is created. + // (By default, it will). + // Parameters: + // a: If true, auto-connect. + // Returns: + // The previous value. + virtual bool acmAutoConnect(bool a) = 0; + + // This routine gets the ACM ID that this API last connected to. + // Parameters: + // acmName: If not NULL, assigned a newly-allocated + // string containing the mathcing ACM name. + virtual const RPCapi::ID *acmIDget(RPCapi::TString **acmName = NULL) = 0; + + // This routine performs an import by file across the network + // Parameters: + // importDataBuffer: The "file" to import + // importDataBufferSize: The size of the import data buffer + // notify: Show status messages to the user + // Returns: + // int: 0 on success + // Error code on failure + virtual int networkImportPerform(const unsigned char *importDataBuffer, unsigned int importDataBufferSize, bool notify = false) = 0; + + // This routine causes this client to send a request to + // its ACM for some reason. + // Parameters: + // serverId: Identifies the web server to + // send the request to. If NULL, + // the request is sent to ArchVision. + // request: The request to send. + // errmsg: If not NULL, this will point to + // a newly-allocated error message + // (if an error occurred). + // Returns: + // 1 on success, or an error code. + virtual int acmSendRequest(RPCapi::TStringArg serverId, + RPCapi::TStringArg request, RPCapi::TString **errmsg) = 0; + + // This routine pings an ACM to determine if it exists in the currently + // configured location. + // Returns: + // true on success, false on failure + virtual bool acmPing(void) = 0; + + // This routine scans for ACMs on the local network. + // All returned objects are newly-allocated, and should + // be deleted by the caller. + // Parameters: + // names: Nicknames of the matching ACMs. + // ids: IDs of the matching ACMs. + // hosts: Hostnames of the matching ACMs. + // ports: Ports the matching ACMs are running on. + // Returns: + // The number of matches, which is the size + // of the returned arrays, or an error code. + virtual int acmScan(RPCapi::TString **&names, RPCapi::ID **&ids, + RPCapi::TString **&hosts, short *&ports) const = 0; + + // These functions get this modules new and compare params functions, + // or (un)register additional new and compare functions. + typedef RPCapi::Param *(*NewFunc)(int typeCode); + virtual NewFunc newFuncGet(void) = 0; + virtual void newFuncRegister(NewFunc nf, bool reg) = 0; + typedef int (*CompareFunc)(const RPCapi::Param *param1, + const RPCapi::Param *param2); + virtual CompareFunc compFuncGet(void) = 0; + virtual void compFuncRegister(CompareFunc cf, bool reg) = 0; + + // This is used to force a reread of the ini file + virtual void contentClientIniRead(void) = 0; + + // This routine retrieves the module handle for RPCapi.dll. + virtual HMODULE getModuleHandle(void) const = 0; +}; + +#endif // ifndef RPC_API_CLIENT_INTERFACE_H diff --git a/SDK/inc/RPCapiML.h b/SDK/inc/RPCapiML.h new file mode 100644 index 0000000..970d93d --- /dev/null +++ b/SDK/inc/RPCapiML.h @@ -0,0 +1,80 @@ +// This header file exists to provide multilanguage functionality +// for the RPC API. An RPCapi object (from RPCapi.h) can be +// dynamically cast to an RPCapiML object in order to +// access this functionality. + +#ifndef RPC_API_ML_H +#define RPC_API_ML_H + +#include "RPCapi.h" + +#ifdef RPC_API_ML + + +class RPCapiML { +public: + // This class represents a collection of translations + // for a single dialog. + class Translator { + public: + // Destructor. + virtual ~Translator(void) {} + + // This routine gets the indicated translator or translation. + // Parameters: + // name: The name of the control to translate. + // res: The newly-allocated translation, or NULL + // if no matching translation exists. + // Returns: + // true iff res is not NULL. + virtual bool translationGet(RPCapi::TStringArg name, RPCapi::TString &res) const = 0; + + }; // class Translator + + + // This class represents all of the currently-loaded Translators. + class TranslatorTable { + public: + // Destructor. + virtual ~TranslatorTable(void) {} + + // These routines get / set the language-code. + virtual const RPCapi::TString &languageCodeGet(void) const = 0; + virtual void languageCodeSet(RPCapi::TStringArg lc) = 0; + + // This routine clears all data cached in this table. + virtual void reset(void) = 0; + + // These routines get or free a translator + // for a particular RPC ID. + // Parameters: + // id: The ID to match. + // lc: The language code to match. + // fn: The filename to match. + // dlg: The name of the dialog to match. + // trans: The translator to free. + // Returns: + // The matching translator, or NULL if + // there is no such translator. + virtual const Translator *translatorGet( + RPCapi::TStringArg fn, RPCapi::TStringArg dlg) = 0; + virtual const Translator *translatorGetByID( + const RPCapi::ID *id, RPCapi::TStringArg lc, RPCapi::TStringArg dlg) = 0; + virtual const Translator *translatorGet( + RPCapi::TStringArg id, RPCapi::TStringArg lc, RPCapi::TStringArg dlg) = 0; + virtual void translatorFree(const Translator *trans) = 0; + + }; // class TranslatorTable + + + // This routine returns the plugin-style ID of the API. + virtual const RPCapi::TString &apiID(void) const = 0; + + // This routine gets the table of Translators. + virtual TranslatorTable *translatorTableGet(void) = 0; + +}; // class RPCapiML + +#endif // ifdef RPC_API_ML + +#endif // ifndef RPC_API_ML_H diff --git a/SDK/inc/RPCapiMem.h b/SDK/inc/RPCapiMem.h new file mode 100644 index 0000000..f1b707b --- /dev/null +++ b/SDK/inc/RPCapiMem.h @@ -0,0 +1,29 @@ +// This header provides an interface for a client application +// to supply the memory allocation routines for the RPC API. +// Use GetProcAddress() after loading RPCapi.dll to get +// the function specified here. Be sure to call this function +// before creating your RPCapi object. + +#ifndef _RPC_API_MEM_H_ +#define _RPC_API_MEM_H_ + + +// This is the typedef describing a memory allocation function. +typedef void *(*RPCmemoryAllocationFuncType) + (size_t size, const char *file, int line); + +// This is the typedef describing a memory deallocation function. +typedef void (*RPCmemoryDeallocationFuncType) + (void *ptr, const char *file, int line); + +// This is the typedef describing the function exported by RPCapi.dll +// to receive memory allocation functions from a client application. +// Use GetProcAddress(), or equivalent, to get this function. +typedef void (*RPCsetMemoryAllocationFuncType) + (RPCmemoryAllocationFuncType ma, + RPCmemoryDeallocationFuncType da); +extern "C" void RPCsetMemoryAllocation( + RPCmemoryAllocationFuncType ma, + RPCmemoryDeallocationFuncType da); + +#endif // ifndef _RPC_API_MEM_H_ diff --git a/SDK/inc/RPCapiSI.h b/SDK/inc/RPCapiSI.h new file mode 100644 index 0000000..7799bd1 --- /dev/null +++ b/SDK/inc/RPCapiSI.h @@ -0,0 +1,244 @@ +// This file exists to provide an RPC Instance -agnostic +// interface to the SelectionWindow suite. + +#ifndef _RPC_API_SI_H_ +#define _RPC_API_SI_H_ + +#define RPC_SEL_INT +#ifdef RPC_SEL_INT +#define RPC_SEL_INT_KEYWORDS + +#include "RPCapiEx.h" + + +class RPCapiSI { +public: + + // This extends the basic RPCapi::Client interface. + class ClientSI { + public: + virtual ~ClientSI(void) {} + + // This routine retrieves the filetypes this client supports. + // Only files of these types will be retrieved from the ACM. + // File types should be integers whose low-order bytes + // match the upper case letters in the file type extension. + // Example: RPC files have fileType == 0x00525043 + // Example: SKP files have fileType == 0x00534b50 + // NOTE: In the absence of this function, + // only RPC files are supported. + // Parameters: + // num: The number of filetypes, returned by referenc. + // Returns: + // An array containing the supported filetypes. + virtual const int *RPCfiletypes(int &num) = 0; + }; // class ClientSI + + + // This class displays a selection window of the indicated type. + class SelectionInterface : public RPCapi::BaseObject { + public: + typedef enum OBJECT_CODE_TYPE { + OBJECT_CODE = 80 // pass to RPCapi::newObject(). + } OBJECT_CODE_T; + + typedef enum WINDOW_CODE { + NONE = 0, + CLASSIC = 1, // Proj / Cat combo and content list + CLASSIC_PLUS = 2, // adds Message Center, Configure, + // About, and Help buttons. + TREE = 3, // Proj combo and cat / content treeview + THUMBS = 4, // thumbnail browser + THUMBS_PLUS = 5 // adds Message Center, Configure, + // About, and Help buttons + } WINDOW_CODE_T; // end typedef enum WINDOW_CODE + + // OR these w/ the window code: + typedef enum WINDOW_OPTIONS { + // By default, set initial selection + // from the RPC Instance. + NO_SELECTION = 0x00010000, // Do not use global selection or + // instance to set initial selection. + GLOBAL_SELECTION = 0x00020000 // Use globally stored + // last selection to set + // initial selection + } WINDOW_OPTIONS_T; // end typedef enum WINDOW_OPTIONS + + typedef enum MODE_CODE { + MODELESS = 1, // Shows window inside parent + FLOATING = 2, // Shows window in its own border + MODAL = 3 // Modal dialog + } MODE_CODE_T; + + + // A caller can register an object of this class with + // a SelectionInterface so that it can be notified + // when the SI's selection has changed. + class SIcallback { + public: + // These are the possible actions that can be + // passed to a SIcallback from a SelectionInterface. + typedef enum ACTION_CODE_TYPE { + NONE = 0, // No action. + SEL_CHANGE = 2, // Selection has changed. + WINDOW_CLOSING = 3 // args: Type: int, Values: IDOK(1), IDCANCEL(2) + } ACTION_CODE_T; // end typedef enum ACTION_CODE_TYPE + + // Destructor + virtual ~SIcallback(void) {} + + // This routine is called by a SelectionWindow2 on a change. + // Parameters: + // sw: The SelectionWindow2 making the callback. + // action: The action taken. See + // SelectionWindow2::Callback::ACTION_CODE. + // args: Additional arguments. + virtual void RPCselectionInterfaceCallbackProc( + const SelectionInterface *sw, int action, va_list args) = 0; + + }; // class SIcallback + + + virtual ~SelectionInterface(void) {} + + // For internal use only. + virtual const void *baseSelTableGet(void) const = 0; + virtual void baseSelTableSet(const void *st) = 0; + + // For internal use only. + virtual void contentDatabase(void *cdb, int cdbPortions = 0) = 0; + + // This routine hides this window (destroys the HWND). + virtual void hide(void) = 0; + + // These routines get / set the currently-selected ID + // of this window. + virtual void idSet(const RPCapi::ID *id) = 0; + virtual const RPCapi::ID *idGet(void) const = 0; + + // For internal use only. + virtual void *projTableGet(void) const = 0; + virtual void projTableSet(void *pt) = 0; + + // This routine registers a callback with this window. + // Parameters: + // callback: The callback to register. + // reg: If true, register. If false, unregister. + virtual void registerCallback(SIcallback *callback, bool reg) = 0; + + // This routine shows this window. + // Parameters: + // phwnd: The handle to the parent window. + // win: Which window to show (see WINDOW_CODE, above). + // m: The mode to display (see MODE_CODE, above). + // x: The initial x position in the parent. + // y: The initial y position in the parent. + // w: If not 0, the initial width. + // h: If not 0, the initial height. + // z: If not NULL, the window to place this + // after in the z-order. + // Returns: + // A handle to the new window + virtual HWND show(HWND phwnd, int win, int m, + int x = 0, int y = 0, int w = 0, int h = 0, HWND z = NULL) = 0; + + }; // class SelectionInterface + + + // This routine gets all category/content/ID tuples + // All returned arrays are newly-allocated. + // All returned entries are constant pointers. + virtual int allSelectionEntries(const RPCapiEx::IDex **&ids, + const TString **&cats, const TString **&cons) = 0; + + // These routines get/set the location of the ACM that + // this API will connect to. + // NOTE: The returned strings should be deallocated by the caller. + // Parameters: + // acmType: Type of the ACM. + // 0 == unspecified. + // 1 == locally started by client. + // 2 == network. + // 3 == none. + // 4 == locally started by client if not already specified. + // 5 == network if not already specified. + // 6 == none if not already specified. + // acmExe: If not NULL, exe of ACM to launch locally. + // acmDNS: If not NULL, the network location of the ACM. + // acmPort: If not 0, the port of the ACM. + virtual void acmGet(int &acmType, char *acmExe, + char *acmDNS, short &acmPort) = 0; + virtual void acmSet(int acmType, TStringArg acmExe, + TStringArg acmDNS, short acmPort) = 0; + + // Add a file to the global SelectionTable. + // Parameters: + // rpcFile: File to add. + // rpcId: If not NULL, receives the RPC's ID. + // cat: If not NULL, receives the RPC's category. + // con: If not NULL, receives the RPC's content. + // Returns true iff successful. + virtual bool idAddFile(TStringArg rpcFile, + RPCapiEx::IDex **rpcId, TString **cat, TString **con) = 0; + + // This routine retrieves all IDs currently in the + // global SelectionTable. + // It returns a newly-allocated array of newly-allocated IDs. + virtual RPCapiEx::IDex **idAll(int &num) = 0; + + // Tell the RPCapi you want keywords w/ RPC IDs before + // calling updateACM for the first time. + #ifdef RPC_SEL_INT_KEYWORDS + virtual void idKeywordSupport(bool s) = 0; + virtual bool idKeywordSupport(void) = 0; + virtual void idKeywords(const RPCapi::ID *id, + TString **&idKeywords, int &num) = 0; + #endif // ifdef RPC_SEL_INT_KEYWORDS + + // This routine gets the filename matching + // the input ID. This routine uses the global SelectionTable + // in RPCcls to accomplish this mapping. + // The return is a newly-allocated TString, + // or NULL if there is no match. + virtual TString *idToFilename(const RPCapi::ID *id) = 0; + + // This routine gets the type of the file associated + // with the input ID. File type means RPC, SKP, etc. + // This routine uses the global SelectionTable in RPCcls + // to accomplish this mapping. + // File types should be integers whose low-order bytes + // match the upper case letters in the file type extension. + // Example: RPC files have fileType == 0x00525043 + // Example: SKP files have fileType == 0x00534b50 + virtual int idToFiletype(const RPCapi::ID *id) = 0; + + // This routine gets the category and content for the input ID. + // This routine uses the global SelectionTable + // in RPCcls to accomplish this mapping. + virtual void idToCatCon(const RPCapi::ID *id, + TString *&cat, TString *&con) = 0; + + // This routine extracts the preview for the indicated RPC. + // The preview is in RGB format, and normally 124x98 (36456 bytes). + // Parameters: + // id: The RPC's id. + // buffer: buffer in which the image data is placed. + // This buffer may be allocated inside this + // function, and a pointer to it is returned by + // reference. + // alloc: if true, this routine allocates buffer. + // if false, this routine fills in the buffer passed + // to it, or returns the size needed if that buffer + // is NULL. + // width: Width, returned by reference. + // height: Height, returned by reference. + // Returns: + // The number of bytes used or required to store the requested, + // image, or < 0 on an error. + virtual int idToPreview(const RPCapi::ID *id, + unsigned char *&buffer, bool alloc, int &width, int &height) = 0; + +}; // class RPCapiSI + +#endif // ifdef RPC_SEL_INT +#endif // ifndef _RPC_API_SI_H_ diff --git a/SDK/inc/RPCcontrol.h b/SDK/inc/RPCcontrol.h new file mode 100644 index 0000000..410a75c --- /dev/null +++ b/SDK/inc/RPCcontrol.h @@ -0,0 +1,373 @@ +#ifndef _RPC_CONTROL_H_ +#define _RPC_CONTROL_H_ + +#include "RPCapi.h" + + +class RPCcon { +public: + virtual ~RPCcon(void) {} + +class BaseControl; + +// Implement this Callback so that you can be notified of control +// value changes while you are displaying a control in a UI. +// Note that some controls may cause other controls' values to change +// (and this callback is how you'll be notified of those changes). +class Callback { +public: + virtual ~Callback(void) {} + + // This routine is used by a control to notify registered + // callbacks that it has changed. + // Parameters: + // con: The control making the callback. + // action: The action that prompted the callback. + typedef enum ACTION { + VALUE_CHANGE = 1, + ENABLED_CHANGE = 2, + CAPTION_CHANGE = 3, + INSTANCE_ID_PRE_CHANGE = 4, + INSTANCE_ID_POST_CHANGE = 5 + } ACTION_T; // typedef enum ACTION + virtual void RPCconCallback(BaseControl *con, int action) = 0; +}; // class Callback + + +// dynamic_cast an RPCapi::Instance to this class in order to get +// that Instance's controls. +class Instance { +public: + virtual ~Instance(void) {} + + // This routine gets this Instance's controls. + // Delete the returned array, but not its contents. + virtual BaseControl **RPCconControlsGet(int &num) = 0; + + // This routine gets a pointer to a single control in this Instance. + // Parameters: + // name: The name of the control to get. + virtual BaseControl *RPCconControlGet(TStringArg name) = 0; + + // (Un)register a callback w/ this Control. + // Instance will send back an INSTANCE_ID_PRE/POST_CHANGE + // when the RPC ID of the Instance changes (like in switch content). + // When this happens, all of the current RPCcon::BaseControls + // will be deleted, and new ones will populate the instance. + virtual void rpcConCallbackRegister(Callback *cb, bool reg) = 0; + +}; // class Instance + + +class BaseControl { +public: + virtual ~BaseControl(void) {} + + // Gets the caption of this control. + virtual const TString &captionGet(void) const = 0; + + // Get the type of this control. + // See TYPE_CODE in control classes. + virtual int controlTypeGet(void) const = 0; + + // Gets the enabled state of this control. + virtual bool enabledGet(void) const = 0; + + // Get / set a handle / identifier for this control. + virtual int handIdGet(void) const = 0; + virtual void handIdSet(int i) = 0; + + // Get the name of this control. + // This corresponds to the names used w/ Instance::getTransform(). + virtual const TString &nameGet(void) const = 0; + + // Get the visible state of this control. + virtual bool visibleGet(void) const = 0; + + // (Un)register a callback w/ this Control. + virtual void rpcConCallbackRegister(Callback *cb, bool reg) = 0; +}; // class BaseControl + + +// A button control is just a push button. +class Button : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 36 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~Button(void) {} + + // Call this routine when the button is pressed. + virtual void buttonPush(void) = 0; +}; // class Button + + +class CheckBox : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 37 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~CheckBox(void) {} + + // These routines get/set the value of this control. + virtual int valueGet(void) const = 0; + virtual void valueSet(int v) = 0; +}; // class CheckBox + + +// A ComboBox contains different single-selectable text values. +// We recommend a drop-down size of 81. +class ComboBox : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 35 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~ComboBox(void) {} + + // Get the value to put in the combo's drop down. + virtual TString **listItemsGet(int &num) const = 0; + + // These routines get/set the value of this control. + virtual const TString &valueGet(void) const = 0; + virtual void valueSet(TStringArg v) = 0; + virtual int indexGet(void) = 0; + virtual void indexSet(int i) = 0; +}; // class ComboBox + + +// An integer control is a text edit box that displays +// a number (possibly w/ units) followed by a spinner control. +// The spinner's min/max range should be set by +// this object's valueMinGet and valueMaxGet routine results. +// We recommend displaying a static text control to the right +// of this control containing its caption. +class EditInteger : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 39 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~EditInteger(void) {} + + // This routine gets the unit type of this control. + // See RPCapi::Units for possible unit types. + // (A control w/ units should display a meaningful + // unit suffix after its scaled numeric value, + // ie: "1m" vs "100cm" vs "1000mm"). + virtual int unitTypeGet(void) const = 0; + + // These routines get/set the value of this control. + virtual int valueGet(void) const = 0; + virtual void valueSet(int v) = 0; + + // These routines get the min/max values for this control. + virtual int valueMaxGet(void) const = 0; + virtual int valueMinGet(void) const = 0; +}; // class EditInteger + + +// An real control is a text edit box that displays +// a real number (possibly w/ units) followed by a spinner control. +// The spinner's min/max range should be set by +// this object's valueMinGet and valueMaxGet routine results. +// We recommend displaying a static text control to the right +// of this control containing its caption. +class EditReal : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 40 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~EditReal(void) {} + + // This routine gets the unit type of this control. + // See RPCapi::Units for possible unit types. + // (A control w/ units should display a meaningful + // unit suffix after its scaled numeric value, + // ie: "1m" vs "100cm" vs "1000mm"). + virtual int unitTypeGet(void) const = 0; + + // These routines get/set the value of this control. + virtual double valueGet(void) const = 0; + virtual void valueSet(double v) = 0; + + // These routines get the min/max values for this control. + virtual double valueMaxGet(void) const = 0; + virtual double valueMinGet(void) const = 0; +}; // class EditReal + + +// This is a box in which text can be editted. +// Note that some EditText controls may be read-only. +// For these, "enabledGet" will return false. +class EditText : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 38 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~EditText(void) {} + + // These routines get / set the value of this control. + virtual const TString &valueGet(void) const = 0; + virtual void valueSet(TStringArg v) = 0; +}; // class EditText + + +// A control to choose a file with. +class FileChooser : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 49 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~FileChooser(void) {} + + // This routine gets the filters used in this chooser. + // NOTE: The caller must delete the returned arrays + // and their contents. + // Parameters: + // num: The number of filters. + // names: The names. + // suffs: The suffixes. One entry per suffix. An entry + // contains 1/more suffixes, separated by semicolons. + // Example: + // names = "Images" + // suffs = "bmp;jpg" + virtual void filtersGet(int &num, TString **&names, TString **&suffs) = 0; + + // Call this routine to launch an RPCapi provided dialog + // to select a file with. + virtual void launchDlg(void) = 0; + + // These routines get / set the value of this control. + virtual const TString &valueGet(void) const = 0; + virtual void valueSet(TStringArg v) = 0; +}; // class FileChooser + + +// An Image control contains a single image. This image may change +// while the image control is being displayed. Handle VALUE_CHANGED +// and use hbitmapGet to display this change. +class Image : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 54 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~Image(void) {} + + // Get the recommended width / height for this control. + // If you have a desired width, + // scale height proportionatley for best results: + // height = (int)ceil(((double)height / (double)width) * desired_width); + virtual void dimensionsGet(int &width, int &height) const = 0; + + // Get the image to display. + // Be sure to clean up this bitmap after you are done using + // it w/ DeleteObject(). + // Parameters: + // hwndDlg: If not NULL, this dialog is used to scale + // the resulting image according to its dialog units. + // iwidth: If hwndDlg is NULL, and this is positive, + // then this is the desired image width. + // iheight: If hwndDlg is NULL, and this is positive, + // then this is the desired image height. + virtual HBITMAP hbitmapGet(HWND hwndDlg, + int iwidth = -1, int iheight = -1) = 0; + + // Get the image to display. It is in RGB format. + // Parameters: + // buffer: buffer in which the image data is placed. + // This buffer may be allocated inside this + // function, and a pointer to it is returned by + // reference. + // alloc: if true, this routine allocates buffer. + // if false, this routine fills in the buffer passed + // to it, or returns the size needed if that buffer + // is NULL. + // width: Width, returned by reference. + // height: Height, returned by reference. + // absSize: If true, the width/height represent the absolute + // size (accept no substitutes). If false, the + // returned image may have lower res than that + // specified. + // Returns: + // The number of bytes used or required to store the requested, + // image, or < 0 on an error. + virtual int imageGet(unsigned char *&buffer, bool alloc, + int &width, int &height, bool absSize = false) = 0; + + // These routines get / set the path to the image. + // This path can be either a file, or a path + // internal to the RPC (denoted w/ a prefix of "__RPC:". + virtual const TString &valueGet(void) const = 0; + virtual void valueSet(TStringArg v) = 0; + +}; // class Image + + +// A ListBox contains different single-selectable text values. +// It is identical to a ComboBox, except in desired appearance +// (ie, a combo-box uses the drop-down list for selection). +class ListBox : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 34 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~ListBox(void) {} + + // Get the value to put in the combo's drop down. + virtual TString **listItemsGet(int &num) const = 0; + + // These routines get/set the value of this control. + virtual const TString &valueGet(void) const = 0; + virtual void valueSet(TStringArg v) = 0; + virtual int indexGet(void) = 0; + virtual void indexSet(int i) = 0; +}; // class ListBox + + +// A rollout control contains other controls in a single +// control that can optionally be opened / closed. +// This can also be implemented as a static frame. +class Rollout : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 42 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~Rollout(void) {} + + // This routine gets the child controls of this rollout. + // Delete the returned array, but not its contents. + virtual BaseControl **controlsGet(int &num) = 0; +}; // class Rollout + + +// An slider control is a slider that selects amonger integer values. +// We recommend displaying a static text control above +// this control containing its caption. +class Slider : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 41 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~Slider(void) {} + + // These routines get/set the value of this control. + virtual int valueGet(void) const = 0; + virtual void valueSet(int v) = 0; + + // These routines get the min/max values for this control. + virtual int valueMaxGet(void) const = 0; + virtual int valueMinGet(void) const = 0; +}; // class Slider + + +// This is just a piece of static text (a label). +// The caption is the text to be displayed; this control has no "value". +class StaticText : public BaseControl { +public: + typedef enum TYPE_CODE { + TYPE_CODE = 33 + } TYPE_CODES_T; // typedef enum TYPE_CODES + virtual ~StaticText(void) {} +}; // class StaticText + +}; // class RPCcon +#endif // ifndef _RPC_CONTROL_H_ diff --git a/SDK/lib/Debug/RPCJPeg.dll b/SDK/lib/Debug/RPCJPeg.dll new file mode 100644 index 0000000..97464cd --- /dev/null +++ b/SDK/lib/Debug/RPCJPeg.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:194b717239ada823a0e04f2eaacae1efe196c1b228779c0e9397d09756c72849 +size 376680 diff --git a/SDK/lib/Debug/RPCJPeg.pdb b/SDK/lib/Debug/RPCJPeg.pdb new file mode 100644 index 0000000..43a0c9c --- /dev/null +++ b/SDK/lib/Debug/RPCJPeg.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6db0a56552a72b67a0c943257d5706452702a62907cbf9f28eaceb8f54a2d4be +size 961536 diff --git a/SDK/lib/Debug/RPCPopulate.pdb b/SDK/lib/Debug/RPCPopulate.pdb new file mode 100644 index 0000000..94c2801 --- /dev/null +++ b/SDK/lib/Debug/RPCPopulate.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c9c23112990e6c820a20e6de84fac4a9a2ec3b8fde042225322512507f065d4 +size 6933504 diff --git a/SDK/lib/Debug/RPCapi.dll b/SDK/lib/Debug/RPCapi.dll new file mode 100644 index 0000000..d45612e --- /dev/null +++ b/SDK/lib/Debug/RPCapi.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1f5e41ee7224b8c4a67b7b119dc32261193de55624a32d42810a2dd3e0a5207c +size 6654304 diff --git a/SDK/lib/Debug/RPCapi.pdb b/SDK/lib/Debug/RPCapi.pdb new file mode 100644 index 0000000..15fb96e --- /dev/null +++ b/SDK/lib/Debug/RPCapi.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2eff35adeccd3087e8f649c43a798ed19758a0389f062ff3fc2b6b3d7778424 +size 16813056 diff --git a/SDK/lib/Debug/RPCcls.dll b/SDK/lib/Debug/RPCcls.dll new file mode 100644 index 0000000..325f437 --- /dev/null +++ b/SDK/lib/Debug/RPCcls.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7e99740d8988c199a3a671a1449a52188b45760426e66450e1763333df1d944 +size 9358176 diff --git a/SDK/lib/Debug/RPCcls.pdb b/SDK/lib/Debug/RPCcls.pdb new file mode 100644 index 0000000..974f8a1 --- /dev/null +++ b/SDK/lib/Debug/RPCcls.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0293f4f6e835446bf7921d5482f09c4c7333f4253d756109bd28767cecf9a091 +size 33958912 diff --git a/SDK/lib/Debug/RPCpopulate.dll b/SDK/lib/Debug/RPCpopulate.dll new file mode 100644 index 0000000..33a8c78 --- /dev/null +++ b/SDK/lib/Debug/RPCpopulate.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a04b8cc9ea95775dacafd55691415412ee5e0f4fa25791d87aafc4efe08f473a +size 2096496 diff --git a/SDK/lib/Release/RPCJPeg.dll b/SDK/lib/Release/RPCJPeg.dll new file mode 100644 index 0000000..69a052e --- /dev/null +++ b/SDK/lib/Release/RPCJPeg.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd4ea6cf48fab90a257d4fa33a6fe64c3373356e37d4f08f0f9a39409afe1690 +size 173928 diff --git a/SDK/lib/Release/RPCJPeg.pdb b/SDK/lib/Release/RPCJPeg.pdb new file mode 100644 index 0000000..0c4b7d4 --- /dev/null +++ b/SDK/lib/Release/RPCJPeg.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fb175772115507e600da5e21c9b62a2479b427259d1a99cd9b7e67ee25770e6 +size 691200 diff --git a/SDK/lib/Release/RPCPopulate.pdb b/SDK/lib/Release/RPCPopulate.pdb new file mode 100644 index 0000000..64937ff --- /dev/null +++ b/SDK/lib/Release/RPCPopulate.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0e43ff6a9a2093fb8b6133fafbdfb45de525610521d602395119732aeb7239a +size 5450752 diff --git a/SDK/lib/Release/RPCapi.dll b/SDK/lib/Release/RPCapi.dll new file mode 100644 index 0000000..74416f8 --- /dev/null +++ b/SDK/lib/Release/RPCapi.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e4b41e38ed932ded8efe6f80a8e01e6e4d4a64c68240ead6ef0a90ccc7ea2e9 +size 2855264 diff --git a/SDK/lib/Release/RPCapi.pdb b/SDK/lib/Release/RPCapi.pdb new file mode 100644 index 0000000..e99b27b --- /dev/null +++ b/SDK/lib/Release/RPCapi.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:564feb67754627c9bfe7b10df15c97d22515a2946ec8b4abff2544414fc00db9 +size 13863936 diff --git a/SDK/lib/Release/RPCcls.dll b/SDK/lib/Release/RPCcls.dll new file mode 100644 index 0000000..611a46b --- /dev/null +++ b/SDK/lib/Release/RPCcls.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26a890d7faa7ee4b88d3112fee18d847638b80e107b47f6927c2ecff8ccdc44e +size 2887008 diff --git a/SDK/lib/Release/RPCcls.pdb b/SDK/lib/Release/RPCcls.pdb new file mode 100644 index 0000000..afdd968 --- /dev/null +++ b/SDK/lib/Release/RPCcls.pdb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36c20e2853497850a9e0863b3dab8a285d45e993e2fac5bdb8c3d76d0da953b4 +size 22768640 diff --git a/SDK/lib/Release/RPCpopulate.dll b/SDK/lib/Release/RPCpopulate.dll new file mode 100644 index 0000000..e6d3d2d --- /dev/null +++ b/SDK/lib/Release/RPCpopulate.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1b7c7d65e1c7e81fb25c9bee3c9c7f3e1475094a60175fbe3e302f80eb46ff4 +size 757104 diff --git a/res/RPC.ico b/res/RPC.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c43c13b8b5b4ad7ddf7d6740923e2927f539293 GIT binary patch literal 34293 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 a^Dcc>f;*9vvwF literal 0 HcmV?d00001 diff --git a/res/RPC.rc2 b/res/RPC.rc2 new file mode 100644 index 0000000..67ac35d --- /dev/null +++ b/res/RPC.rc2 @@ -0,0 +1,13 @@ +// +// RPC.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/res/rpc_acm.ico b/res/rpc_acm.ico new file mode 100644 index 0000000000000000000000000000000000000000..d7bd264068308b221dab5ef6ccbf0aa9492a14c9 GIT binary patch literal 26448 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 F{{TfTS(yL; literal 0 HcmV?d00001 diff --git a/stdafx.cpp b/stdafx.cpp new file mode 100644 index 0000000..2ffd445 --- /dev/null +++ b/stdafx.cpp @@ -0,0 +1,7 @@ +// stdafx.cpp : source file that includes just the standard includes +// RPC.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + diff --git a/stdafx.h b/stdafx.h new file mode 100644 index 0000000..728b4e0 --- /dev/null +++ b/stdafx.h @@ -0,0 +1,117 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently + +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers +#endif + +// This plug-in is Rhino 6 ready +#define RHINO_V6_READY + +// If you want to use Rhino's MFC UI classes, then +// uncomment the #define RHINO_SDK_MFC statement below. +// Note, doing so will requrie that your plug-in is +// built with the same version of Visual Studio as was +// used to build Rhino. +#define RHINO_SDK_MFC + +// Plug-ins must use the release version of MFC used by Rhino. +// Plug-ins that require debugging information need to be built with +// RHINO_DEBUG_PLUGIN defined. +#if defined(RHINO_DEBUG_PLUGIN) && defined(_DEBUG) +// Rhino 6 Debug plug-ins should define RHINO_DEBUG_PLUGIN, +// but not define _DEBUG in the .vcxproj file. +#error Do not define _DEBUG - use RHINO_DEBUG_PLUGIN instead +#endif + +// Rhino SDK Preamble +#include "RhinoSdkStdafxPreamble.h" + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit + +#include // MFC core and standard components +#include // MFC extensions + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC OLE classes +#include // MFC OLE dialog classes +#include // MFC Automation classes +#endif // _AFX_NO_OLE_SUPPORT + +#ifndef _AFX_NO_DB_SUPPORT +#include // MFC ODBC database classes +#endif // _AFX_NO_DB_SUPPORT + +#ifndef _AFX_NO_DAO_SUPPORT +#include // MFC DAO database classes +#endif // _AFX_NO_DAO_SUPPORT + +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + +// TODO: include additional commonly used header files here +#include // CriticalSection + +using namespace std; +#include + +#if defined(_M_X64) && defined(WIN32) && defined(WIN64) +// The afxwin.h includes afx.h, which includes afxver_.h, +// which unconditionally defines WIN32 This is a bug. +// Note, all Windows builds (32 and 64 bit) define _WIN32. +// Only 64-bit builds define _WIN64. Never define/undefine +// _WIN32 or _WIN64. Only define EXACTLY one of WIN32 or WIN64. +// See the MSDN "Predefined Macros" help file for details. +#undef WIN32 +#endif + +// Rhino SDK classes +#include "RhinoSdk.h" + +// Rhino Render Development Kit (RDK) classes +#include "RhRdkHeaders.h" +#include "RhinoSdkUiThemeDraw.h" +#include "RhinoSdkUiWindowsPaintManager.h" +#include "RhRdkRegisteredPropertiesEventSink.h" +#include "IRhRdkRegisteredPropertyManager.h" +#include "IRhRdkRegisteredProperty.h" + +// TODO: include additional Rhino-related header files here +#define LBPRHLIB +#define LBPLIB_RHINOVER 6 +#define LBPLIB_WINDOWS_SPECIFIC +#define LBPMALLOC onmalloc +#define LBPFREE onfree +#define LBPREALLOC onrealloc +#define LBPCALLOC oncalloc + +#if defined(RHINO_DEBUG_PLUGIN) +// Now that all the system headers are read, we can +// safely define _DEBUG so the developers can test +// for _DEBUG in their code. +#define _DEBUG +#endif + +// LBP +#include "LBPString.h" +#include "LBP_XML.h" +#include "LBP_UTF.h" +#include "LBPRhObjectWrapper.h" +#include "LBPBuffer.h" +#include "LBPRh_XMLUserData.h" + +// RPC +#define PORT_WINDOWS +#include "SDK/inc/RPCapi.h" +#include "SDK/inc/RPCapisi.h" +#include "SDK/inc/RPCapiMem.h" + +#include "RpcDefinitions.h" + +// Rhino SDK linking pragmas +#include "rhinoSdkPlugInLinkingPragmas.h" diff --git a/targetver.h b/targetver.h new file mode 100644 index 0000000..07ed9d1 --- /dev/null +++ b/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include "rhinoSdkWindowsVersion.h" + +#include