SDK:Tutorial A Menu Command

From Vectorworks Developer
Revision as of 15:39, 12 August 2013 by Root (talk | contribs) (1 revision)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

.SDK|SDK ..SDK:Types|SDK Types ..SDK:Using the SDK|Using the SDK ..VCOM:VCOM (Vectorworks Component Object Model)|VCOM Basics ..VCOM:Class Reference|VCOM Class Reference

By Vladislav Stanev

Prev: An Extension Implementation | Next: A Parametric Object

Intro

We will be looking at the TesterModule sample provided with the SDK. The two files implementing the menu extensions are ExtMenu.cpp and ExtMenu.h. Note that the classes are inside a C++ namespace so they can be nicely distinguished when this project grows big, and there are many extensions and code inside the project.

It is especially useful to put the extension definition constants into a namespace, so they don't get mixed up with one another.

Define the menu extensions

Define an extension is basically defining a C++ class that inherits the SDK class VWExtensionMenu. The only thing that is required is using the macro DEFINE_VWExtension. This will override certain virtual functions which are typical for a new menu extension definition. This macro helps simplify the process of defining an extension.

namespace TesterModule
{
  // ------------------------------------------------------------------------------------------------------
  class CExtMenu : public VWExtensionMenu
  {
    DEFINE_VWExtension;
  public:
                    CExtMenu(CallBackPtr cbp);
    virtual         ~CExtMenu();

  };
}

Implementing this class is just implementing the constructor and the destructor.

CExtMenu::CExtMenu(CallBackPtr cbp)
  : VWExtensionMenu( cbp, gMenuDef )
{
}

CExtMenu::~CExtMenu()
{
}

As you can see the constructor of the base class requires a parameter. This parameter accepts the data for this menu extension. This is basically implementing the virtual functions. This data structure is designed so defining a menu extension is simple.

So, the sample defines a global variable of type SMenuDef in a namespace, so it is not confused with other menu extension data:

namespace TesterModule
{
  static SMenuDef    gMenuDef = {
    /*Needs*/             0x00,
    /*NeedsNot*/          0x00,
    /*Title*/             {12000, 1},
    /*Category*/          {12000, 2},
    /*HelpText*/          {12000},
    /*VersionCreated*/    16,
    /*VersoinModified*/   0,
    /*VersoinRetired*/    0,
    /*ContextualHelpID*/  ""
  };
}
  • Needs -- These flags determine the condition on which this menu command will be available. Currently we recommend keeping this 0x00. This will make the command available if there is a document open. Then the command should show a warning dialog if it requires selection or any other condition. This will help the users use the command properly.
  • NeedsNot -- see the 'Needs' flag. We recommend keeping this 0x00 always.
  • Title -- This is an index to a STR# resource providing the localized name of the menu command that will show up on the workspace.
  • Category -- This is an index to a STR# resource providing the localized name of the category under which the menu command will be placed in the workspace editor.
  • HelpText -- This is an index to a TEXT resource containing help text for this menu command.
  • VersionCreated -- The Vectorworks version number in which the menu command was introduced. Vectorworks 2011 is version 16.
  • VersoinModified -- The Vectorworks version number in which the menu command was modified. Vectorworks 2011 is version 16.
  • VersoinRetired -- The Vectorworks version number in which the menu command was removed. Vectorworks 2011 is version 16.
  • ContextualHelpID -- This is the help ID of the Vectorworks help system associated with this menu command.

Additionally to creating the extension class is to define the extension definition data and link this extension instance to a class that will be the menu event sink. This is done by using the macro IMPLEMENT_VWExtension. This is the corresponding macro to the one used in the definition of the extension class. The sample uses this macro just before the extension's constructor:

