TweetFollow Us on Twitter

Drag And Drop
Volume Number:10
Issue Number:6
Column Tag:New Apple Technology

Macintosh Drag And Drop

You’ve wanted it for years, they finally delivered it, but they hardly told anyone!

By Steve Kiene, Software Illusionist, MindVision Software

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

About the author

Steve has chosen the title “Software Illusionist” because he feels in most of things that he has been writing lately there is very little correlation between what the code actually does and what the user thinks is happening. Steve says, “Programming is an art; the user doesn’t care what your code is doing. All they want is a specific result, whether it’s having more disk space, a faster machine, or whatever. The implementation is irrelevant. The result is what is relevant. Look at the 680x0 emulator in the Power Macintosh. It’s the ultimate illusion. It convinces not only the user, but the software as well, even software like TMON Pro or Macsbug which think they know a 680x0 when they see one.”

In September of last year Apple released an important new technology called Macintosh Drag and Drop. With all the fanfare over OpenDoc, AOCE, PowerPC, and other new technologies, most people haven’t had a chance to see Macintosh Drag and Drop in action. I don’t generally get excited about new technologies, but I think Macintosh Drag and Drop is totally cool. I remember talking to some friends at Apple a couple of years ago about doing something like Macintosh Drag and Drop. I’m glad somebody at Apple had the initiative to write this because once you’ve used it, you’ll wonder how you lived without it.

What is Macintosh Drag and Drop?

A key concept of the Macintosh interface is direct manipulation. For example, in the Finder you can drag a document icon and drop in into an application icon, which results in the document being opened with the application. Macintosh Drag and Drop is an extension to the Macintosh User Interface that allows direct manipulation of data between different windows and applications as easy as dragging icons in the Finder. A user simply selects the data they want to drag and then drag it to the desired location. Macintosh Drag and Drop is an intuitive alternative to cut and paste, but it is much more than that. Macintosh Drag and Drop implements the Drag Manager in the OS, and both terms are used by people interchangeably. The Drag Manager integrates with the Translation Manager to provide transparent translation of data types. If you want to provide data in a specific format, you can ask the Translation Manager to perform the translation for you. You can also find out whether the user dropped the selection on the Trash, so you can delete whatever it was they were dragging.

There are existing applications which followed the Finder’s direct-manipulation lead while blazing new trails for third-party applications, and implemented Drag Manager-like features within their context. Microsoft Word allows you to drag selected text within the same document window. QuarkXPress allows you to drag objects between other XPress document windows. Other examples abound. Recently Iadded Drag and Drop (the real thing from Apple) support to our installer product Developer Vise.

The three screen shots below show a drag in action. The first shows an item being selected from an archive’s Finder-style list view. The second shows the outline of the item being dragged. The third screen shot shows the result of dropping the item on the desktop - the file was extracted from the archive and saved as a disk file. It was positioned on the desktop in the place the user dropped the item. This is a really cool way to get items out of an archive file. To add files to the archive you simply grab them in the Finder and drag them to an archive window. Macintosh Drag and Drop lets you do all this, and is much easier than using the Standard File dialogs to add files to the archive. [Death to StandardFile! - Ed stb]

Step 1: Selecting something to drag

Step 2: Dragging a file out of the archive

Step 3: Dropping the file on the desktop

With any new, totally cool technology there are always new, totally cool terms to learn.

Macintosh Drag and Drop

The System Extension that implements the Drag Manager. It is also the terminology used for the feature when indicating that a product can support the Drag Manager (as in “Developer VISE supports Macintosh Drag and Drop”).

Drag Manager

A part of the Macintosh Toolbox, like the Control Manager, Menu Manager, or Window Manager.

Clipping file

A clipping file is a file created in the Finder that contains whatever data was dragged to the Finder. For example, if you select some text in a Drag Manager savvy text editor, you can drag the text to the Finder and it will create a clipping file. You can open this clipping file in the Finder and view the contents. You can also drag a clipping file to a Drag Manager savvy application and it will accept the data as if it was dragged from the original application that produced the clipping file. Clipping files are like storage bins for drag objects.

Flavor

A flavor is a specific data format such as PICT, TEXT, snd, etc.

Clipping Extension

A System Extension that allows the Finder to support clipping files.

Dragging Enabler

A System Extension that allows System 7.1 to support Macintosh Drag and Drop. It is not needed when running System 7 Pro.

Where to Find More on Macintosh Drag and Drop

The Macintosh Drag and Drop Developer’s Kit is available from APDA for the rob-you-blind price of $75.00. There is an excellent article written by the authors of the Drag Manager in Develop Issue 16. Online documentation for the Drag Manager is on the February 1994 Developer CD.

Determining if the Drag Manager is Present

You can determine if the Drag Manager is present by calling the Gestalt function with the selector gestaltDragMgrAttr.


/* 1 */
#define gestaltDragMgrAttr'drag'

The Gestalt function will set or clear the following bit depending on whether the Drag Manager is present:


