Chapter 6. Available Objects

The Realsoft 3D user interface is not hard coded. Select window, view windows, tool buttons, just to name a few, are all created using drag & drop based user interface tool kit included in Realsoft 3D.

The Available Objects window shows the user all the available interface objects that can be inserted into the program.

The folder 'samples/plugins/gui' contains a few sample plugins demonstrating how completely new kind of user interface objects can be plugged into the available objects window.

Plugging in new objects into the Available Objects Window

To plug new user interface elements into the 'Available Object' window, you have to implement the following classes:

  1. The actual user interface class to be plugged in, of course (such as the view window, the select window etc.)

  2. So called Drop Tool class. This class provides the tool button for the Available Objects window and allows the user to drag & drop it into the desired position in the Realsoft 3D user interface.

  3. Property gadget for gui editing (optional).

The third class is optional because some user interface objects may not have any attributes that the user could edit. When the user clicks one of the icons in the available objects window, the window shows this property gadget for the user. Also, if the user selects 'Edit' in GUI editing mode, this gadget is shown.

For example, the splitter control defines option which allows the user to select the orientation of the splitter (horizontal/vertical). When the user then drags & drops the icon into the actual application window, the drop tool class is called with the defined options.

The drop tool class then creates the actual GUI object according to the specified options.

Drop tool classes also take care of loading/saving the actual GUI objects as well as define icon for the available tools window.

To plug-in new GUI object into the Available Objects window, call:


    R3DoClassA(R3CLID_DROPTOOL, R3DROPTOOLCM_INSTALLTOOLCLASS, (void *)R3CLID_MYDROPTOOL);
    R3DoClassA2(R3CLID_DROPTOOL, R3DROPTOOLCM_INSTALLIO,
                (void*)R3CLID_MYGUICLASS, (void*)R3CLID_MYDROPTOOL);

where 'R3CLID_MYGUICLASS' is the class identifier of the actual GUI object and 'R3CLID_MYDROPTOOL' is the drop tool class handling creation, deletion reading and writing of the actual GUI object.

Implementing new Windows

You can plug in anything which is derived from r3widget.h class and which uses standard model-view concept to connect to their models (some windows such as a window showing your company logo, may not even need any models).

Implementing Drop Tool Classes

Drop tool class can be derived from one of the following base classes depending on the super class of the actual GUI object.


    inc/real/widget/r3drwito.h /* gadget */
    inc/real/widget/r3drwnto.h /* window */
    inc/real/widget/r3drgmto.h /* geometry manager */

If you are going to plug-in classes which are derived from r3gadget.h base class, derive your drop tool from the r3drwito.h class (drop tool widget).

If your class is derived from 'r3window.h', derive the drop tool class from 'r3drwnto.h'.

New geometry manager objects can also be plugged in. Derive the drop tool class from 'r3drgmto.h' base class.

All drop tools must create one button object. This button is shown in the available objects window and can be dragged & dropped into the actual application. Create the button in the R3RM_CREATE method as usual.

You also have to catch the R3DROPTOOLA_Button tag in the R3RM_GET method (the available window asks the button this way).

Property Gadget

