|
![]() |
![]() |
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 */