/* 2 */
#define gestaltDragManagerPresent  0

Drag Item Flavors

Most items that can be dragged, such as text, graphics, and sound, can be represented using several different data formats. For applications to communicate what type of data is being dragged, the Drag Manager introduces the concept of drag item flavors. Flavors allow a dragable item to be represented in one or more forms in order for other applications to be able to interpret the item. For example, a text selection of “MacTech Magazine” can be represented simply as ‘TEXT”. In addition, other “flavors” can be provided to richen the data. A ‘styl’ flavor may be provided to give text style information. Another common flavor is rich text format, or RTF. The RTF includes much more information about the fonts, styles, page size, margins, etc. The application that initiated the drag might be a high-end word processor that supports very rich style information in it’s own proprietary format, in which case it could add another flavor to allow data transfer in it’s internal format. Because there is no way to know where the drag selection will be dropped, it is advisable to provide all possible flavors so that the receiver can decide which format they would like. It’s kind of like going to a place with thirty-some-odd flavors; they make sure there are plenty of flavors to choose from so you can pick the one that you want.

Drag Handlers

In order for the Drag Manager to know your application can receive dragged items, you must register with the Drag Manager by installing drag handlers for any windows which can receive data. There are two types of drag handlers that you install:

• A drag tracking handler that the Drag Manager calls when the user drags a selection through the content region of one of your windows. This allows you to track the drag and indicate whether you can accept the drag or not.

• A drag receive handler that the Drag Manager calls when the user finishes a drag in one of your windows. This allows you to receive the data the user was dragging.

The Drag Manager conveniently provides routines to allow you to register handlers for your windows. You can install different handlers for different windows, multiple handlers for a single window, or no handlers at all. The choice is yours.

Drag Tracking

When the user drags a selection into one of your windows which has installed a tracking handler you will receive messages from the Drag Manager. These messages allow you to track the drag as it passes through your window. As the drag is taking place, you can use several Drag Manager routines to determine the characteristics of what is being dragged and decide whether or not you can accept the data. The messages are as follows:

• An enter handler message arrives when a drag enters a window that is handled by your tracking handler when the drag has come from a window that is not handled by the same tracking handler. This allows you to perform any setup required for that particular handler to operate.

• An enter window message arrives when a drag enters a window that is handled by your tracking handler. It is important to understand the difference between this message and the enter handler message. An enter handler message is delivered when the focus of a drag enters a window which has a different tracking handler than the window that the drag just left. An enter window message is received regardless of which handler the previous window had installed.

• An in window message arrives as the user drags within a window handled by your tracking handler. This is where you give visual feedback to the user indicating whether this window can accept the drag. The feedback is generally in the form of drawing a blue outline around the content region of the window. It gets called a lot, and doesn’t wait for the mouse to move.

• A leave window message when the drag leaves a window handled by your tracking handler.

• A leave handler message when the drag enters a window that is not handled by the current tracking handler.

Receiving Data

When the user drops a collection of data on a window that has a drag receive handler installed, the Drag Manager calls any receive handlers so they can receive the data the user dropped. The Drag Manager provides several functions for inspecting the data as well as actually receiving the data.

Using the Drag Manager

The code snippets shown here come from Developer VISE 3.0, an installer builder that MindVision sells. Developer VISE includes functions like an archiver in that it can create an archive in which files are stored. The user interface for this is a window with a file list showing the files in the archive. This type of product is a natural place to implement Macintosh Drag and Drop, so I’ve taken pieces of code directly from our source to show how easy it is to add Drag Manager support to your existing application. I have, however, removed some parts (mostly error checking) for clarity, but you should always check error results, and the Drag Manager is no exception.

Installing Drag Handlers

You can register a drag handler with the Drag Manager by calling InstallTrackingHandler and InstallReceiveHandler. Both functions take a pointer to the window that you are installing the handler for. If you want the handler to be the default handler for all your windows, pass nil. These handlers are called default handlers. You can specify a reference constant when installing a handler. This can be used to provide additional information to your handler, such as a pointer to a data structure used by your handler. Because you cannot be assured that the Drag Manager is installed, you must always check before calling a Drag Manager function. Add this code after you create a window in which dragging is allowed.


/* 3 */
 if (gHasDragManager) {
 err = InstallTrackingHandler(MyTrackingHandler,
 theWindow, (void *)dataPtr);
 err = InstallReceiveHandler(MyReceiveDropHandler,
 theWindow, (void *)dataPtr);
 }

MyTrackingHandler is a pointer (that’s a UniversalProcPtr for you PowerPC types) to your tracking handler. MyReceiveDropHandler is a pointer to your receive handler. As always with callback routines, make sure they are in locked segment. If the code block moves, the Drag Manager will not know this and will blindly call where it thinks your code is anyway, and that’s generally a bad idea.

Removing Drag Handlers

To remove a drag handler from the Drag Manager’s registry, call the corresponding remove handler functions:


