Create and use an external area

4D - Documentation   Français   English   German   4D Plugin API, Command Theme List   4D Plugin API, Command Alphabetical List   Back   Previous   Next

version 2004.1 (Modified)


A plug-in area is an area (in a form or in its own window) that belongs to the plug-in.

Once created, 4th Dimension send it all the necessary messages and events to deal with the user: mouse clicks, keystrokes, menu selection, drag and drop, idle, etc …

To create an external area, you use the PluginWizard, selecting the necessary options. An area can exist only within a form (its name starts with a "%"), or it can run within a form and in its own window (its name starts with an underscore, it then is listed in the "Plug-ins" menu of the User mode).

Here are the subjects that will be discussed in this section:

- brief look at the skeleton of the code of an external area

- Area events

- Area Reference

- GrafPort and DeviceContext

- Using properties and Advanced properties

- build a sample area that demonstrates the basic way to code an external area.

Overview of external areas

An external area receives events that are passed to it by 4th Dimension. Its main routine usually consist of a dispatch of the events the plug-in wants to respond.

When 4th Dimension wants to interact with the area, it calls the PluginMain routine with the appropriate selector value (as usual, it is the index of the routine in the STR# resource of the plug-in – PluginWizard takes care of that). 4D passes the plug-in the standard pointer PA_PluginParameters, which contains different kind of data depending of the event that has to be managed.

Those events are listed in an enum of type AE_AreaEvent that is defined in the "PublicTypes.h" header file of this API.

typedef enum
{
   eAE_Idle         = 0,
   eAE_MouseDown   = 1,
   eAE_MouseUp   = 2,
   eAE_KeyDown   = 3,
   eAE_KeyUp   = 4,
   . . .
   . . .
   eAE_DesignInit   = 605
} PA_AreaEvent;

For each event, the plug-in can do about whatever it wants. Some events are critical for the plug-in, such as initialization/de-initialization of private data.

Basically, there are 3 kinds of events.

- Design environment events: customizing the area (especially with the Advanced Properties Dialog), draw it in the form editor.

- Initialization/De-initialization events at Runtime: the plug-in allocates memory, get some default values from the Advanced Properties.

- Runtime events: mouse events, key strokes, drag and drop

So, here is a skeleton of the code of a plug-in area:

void PluginMain( long selector, PA_PluginParameters params )
{
// Dispatch selector
   switch ( selector )
   {
      case kInitPlugin :
         // Enter some initialization code for the plug-in
         break;
      case kDeinitPlugin :
         // enter some de-initialization code here if needed
         break;
      
      case 1 :
         // enter code for first  routine  here
         break;
      case 2 :
         // enter code for second routine here
         break;
      . . .
      . . .
      case 11 : // in this sample, the 11th selector is an external  area
      // Call  the dispatcher
         DoMyPluginArea( params );
         break;
   }
}
void DoMyPluginArea ( PA_PluginParameters params )
{
   /* . . . local variables . . . */
   PA_AreaEvent      event;
// Get the event…
   event = PA_GetAreaEvent( params );
// … to dispatch it:
   switch ( event )
   {
// =========================initialization/DE-INITIALIzATION AT RUNTIME
      case eAE_Init :
   // If any, get the advanced properties.
   // Allocate some private data (4th Dimension will give back this data at each call)
         . . .
         break;
      case eAE_Deinit :
   // If any, get our private data and dispose of it
         . . .
         break;
      
// ========================= Runtime EVENTS
   // 4D wants to know if our area is able to be focused   
      case eAE_IsFocusable :
         . . .
         break;
   // 4D tells us it is time to redraw our area
      case eAE_Update :
         . . .
         break;
   // We've got a mousedown in our area
      case eAE_MouseDown:
         . . .
         break;
   // and so on with other Runtime events
         . . .
// ========================= DESIGN MODE EVENTS
   // First time the form is opened in Design Mode
      case eAE_DesignInit :
         . . .
         break;
   // 4D wants to redraw the area in a form, in design mode
      case eAE_DesignUpdate :
         . . .
         break;
   // 4D asks us if we want to use the AdvancedProperties Dialog button
      case eAE_AreAdvancedPropertiesEditable :
         . . .
         break;
   // We edit the properties
      case eAE_EditAdvancedProperties :
         . . .
         break;
   // 4D has finish with the properties and has stored them with the form
   // we can now dispose of it.
      case eAE_DisposeAdvancedProperties :
         . . .
         break;
// ========================= OTHER EVENTS ARE IGNORED
      default :
         PA_DontTakeEvent(params);
         break;
   }
}

Area events

The different events an area can manage are listed in a PA_AreaEvent enumeration, in the "PublicTypes.h" header file.

eAE_Initinitialization at Runtime. Area can initialize itself, read properties
(PA_GetPluginProperties, PA_GetAdvancedProperties), set values using
advanced properties.
eAE_DeinitDe-initialization at Runtime (release memory if necessary).
eAE_IdleThe user does nothing. The plug-in could, as an example, draw a clock.
A polite plug-in should not take too much time on this event. Area
can call PA_CallPluginAreaMethod.
eAE_MouseDownThe mouse button is down. Area can call PA_CallPluginAreaMethod.
eAE_MouseUpThe mouse button is up.
eAE_KeyDownA key is pressed. Area can call PA_CallPluginAreaMethod.
eAE_UpdateArea must be updated, redrawn.
aAE_CursorMouse has moved (even if the area is not selected) over the area.
eAE_IsFocusable4D wants to know if the plug-in can get the focus by tab (use
PA_SetAreaFocusable to answer).
eAE_SelectThe user wants to select the area (PA_AcceptSelect).
eAE_DeselectThe user wants to leave the area (PA_AcceptDeselect).
eAE_ScrollArea rect has changed, call PA_GetAreaRect.
eAE_TestPrintSize
eAE_GetPrintSize
eAE_PrintBand
eAE_UndoCommandUser selects "Undo" in the Edit menu.
eAE_CutCommandUser selects "Cut" in the Edit menu.
eAE_CopyCommandUser selects "Copy" in the Edit menu.
eAE_PasteCommandUser selects "Paste" in the Edit menu.
eAE_SelectAllCommandUser selects "Select all" in the Edit menu.
eAE_SelectDeselect
eAE_UpdateEditCommands4D asks the plug in to redraw the Edit menu (PA_UpdateEditMenu).
eAE_LoadRecordA record of the table of the form that owns the area is loaded. The area
can get a field of this record (ie, a field whose name is areaName_, like
4DWrite).
eAE_SaveRecordThe record of the table that owns the area is saved. The area can save
its content back to a field.
eAE_AllowDropArea accepts a drop ? (call PA_AllowDrop). If it accepts, it can get
information about the drop (PA_GetDragAndDropInfo,
PA_GetDragAndDropKind, PA_GetDragAndDropVariable,
PA_GetDragAndDropField).
eAE_DragAn object is dragged over the area. Get info about the dragging position
of the object (PA_GetDragPositions). Tell 4D the area will handle itself
the way it will show that something is dragging over
(PA_CustomizeDragOver).
eAE_DropSomething is dropped, get its value (PA_GetDragAndDropInfo,
PA_GetDragAndDropKind, PA_GetDragAndDropVariable,
PA_GetDragAndDropField).
eAE_GetMenuIcon4D requests the ID of the 'cicn' resources used in plug-in menu of the
tools palette in the Form Editor.
eAE_EndExecutionCycleEnd of an execution cycle in the form. Use PA_RequestRedraw to refresh
the area.
eAE_PageChangeThe current form page has changed. Use PA_GetCurrentPage.
eAE_DesignInitLayout is opened in Design Mode
eAE_DesignUpdateArea must be redrawn in Design Mode. Can use
PA_GetPluginProperties.
eAE_AreAdvancedPropertiesEditable4D wants to know if the Area uses the "Advanced
Properties. Area answers with
PA_SetAdvancedPropertiesEditable.
eAE_EditAdvancedPropertiesUser clicks on "Edit…" button of the Advanced Properties.
Call PA_SetAdvancedProperties.
eAE_DisposeAdvancedProperties4D has stored the advanced properties in the form
properties, you can now dispose this data
(PA_GetAdvancedPropertiesToDispose).

Calling Area object method

It is possible for the area to call its own 4D object method. In order to do this, the area must have the On plug in area event checked. If this event is set, then the area can call its own object method by calling PA_CallPluginAreaMethod.

This call is available for only 3 events : eAE_MouseDown, eAE_KeyDown, and eAE_Idle.

The example of uses are numerous :

- Let the 4D developer filter each key pressed

- we can imagine an area that makes some hard math calculation during its idle time, and, once done, it calls its object method to inform the user the operation is done.

- An other example could be a time-bombed plug-in, in which an area is available only for x seconds or minutes in a form.

- . . .

Ignoring events

We have seen that a typical area code consists of a dispatch of the received event. It is a good habit to use the "default" keyword of the switch operator, and to call PA_DontTakeEvent at this time. This way, 4D can handle the event for itself if necessary.

Area Reference

Usually, an area will need to have some private data, handling miscellaneous information about itself. To get and retrieve this data, the plug-in should follow those instructions :

- allocate a block of memory at initialization time (eAE_AreaInit), and fill it with the desired values. Once initialized, the area calls PA_SetAreaReference, passing it a pointer to the allocated memory, and its size. This "links" this particular pointer to this particular area.

- Each time the area is called, whatever the event is, it can get back a pointer to its private data by calling PA_GetAreaReference.

Here is a sample code using this. The area stores the number of time it has been called with a eAE_Idle event and a eAE_Update event. When the user clicks in the area, the area displays an alert given those number. An area can use whatever it wants as private data, just remember that this data is stored in RAM.

typedef struct
{
   long   idleCount;
   long   updateCount;
}AREA;
void DoMyArea (PA_PluginParameters params)
{
   AREA   *privateData; // the pointer to our data
   PA_AreaEvent   event;
   
   event = PA_GetAreaEvent(params);
   switch(event)
   {
      case eAE_AreaInit:
   // So, we allocate our data
         privateData = (AREA *) malloc(sizeof(AREA));
   // we initialize it (no error check here)
         privateData->idleCount = 0;
         privateData-> updateCount = 0;
   // Then, we link it to the area
         PA_SetAreaReference(params, privateData, sizeof(AREA));
         break
         
// =========================================================
// From now, each time we call PA_GetAreaReference, we get
// our pointer to the AREA private structure allocated
// =========================================================
      case kAE_AreaDeinit: // Free the memory used
         privateData = (AREA *) PA_GetAreaReference(params);
         free(privateData);
         break;
      case kAE_Idle:
         privateData = (AREA *) PA_GetAreaReference(params);
         privateData-> idleCount ++
         break;
      case kAE_Update:
         privateData = (AREA *) PA_GetAreaReference(params);
         privateData-> updateCount ++;
         break;
      case kAE_MouseDown:
         DisplayData(params);
         break;
      default:
         PA_DontTakeEvent();
         break;
   }
}

NOTE

Although the private data is usually allocated at eAE_AreaInit event, it can be allocated at any time, provided that a previously allocated data is disposed before.

Where to draw (GrafPtr, HDC, rectangle) ?

To draw its area, the plug-in must have a valid GrafPort under MacOS or a valid DeviceContext under Windows (again, if the developer uses Altura under Windows, GrafPorts should be used). The drawing region is already clipped for you, limited to the plug-in area.

- Under MacOS, the port is already fixed by 4th Dimension to the current window that holds the area. If you want to draw in the area from another place, you usually get the port at initialization phase.

- Under Windows, you use the PA_GetUpdateHDC routine to get a handle to the current device context, already prepared to draw only in the area.

So, under Windows, at eAE_Update event, the code will looks like:

   /*…previous switch…*/
   case eAE_Update:
   {
      HDC         hdc;
      HBRUSH      hbrush;
      RECT         rect;
      HGDIOBJ   old;
      PA_Rect   areaRect;
   // Get the current HDC
      hdc = (HDC) PA_GetUpdateHDC();
      rect = PA_GetAreaRect( params );
   // transfer areaRect in the "regular" RECT
      MyPA_RectToRECT(areaRect, &rect);
   // Paint something
      hbrush = CreateSolidBrush( 0 );
      old = SelectObject( hdc, hbrush );
      FrameRect( hdc, &rect, hbrush );
      SelectObject( hdc, old );
   }
      break;
   /* .  . . next switch . . . */

The area coordinates in the window, its rectangle, is updated if the user used the scrollbars. This rectangle is expressed in local coordinates to the window, and, when drawing or writing code that uses this rectangle, the developer should always call PA_GetAreaRect.

Note that it is also possible to save the port/device that holds the area at eAE_AreaInit, by reading the appropriate fields of the PA_PluginProperties structure (fMacWindow, fMacPort, fWinHWND, and fWinHDC).

Using properties and Advanced properties

Plug-in properties (defined in the PA_PluginProperties structure) are settings that the 4D developer has selected in Design Mode for the area. Using that information, the area can act according to the user choices:

- font (ID, fac, size, color),

- colors (fore and back),

- justification,

- owning table

- area is draggable/droppable

- platform

- The plug-in properties contains also some information updated depending on the context: the area is being printed…

The advanced properties are a fast way to customize, at Runtime, an area. The 4D developer can set different values or settings of an area in Design Mode. At Runtime, the area get those settings and apply them. This is much easy-to-use for the 4D developer than calling long sequences of "plug_Set This", "plug_Set that" routines at "On load" form event.

In order to manage the advanced properties, 4D and the plug-in will talk each others. The scheme of use is this one.

o In Design mode

- 4D asks the area if it wants to use AdvancedProperties, calling the area with the event eAE_AreAdvancedPropertiesEditable. It the area answers "yes" (PA_SetAdvancedPropertiesEditable(params, 1)), 4D enables the "Edit…" button of the "Advanced properties", in the properties palette.

- When the user clicks this button, 4D calls the area with the eAE_EditAdvancedProperties event. The area interacts with the user (it can use the Dialogs routines of this API), and then call PA_SetAdvancedProperties to 4D, so they will be stored with the form and 4D can give back those properties at Runtime.

- Once 4D has stored the advanced properties, it calls the plug-in with the eAE_DisposeAdvancedProperties event. The plug-in gets back a pointer to its advanced properties data by calling PA_GetAdvancedPropertiesToDispose. It can then dispose of it.

o At Runtime

If the plug-in has some advanced properties, it can can get them back at initialization time, by calling first PA_GetPluginProperties, passing those properties as a parameter to PA_GetAdvancedProperties. This routine extracts the advanced properties from the plug-in properties.

IMPORTANT NOTE

The PA_PluginProperties (and, so, the AdvancedProperties) are available ONLY AT eAE_AreaInit at Runtime (or at update in the form editor). If the plug-in needs to use later some information handled by those structures, it MUST read them at initialization time and duplicate them in its private data structure.

If PA_GetPluginProperties is called for an other event, PA_GetLastError returns an error.

Since the AdvancedProperties are passed as a pointer to a block of memory, using PA_GetPluginProperties (without checking the PA_GetLastError) and PA_GetAdvancedProperties when it is not the time can lead to a system crash, since the received pointer is a fool pointer.

To avoid this problem, and if the area needs to use later some information stored in the properties or in the advanced properties, it should duplicate the structures (or some fields of those structure) in its private data, as shown in the following sample (no error checking shown) :

// Structure used by the advanced properties
typedef struct
{
   long   field1;
   long   field2;
   char   string1[256];
   /* . . . */
} ADVANCED;
// Structure used as private data of the area
typedef struct
{
   PA_PluginProperties      props;
   ADVANCED            advanced;
   /* . . . */
} AREA;
// Main routine of the area
void DoMyArea (PA_PluginParameters params)
{
   AREA         *areaData;
   AE_AreaEvent   event;
   long         size;
   
   event = PA_GetAreaEvent(params);
   switch (event)
   {
// initialization.
      case eAE_AreaInit:
// We allocate an AREA structure
         areaData = (AREA *) malloc(sizeof(AREA));
// We save the properties
         PA_GetPluginProperties(params, &areaData->props);
// We save the advanced properties
         areaData->advanced = PA_GetAdvancedProperties(areaData->props);
// Link areaData to this area
         PA_SetAreaReference(params, areaData, sizeof(AREA);
         break;

      case eAE_AreaDeinit;
// Free memory
         areaData = (AREA *) PA_GetAreaReference(params);
         free(areaData);
         break;

/*
   From now, each time we need to use the properties (to draw using
   font, face, size, color set by the 4DE developer) or
   the advanced properties, we just have to use:
      areaData = (AREA *) PA_GetAreaReference(params);
   and to access the different fields.
*/
   }
}

4D - Documentation   Français   English   German   4D Plugin API, Command Theme List   4D Plugin API, Command Alphabetical List   Back   Previous   Next