// --------------------------------------------------------------------------------------------------------
// {5B6448A9-1DBB-4541-B2D7-862FB517C868}
IMPLEMENT_VWExtension(
  /*Extension class*/  CExtMenu,
  /*Event sink*/       CMenu_EventSink,
  /*Universal name*/   "CExtMenu",
  /*Version*/          1,
  /*UUID*/             0x5b6448a9, 0x1dbb, 0x4541, 0xb2, 0xd7, 0x86, 0x2f, 0xb5, 0x17, 0xc8, 0x68 );
  • Extension class -- this is the name of the class defining this menu extension implementation.
  • Event sink -- this is the class that will be the main event sink for this exension.
  • Universal name -- the name that is going to be used by Vectorworks to identify this particular menu and identify it among the rest.
  • Version -- currently unused. Must be 1.
  • UUID -- the Universal Unique Identifier of the VCOM interface. This has to be a new ID for each menu instance. Mac and win have tools to generate this number. This number should never change once the menu is created. You can also use this to generate a UUID: http://www.cesyam.fr/PHP/get_uuid.php

Note that the comment before this macro definition is the UUID used. This is for convenience and it is recommended to keep in sync with the UUID used in the macro. Anyway this UUID never changes once the menu is created.

Define the main menu event sink

The macro IMPLEMENT_VWExtension not only defines the standard data for the extension, but it also defines the class that will be the main event sink for this extension.

The event sink is a class that inherits the SDK class VWMenu_EventSink.

namespace TesterModule
{
  class CMenu_EventSink : public VWMenu_EventSink
  {
  public:
              CMenu_EventSink(IVWUnknown* parent);
    virtual   ~CMenu_EventSink();

  public:
    virtual void    DoInterface();
  };
}

What will happen is that Vectorworks will use the extension to identify the menu command as such and put it in the workspace. But whenever the user executes the menu command, Vectorworks will request the main event sink from the extension, and due to the macro IMPLEMENT_VWExtension it will create an instance of 'CMenu_EventSink' and return it back to Vectorworks. Then Vectorworks will call the 'DoInterface' virtual function to let the sink respond to the menu command. After that Vectorworks keep that pointer until Vectorworks is shutdown. This is very important, since the event sink can store local data as member variables of that class which will be preserved between the 'DoInterface' calls.

CMenu_EventSink::CMenu_EventSink(IVWUnknown* parent)
  : VWMenu_EventSink( parent )
{
}

CMenu_EventSink::~CMenu_EventSink()
{
}

void CMenu_EventSink::DoInterface()
{
  gSDK->AlertInform( "CMenu_EventSink::DoInterface" );
}

Register the extension in the module main

At this point we have the extension defined, and the event sink class prepared to receive the events for that extension. The only thing that remains is to let Vectorworks know about that extension, so our menu command will show up in the workspace editor.

We do that inside the module main function.

extern "C" long GS_EXTERNAL_ENTRY plugin_main(long action, void* pInfo, long& ioData, CallBackPtr cbp)

We use the template function REGISTER_Extension to register the extension in Vectorworks and later provide instance of it when requrested.

REGISTER_Extension<TesterModule::CExtMenu>( GROUPID_ExtensionMenu, action, pInfo, ioData, cbp, reply );

The template parameter of the macro is the class name (decorated with the namespace) which will be instantiated when necessary.

This macro also defines the extension under a specific group. The group for the menu commands is: GROUPID_ExtensionMenu.

Menu command chunks

A menu command can be just a single menu item. But also, it could be a group of menu items (menu chunk). You can define menu items for this menu command by defining an array of SMenuChunkDef and pass it in to the extension's constructor.

namespace TesterModule
{
  static SMenuChunkDef gMenuChunksDef[] = {
    { {12000, 4}, "" },
    { {12000, 5}, "" },
    { {12000, 6}, "" },
    { {12000, 7}, "" },
    { {12000, 8}, "" },
    { {NULL, NULL}, NULL }
  };
}

The variable 'gMenuChunksDef' is placed inside a namespace so it is not confused with other code in this module. It defines an array of SMenuChunkDef structures. The stucture defines a STR# localizable string the menu item, and a static string with the Contextual Help ID for the Vectorworks help system.

Then you should pass in this array pointer to the constructor of the extension after the menu definition:

CExtMenu::CExtMenu(CallBackPtr cbp)
  : VWExtensionMenu( cbp, gMenuDef, gMenuChunksDef )
{
}

Next Chapter

Next: A Parametric Object