/* 4 */
 if (gHasDragManager) {
 RemoveTrackingHandler(MyTrackingHandler, theWindow);
 RemoveReceiveHandler(MyReceiveDropHandler, theWindow);
 }

MyTrackingHandler is a pointer to the tracking handler that you installed for the specified window. MyReceiveHandler is a pointer to the receive handler that you installed for the specified window. If you pass nil as the window pointer, the Drag Manager will remove the specified drop handler as the default handler. Call this code before closing the specified window.

Performing a Drag

For brevity, I will assume your program can already perform dragging of objects within a window. To support dragging of objects outside your window you need to fork your code when the user starts to drag an object.


/* 5 */
 if (gHasDragManager) {
 tLong = DragItem(theWindow);
 }
 else {
 tLong = DragGrayRgn(gRgn, origClick, &limitRect,
 &slopRect, 0,
 (DragGrayRgnProcPtr)&FileDragHook);
 }

DragItem is our own procedure which does the necessary work to perform a drag using the Drag Manager. Here it is quickly, and we’ll go over some of the details in a moment.


/* 6 */
long DragItem(WindowPtr theWindow)
{
 ArcFileTypetheFile; //program-specific structure
 DragReference   theDrag;
 RgnHandletempRgn;
 Point  tPt;
 short  result;
 PromiseHFSFlavorhfsFlavor;
 OSErr  err;
 
 theFile = GetSelectedFile(theWindow);

//set up to do the drag
 err = NewDrag(&theDrag);
 
 hfsFlavor.fileType = theFile.af_FInfo.fdType;
 hfsFlavor.fileCreator = theFile.af_FInfo.fdCreator;
 hfsFlavor.fdFlags = theFile.af_FInfo.fdFlags;
 hfsFlavor.promisedFlavor = 'VCT1';
 AddDragItemFlavor(theDrag, 1, flavorTypePromiseHFS,
 &hfsFlavor, sizeof(hfsFlavor), flavorNotSaved);

 AddDragItemFlavor(theDrag, 1, 'VCT1', nil, 0, 0);

 AddDragItemFlavor(theDrag, 1, kDragItem, nil, 0,
 flavorSenderOnly);

 SetDragSendProc(theDrag, MySendDataProc, (void *)theWindow);

 SetPt(&tPt, 0, 0);
 LocalToGlobal(&tPt);
 OffsetRgn(gRgn, tPt.h, tPt.v);

 SetDragItemBounds(theDrag, 1, &(**gRgn).rgnBBox);

 tempRgn = NewRgn();
 CopyRgn(gRgn,tempRgn);
 InsetRgn(tempRgn,1,1);
 DiffRgn(gRgn,tempRgn,gRgn);
 DisposeRgn(tempRgn);

// do the drag
 result = TrackDrag(theDrag, &gEvent, gRgn);

//clean up and return the result
 DisposeDrag(theDrag);

 if (result) {
 return (0x80008000);
 }
 else {
 return (0);
 }
}

The GetSelectedFile routine is our own routine to determine what is selected. We call NewDrag to allow the Drag Manager to create a new instance of a drag. The next step is to add flavors to the drag. This is done by calling AddDragItemFlavor, which is defined as:


/* 7 */
pascal OSErr AddDragItemFlavor(DragReference theDragRef,
   ItemReference theItemRef,
   FlavorType theType,
   void *dataPtr,
   Size dataSize,
   FlavorFlags theFlags);

The Drag Mgr creates a new drag item if the specified item reference is different from all other item reference numbers, otherwise it uses the existing instance. To add multiple flavors to the same drag item, specify the same item reference. Think of an item reference as a ref num for a file. It is used to uniquely identify the particular drag item. FlavorType is the type of flavor we are providing. The Drag Manager defines the following special flavor types:


/* 8 */
#define flavorTypeHFS'hfs ' // flavor type for HFS data
#define flavorTypePromiseHFS'phfs' // flavor type for promised HFS data
#define flavorTypeDirectory 'diry' // flavor type for AOCE directories

In this case we are creating a flavorTypePromiseHFS, which means we will deliver a file system object (in our case it will be a disk file), but only if the receiver requests that we do so. We promise the file (rather than deliver it directly) because we must decompress the file from our archive to deliver it, and it makes no sense to spend the time decompressing if the drag is not successful. If the drag is successful and the receiver decides that they want our file, they ask us to deliver the file at that time.

The next parameter is a pointer to the flavor data to add. Because we are using a predefined flavor, we have a corresponding predefined data structure for that flavor:


/* 9 */
struct PromiseHFSFlavor {
 OSType fileType;// file type
 OSType fileCreator; // file creator
 unsigned short  fdFlags; // Finder flags
 FlavorType promisedFlavor; // promised flavor containing an FSSpec
};
typedef struct PromiseHFSFlavor PromiseHFSFlavor;

