Export Filters (How to Create a Plug-In) |
www.CAD6.com |
The implementation of an export filter for the CAD6interface is very simple. The interface offers some enumeration procedures that can resolve data into several logic levels, allowing the developer to choose the level that fits best to the addressed export format. If required, the interface resolves all data, including instances, blocks and texts, into a sequence of simple polylines and, if possible, polygons. However, if the export format is "intelligent" enough, the data can be retrieved in form of complex curve data consisting of lines, Bézier curves and circular arcs, plus texts.
If the user executes an export command controlled by an external export filter, he is automatically asked to select the entities he wants to export. As a result, the export filter itself does not have to control this selection, it can simply rely on it (e.g., the procedure MKI_EnumerateAll, which is usually used to export data to a file, does automatically enumerate those user-selected objects).
Simple Export FilterFor example, lets start with a very simple export filter that is only used to export a sequence of coordinates describing the nodes of polylines. Each line starts with an identifier indicating whether this is the last line of the file or not. If not, a pair of coordinates follows, separated by commas and terminated by a semicolon. Referring to the standard point types in MKD files, the identifier is MKI_DB_POINT_ANY if another coordinate follows and MKI_DB_END if the file ends. A possible file created by this export filter could be:
0,10.0,20.0; 0,20.0,20.0; 0,20.0,10.0; 0,10.0,10.0; 999;
This file describes four points at the coordinates (10.0,20.0), (20.0,20.0), (20.0,10.0) and (10.0,10.0).
First, let's have a look at the basic command procedure of that export filter plug-in. The command procedure is responsible for retrieving the name of the file to which the data shall be exported. It then creates this file, call the export function and then handles possible errors. This procedure is basically the same for all export filters.
C++ Source Code// Static variable is used to report errors. // This is more effective than returning an // error code from every procedure. static __int32 g_nError;
//---------------------------------------------------------------------- // Export Filter entry point. MKI_INTERFACE_EXPORT __int32 __int32 f_nCommandID, __int32 f_nExecMode, void* f_pData ) { bool fResult = false;
switch( f_nExecMode ) { case MKI_EXECMODE_HELP: // Display associated help file. MKI_DialogHelpTopic( hGlobalWnd, "EXPORT", 1 ); break;
case MKI_EXECMODE_USER: // If the plug-in has options to be edited by the user, // they should be displayed here! Please use GetActiveWindow() // as parent window for any dialog displayed. break;
case MKI_EXECMODE_SYSTEM: // Nothing to do here... break;
case MKI_EXECMODE_GET_PROFILE: // Nothing to do here... break;
case MKI_EXECMODE_SET_PROFILE: // Nothing to do here... break;
case MKI_EXECMODE_MENU_INIT: // Nothing to do here... break;
case MKI_EXECMODE_READY: // Nothing to do here... break; } return( fResult ); }
Second, let's implement the export callback procedure required in every export filter.
C++ Source CodeMKI_INTERFACE_EXPORT bool const LPCWSTR f_pszFileName, __int32 f_nFilterMode, __int32 f_nFlag, const LPCWSTR f_pszCollectionName, const LPCWSTR f_pszItemName, void* f_pData ) { HANDLE hFileHandle; bool fResult = false;
// Create export file. if( MKI_FileCreate( &hFileHandle, f_pszFileName ) ) { // Export data (implementation see below). PlugInExport( hFileHandle, f_pszFileName, f_nFlag );
// Close export file. MKI_FileClose( hFileHandle );
// Error handling. switch( g_nError ) { case 999: MKI_FileDelete( f_pszFileName ); MessageBox( g_hGlobalWnd, "Export canceled.", "Export Filter", MB_OK ); fResult = true; break;
case 0: fResult = true; break;
default: MKI_FileDelete( f_pszFileName ); MessageBox( g_hGlobalWnd, "Export error.", "Export Filter", MB_OK ); fResult = false; break; } } return( fResult ); }
Now, let's have a look at the internal export procedure that is called from within the export callback procedure. This export procedure simply tells the serving application to pass all object data in a specified format to the plug-in's enumeration callback procedure.
C++ Source Codevoid PlugInExport( HANDLE f_hFileHandle, const LPCWSTR f_pszFileName, __int32 f_nFlag ) { MKI_FILENAMEW szFileName2; MKI_DUMMYSTRW szDummyStr;
// Until now, no error occurred, so reset g_nError. g_nError = 0;
// Prepare writing to the file, i.e. initialize the file buffer. if( !MKI_FileWriteInitDisk( f_hFileHandle, false ) ) { g_nError = 1; return; }
// Display a progress indication window. For this // reason, assemble a string containing the file's // name and a description of the task performed. MKI_FileSplitName( f_pszFileName, nullptr, szFileName2 ); MKI_CopyW( szDummyStr, "Exporting to file\n", szFileName2 ); MKI_DialogShowProgress( "Export Filter", szDummyStr, false );
// Start the enumeration of all chosen entitites. // The enumeration mode MKI_ENUM_PLAIN causes all nested // instances to be resolved, the mode MKI_ENUM_LINES // causes simple polylines to be generated from all // entity types. MKI_EnumerateAll( f_nFlag, f_nFlag, MKI_ENUM_PLAIN | MKI_ENUM_LINES, (MKI_ENUMOBJECT_PROC) PlugInEnumCallback, nullptr );
// Write the terminating line to the file. MKI_FileWriteInt16( DB_END );
// Finish writing to the file, i.e. // write the file buffer to the disk. if( !MKI_FileWriteFlush() ) g_nError = 1;
// Hide the progress indication window. }
Finally, let's have a look at the enumeration callback procedure PlugInEnumCallback whose address has been passed to the MKI_EnumerateAll procedure. This callback procedure is responsible for translating the enumeration data into data of the desired export file format. As the enumeration can be controlled by stating different enumeration modes, the translation will usually be a simple one-to-one translation.
(When working with complex export file formats, its a good idea to start with a very simple version of this translation by stating MKI_ENUM_PLAIN combined with MKI_ENUM_LINES. Once this simple translation works, refine the translation by allowing more complex data types to be passed to the callback procedure.)
C++ Source Codebool PlugInEnumCallback( MKI_CONST_ENUMDEF_DATA_PTR f_pEnumData, void* f_pUserData ) { MKI_DUMMYSTRW szText1, szText2;
// Check what type of data was passed. As // MKI_ENUM_LINES was used, only objects of // type CURVE, AREA or MARK will be passed. Bézier // curves, circles, characters and other complex // data types have been resolved to polylines. switch( f_pEnumData->EnumData ) { case MKI_ENUMDATA_CURVE: case MKI_ENUMDATA_SURFACE: case MKI_ENUMDATA_MARK: // Runs through all points of the object... for( __int32 nCount = 0; nCount < f_pEnumData->EnumCount; nCount++ ) { // ...and write their coordinates to the file. MKI_FileWriteInt16( MKI_DB_POINT_ANY ); MKI_FileWriteDouble( f_pEnumData->PointPtr[nCount].x ); MKI_FileWriteDouble( f_pEnumData->PointPtr[nCount].y );
// Check whether a write error occurred. if( MKI_FileWriteError() ) { g_nError = 1; break; }
// Update the progress indication window. MKI_PrintW( szText1, L"Line %ld", MKI_PrintW( szText2, L"%ld KBytes", ( MKI_FileWriteCurrentSize() + 1023 ) / 1024 ); MKI_DialogUpdateProgress( szText1, szText2, 0, 0 );
// Check whether the user pressed the CANCEL // button in the progress indication window. if( MKI_DialogIsCancelled() ) { g_nError = 999; break; } } break; }
// If any error occurred, cancel the enumeration // by returning false. return( !g_nError ); }
Export filters that want to allow silent running, i.e. that will hook onto the standard File > Save as command as well as offering a menu command, must export a MKI_PlugInExport callback procedure that serves this task.
For further experience, you can find a more sophisticated version of this export filter in the sources of EXPORT_.DLL. This implementation includes additional code for language-dependent data libraries.
|
CAD6interface 2024.2 - Copyright 2024 Malz++Kassner® GmbH