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 0000000..0c43c13 Binary files /dev/null and b/res/RPC.ico differ 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 0000000..d7bd264 Binary files /dev/null and b/res/rpc_acm.ico differ 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