Drag and Drop

From EDM2
Jump to: navigation, search

Written by Gordon Zeglinski

Introduction

As my project enters its final stages, the work load associated with it has gone through the roof. Therefore, the column will be a tad shorter than usual.

Drag and drop is one of OS/2's nicest features, but probably one of the most feared by new programmers. It's particularly painful, because you can't set breakpoints inside the processing of the DM_DRAG, DM_DRAGOVER, etc. messages. But since this is a C++ column, and drag and drop is a bit complex for a single "tad smaller than usual" article, we'll look at the drop half of drag and drop using the IBM PM class libraries. These class libraries do a lot more than just encapsulate the PM infrastructure; they abstract PM to such an extent, that they could be developed into a cross platform toolkit.

Processing Drop

Drop Basics

Before moving on to looking at the code which processes the drop actions, we will briefly look at the stages involved in processing a drop:

  1. The application is notified of a drag occurring. The application checks what is being dragged over it. It then sets the return value to indicate to the OS, whether the dragged object(s) can be dropped.
  2. When the user drops the dragged objects, the application is notified of the drop. It then performs the actions associated with the drag.

Both the initiator and recipient of the drag and drop operation must agree upon the information being exchanged; this is done by using a rendering mechanism and format specifier, both of which are text strings. The OS/2 PM programming reference defines a set of standard rendering and format strings. In addition to these, the programmer can define application-specific rendering and format strings.

Drop in the ICLUI

The ICLUI uses several objects to encapsulate drag and drop. We will look at those classes which we will use in the sample program.

Instances of the IDMItem or it's ancestors represent the objects being dragged. By subclassing IDMItem, we can create specialized drag items if we choose to implement our own rendering mechanisms. For this example, we create a new class called AFileItem but give it no "special" properties.

class AFileItem : public IDMItem {
public:
  AFileItem ( const IDMItem::Handle &item );

virtual Boolean
  targetDrop ( IDMTargetDropEvent & );
};

The method targetDrop is called when the dragged objects are dropped. We override this function to handle our application specific processing of the drop event.

The template class IDMItemProviderFor is derived from the class IDMItemProvider. We subclass IDMItemProviderFor to control various aspects of the drag and drop process. IDMItemProvider implies that this object creates IDMItem objects. However, it can do a lot more than just that. Below, we subclass the template class IDMItemProviderFor to provide support for drag over type events.

class AFileProvider : public IDMItemProviderFor< AFileItem > {

public:
virtual Boolean
  provideEnterSupport ( IDMTargetEnterEvent &event );
};

We override the method provideEnterSupport so that when objects are dragged over our main window, we can test them to see if they can be dropped. Both the ICLUI classes we have looked at provide many more member functions that we can override to provide other drag and drop features, but we will not look at these here.

Building a Simple Test Application

Our simple test application will consist of a frame window and a multi-line edit control. The frame window class will be subclassed by MyFrame. The MLE control will be a member of MyFrame. When a file is dropped on the MLE, the contents of the MLE are discarded and the file is loaded into the control. The following section of code checks the objects being dragged over the MLE.

Boolean AFileProvider::provideEnterSupport( IDMTargetEnterEvent &event ){

  // Get handle to the drag target operation

  IDMTargetOperation::Handle targetOp =
IDMTargetOperation::targetOperation();

  // only want to accept 1 file
  if(targetOp->numberOfItems()!=1){
     event.setDropIndicator(IDM::neverOk);
     return(true);
   }

  // Get the types for the drag item.
  IString strTypes = targetOp->item(1)->types();

  // See if it's marked as a "plain text" file
  if ((strTypes.indexOf(IDM::plainText))){
    event.setDropIndicator(IDM::ok);
    return(true);
  }

  // Type is not recognized - set the drop indicator to prevent a drop!
  event.setDropIndicator(IDM::neverOk);
  return(true);
}

If there is more than one object being dropped over, or the object isn't identified as "plain text", the application sets the drop indicator to neverOk. This will then prevent the drop from occurring, and change the pointer to indicate that a drop is not permitted. The following code snippet, loads the MLE with the dropped file.

Boolean AFileItem::targetDrop( IDMTargetDropEvent & Event){

   IMultiLineEdit *DropWin=(IMultiLineEdit *)
    this->targetOperation()->targetWindow();

   IString
    fname = this->containerName() + this->sourceName();

   //erase the edit window
   DropWin->removeAll();

   //load the file into the edit window
   DropWin->importFromFile(fname);

   return true;
}

The constructor for the class MyFrame follows. The constructor enables drag and drop for the MLE and attaches an instance of our FileProvider to the MLE.

MyFrame::MyFrame(const char *Title):
   IFrameWindow(Title,IResourceId(1),  //-------------------
      IFrameWindow::titleBar|          //
      IFrameWindow::sizingBorder|      //
      IFrameWindow::minimizeButton|    // Create the Frame
      IFrameWindow::systemMenu|        //  Window
      IFrameWindow::shellPosition|     //
      IFrameWindow::minimizeButton|    //
      IFrameWindow::windowList|        //
      IFrameWindow::maximizeButton),   //--------------------
   EditWin(10,this,this){              // Create the Edit Window
                                       // ID=10, use this frame
                                       // window as the parent and
                                       //owner
                                       //---------------------

   setIcon(IResourceId(1));
   setClient(&EditWin);

   //enable default drag and drop handler
   IDMHandler::enableDragDropFor(&EditWin);
   //attach the provider
   EditWin.setItemProvider(&FileProvider);

   show();
}

The file CPPDD.CPP contains the complete source code for the simple app. To compile the code simply execute the Rexx program GO.CMD.

Summary

Drag and drop is one of OS/2's most intuitive user interface features and the ICLUI completely encapsulates the direct manipulation API. However, as with other parts of the ICLUI its encapsulation strategy is not clearly documented. In this article, we have explored how to enable our ICLUI applications to accept dropped files.

Yet, we still have only barely touched this topic's surface.