[ Next ] [ Previous ] | Chapter 20 |
While the capability to drag and drop an icon
from one window to another has been present since OS/2 1.1, a
standardized, robust method for providing this essential function was
not introduced until OS/2 1.3 with the Drg functions and their
associated DM_ messages. But what is drag and drop, really ?
Drag and drop is the capability of using the mouse to manipulate
directly the transfer and placement of data within single or multiple
applications. Objects can either be "moved" or "copied" from a source
window to a target window. ("Moved" and "copied" are
application-defined concepts.)
Drag and drop can be seen from two viewpoints: from the viewpoint of
the source, who initiates the drag; and from the aspect of the target,
which can accept or reject a dragging operation. We will examine both
of those as well as what to do once the target is established.
In a nutshell, the source window is responsible for determining that the
user is attempting to drag an object, initializing the appropriate data
structures, and finally calling either DrgDrag or DrgDragFiles (a version of DrgDrag
specifically for file objects). Determining that the user is attempting
to drag an object is the easiest part, since the system will send a
WM_BEGINDRAG message with the pointer position in mpParpm1.
(This is not entirely true. If a child control receives a WM_BEGINDRAG
message, it might alert the programmer to this through a WM_CONTROL
message, but it is not required that it do so).
After it has been decided that a drag operation is necessary, the
application needs to allocate and initialize three structure types:
DRAGINFO, DRAGITEM and DRAGIMAGE. (There are actually four; the
DRAGTRANSFER structure is used ones a target has been established.) The
DRAGINFO structure contains information about the drag as an entity.
The DRAGITEM structures describe each object being dragged. Finally,
the DRAGIMAGE structures each describe the appearance of the object
under the pointer while it is being dragged.
typedef struct _DRAGINFO /* dinfo */
{
ULONG
cbDraginfo;
/* Size of DRAGINFO and DRAGITEMs*/
USHORT
cbDragitem;
/* size of
DRAGITEM
*/
USHORT
usOperation;
/* current drag operation */
HWND
hwndSource;
/* window handle of source */
SHORT
xDrop;
/* x coordinate of drop position */
SHORT
yDrop;
/* y coordinate of drop position */
USHORT
cditem;
/* count of
DRAGITEMs
*/
USHORT
usReserved;
/* reserved for future use */
} DRAGINFO;
typedef DRAGINFO *PDRAGINFO;
In the DRAGINFO structure, cbDraginfo is the size of the DRAGINFO structure in bytes. cbDragitem is the size of the DRAGITEM structure contained therein. usOperation
is the default operation that can be, but is not required to be, set by
the source and inspected by the target; it is a DO_ constant. hwndSource is the only field not initialized by DrgAllocDragInfo, and is the handle of the window initiating the drag-and-drop operation. xDrop and yDrop are the coordinates of the object as dropped. cdItem specifies the number of DRAGITEM structures stores along with the DRAGINFO structure. usReserved is reserved and must be set to 0.
typedef struct _DRAGITEM /* ditem */
{
HWND
hwndItem;
/* conversation
partner */
ULONG
ulItemID;
/* identifies item being dragged */
HSTR
hstrType;
/* type of
item
*/
HSTR
hstrRMF;
/* rendering mechanism and format*/
HSTR
hstrContainerName; /*
name of source container */
HSTR
hstrSourceName;
/* name of item at source */
HSTR
hstrTargetName;
/* suggested name of item at dest*/
SHORT
cxOffset;
/* x offset of the origin of the */
/*
image from the mouse hotspot*/
SHORT
cyOffset;
/* y offset of the origin of the */
/*
image from the mouse hotspot*/
USHORT
fsControl;
/* source item control flags */
USHORT
fsSupportedOps;
/* ops supported by source */
} DRAGITEM;
typedef DRAGITEM *PDRAGITEM;
In the DRAGITEM structure, hwndItem is the handle of
the window with which the target should communicate to transfer the
information necessary to complete the operation. The only time this
would be different from the hwndSource field of the DRAGINFO structure is when an application contains many "standard" windows as a children of the main window. hstrType is the type of the item represented by the DRAGITEM structure. hstrRMF is the rendering mechanism used to transfer the information and format of data being transfered. hstrContainerName
is the name of the container that holds the object being dragged. With a
file object, for example, this would be the directory where the file
resides. hstrSourceName and hstrContainerName is the names of the object at its original location and the suggested
name of the object at the target location. The target does not
have to use the suggested name; it is up to the application programmer. cxOffset and cyOffet
specify the offset from the hotspot of the pointer to the lower left
corner of the image representing the object and is copied here by the
system from the corresponding fields in the DRAGIMAGE structure. fsControl specifies one or more DC_ constants describing any special attributes of the objects being dragged. Finally, fsSupportedOps
specifies the operations that can be performed as part of the drag -
the object may be copied, moved, linked ("shadowed"), and so on.
In the DRAGIMAGE structure, cb specifies the size of the structure in bytes. fl specifies a number of DRG_ constants describing the type of data that is given in this structure.
Constant |
Description |
DRG_BITMAP |
hImage specifies a bitmap handle |
DRG_CLOSED |
The polygon specified is to be closed. If specified, DRG_POLYGON also must be specified. |
DRG_ICON
|
hImage specifies an icon handle. |
DRG_POLYGON |
hImage specifies an array of POINTL structures. |
DRG_STRETCH |
The bitmap or icon is to be stretched to fit the specified size. If specified, DRG_BITMAP or DRG_ICON also must be specified. |
DRG_TRANSPARENT |
An outline of the icon is to be shown only. If specified, DRG_ICON also must be specified. |
cPtl specifies the number of points if fl contains DRG_POLYGON. hImage can specify one of many things, depending on what flags are set in fl, as seen in Table 20.1. sizlStretch specifies the size that the bitmap or icon should be stretched to. cxOffet and cyOffest
specify the offset of the lower left corner of the image, relative to
the hotspot of the cursor as the object is dragged. these two fields
are copied into the DRAGITEM structure.
At this point, probably few of the fields in these structures make any
sense. It is important to realize that, because the target will more
likely than not exist as part of another process, simple allocation of
these structures will not suffice, due to OS/2's memory protection
features. They must be allocated in shared memory through the use of
the DrgAllocDraginfo and DrgAddStrHandle functions.
PDRAGINFO APIENTRY DrgAllocDraginfo(ULONG cditem);
HSTR APIENTRY DrgAddStrHandle(PCSZ psz);
The former accepts the number of items being dragged and returns a
pointer to the shared DRAGINFO structure, whose individual DRAGITEM
structures must be initialized using the DrgSetDragitem
function. The latter takes a pointer to a string and returns a "string
handle" - a pointer to a shared memory block containing (among other
things) the string passed to the function.
The following is the typical initialization code used in a Presentation
Manager application to initiate a drag-and-drop operation.
HWND hwndWindow;
PDRAGINFO pdiDrag;
DRAGITEM ditem;
pdiDrag = DrgAllocDraginfo(1);
//-------------------------------------------------------------
// Note that DrgAllocDraginfo() initializes all of the DRAGINFO
// fields *except* hwndSource.
//-------------------------------------------------------------
pdiDrag->hwndSource = hwndWindow;
diItem.hwndItem = hwndWindow;
diItem.ulItemID = 1L; //Unique identifier
diItem.hstrType = DrgAddStrHandle(DRT_TEXT);
diItem.hstrRMF = DrgAddStrHandle("<DRM_OS2FILE,DRF_TEXT>");
diItem.hstrContainerName = DrgAddStrHandle("C:\");
diItem.hstrSourceName = DrgAddStrHandle("CONFIG.SYS");
diItem.hstrTargetName = DrgAddStrHandle("CONFIG.BAK");
diItem.cxOffset = 0;
diItem.cyOffset = 0;
diItem.fsControl = 0;
DrgSetDragItem(pdiDrag, &diItem, sizeof(diItem), 0);
The following sections will explain this listing in more detail.
Before actually taking our forceps to the code, a few concepts need to be introduced. The first is that of the type and the true type of an object being dragged. The type is just that - a string that describes what the object consists of. The true type
is a type that more accurately describes the object, if such a true
type exists. For example, a file that contains C source code might have
the type "Plain Text" but have a true type of "C code". An object can
have more than one type, with each separated by commas and the true
type appearing as the first type listed. Thus, the hstrType field for the C source code would be initialized as DrgAddStrHandle("C Code, Plain Text"). OS/2 defines a set of standard types in the form of DRT_ constants.
The second concept that needs to be discussed is the rendering mechanism and format (RMF). The rendering mechanism is the method by which the data will be communicated from the source to the target. The format
is the format of the data if the corresponding rendering mechanism as
used to transfer the data. These RMF pairs take the form "<rendering
mechanism, format>", with multiple RMF pairs separated by commas.
OS/2 also defines a set of rendering mechanisms, also no constants are
defined for them.
Note that if programmers have a fully populated set of RMF pairs
("fully populated" meaning that for every rendering mechanism, every
format is available), a shorthand cross-product notation can be used.
For example, if there are the rendering mechanisms RA, RB and RC and
the formats FA, FB and FC, and the following RMF pairs are available:
"<RA,FA>,<RA,FB>,<RA,FC>,<RB,FA>,<RB,FB>,<RB,FC>,<RC,FA>,<RC,FB>,<RC,FC>"
then this can be represented as "(RA,RB,RC) X (FA,FB,FC)".
Obviously, this is a much more concise way of describing the mess. If
the thought of having to parse such a monster with so many different
combinations just to discover if <RD, FD> is supported drives
programmers crazy, they should have no fear - there are functions that
will determine this.
Analogous to the relationship between type and true type, there also
exists a native RMF, which describes the preferred RMF for this object.
It is always the first RMF pair listed or the first RMF pair generated
in a cross-product. The native RMF might employ faster data transfer
algorithms or other such performance boosters, so it should be used by
the target whenever possible.
Just because OS/2 defines set of types, rendering mechanisms, and
formats doesn't mean programmers are limited to those sets. If an
application needs to use a new format, it can register the appropriate
strings describing this with the DrgAddStrHandle function.
However, the transfer protocol for the rendering mechanisms and the
corresponding data formats also should be published so that other
applications can understand the new type of RMF.
The next concepts are that of source name, source container Drag and drop:, and target name Drag and drop:. The source name
is the name of the object being dragged. It is useful because the
target application may be able to perform the requested operation
without having to interact with the source application. Typically, this
is used when dealing with files. The source container
describes where the object resides. This, again, is useful when
deciding how to complete the action. When dealing with files, for
example, the source container would be directory name containing the
file. Finally, the target name is actually a suggested name, since the
target could determine that an object with that name already exists and
that the object will receive a new, unique name.
Now that these concepts have been explained, the structures and sample
code shown earlier in this chapter should be easier to understand. We
are dragging one item, as evidenced in the DrgAllocDragInfo
call. The one item is of type "text" and will be transferred via the
file system using the format "unknown". The file system object resides
in the container/directory "C:\" and has the name "CONFIG.SYS". The
suggested target name is "CONFIG.BAK", although the target application
is free to select a different name.
Assuming that the last section has been understood and that programmers
have successfully (and correctly) initialized the DRAGINFO structure
and each DRAGITEM structure for each object, we are now ready to call
the function that makes all of this hard work worthwhile: DrgDrag:
HWND APIENTRY DrgDrag(HWND hwndSource,
PDRAGINFO pdinfo,
PDRAGIMAGE pdimg,
ULONG cdimg,
LONG vkTerminate,
PVOID pRsvd);
hwndSource is the handle of the window initiating the drag operation. pdinfo points to the DRAGINFO structure returned from DrgAllocDraginfo. pdimg points to an array of one or more DRAGIMAGE structures, and cdimg specifies how many images the array contains. vkTerminate describes the manner by which the drag is ended and is a VK_ constant.
Constant |
Description |
VK_BUTTON1 | Drag is ended using mouse button 1. |
VK_BUTTON2 | Drag is ended using mouse button 2. |
VK_BUTTON3 | Drag is ended using mouse button 3. |
VK_ENDDRAG |
Drag is ended by the mouse button defined in the "System Setup" folder to end a drag. This should be used when dragging is performed in response to a WM_BEGINDRAG message. |
Since the data transfer actively involves both the source and target
windows, now is a good time to view the target's perspective from the
beginning. Remember that it is the target's responsibility to provide
visual feedback to the user during the drag operation and to initiate
the data transfer once the drop has occurred. Visual feedback is
accomplished by responding to the appropriate DM_ messages that are
sent to the target during the drag.
DM_DRAGOVER This message is sent whenever the pointer enters the target window space to allow it the opportunity to add target emphasis
to the destination of the drag. This is also sent whenever a key is
pressed or released. The message contains a pointer to the DRAGINFO
structure which cab accessed by calling DrgAccessDragInfo.
DM_DRAGLEAVE This message is sent to any window previously sent a
DM_DRAGOVER message whenever the pointer leaves the target window space
to allow it the opportunity to remove any "target emphasis" previously
drawn. Note that since this occurs only for a window, the target is
responsible for monitoring the mouse position of the DM_DRAGOVER
messages when it is a container for other items. This message is not
sent if the object(s) are dropped on the window.
DM_DROP This message is sent to the target window when the user drops
the object(s) on it. As with DN_DRAGLEAVE, any target emphasis should
be removed ones this message is received. Normally this message is
responded to before any data transfer takes place so that the source
can learn the window handle of the target.
DM_DROPHELP This message is sent whenever the user presses F1 during a
drag operation. The target should respond by displaying help on the
actions that would occur if the object(s) were dropped at the point
where F1 was pressed.
Whenever a DM_DRAGOVER message is received, the potential target must
determine if the drag operation is valid. For example, a C source file
could be dropped on a C compiler object, but not a Pascal source file;
by holding down the CTRL key, a file could be copied to the printer,
but it is (probably) unlikely that a file could be moved to the
printer. At a minimum, the following two conditions must be met for a
drop to be possible:
When determining the state of these conditions, the functions DrgVerifyType, DrgVerifyRMF, DrgVerifyTrueType, and DrgVerifyNativeRMF help considerably.
BOOL APIENTRY DrgVerifyType(PDRAGITEM pditem, PCSZ pszType);In all of these functions, pditem points to the DRAGITEM structure describing the item being tested. pszType specifies the type to compare with. pszMech specifies the rendering mechanism. pszFmt specifies the data format. pszRMF specifies a rendering mechanism and format. All of these functions return TRUE if the condition is met and FALSE if not.
BOOL APIENTRY DrgVerifyRMF (PDRAGITEM pditem, PCSZ pszMech, PCSZ pszFmt);
BOOL APIENTRY DrgVerifyTrueType (PDRAGITEM pditem, PCSZ pszType);
BOOL APIENTRY DrgVerifyNativeRMF(PDRAGITEM pditem, PSZ pszRMF);
Constant | Description |
DOR_DROP |
Returned whenever the drag is acceptable. This is the only response that can be equated with "Yes, you can drop here". |
DOR_NODROP
|
Returned whenever the location of the object(s) in the target window is unacceptable |
DOR_NODROPOP
|
Returned whenever the operation (copy or move) is unacceptable; this implies that the drag might be valid if the operation is changed. |
DOR_NEVERDROP |
Returned whenever a drag is never acceptable; no further
DM_DRAGOVER messages will be sent to the application until the mouse
leaves the window and returns. |
![]() |
Gotcha!
|
Okay, let's assume that the user selected one or more objects,
depressed the appropriate mouse button, dragged the object(s) over a
window, received the feedback that the target is willing to accept the
object(s), and let go of the mouse button. What happens next ? The
answer to this depends on the RMF chosen to transfer the data with. For
example, if DRM_OS2FILE is chosen, the target could choose to render
the data itself, or maybe it doesn't know the name of the source data
(e.g. for security reasons, the source window didn't fill this in), so
it must ask the source window to render the data before it can complete
the drop operation.
Let us consider each of the three system-defined rendering mechanisms to see the possible chain of events within each.
DRM_OS2FILE This mechanism would be used to transfer the data via
the file system. The data does not have to exist already in this form,
but could be placed there by the source after receiving a DM_RENDER
message from the target.
If the target understands the native RMF and if the true type of the
object, then the target can render the operation without the
intervention of the source. However, this might not be feasible; in
that case, a DN_RENDER message would need to be sent to the source so
that it can perform the operation. (This could occur if the source
does not know the name of the file containing the data to be
transferred.) If so, the target needs to allocate a DRAGTRANSFER
structure (via DrgAllocDragtransfer) and fill in the hstrRenderToName field; the source sends back a DM_RENDERCOMPLETE message to indicate that the operation is done.
DRM_PRINT This mechanism would
be used when the data is dropped onto a printer, and should be used
only if the source understands and can process the DM_PRINT message
that will be sent to it by the target. This message contains the name
of the print queue to which the operation is to be performed.
![]() |
Gotcha!
|
A lot of material has been explained so far, and an example is sorely needed to cross the boundary from the abstract to the applied. The following application can act as both source and target for direct manipulation. While it is a simple program, it demonstrates the concepts previously described. DRAG1.C
Since main is fairly standard, we'll ignore it except for the fact that we're reserving space for a pointer in a call to WinRegisterClass.
This will be used to store a pointer to the client's instance data, so
that we can avoid global variables. This instance data is allocated and
initialized in the WM_CREATE message and is freed in the WM_DESTROY
message. typedef struct _CLIENTINFO The pdiDrag field is used only by the source window and points to the DRAGINFO structure allocated via DrgAllocDraginfo. bDragging and bEmphasis specify whether a dragging operation is in progress and whether the client is displaying emphasis, respectively. achLine is
used only by the target window and contains the line of text that was
dropped on the window. for clarity, the processing of the
direct-manipulation messages has been separated into those usually
associated with the source and the target windows. (See doSource and doTarget.) |
![]() |
![]() |
|
![]() |
|
What
the program does is allow the dragging of text from the left half of
the window into either the right half of this window or another
instance of this window. (Try starting two copies of DRAG1.EXE to do
this.) Whenever the source receives a WM_BEGINDRAG message, the
appropriate data structures are initialized and DrgDrag is
called. The target adds emphasis whenever it receives a DM_DRAGOVER
message and returns the appropriate DOR_ value. After the object
has been dropped, the target completely renders the data provided by
the source and sends the source a DM_ENDCONVERSATION message to
terminate the dragging operation. |
![]() |
![]() |
Gotcha!
It needs to be stated somewhere, and what a better place than here,
that there appears to be a bug in OS/2 Warp when using DRG_BITMAP
for the DRAGIMAGE to be displayed. The first time the drag and drop is
performed, everything works fine; but if the application is exited and
restarted, dragging the object using DRG_BITMAP leaves "mouse
droppings" behind, making the display quite ugly. We have no
information regarding the availability of a fix. |
![]() |
Gotcha!
Another important item is that the cxOffset and cyOffset fields of the DRAGITEM structure cannot be used for the programmer's own purposes, since DrgDrag copies the corresponding fields from the DRAGIMAGE structure here. Likewise, hwndItem
should specify a valid window handle, or unexpected results will occur.
Any associated structures that need to be "attached" to a
DRAGITEM structure may do so safely by casting the structure to a ULONG
and passing the pointer to the ulItemID field. |
Let's complicate things by modifying our program to have the source window render the data.
DRAG2.C
DRAG2.MAK
DRAG2.DEF
As can be seen, the case when the source does not render the data prior to calling DrgDrag is a bit more involved. This is communicated to the target by not specifying the source name in hstrSourceName. After determining that this did not happen, the program allocates another shared structure - DRAGTRANSFER - using a call to DrgAllocDragtransfer and sends the source a DM_RENDER message with the target name in the DRAGTRANSFER structure.
PDRAGTRANSFER APIENTRY DrgAllocDragtransfer(ULONG cdxfer);cdxfer specifies the number of structures to allocate and must be greater than 0. It returns a pointer to the array of structures allocated.
typedef struct _DRAGTRANSFER /* dxfer */
{
ULONG cb; /* size of control block */
HWND hwndClient; /* handle of target */
PDRAGITEM pditem; /* DRAGITEM being transferred */
HSTR hstrSelectedRMF; /* rendering mech & fmt of choice*/
HSTR hstrRenderToName; /* name source will use */
ULONG ulTargetInfo; /* reserved for target's use */
USHORT usOperation; /* operation being performed */
USHORT fsReply; /* reply flags */
} DRAGTRANSFER;
typedef DRAGTRANSFER *PDRAGTRANSFER;
cb is the size of the structure in bytes. hwndClient specifies the handle of the window on which the item was dropped. pditem points to the DRAGITEM structure withing the DRAGINFO structure that was passed via the DM_DROP message representing the item of interest.
hstrSelectedRMF specifies a string handle that describes the RMF to use when transferring the item. hstrRenderToName specifies a string handle that describes the name to be used when rendering the data. ulTargetInfo specifies any application-specific data that the target window wishes to communicate to the source. usOperation specifies the operation to use - for example, copy, move, or link. fsReply is filled in by the source window and specifies a DMFL_ constant. Table 20.4 lists the available constants.
Constant | Description |
DMFL_NATIVERENDER |
The source does not support rendering of the object. This should
not be specified unless the source gives enough information for the
target to perform the rendering |
DMFL_RENDERRETRY | The source does support rendering of the object, but not using the RMF specified. |
For drag operations involving only files, a much simplified version of DrgDrag can be used: DrgDragFiles.
BOOL APIENTRY DrgDragFiles(HWND hwnd,
PCSZ *apszFiles,
PCSZ *apszTypes,
PCSZ *apszTargets,
ULONG cFiles,
HPOINTER hptrDrag,
ULONG vkTerm,
BOOL fSourceRender,
ULONG ulRsvd);
hwnd is the handle of the window calling the function. apszFiles, apszTypes, and apszTargets are array of pointers to the filenames, file types and target filenames, respectively. cFiles specifies the number of pointers in the apszFiles, apszTypes, and apszTargets arrays. hptrDrag is the handle to the pointer to display while dragging. vkTerm has the same meaning as in DrgDrag, discussed earlier. fSourceRender
specifies whether the caller needs to render the files before the
transfer can take place. If so, a DM_RENDERFILE message is sent for
each file.
That's it! The system takes care of the rest, since files are the only allowed object type.
Table 20.5 details the chain of events from the beginning of the drag notification to the end of the data transfer.
Step | Source | Target |
1 |
Receives a WM_BEGINDRAG message | |
2 | Allocates the DRAGINFO/DRAGITEM structures using DrgAllocDraginfo | |
3 |
Creates the strings for the type and RMF using DrgAddStrHandle |
|
4 |
Initializes the appropriate number of DRAGIMAGE structures |
|
5 |
Calls DrgDrag |
|
6 |
Receives DM_DRAGOVER | |
7 |
Calls DrgAccessDraginfo | |
8 |
Decides if object are acceptable (both type ans RMF). | |
9 |
Returns the appropriate DOR_ value; if not DOR_DROP, go to step 20. | |
10 |
If the user presses F1, target receives a DM_DROPHELP; after providing help, go to step 20 | |
11 |
If the user presses ESC, go to step 20 |
|
12 |
User drops objects on target. |
|
13 |
If target can render the objects on its own, do so. Go to step 18 |
|
14 |
Allocates DRAGTRANSFER structures for each object (DrgAllocDragtransfer) |
|
15 |
Renders the object |
|
16 |
Copies the objects and deletes the from the source. |
|
17 | Frees HSTRs for DRAGTRANSFER and DRAGTRANSFER structures (DrgDeleteStrHandle and DrgFreeDragtransfer). |
|
18 |
Frees HSTRs for DRAGINFO and DRAGINFO structure (DrgDeleteDraginfoStrHandles and DrgFreeDragtransfer). | |
19 |
Sends source a DM_ENDCONVERSATION message. |
|
20 |
Free HSTR for DRAGINFO and DRAGINFO structure (DrgDeleteDraginfoStrHandles and DrgFreeDragtransfer ). |
OS/2 Warp introduced a new twist
on the direct manipulation concept. Because drag and drop is a modal
operation - meaning that nothing else can occur while a direct
manipulation is in progress - it can be limiting at times. What happens
if you start to drag an object and then realize that the target window
isn't open yet ? You have to press Escape, find the target window
and open it, then repeat the operation.
Pickup and drop alleviates the headaches cause in these
situations by allowing the user to continue using the mouse in the
normal fashion while the operation is in progress. Because of this
characteristics, pickup and drop is often referred to as lazy drag and drop.
Obviously,
there are some profound differences from the user's perspective between
the modal and modeless versions of direct manipulation. And this means
that there are differences in the coding of the two types; fortunately,
IBM decided in its wisdom to minimize the impact of choosing one or the
other (or both) in your application by changing as little as possible
in the manner in which the modeless version is coded. The interface
differences are listed here:
In order to make the programmer's job easier, IBM provided many new functions specifically for use with lazy drag.
PDRAGINFO APIENTRY DrgReallocDraginfo (PDRAGINFO pdinfoOld, ULONG cditem);
This function reallocates memory to hold a new number of DRAGITEM
structures when additional items are to be added to the pickup item
set. pdinfoOld points to the old DRAGINFO structure. cditem specifies
the new number of DRAGITEM structures to be contained by the new
DRAGINFO structure. This function returns a pointer to the new DRAGINFO
structure and frees the memory pointed to by the old structure. Once
this function is called, DrgLazyDrag must be called again to reinitiate the lazy drag operation.
PDRAGINFO APIENTRY DrgQueryDraginfoPtr( PDRAGINFO pRsvd );
pRsvd is reserved and must be NULL. This function returns
a pointer to the DRAGINFO structure currently in use by a direct
manipulation operation. DrgQueryDragStatus must be called to determine what type of operation is in progress, however. If NULL is returned, no operation is in progress.
PDRAGINFO APIENTRY DrgQueryDraginfoPtrFromDragitem( PDRAGITEM pditem );pditem points to a DRAGITEM structure returned from DrgQueryDragitemPtr. This function returns a pointer to the DRAGINFO structure with which the DRAGITEM is associated.
PDRAGINFO APIENTRY DrgQueryDraginfoPtrFromHwnd( HWND hwndSource );hwndSource is the handle to the source window in a direct manipulation operation. This function returns a pointer to the DRAGINFO structure allocated by the source window.
ULONG APIENTRY DrgQueryDragStatus(VOID);This function returns a DGS_ constant specifying what type of drag operation is in progress. Table 20.6 lists the available constants.
Constant | Description |
0 |
No direct manipulation operation in progress |
DGS_DRAGINFOPROGRESS |
Modal operation is in progress |
DGS_LAZYDRAGINPROGRESS | Modeless operation is in progress
|
BOOL APIENTRY DrgLazyDrag( HWND hwndSource,This function initiates a lazy drag operation. hwndSource specifies the source window handle. pdinfo points to the DRAGINFO structure. pdimg points to one or more DRAGIMAGE structures. cdimg specifies the number of DRAGIMAGE structures pointed by pdimg. pRsvd is reserved and must be NULL.
PDRAGINFO pdinfo,
PDRAGIMAGE pdimg,
ULONG cdimg,
PVOID pRsvd );
BOOL APIENTRY DrgLazyDrop( HWND hwndTarget,
ULONG ulOperation,
PPOINTL pptlDrop );
This function is called by a target to complete the lazy drag operation.
hwndTarget is the target window handle. ulOperation specifies the operation to be performed and is a D)_ constant. pptlDrop
points to a POINTL structure containing the mouse position
in desktop-related coordinates. This function returns TRUE if the
operation was successfully initiated or FALSE otherwise.
BOOL APIENTRY DrgCancelLazyDrag( VOID );This function is used to cancel a lazy drag operation. It returns TRUE if successful, or FALSE otherwise.
![]() |
Gotcha!
With the DrgQueryDraginfoPtr, DrgQueryinfoPtrFromHwnd and DrgQueryDraginfoPtrFromDragitem functions, the application must still call DrgAccessDragInfo to get access to the structure returned. |
![]() |
Gotcha!
Be sure that if you initiate a lazy drag operation it is
either completed or canceled before your application terminates. The
authors noticed that when the sample application (see below) was
terminated without doing this that the direct manipulation subsystem
seemed to get confused and no longer worked correctly. |
![]() |
Gotcha!
The Workplace Shell seems to be able to correctly determine if a
lazy drag operation is in progress because it offers a "Cancel drag"
menu item on context-sensitive menus. However, selecting the menu item
has no apparent effect. We cannot determine why this happens. (?) |
Below is a sample application which demonstrates the use of lazy drag and drop.
DRAG3.C
DRAG3.RC
DRAG3.H
DRAG3.MAK
DRAG3.DEF
This sample was based on DRAG1, allowing the target to
render the data so that the sample is not burdened with details not
necessary to the discussion.
The first difference that you will note are the use of WM_PICKUP
instead of WM_BEGINDRAG to begin the operation and the processing of
the DM_DROPNOTIFY as the signal of the completion of the operation.
case WM_PICKUP :
case DM_DROPNOTIFY :
case DM_ENDCONVERSATION :
return doSource(hwndClient,
ulMsg,
mpParm1,
mpParm2);
Also, since the user must specify to the application that the
operation is to be completed or canceled, the WM_CONTEXTMENU,
WM_MENUEND, and WM_COMMAND messages are processed to handle the user
interface.
case DM_DRAGOVER :The real work is done in doSource and doTarget, as was the case in the earlier samples.
case DM_DRAGLEAVE :
case DM_DROP :
case DM_DROPHELP :
case MYM_DEWDROP :
case WM_CONTEXTMENU :
case WM_MENUEND :
case WM_COMMAND :
return doTarget(hwndClient,
ulMsg,
mpParm1,
mpParm2);
case WM_PICKUP :
{
RECTL rclWindow;
FILE *pfFile;
DRAGITEM diItem;
DRAGIMAGE diImage;
BOOL bSuccess;
if (DrgQueryDragStatus() == DGS_LAZYDRAGINPROGRESS)
{
return MRFROMSHORT(FALSE);
} /* endif */
Note how we check for a lazy-drag-in-progress and return immediately if
this is true. This was done to keep the sample simple. The processing
of WM_PICKUP then continues as it did for WM_BEGINDRAG exept we call DrgLazyDrag instead of DrgDrag.
bSuccess = DrgLazyDrag(hwndClient,
pciInfo->pdiDrag,
&diImage,
1L,
NULL);
From the target's perspective, we need to provide an interface to the
user allow them to complete or cancel the operation. This is done via
the WM_CONTEXTMENU, WM_MENUEND, and WM_COMMAND messages.
case WM_CONTEXTMENU :
{ POINTL ptlPoint;
RECTL rclWindow;
HWND hwndMenu;
if (DrgQueryDragStatus() == DGS_LAZYDRAGINPROGRESS)
{ WinQueryPointerPos(HWND_DESKTOP,
&ptlPoint);
WinQueryWindowRect(hwndClient,
&rclWindow);
if (ptlPoint.x < rclWindow.xRight/2)
{ return MRFROMSHORT(FALSE);
} /* endif */
hwndMenu = WinLoadMenu(HWND_OBJECT,
NULLHANDLE,
M_LAZYDRAG);
WinPopupMenu(HWND_DESKTOP,
hwndClient,
hwndMenu,
ptlPoint.x,
ptlPoint.y,
0,
PU_MOUSEBUTTON1|PU_KEYBOARD);
} /* endif */
}
break;
case WM_MENUEND :
if (SHORT1FROMMP(mpParm1) == FID_MENU)
{ WinDestroyWindow(HWNDFROMMP(mpParm2));
} /* endif */
break;
case WM_COMMAND :
switch (SHORT1FROMMP(mpParm1))
{ case MI_DROP :
{ POINTL ptlPoint;
WinQueryPointerPos(HWND_DESKTOP,
&ptlPoint);
DrgLazyDrop(hwndClient,
DO_DEFAULT,
&ptlPoint);
}
break;
case MI_CANCELDRAG :
DrgCancelLazyDrag();
break;
default :
return WinDefWindowProc(hwndClient,
ulMsg,
mpParm1,
mpParm2);
} /* endswitch */
break;
default :
break;
} /* endswitch */
It should be pretty obvious that we are simply providing a popup menu
for the user to select one of two choices - drop or cancel - and
handling each choice appropriately.
Everything else about this sample is as it was in DRAG1, which
demonstrates the ease with which a programmer can switch between using
one mode or the other.
Before we close this topic, a question must be asked: how does the
target specify whether or not a set of objects that were picked up can
be dropped on it or not ? In modal drag and drop, you receive the
DM_DRAGOVER and DM_DRAGLEAVE to allow for user feedback, but these
messages are not sent automatically by the system when a lazy drag
operation is in progress. IBM's documentation states that these
messages are sent when the user presses a key indicating that intention
to drop the object, but nowhere do they state what this mythical key
is. It is the opinion of the authors that this "key" is a concept and
not an actual key on the keyboard, and we chose to implement the "key"
concept as a popup menu. It is then, therefore, that the target
determines the validity of the operation and acts appropriately.
[ Next ] [ Previous ] | Chapter 20 |