The first three fields are self-explanatory. The fourth field, promisedFlavor, is a flavor that will contain an FSSpec to the file system object if the receiver requests it. This is used by the receiver to know what flavor to ask for in order to find out where to find the file system object.

The fifth parameter in AddDragItemFlavor is the size of the data being provided. The last parameter, FlavorFlags, provides additional attribute information about the flavor. They are defined as:


/* 10 */
enum {
 flavorSenderOnly= 0x00000001L,  // flavor is available to sender only
 flavorSenderTranslated = 0x00000002L, // flavor is translated by sender
 flavorNotSaved  = 0x00000004L,  // flavor should not be saved
 flavorSystemTranslated = 0x00000100L// flavor is translated by system
};

flavorSenderOnly

Set if this flavor is only available to the sender. You might use this when your objects contain information proprietary to your specific application. Because no one else would understand this flavor there would be no reason to send it to everyone.

flavorSenderTranslated

Set if the sender performs a translation on the data to be able to offer this flavor. This is useful to a receiver if it needs to determine if the sender is performing its own translation to generate this data type. Typically, receivers that store dragged data without interpreting each data type do not store translated types. For example, if you are trying to be very flexible and provide the data in as many flavors as possible, you would set this flag for any flavors that you have to translate from a different format to generate. This way the receiver can decide which format they want if they are looking for a specific type. But, if the receiver is simply storing all the flavors, there might be no need to store many different flavors that it may be able to generate itself.

flavorNotSaved

Set if the flavor data should not be stored by the receiver. This is useful for marking flavor data that will become invalid after the drag has completed. For example, set this flag if you are providing a flavor that is promised, or for a flavor that is simply providing information that is only useful while the drag is taking place.

flavorSystemTranslated

Set if the flavor data is provided by the Translation Manager. If this flag is set, the Drag Manager will obtain the required data types from the sender and then it will use the Translation Manager to provide the data type the receiver requested.

In our case we use flavorNotSaved because the data has no use once the drag is completed. When the drag is completed the receiver will ask for the promised data and we will provide the actual disk file at that time.


/* 11 */
 AddDragItemFlavor(theDrag, 1, 'VCT1', nil, 0, 0);

This will add the flavor type we specified as the promised flavor in the previous AddDrawItemFlavor. There is no need to pass any data now because the receiver will explicitly ask for the data once the drag has completed successfully. This is the flavor type that the receiver will request when it wants the file system object that we promised.


/* 12 */
 AddDragItemFlavor(theDrag, 1, kDragItem, nil, 0, flavorSenderOnly);

This adds our own internal representation of an item in an archive. It is for use only by our application, so the flags are flavorSenderOnly.


/* 13 */
 SetDragSendProc(theDrag, MySendDataProc, (void *)theWindow);

The SetDragSendProc functions sets the drag send procedure for the given drag reference. A drag’s send procedure is called by the Drag Manager when the receiver of a drop requests the data of a flavor that was promised (no data was provided by the AddDragItemFlavor). If you always provide the flavor data when calling AddDragItemFlavor, there is no need to call this. In our case, we have promised the data, so we must install a send procedure. The last parameter is a reference constant that can be used to pass specific data to the send procedure. In our case we pass the window where the drag originated. The specifics of the send procedure are explained later.


/* 14 */
 SetPt(&tPt, 0, 0);
 LocalToGlobal(&tPt);
 OffsetRgn(gRgn, tPt.h, tPt.v);

In this snippet of code, gRgn is a region containing the outline of the selected item in the window. We normally use this region in local coordinates, however, the Drag Manager needs it in global coordinates so this converts the region to global coordinates. The illustration below shows the drag region for this example:


/* 15 */
 SetDragItemBounds(theDrag, 1, &(**gRgn).rgnBBox);

The SetDragItemBounds function sets the bounding rectangle of the given drag item. The rectangle must be specified in global coordinates. The illustration below shows the drag item bounds:


/* 16 */
 tempRgn = NewRgn();
 CopyRgn(gRgn, tempRgn);
 InsetRgn(tempRgn, 1, 1);
 DiffRgn(gRgn, tempRgn, gRgn);
 DisposeRgn(tempRgn);

This code snippet builds an outline of the selected item as a region to pass to the Drag Manager.


/* 17 */
 result = TrackDrag(theDrag, &gEvent, gRgn);

The TrackDrag function performs a drag operation with the specified drag reference. You must pass the event record that resulted in the mouseDown event which caused the dragging to be started. You must also pass a region in global coordinates which is the outline of the objects being dragged. In this case we pass our region gRgn. If the drag is successful, meaning the user dropped the object in a window which accepted the data, TrackDrag will return noErr. If the drag is not successful, meaning the receiver did not accept the data, TrackDrag will return userCanceledErr.


/* 18 */
err = DisposeDrag(theDrag);

The DisposeDrag function disposes of a drag reference and its associated data when a drag has been completed.

Providing Flavor Data on Demand

