Drag and drop with a plugin area

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

version 2003


4D PluginAPI provides the developer with routines to handle drag and drop within 4th Dimension (the routines are internal to 4D, you can't use them to drag/drop objects into other applications).

Drag from the area

To let the user drag something from the area, call PA_DragAndDrop when the area receives the event eAE_MouseDown. Be sure that you respect the 4D developer settings in the Design environment. If the "draggable" property of the area is not checked then you should not let the user drag anything.

To see if it is draggable, the area can look at the fDraggable field of the PA_PluginProperties structure, received at eAE_Init time.

Once the area receives the eAE_MouseDown event, it calls PA_GetClick to get the mouse coordinates. It translates those coordinates to global (they are received as local to the window/device that owns the area), and then uses PA_DragAndDrop, passing it the global mouse coordinates and the local coordinates of the area rectangle.

See the sample below.

Managing a drag and receiving a drop

If the area is droppable, it can receive 3 kinds of events : eAE_AllowDrop, eAE_Drag, and eAE_Drop.

- 4D first calls the area with the eAE_AllowDrop event. The area gets the dragging object information by calling PA_GetDragAndDropInfo. If the area is compatible with the object kind, then the area tells 4D that it accepts the drag by calling PA_AllowDrop, passing it 1 as second the parameter. If it does not want this object to be dropped onto it, 0 is passed.

- If the drop is allowed, 4D calls the area repetitively while the object is dragged within its rectangle. It the plug-in wants to handle itself the way something droppable being dragged is displayed, it calls PA_CustomizeDragOver. If this routine is not called, 4D draws the user interface for it. By handling the interface itself, the plug-in could, for example, highlight only parts of its content according to the type of data being dragged.

NOTE

Even if the area does not manage its own user interface, the dispatch of the event type must catch eAE_Drag. If it does not, and PA_DontTakeEvent is called in the "default" of the switch (as is desire, and is done in all the samples of this API), 4D will not draw the user interface.

- When the user drops the object, 4th Dimension calls the area with the event eAE_Drop. The area obtains the information by calling PA_GetDragAndDropInfo, then it retrieves the object kind and value, by calling one of the accessor routine of this API (PA_GetVariable, PA_GetStringField).

NOTE

The coordinates received in the PA_DragAndDropInfo structure are locals to the owning Window/Device.

Tips

At eAE_AreaInit, the plug-in should keep track of PA_PluginProperties fields, such as the fTable field (which give the table number of the table that owns the form), and the fWinHDC for Windows programmers.

Getting the fTable field is mandatory because when the object being dragged is a field of the current table in the form, the PA_GetDragAndDropField returns 0 as table number (and the correct field number). In order to check the kind of field (PA_GetFieldProperties) or to get the value of the field (PA_GetxxxField), the area must pass a valid table number to those routines.

Sample Drag and Drop code

/* -------------------------------------------------
   Sample plug-in written using the 4D Plug-in API.

   Using Drag and Drop in an area. Each time a
   string (variable or field) is dropped, the
   area draws it at the mouse location.

   So:
   - At eAE_AllowDrop, the plug-in check the type
   of data being dragged.  If it is a string
   (variable or field), the drop is allowed.
   
   - at eAE_Drag, we do nothing (see below)
   
   - at eAE_Drop, the plug-in gets the value of
   the object, and draws it at the mouse location
   
   The area is also draggable (if this property is set
   in design environment). Once a 4D object receives a drop
   from the area, it calls the routine GetDraggedString.
------------------------------------------------- */
/*   --------------------------------------------------
   Cross platform common source.
   -------------------------------------------------- */
#define _USE_MAC_APIs_   (_ASIPORT_ || VERSIONMAC)
/*   --------------------------------------------------
   includes
   -------------------------------------------------- */
#include "4DPluginApi.h"
#include <stdlib.h>
#include <string.h>
#if VERSIONWIN && (!_USE_MAC_APIs_)
   #include <windows.h>
#endif
//   -------------------------------------------------------
//   List of plug-in routines (usually built with PluginWizard)
//   -------------------------------------------------------
enum {
   kSuperArea   = 1,    // defined as "%AreaDragDrop"
   kGetAreaString   = 2   // GetDraggedString:S
   // . . .
};
//   -------------------------------------------------------
//   CONSTANTS
//   -------------------------------------------------------
#define kPluginID   17458
// Default sentence if nothing is modified by the user
#define kPROMPT   "Drag-Drop a string in this area"
//   -------------------------------------------------------
//   STRUCTURES
//   -------------------------------------------------------
typedef struct
{
// if the area is not draggable we'll do nothing on mouseDown:
   short   isDraggable;
// Table number of the table that owns the form
   short   table;
// where to draw the string:
   short   offsetX;
   short   offsetY;
// sample thing to draw:
   char   toDraw[256];
#if VERSIONWIN
   HWND   hwnd; // HDC of the window that holds the area
#endif
} AREA;
//   -------------------------------------------------------
//   GLOBALS
//   -------------------------------------------------------
char      gToDraw[256]; // to fill when the area is dragged out
//   -------------------------------------------------------
//   PROTOTYPES
//   -------------------------------------------------------
void DragDropArea   ( PA_PluginParameters params );
void Area_Init   ( PA_PluginParameters params );
void Area_Deinit   ( PA_PluginParameters params );
void Area_Update   ( PA_PluginParameters params );
void Area_AllowDrop   ( PA_PluginParameters params );
void Area_Drop   ( PA_PluginParameters params );
void Area_DragOut   ( PA_PluginParameters params );
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*   ROUTINES            *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*   =================================================================
   Main routine of the plug-in
   ================================================================= */
void PluginMain( long selector, PA_PluginParameters params )
{
   switch ( selector )
   {
      case kInitPlugin :
   // initialize the string returned after a drag out
         gToDraw[0] = (char) 0;
         break;
      case kDeinitPlugin :
      // enter some de-initialization code here if needed
         break;
      case kSuperArea :
         DragDropArea( params );
         break;
      
      case kGetAreaString :
      // A drag out is over. User wants the data
         PA_ReturnString(params, gToDraw);
         gToDraw[0] = (char) 0;
         break;
   }
}
/*   =================================================================
   Main routine of the area
   ================================================================= */
void DragDropArea (PA_PluginParameters params)
{
   AE_AreaEvent      event;
   
// Get the event and dispatch it
   event = PA_GetAreaEvent( params );
   switch ( event )
   {
//   -------------------------------------------------------------------
//               INITIALIZATION / DE-INITIALIZATION
//   -------------------------------------------------------------------
      case eAE_Init :
         Area_Init(params);
         break;
      case eAE_Deinit :
         Area_Deinit(params);
         break;
//   -------------------------------------------------------------------
//               RUNTIME EVENTS
//   This basic sample demonstrates only some DragAndDrop bases.
//   -------------------------------------------------------------------
      case eAE_AllowDrop :
         Area_AllowDrop( params );
         break;
         
   // Even if we do nothing with it in this sample, eAE_Drag
   // must be "taken". If we do not do that, the switch will go
   // to "default", where PA_DontTakeEvent is called. In this
   // case, the user interface will not be drawn by 4D.
      case eAE_Drag:
         break;
      
      case eAE_Drop :
         Area_Drop (params);
         break;
      case eAE_MouseDown :
         Area_DragOut (params);
         break;
         
      case eAE_Update :
         Area_Update(params);
         break;
      
      default :
         PA_DontTakeEvent (params);
         break;
   }
} /* DragDropArea */
/*   =================================================================
   Initialization of the area
   
   We initialize an AREA structure.
   
   IMPORTANT: the plug-in properties (and the AdvancedProperties) can only be
   read at initialization time during  Runtime.
   ================================================================= */
void Area_Init (PA_PluginParameters params)
{
   AREA   *privateData = 0L;
   PA_PluginProperties props;
   PA_Rect   r;
   
// Allocate the area private data.
   privateData = (AREA *) malloc (sizeof(AREA));
   if(privateData)
   {
      r = PA_GetAreaRect(params);
      PA_GetPluginProperties(params, &props);
      privateData->isDraggable = props.fDraggable;
// It is the only event from witch we can get this info
      privateData->table = props.fTable;
// default values for the string
      privateData->offsetX = 5;
      privateData->offsetY = 35;
      privateData->toDraw[0] = (char) 0;
#if VERSIONWIN
      privateData->hwnd = (HWND) props.fWinHWND;
#endif
   }
// Save our data. 4D will give it back to us later.
   PA_SetAreaReference( params,  (char *) privateData );
   
} /* Area_Init */
/*   =================================================================
   De-initialization of the area
   Free our data. If we do not, the memory used
   will be lost, using space in heap for nothing.
   ================================================================= */
void Area_Deinit (PA_PluginParameters params)
{
   AREA   *privateData;
   
// Get our data...
   privateData = (AREA *) PA_GetAreaReference( params );
// ... release it.
   if(privateData)
   {
      free( (char *) privateData );
      PA_SetAreaReference(params, 0L);
   }
   
} /* Area_Deinit */
/*   =================================================================
   Something is coming over the area
   We only allow the drop if the object is a string.
   So, we check the kind of the drag.
      - if it is a variable, we check its kind, using PA_GetVariableKind
      - if it is a field, we check its type using PA_GetFieldProperties
   ================================================================= */
void Area_AllowDrop (PA_PluginParameters params)
{
   PA_DragAndDropInfo   info;
   PA_DragKind         kind;
   char            allow = (char) 0;
   AREA            *privateData;
   
// Get the info
   info = PA_GetDragAndDropInfo ( params );
   if(PA_GetLastError() == eER_NoErr)
   {
// Get the kind of object : variable or field
      kind = PA_GetDragAndDropKind( info );
// Check if it is a string
      switch(kind)
      {
         case eDK_DragVariable :
         {
            PA_Variable         var;
            PA_VariableKind      varKind;
            var = PA_GetDragAndDropVariable( info, 0 );
            varKind = PA_GetVariableKind(var);
            if( (varKind == eVK_String) || (varKind == eVK_ArrayString) )
               allow = (char) 1;
               
         }
            break;
            
         case eDK_DragField :
         {
            short         table, field;
            PA_FieldKind   fieldKind;
            PA_GetDragAndDropTableField( info, &table, &field );
         // If table is 0, it means that a field of the table that
         // owns the form is being dragged. We must use the table number
         // stored at kInitArea
            if(table == 0)
            {
               privateData = (AREA *) PA_GetAreaReference(params);
               if(privateData)
                  table = privateData->table;
            }
            if(table)
            {
               PA_GetFieldProperties(table, field, &fieldKind, 0L, 0L, 0L);
               if( fieldKind == eFK_AlphaField )
                  allow = (char) 1;
            }
         }
            break;
      }
   }
   
// Now, tell 4D if we allow the drop or not
   PA_AllowDrop(params, allow);
   
} /* Area_AllowDrop */
/*   =================================================================
   The dragged object is dropped
   We get its value and draw the new string.
   We do not have to check the kind of variable or field, we know it
   is OK since PA_AllowDrop has been called once.
   ================================================================= */
void Area_Drop (PA_PluginParameters params)
{
   AREA         *privateData;
   PA_DragAndDropInfo   info;
   PA_DragKind      kind;
   PA_Rect         areaRect;
   
// Get the info
   info = PA_GetDragAndDropInfo ( params );
   if(PA_GetLastError() == eER_NoErr)
   {
// Get our privateData
      privateData = (AREA *) PA_GetAreaReference(params);
      if(privateData)
      {
   // Get the kind of object : variable or field
         kind = PA_GetDragAndDropKind( info );
   // Get its value
         switch(kind)
         {
            case eDK_DragVariable :
            {
               PA_Variable   var;
               var = PA_GetDragAndDropVariable( info, 0 );
               PA_GetStringVariable(var, privateData->toDraw);
                  
            }
               break;
               
            case eDK_DragField :
            {
               short   table, field;
               PA_GetDragAndDropTableField( info, &table, &field );
         // If table is 0, it means that a field of the table that
         // owns the form is being dragged. We must use the table number
         // stored at kInitArea
               if(table == 0)
               {
                  privateData = (AREA *) PA_GetAreaReference(params);
                  if(privateData)
                     table = privateData->table;
               }
               if(table)
                  PA_GetStringField(table, field, privateData->toDraw);
            }
               break;
         }
// Prepare for drawing.
// We must convert the global coordinate to local in order
// to properly draw the string at the mouse location.
// Then, we calculate the offset from the area rectangle
         areaRect = PA_GetAreaRect(params);
#if _USE_MAC_APIs_
{
         Point   p;
         p.h = info.fToWhereH;
         p.v = info.fToWhereV;
         GlobalToLocal(&p);
         privateData->offsetX = (p.h - areaRect.fLeft);
         privateData->offsetY = (p.v - areaRect.fTop);
}
#else
{
         POINT   p;
         p.x = info.fToWhereH;
         p.y = info.fToWhereV;
         ScreenToClient(privateData->hwnd, &p);
         privateData->offsetX = (short) (p.x - areaRect.fLeft);
         privateData->offsetY = (short) (p.y - areaRect.fTop);
}
#endif
// At least, let's draw the string
         Area_Update(params);
      } // if (privateData)
   }
   
} /* Area_Drop */
/*   =================================================================
   Drag the area itself
   ================================================================= */
void Area_DragOut   (PA_PluginParameters params)
{
   PA_Rect      areaRect;
   short      x, y;
   AREA      *privateData;
   
   privateData = (AREA *) PA_GetAreaReference(params);
   if(!privateData)
      return;
   
// we do not start a drag if the area is not draggable
   if( !privateData->isDraggable)
      return;
   
   PA_GetClic(params, &x, &y);
   if(PA_GetLastError() == eER_NoErr)
   {
// We must convert those local coordinates to global
#if _USE_MAC_APIs_
      Point   p;
      p.h = x;
      p.v = y;
      LocalToGlobal(&p);
      x = p.h;
      y = p.v;
#else
      POINT   p;
      
      p.x = x;
      p.y = y;
      ClientToScreen( privateData->hwnd, &p);
      x = (short) p.x;
      y = (short) p.y;
#endif
// prepare the returned string
      PA_MoveBlock(privateData->toDraw, gToDraw, 256);
// Start the drag
      areaRect = PA_GetAreaRect(params);
      PA_DragAndDrop(x, y, areaRect);
   }
   
} /* Area_DragOut */
/*   =================================================================
   Draw the area
   ================================================================= */
void Area_Update   (PA_PluginParameters params)
{
   PA_Rect      areaRect;
   AREA      *privateData;
   
// Get our data
   privateData = (AREA *) PA_GetAreaReference( params );
   if(!privateData)
      return;
      
// Get area rect
   areaRect = PA_GetAreaRect(params);
   
// ----------------------------------------- Draw things under MacOS - Windows+Altura
#if _USE_MAC_APIs_
   {   
   // on Mac, the PA_Rect is a regular Rect      
      EraseRect( (Rect *) &areaRect);
   // Draw the prompt
      MoveTo(areaRect.fLeft + 5, areaRect.fTop + 20);
      DrawText(kPROMPT, 0, strlen(kPROMPT));
   // Draw the string, if any
      if(privateData->toDraw[0])
      {
         MoveTo(   areaRect.fLeft + privateData->offsetX,
               areaRect.fTop + privateData->offsetY );
         DrawText(privateData->toDraw, 0, strlen(privateData->toDraw));
      }
   }
//------------------------------------------- Draw things under Windows
#else
   {
      HDC      hdc;
      RECT   r;
      
      hdc = (HDC) PA_GetUpdateHDC();
   // On window, a RECT is made with long, not shorts
      r.top = areaRect.fTop;
      r.left = areaRect.fLeft;
      r.bottom = areaRect.fBottom;
      r.right = areaRect.fRight;
      
   // Draw the prompt
      TextOut(hdc, r.left + 5, r.top+20, kPROMPT, strlen(kPROMPT));
   // Draw the string if any
      if(privateData->toDraw[0])
         TextOut(   hdc,
                  (int) r.left + privateData->offsetX,
                  (int) r.top + privateData->offsetY,
                  (const char *) privateData->toDraw,
                  strlen(privateData->toDraw) );
   }
#endif
   
} /* Area_Update */

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