When the user selects your GUI button, the create controls method of your drop tool class is called.


    static void *R3CreateControls(R3CLASS *cl, R3OBJ *obj, R3TAG *tags)
    {
        R3IDATA *self = R3CL_IADDR(cl, obj);
        R3OBJ *window, *controls;

        /* let the super class insert its controls into the avail.obj window */
        R3DoSuperA3(cl, obj, R3DROPTOOLM_CREATECONTROLS, NULL, NULL, tags);

        /* create your GUI property gadget here */
        R3TagGet(tags, R3DROPTOOLA_CtrlWindow, &window, R3TAG_END);
         ...

        /* and insert it into the avail. object as well */
        R3GetAttrs(obj, R3DROPTOOLA_Controls, &controls, R3RA_Done);
        R3Do(controls, R3GMM_INSERT,
             ...
        return (void *)TRUE;
    }

In addition to this, you will have to catch number of methods which are sent to you when the user attempts to drag & drop the button to an application. These methods are listed below: R3DROPTOOLM_DRAG

This method is sent to you when the user starts dragging your button. In this method you must create a drop message object which tells the system the class identifier of the actual object as well as object + method to be called when the user drops the message to an application. The drop tool class defines the method identifier for you so you can al


static void *r3droptoolm_drag(R3CLASS *cl, R3OBJ *obj, R3WINDOWEVENT *msg)
{
    R3IDATA *self = R3CL_IADDR(cl, obj);

    return R3New(R3CLID_DROPMESSAGE,
                 R3DRPMSGA_DropObject, NULL,
                 R3DRPMSGA_DropClid, R3CLID_MYGUICLASS,
                 R3DRPMSGA_CreatorObject, obj,
                 R3DRPMSGA_CreateMethod, R3DROPTOOLM_CREATEDROPOBJECT,
                 R3TAG_END);
}

R3DROPTOOLM_CREATEDROPOBJECT

When the user drops the dragmessage into the application, the above mentioned R3DRPMSGA_CreatorObject is called with specified R3DRPMSGA_CreateMethod.

In this method you create the actual GUI object and attach it to appropriate model. You also have to create unique name for your object so that macros and other sub systems can refer them.

Let's assume you are going to plug-in a new gadget which uses the 'unitconverter' object as model.


    static void r3droptoolm_createdropobject(R3CLASS *cl, R3OBJ *obj, R3CLID clid, R3OBJ *parent, R3TAG *tags)
    {
        R3IDATA *self = R3CL_IADDR(cl, obj);
        R3OBJ *layerlist, *uconv;
        char objname[LEN_STRBUF];

        /* generate unique name for us */
        R3DoSuperA3(cl, obj, R3DROPTOOLCM_CREATEUNIQUENAME, (void*)clid, NULL, (void*)objname);

        /* find our model */
        R3GetAttrs(obj, R3DROPTOOLA_LayerList, &layerlist, R3TAG_END);
        R3GetAttrs(layerlist, R3LAYLA_UnitConverter, &uconv, R3TAG_END);

        /* create the actual GUI object */
        return R3New(clid,
                     R3RA_Name, objname,
                     R3WGA_Parent, parent,
                     R3WGA_Model, uconv,
                     R3TAG_END);
     }

Once you have got the above methods implemented, the user will be able to drag & drop your objects into the actual application.

In some cases, user interface objects can define attributes which also need to be saved and loaded. The actual user interface doesn't understand anything about saving/loading. This is something the drop tool classes plug-in. For example, if your GUI uses 'splitter' object, the current position of the splitter is something you might want to save/load so that when the user loads his/her GUI next time, the splitter shows up exactly where it was when the GUI was saved.

Two methods need to be implemented for handling saving/loading.

R3DROPTOOLCM_READCREATEONLYATTRIBUTES R3DROPTOOLCM_FREECREATEONLYATTRIBUTES

The methods refer to 'create only' since many GUI attributes are 'create only'. For example, it is not possible to create a window without passing the R3WGA_Parent attribute with the create method. If your class defines this kind of create only attributes, read and save them in the above two methods.

For example, let's imagine your class defines two create only attributes: R3WGA_Model and 'MYGUIA_Foo'. The read/write methods would look as follows:


    static void *r3droptoolm_readcreateonlyattributes(R3CLASS *cl, R3OBJ *obj, R3CLID clid, R3OBJ *file, R3TAG *tags)
    {
        R3LIST *createattribute_list;
        R3TAG *clidversions;
        R3CREATEATTRIBUTENODE *cn;
        R3TAG *createattributes;
        R3OBJ *llist, *uconv;

        /* let the super class read class id for us */
        if (!R3DoSuperA3(cl, obj, R3DROPTOOLCM_READCREATEONLYATTRIBUTES, (void*)clid, file, tags))
            return NULL;

        /* fetch createattribute list, class version list and the root model */
        R3TagGet(tags,
                 R3DROPTOOLA_CreateAttributeList, &createattribute_list,
                 R3DROPTOOLA_ClidVersions, &clidversions,
                 R3DROPTOOLA_LayerList, &llist,
                 R3TAG_END);

        /* fetch the unit converter model */
        R3GetAttrs(llist, R3LAYLA_UnitConverter, &uconv, R3TAG_END);

        /* construct tag list which contains our 'create only' attributes */
        if(!(cn = R3Alloc(sizeof(R3CREATEATTRIBUTENODE), R3MEMF_CLEAR)))
            return NULL;
        if(!(cn-tags = R3Alloc(3 * sizeof(R3TAG), R3MEMF_CLEAR))) {
            R3Free(cn, sizeof(R3CREATEATTRIBUTENODE));
        return NULL;
    }
    R3TagCopy(cn-tags,
              R3WGA_Model, llist,
              R3ANIMSETGA_UnitConverter, uconv,
              R3TAG_END);

    R3AddTail(createattribute_list, (R3NODE*)cn);
     return (void*)TRUE;
}

You'll have to free the memory you allocated in the readcreatonlyattributes method. This is done in the r3droptoolm_freecreatonlyattributes method:


    static void *r3droptoolm_freecreateonlyattributes(R3CLASS *cl, R3OBJ *obj, R3CLID clid, R3TAG *tags)
    {
        R3LIST *createattribute_list;
        R3TAG *clidversions;
        R3CREATEATTRIBUTENODE *cn;

        if (!R3DoSuperA3(cl, obj, R3DROPTOOLCM_FREECREATEONLYATTRIBUTES, (void*)clid, NULL, tags))
            return NULL;

        R3TagGet(tags,
                 R3DROPTOOLA_CreateAttributeList, &createattribute_list,
                 R3DROPTOOLA_ClidVersions, &clidversions,
                 R3TAG_END);

        cn = (R3CREATEATTRIBUTENODE*)R3RemHead(createattribute_list);
        if (!cn)
            return NULL;
        R3Free(cn-tags, 3 * sizeof(R3TAG));
        R3Free(cn, sizeof(R3CREATEATTRIBUTENODE));
        return (void*)TRUE;
    }

The user will also have to be able to remove your GUI object from an application, in which case your drop tool class gets R3DROPTOOLCM_RELEASE class method. It does this by setting the R3WGA_Parent of your GUI object to NULL. This causes the parent window to send R3RM_UNREF to your window.

In the r3droptoolcm_release method you should detach your object from its model. Otherwise, the model keeps your GUI object alive and removal fails. (When you attached your GUI object to a model, such as 'unit converter' using R3MM_ADDEPENDENT method, the model called your view object with 'R3RM_REF' to make sure the view stays alive and it is safe to send R3RM_UPDATE to it.).


    static void *R3Release(R3CLASS *cl, R3OBJ *obj, R3OBJ *myguiobj)
    {
        R3SetAttrs(myguiobj,
                   R3ANIMSETGA_UnitConverter, NULL,
                   R3TAG_END);
        return R3DoSuperA3(cl, obj, R3DROPTOOLCM_RELEASE, 0, 0, myguiobj);
    }