If the receiver of a drop requests flavor data that has not been cached by the Drag Manager (in our example, the promised HFS data), the Drag Manager will call the drag’s send data procedure to demand the data. Below is the code for our send data procedure:


/* 19 */
pascal OSErr MySendDataProc(FlavorType theFlavor, 
 void *refCon,
 ItemReference theItem, 
 DragReference theDrag)
{
 AEDesc dropLoc;
 FSSpec target;
 OSErr  err;
 BooleanwasChanged;

 if (theFlavor== 'VCT1') {
 err = GetDropLocation(theDrag, &dropLoc);

 err = ResolveAlias(nil, (AliasHandle)dropLoc.dataHandle,
  &target, &wasChanged);
 if (err) {
 return (err);
 }

 // Decompress item
 err = ExtractSelectedItems((ArchiveWPtr)refCon, &target);
 if (err) {
 return (err);
 }

 err = SetDragItemFlavorData(theDrag, theItem, 'VCT1',
 (Ptr)&target, sizeof(target), 0L);
 }

 return (noErr);
}

When the Drag Manager calls our send data procedure it provides us with the flavor type requested, a reference constant, the item reference number, and the drag reference. Recall that we used flavorTypePromiseHFS which requires us to promise a specific flavor so the receiver can request the file system object. In our case we promised a flavor of ‘VCT1’. The drop receiver will ask us for that flavor when it wants the file system object, so we check to see if the requested flavor matches ‘VCT1’. If it does we can proceed with the rest of the function.

The GetDropLocation function will return an AppleEvent descriptor describing the drop location. If the destination is the Finder, the Finder sets the drop location to be an alias to the location in the file system that received the drag.

We call ResolveAlias to determine the location where the file should be placed. We then call one of our functions to extract the selected items from the archive and store them in the location specified by target.

The SetDragItemFlavorData function allows us to fill in the data for a specified flavor. This is commonly done when data was promised and now it is time to provide the actual data. We promised to provide an FSSpec describing where the file system object was stored. Since we extracted the file to the location it was dropped by the user, we simply provide the FSSpec that we obtained in the ResolveAlias call.

Tracking a Drag

During a drag, as the user moves the drag items on the screen, searching for a destination for the items, the Drag Manager sends a sequence of tracking messages to the tracking handler for each window that has a registered handler as the mouse moves over the window. Our tracking handler is responsible for providing all visual feedback to the user indicating that the group of items can be dropped in the current location. This code is the part of the drag tracking handler from Developer VISE.

 
/* 20 */
pascal OSErr MyTrackingHandler(short message, 
 WindowPtr theWindow,
 void *handlerRefCon, 
 DragReference theDrag)

{short  result;
 unsigned short  count;
 unsigned short  index;
 unsigned long   flavorFlags;
 unsigned long   attributes;
 ItemReference   theItem;
 RgnHandletheRgn;
 ArchiveWPtrdataPtr = (ArchiveWPtr)handlerRefCon;
 ArchiveWPtrhitDataPtr;
 Point  theMouse;
 Point  localMouse;
 Rect   tRect;

//
// We decide whether we can accept the data when we are called with
// a message of dragTrackingEnterHandler. If we can, we set 
// gCanAcceptItems to true.
//
 
 if ((message != dragTrackingEnterHandler) 
 && (!gCanAcceptItems)) {
 return (noErr);
 }

 GetDragAttributes(theDrag, &attributes);

 switch (message) {
 case dragTrackingEnterHandler:
//
// by default our window can always accept items
//
 gCanAcceptItems = true;
 
//
// find out how many items have being dragged
//
 CountDragItems(theDrag, &count);

 for (index = 1; index <= count; index++) {
//
// get the item reference number for the nth item being dragged
//
 GetDragItemReferenceNumber(theDrag, index, &theItem);
 
//
// See if a flavor of flavorTypeHFS exists for this drag item
// If one does exist, then we can accept this item, so continue
// checking the rest of the items.
// An item being dragged from the Finder to our window would qualify
// as this type.
//
 result = GetFlavorFlags(theDrag, theItem,
 flavorTypeHFS, &flavorFlags);

 if (result == noErr) {
 continue;
 }

//
// See if a flavor of kDragItem (our proprietary flavor) exists.
// If it does and the drag flags indicate we started the drag then
// we are simply moving the file in the file list, so we can accept
// this type.
//
 result = GetFlavorFlags(theDrag, theItem, 
 kDragItem, &flavorFlags);

 if ((result == noErr) 
 && (flavorFlags & flavorSenderOnly)) {
 continue;
 }

//
// We couldn’t find and flavors that we wanted so we will not
// accept the data.
//
 gCanAcceptItems = false;
 break;
 }

 break;

 case dragTrackingEnterWindow:
 gCursorInContent = false;
 break;

 case dragTrackingInWindow:
//
// The user is dragging items over our window.
// We need to determine if we’ve left our window because the Human
// Interface Guidelines state that no hiliting is to be done until
// the user drags outside of the sender window.
//
 GetDragMouse(theDrag, &theMouse, 0L);
 localMouse = theMouse;
 GlobalToLocal(&localMouse);

 if (attributes & dragHasLeftSenderWindow) {
//
// The user has dragged outside of the sender window so we are now
// free to perform hiliting to indicate whether or not the drag
// can be accepted by the window.
// We check to see if the user is dragging into our window, and
// if so, have we shown the drag hilite? If it is appropriate we
// will show the drag hilite, meaning the user can drop the drag
// items here.
// If the user is not dragging in our window, we check to see if
// the drag hilite is shown. If it is we hide the drag hilite.
//
 tRect = (**(dataPtr->ArcList)).rView;
 InsetRect(&tRect, 2, 1);

 if (PtInRect(localMouse, &tRect)) {
 if (!gCursorInContent) {
 InsetRect(&tRect, -2, -4);
 tRgn = NewRgn();
 RectRgn(theRgn, &tRect);
 ShowDragHilite(theDrag, theRgn, true);
 DisposeRgn(theRgn);
 }

 gCursorInContent = true;

 }
 else {
 if (gCursorInContent) {
 HideDragHilite(theDrag);
 gCursorInContent = false;
 }
 }
 }

//
// This is our own routine which will return a pointer to a data
// structure we maintain for each window.
// We check to see if we are dragging in the sender window. If we
// are, we call one of our routines to handle hiliting of items in
// the window, autoscrolling, and other various operations.
//
 FindDataPtrGivenMousePt(theMouse, &hitDataPtr);

 if (hitDataPtr == dataPtr) {
 HandleSameWindowDrag(theWindow);
 }

 break;

 case dragTrackingLeaveWindow:
//
// Remove window highlighting, if showing.
//
 if ((gCursorInContent) 
 && (attributes & dragHasLeftSenderWindow)) {
 HideDragHilite(theDrag);
 }

 break;

 case dragTrackingLeaveHandler:
 break;
 }

 return (noErr);
}

