OOPS Avenue - Drag and DropWritten by Gordon Zeglinski |
IntroductionAs 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 DropDrop BasicsBefore moving on to looking at the code which processes the drop actions, we will briefly look at the stages involved in processing a drop:
Drop in the ICLUIThe 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 ApplicationOur 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.
SummaryDrag 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. |