Receiving a Drop

When the user has chosen the destination for the items being dragged, the Drag Manager calls the destination window’s receive drop handlers to allow your application to request and process the drag item flavors it wishes to accept. The following code is part of the receive drop handler from Developer VISE.


/* 21 */
pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, void *handlerRefCon, 
DragReference theDrag)
{
 unsigned short  items;
 short  index;
 ItemReference theItem;
 FlavorFlagstheFlags;
 Size   dataSize;
 HFSFlavortheHFS;
 OSErr  result;

 //
 // find out how many items where dragged
 //
 CountDragItems(theDrag, &items);

 for (index = 1; index <= items; index++) {
//
// Get the item reference number for this drag item so we can find
// out additional information later on.
//
 GetDragItemReferenceNumber(theDrag, index, &theItem);

//
// Get the flavor flags for this object’s flavor of kDragItem.
// If we can find flags for this flavor, then we know that the drag
// was completed in the sender window, so what the user did was 
// simply move an item in our file list.
//
 result = GetFlavorFlags( theDrag, theItem, kDragItem,
 &theFlags);
 if ((result == noErr) && (flavorFlags & flavorSenderOnly)) {
//
// Move the item that the user dragged to the new location in the
// file list
//
 MoveListItem(theWindow, 
 ((ArchiveWPtr)theWindow)->ArcnIndexes, gLastLine);
 }
 else {
// it wasn’t a drag from one of our windows, so let’s see if a
// file system object was dragged to our window.
//
// Get the flavor flags for this object’s flavor of flavorTypeHFS
//
 result = GetFlavorFlags( theDrag, theItem,
 flavorTypeHFS, &theFlags);

 if (result == noErr) {
//
// If we are here then the object contained a flavorTypeHFS so we
// need to know how much data it contains (we really do know how
// much data is has, but it doesn’t hurt to make this call. 
//
 GetFlavorDataSize(theDrag, theItem, flavorTypeHFS,
 &dataSize);

//
// Get the actual flavor data 
//
 GetFlavorData(  theDrag, theItem, flavorTypeHFS,
 &theHFS, &dataSize, 0L);

//
// Make sure the window does not have it’s drag hilite showing
//
 HideDragHilite(theDrag);

//
// Add the dragged files to our archive
//
 AddFilesToArchive(theWindow, &theHFS);
 }
 }

 return (noErr);
}

Implementing the Drag Manager in a Text Editor

My first experience with the Drag Manager was in October of last year when I had the opportunity to add Drag Manager support to a text editor. I received the source code for the text editor and the documentation for Macintosh Drag and Drop and was asked to add Macintosh Drag and Drop support. Later that same night I had completed implementing all of the features of the Drag Manager that were relevant to the text editor. I think this is a tribute to just how easy it is to add Drag Manager support to your application. The sample code provided with Macintosh Drag and Drop makes it incredibly easy to use the Drag Manager.

Dealing with dragging in a text editor deserves a few comments, so here are some quick observations about working with text.

There is a little more work to be done when adding support in a text editor. When the user is dragging in an editor window you need to provide visual feedback by showing the insertion point if the text was dropped at the location. This is simple, but takes a little code. You have to find the offset in the text where the user is dragging over, find the nearest character break, and then draw an appropriate sized caret.

You can also add nice touches in the text editor by supporting what I call “smart spaces”. Basically if the user selects a word and drags it somewhere else, you will be left with two spaces where the word was. It’s cool to remove one of the spaces. Correspondingly, when the user drops the text, make sure you add a space if they are dropping the text at the end of another word.

Building the drag outline for the selected text is easy if you are using TextEdit, thanks to a new feature of TextEdit that will provide you with a region of the selected text. There is a Gestalt function to determine if this feature is available.


/* 22 */
#define gestaltTEAttr‘teat’

The Gestalt function returns information by setting the following bit if this feature is available:


/* 23 */
#define gestaltTEHasGetHiliteRgn 0

This new feature of TextEdit was introduced with the Drag Manager and is very useful in building your drag region.

Working with text is a bit more “interesting” than some other data types, but the sample code that Apple put together really shows you what you need to know to go right to it.

Conclusions

We’ve covered what the Drag Manager is and shown you a little about how to use it. Now it’s your turn to go prove to yourself that it’s not only easy to implement in your applications, almost nothing gives you more bang for the buck when you’re demo’ing your software to a prospective client. Drag and Drop seems to be the second-most-liked feature of the programs when they’re demo’ed (first-most-liked usually goes to the reason the program was written in the first place). Here’s a technology that even small developers can use to level the playing field against the corporate giants (as opposed to technologies which require multi-million dollar investments to exploit and don’t even phase the corporate big boys).

Overall, the APIis extremely clean, and we’re looking forward to a new, even better version some time this year in System 7.5.

[Let us know how your experience goes with adding Drag and Drop to your software at editorial@xplain.com - ed stb]

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dropbox 193.4.5594 - Cloud backup and sy...
Dropbox is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files, manage... Read more
Google Chrome 122.0.6261.57 - Modern and...
Google Chrome is a Web browser by Google, created to be a modern platform for Web pages and applications. It utilizes very fast loading of Web pages and has a V8 engine, which is a custom built... Read more
Skype 8.113.0.210 - Voice-over-internet...
Skype is a telecommunications app that provides HD video calls, instant messaging, calling to any phone number or landline, and Skype for Business for productive cooperation on the projects. This... Read more
Tor Browser 13.0.10 - Anonymize Web brow...
Using Tor Browser you can protect yourself against tracking, surveillance, and censorship. Tor was originally designed, implemented, and deployed as a third-generation onion-routing project of the U.... Read more
Deeper 3.0.4 - Enable hidden features in...
Deeper is a personalization utility for macOS which allows you to enable and disable the hidden functions of the Finder, Dock, QuickTime, Safari, iTunes, login window, Spotlight, and many of Apple's... Read more
OnyX 4.5.5 - Maintenance and optimizatio...
OnyX is a multifunction utility that you can use to verify the startup disk and the structure of its system files, to run miscellaneous maintenance and cleaning tasks, to configure parameters in the... Read more
Hopper Disassembler 5.14.1 - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
WhatsApp 24.3.78 - Desktop client for Wh...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
War Thunder 2.33.0.135 - Multiplayer war...
In War Thunder, aircraft, attack helicopters, ground forces and naval ships collaborate in realistic competitive battles. You can choose from over 1,500 vehicles and an extensive variety of combat... Read more
Iridient Developer 4.2 - Powerful image-...
Iridient Developer (was RAW Developer) is a powerful image-conversion application designed specifically for OS X. Iridient Developer gives advanced photographers total control over every aspect of... Read more

Latest Forum Discussions

See All

SwitchArcade Round-Up: Reviews Featuring...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for March 4th, 2024. Another week is upon us, and while the Switch just blew out seven candles on its cake, things continue to move along around here at a rapid speed. We’ve got a... | Read more »
Gorgeous Tactical Puzzle Game ‘Howl’ is...
Following its release on PC and Nintendo Switch this past November, and it’s arrival on Xbox and PlayStation back in January, publisher Astragon Entertainment and developer Mi’pu’mi Games are now bringing their super stylish tactical puzzler Howl to... | Read more »
Best iPhone Game Updates: ‘Shoot the Moo...
Hello everyone, and welcome to the week! It’s time once again for our look back at the noteworthy updates of the last seven days. It feels like a bit of a dry spell this week, at least in terms of really interesting updates. I mean, I found some... | Read more »
Celebrate Phobies spooky second annivers...
Get ready to have that classic song stuck in your head, as Phobies celebrates its second anniversary with the release of its latest update; Birthday Bash, Monster Mash. Starting March 5th and lasting for four weeks, it will be a month of... | Read more »
‘Dissidia Final Fantasy Opera Omnia’ Sto...
Square Enix finally shut down Dissidia Final Fantasy Opera Omnia (Free) on iOS and Android last week following the end of service announcement back in November last year. Following the game shutting down, Square Enix | Read more »
‘Monster Hunter Now’ Is Celebrating the...
Niantic and Capcom have begun celebrating the 20th anniversary of Capcom’s best franchise from today inside Monster Hunter Now (Free) on iOS and Android for a limited time. | Read more »
New ‘Warframe Mobile’ Update Adds 60fps...
Warframe Mobile (Free) launched worldwide on iOS just under two weeks ago. I’ve been playing it for review across multiple iOS devices, but have also been picking it up on Steam Deck and Switch to compare. Right from launch, I was impressed with... | Read more »
Passionate About Fidget Toys – The Touch...
In this week’s episode of The TouchArcade Show we kick things off with some passionate discussion about… fidget toys? For some reason? We quickly change gears to talk about the card-based rogulike Balatro, which we’ve both been playing and enjoying... | Read more »
TouchArcade Game of the Week: ‘Flying Ta...
For me Hexage is one of those developers that harkens back to the early days of the App Store and really the beginnings of iPhone gaming. I have spent many collective hours playing the likes of Totemo, Radiant, Radiant Defense, EVAC, Reaper… the... | Read more »
SwitchArcade Round-Up: ‘Ufouria 2: The S...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for March 1st, 2024. In today’s article, we’re looking at the remaining releases of the week. There are a few really good ones today, but the bin bunch certainly isn’t going home hungry... | Read more »

Price Scanner via MacPrices.net

Deal Alert! B&H is now selling 13-inch M2...
B&H Photo has 13″ MacBook Airs with M2 CPUs and 256GB of storage in stock and on sale for $100 off Apple’s new MSRP, now only $899. Free 1-2 day delivery is available to most US addresses. Their... Read more
At $999, Apple’s 13-inch M2 MacBook Air is th...
With today’s introduction of the new 13-inch M3 MacBook Air for $1099, Apple dropped prices on the previous-generation 13-inch M2 MacBook Air to $999. At the same time, Apple discontinued the 13-inch... Read more
Apple discontinues 15-inch M2 MacBook Airs, d...
With today’s introduction of new M3-powered 15″ MacBook Airs, Apple has dropped prices on clearance, Certified Refurbished, 15″ M2 MacBook Airs to a new low of $1019. These are the cheapest 15″... Read more
Price Drop! 13-inch M2 MacBook Airs at Apple...
Apple has dropped prices on Certified Refurbished 13″ M2 MacBook Airs to a new low of $849. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty is included,... Read more
Apple finally discontinues the 13-inch M1 Mac...
With the introduction of M3-powered 13″ MacBook Airs today, Apple has dropped prices on clearance 13″ M1 MacBook Airs, Certified Refurbished, to $759 for 8-Core CPU/7-Core GPU/256GB models and $929... Read more
Updated Apple iPad Price Trackers
Our Apple award-winning iPad Price Trackers are the best place to find the latest information on iPad sales and deals. We track prices from 20+ Apple retailers, including Apple, Amazon, Best Buy,... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″, 14″, and (recently-discontinued) 13″ MacBook Pros... Read more
Mac Studios with Apple M2 Max and M2 Ultra CP...
B&H Photo has the standard-configuration Mac Studio model with Apple’s M2 Ultra CPU in stock today and on sale for $300 off MSRP, now $3699 (24-Core CPU and 64GB RAM/1TB SSD). B&H Photo has... Read more
Extended: Switch to Verizon and get the Apple...
Verizon has the iPhone 15 on sale for $0 per month when you add a new line if service. Discount is applied to your account monthly over a 36 month term and is valid for the 128GB model. For the first... Read more
Select 16-inch M3 Pro and M3 Max MacBook Pros...
B&H Photo has select 16-inch M3 Pro and M3 Max MacBook Pros on sale for $250 off MSRP. Their prices are the lowest currently available for these configurations. Free 1-2 day shipping is available... Read more

Jobs Board

Teller Part Time *Apple* Valley MN *Apple*...
…is not eligible for Visa sponsorship **Posting Location:** + 15574 Pilot Knod Road Apple Valley, MN 55124 @RWF22 **Posting End Date:** Job posting may come down Read more
*Apple* End User Support Specialist - North...
…that they are performed. + Responsible for support of all College owned Apple computers, mobile ios devices, and peripherals, and for diagnosing and resolving Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Teller Part Time *Apple* Valley MN *Apple*...
…is not eligible for Visa sponsorship **Posting Location:** + 15574 Pilot Knod Road Apple Valley, MN 55124 @RWF22 **Posting End Date:** Job posting may come down Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center WellSpan Medical Group, York, PA | Advanced Practice Providers | Certified Registered Nurse Anesthetists | FTE: 1 | Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.