Drag and Drop and the Clipboard
There are two approaches for exchanging data within an application and between applications: drag and drop (DND) and the clipboard.
This chapter shows you how to enable your applications to supply data and to accept data in the drag-and-drop process, and how to use the clipboard to exchange data within an application or between different applications. Through practical examples, you learn how to equip your applications with DND and clipboard functions.
Using Drag and Drop
SWT supports native drag and drop. You can drag data from a control to another location in the same control, to a different control within the same application, or to a different application.
A drag source provides the data in a drag-and-drop transfer. It is also the object that originates a DND operation. The drag source specifies the types of data and a set of operations that a control supports. Each control may have no more than one drag source object associated with it. Drag source listeners are used to listen for drag events, supply the content of data being transferred and, optionally, to update the control UI after transfer.
A drop target identifies the control over which data can be dropped (transferred). More specifically, a drop target specifies the types of data that can be dropped on the control and a set of operations that can be performed. Each control may have no more than one drop source object associated with it. Drop target listeners listen for drop events and control the dropping process.
A successful drag and drop will not happen unless the drop target can "understand" the data supplied by the drag source. In SWT, transfer objects provide the data translation mechanism. They are capable of converting between a Java representation of data (a Java object) and a platform-specific representation of data and vice versa.
The rest of this section steps you through a word jumble program in order to provide a detailed look at DND. The game is shown in Figure 16-1. The program randomly scrambles the letters of a word, and the goal is to unscramble the word in the second row by dragging the letters from the first row and organizing them in the correct order.
Figure 16-1
In this program, controls (probably labels) in the first row are drag sources, whereas controls in the second row are drag targets. In the next few sections, you learn how to add the drag-and-drop mechanism.
Creating Drag Sources
First, create two labels in row 1 and row 2 for each letter in the word:
String word = "ECLIPSE"; Label[] labelsRowOne; Label[] labelsRowTwo; shell.setText("Word Jumbles"); labelsRowOne = new Label[word.length()]; labelsRowTwo = new Label[word.length()]; int width = 40; for (int i = 0; i < word.length(); i++) { final Label labelRowOne = new Label(shell, SWT.BORDER); labelsRowOne[i] = labelRowOne; labelRowOne.setBounds(10 + width * i, 10, width - 5, width - 5); labelRowOne.setFont(font); labelRowOne.setText(word.charAt(randomPermutation[i]) + ""); labelRowOne.setAlignment(SWT.CENTER); setDragSource(labelRowOne); final Label labelRowTwo = new Label(shell, SWT.BORDER); labelsRowTwo[i] = labelRowTwo; labelRowTwo.setBounds(10 + width * i, 20 + width, width - 5, width - 5); labelRowTwo.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); labelRowTwo.setFont(font); labelRowTwo.setAlignment(SWT.CENTER); setDropTarget(labelRowTwo); }
The setDragSource method should be implemented to specify a drag source for the label passed. The following is a possible implementation:
public void setDragSource(final Label label) { // Allows text to be moved only. int operations = DND.DROP_MOVE; final DragSource dragSource = new DragSource(label, operations); // Data should be transferred in plain text format. Transfer[] formats = new Transfer[] { TextTransfer.getInstance()}; dragSource.setTransfer(formats); dragSource.addDragListener(new DragSourceListener() { public void dragStart(DragSourceEvent event) { // Disallows drags if text is not available. if (label.getText().length() == 0) event.doit = false; } public void dragSetData(DragSourceEvent event) { // Provides the text data. if (TextTransfer.getInstance().isSupportedType(event.dataType)) event.data = label.getText(); } public void dragFinished(DragSourceEvent event) { // Removes the text after the move operation. if (event.doit == true || event.detail == DND.DROP_MOVE) { label.setText(""); } } }); label.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { dragSource.dispose(); } ); }
First, construct a drag source object with the label and include the allowed operations as the arguments with the constructor of the DragSource class:
public DragSource(Control control, int style)
Next, specify the control that the user clicks to initiate the drag in the first argument and the allowed operations in the second argument. Following is a list of all of possible operations:
- DND.DROP_NONE: No drag-and-drop operation performed
- DND.DROP_COPY: A copy of the data in the drag source is added to the drop target
- DND.DROP_MOVE: A copy of the data in the drag source is added to the drop target and the original data is removed from the drag source
- DND.DROP_LINK: The drop target makes a link to the data in the drag source
In this case, you can allow only the move operation. If multiple operations are allowed, you can bitwise OR the desired operations. For example, to allow copy and move operations, you specify DND.DROP_COPY | DND.DROP_MOVE in the second argument of the constructor of the DragSource class.
Now that you have specified the allowed operation, you are ready to specify the data types that the drag source can provide with the setTransfer method of the DragSource class:
public void setTransfer(Transfer[] transferAgents)
Currently, several data types are implemented in the org.eclipse.swt.dnd package, as described in the following table.
Data Type |
Corresponding Java Type |
Example |
---|---|---|
TextTransfer (plain text) |
String |
Hello World |
RTFTransfer (rich text file) |
String |
{ tf1ansideff0 f0fs60 Hello, World!} |
FileTransfer (a list of files) |
String[] |
File file1 = new File("C:file1"); File file2 = new File("C:WINDOWSfile2"); new String[] {file1.getAbsolutePath(), file2.getAbsolutePath() }; |
Here, the data to be transferred is available only in the plain text format (TextTransfer). You may notice that setTransfer allows you to specify more than one data type. For example, setTransfer(new Transfer[] {TextTransfer.getInstance(), RTFTransfer.getInstance()}) states that the control can provide data in two types, plain text and RTF text. So in which format should the data be prepared when the drop target sends a request? You'll get an answer to this question very soon when you learn about the drag source listener.
The drag source itself cannot handle data transfer. To do that, you need to use a drag source listener. A drag source listener listens for drag events and acts accordingly. There are three methods in the DragSourceListner interface:
public void dragStart(DragSourceEvent event) public void dragSetData(DragSourceEvent event) public void dragFinished(DragSourceEvent event)
dragStart()
The dragStart method is called when the user initiates a drag-and-drop operation. The initiating action taken by the user is platform-specific. For example, on Windows, the user initiates a DND operation by pressing down with the left mouse button and dragging. However, on Motif, the user does it by pressing down the middle button of the mouse. In this drag source listener, you cancel the DND operation (if the text of the label is empty) by setting the doit field to FALSE on the drag source event object. Otherwise, DND operations are allowed.
dragSetData()
The dragSetData method may be called multiple times when the drop target requests data. The type of data requested by the drop target is stored in the dataType field of the DragSourceEvent object. The drag source can then use this information to provide data in the correct format. If the data type request is supported by the drag source, the drag source provides the data to the drop target by storing the data in the data field of the drag source event object. Because transfer classes are implemented in a platformdependent way, you cannot access the members of transfer classes directly to make your application portable. Rather, you should always use the following pattern to perform type checking:
if ([Transfer class].getInstance().isSupportedType(event.dataType)) ...
In the preceding code, [Transfer class] can be any of the following: TextTransfer, RFTTransfer, or FileTransfer.
dragFinished()
When the drag-and-drop operation finishes, the dragFinished method is called. A DND operation finishes when the user drops the data on a valid control, drops the data on an invalid control, or terminates the DND operation by pressing the Escape key. If the operation is performed, the doit field of the drag source event object is true; otherwise it is false. Additionally, you can check the type of operation performed by querying the detail field of the event object. In the game program, the text is removed only if a move operation has been performed.
Because instances of DragSource and DropTarget classes use native operating system resources, you should dispose of them when they are not in use anymore. To do that, you add a dispose listener to the label so that the drag source object associated with the label is disposed of when the label is about to be disposed of.
You have now finished coding the drag source part. To test it, you can run the program and drag a letter in the first row onto a text editor such as Microsoft Word. However, you cannot drop a letter on any of the labels in the second row. To enable the labels on the second row to accept data, you need to create drop targets with them.
Creating Drop Targets
After implementing the setDragSource method, we are going to implement the setDropTarget method as used in setDropTarget(labelRowTwo). Creating a drop target for a control is very similar to creating a drag source on a control, as you saw in the preceding section. First, you need to create the drop target object with the control and allowed operations as the argument. Then you set data types that the drop target accepts. After that, you need to register a drop listener to handle drop events. Here is the code for the setDropTarget method:
public void setDropTarget(final Label label) { int operations = DND.DROP_MOVE; final DropTarget dropTarget = new DropTarget(label, operations); // Data should be transferred in plain text format. Transfer[] formats = new Transfer[] { TextTransfer.getInstance()}; dropTarget.setTransfer(formats); dropTarget.addDropListener(new DropTargetListener() { public void dragEnter(DropTargetEvent event) { // Does not accept any drop if the label has text on it. if(label.getText().length() != 0) event.detail = DND.DROP_NONE; } public void dragLeave(DropTargetEvent event) { } public void dragOperationChanged(DropTargetEvent event) { } public void dragOver(DropTargetEvent event) { } public void drop(DropTargetEvent event) { if (TextTransfer.getInstance().isSupportedType(event.currentDataType)){ String text = (String) event.data; label.setText(text); // Checks the result. check(); } } public void dropAccept(DropTargetEvent event) { } }); label.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { dropTarget.dispose(); } }); }
As you learned earlier, setting allowed operations and data types for a drop target is very similar to setting them for a drag source. Let's skip the first part and look at the drop target listener in detail.
Overview of Methods in DropTargetListener
As the user drags the cursor into, over, and out of a control designated as a drop target, various drop target events are generated, and the corresponding methods in drop target listeners are invoked. Six methods are declared in the DropTargetListener interface:
public void dragEnter(DropTargetEvent event) public void dragOperationChanged(DropTargetEvent event) public void dragOver(DropTargetEvent event) public void dropAccept(DropTargetEvent event) public void dragLeave(DropTargetEvent event) public void drop(DropTargetEvent event)
The first four methods may be invoked before the drop happens. You can use them to
- Set the type of data that should be transferred in the currentDataType field of the drop target event object.
- Change the operation to be performed by modifying the detail field of the event object. (You can stop the drop from happening by setting the detail field to DND.DROP_NONE.)
- Set the visual feedback effects on the drop target control (except the dropAccept method).
The drop method is called when the data is being dropped. You learn about each method in detail next.
dragEnter()
The dragEnter method is invoked when the dragging cursor enters the drop target boundary. The currentDataType field of the event object is determined by the first transfer type specified in the setTransfer() method that matches a data type provided by the drag source. You can change the default data type by setting the currentDataType field of the event object. The dataTypes field of the event object contains all the data types that the drag source can provide. The value of the currentDataType must be one of the types in the dataTypes.
The detail field of the event object specifies the operation to be performed. The operation is determined by the modifier keys pressed by the user. If no keys are pressed, the detail field of the event object has the value as DND.DROP_DEFAULT. You can modify a drop operation by changing the detail field to the desired operation. In my code, if the label has text on it, you disable the drop operation by setting the detail field to DND.DROP_NONE. The detail field value can be only DND.DROP_NONE or an operation supported by the drag source (stored in the operations field of the event object).
If the drop target control is a tree or a table, you can optionally specify visual feedback effects for it by setting the feedback field of the event object with the values listed in the following table.
Feedback |
Description |
---|---|
DND. FEEDBACK_EXPAND |
The item currently under the cursor is expanded to allow the user to select a drop target from a sub item. (Applies to trees) |
DND. FEEDBACK_INSERT_AFTER |
An insertion mark is shown after the item under the cursor. (Applies to tables and trees) |
DND. FEEDBACK_INSERT_BEFORE |
An insertion mark is shown before the item under the cursor. (Applies to tables and trees) |
DND. FEEDBACK_NONE |
No feedback effect is shown. |
DND. FEEDBACK_SCROLL |
The widget is scrolled up or down to allow the user to drop on items that are not currently visible. (Applies to tables and trees) |
DND. FEEDBACK_SELECT |
The item under the cursor is selected. (Applies to tables and trees) |
The default value of the feedback field is DND.FEEBACK_SELECT.
dragOperationChanged()
During a drag operation, the dragOperationChanged method is called when the operation being performed has changed. The cause of the change is usually that the user changes the modifier keys while dragging. For example, on Windows, pressing the Ctrl key indicates a copy operation (DND.DROP_COPY) and pressing both the Ctrl and Shift keys indicates a link operation (DND.DROP_LINK). With no modifier, the default operation (DND.DROP_DEFAULT) is requested.
Similar to the dragEnter method, you can change the operation, data type, and visual feedback for the drop target in this method.
dragOver()
The dragOver method is called constantly when the cursor is moving over the drop target. Like the dragEnter and dragOperationChanged method, you can modify the operation, data type, and visual feedback for the drop target in this method.
dragAccept()
The dragAccept method is invoked when the drop is about to be performed. This method provides the last chance to specify the operation and to define the current data type.
dragLeave()
The dragLeave method may be called in any the following cases:
- The cursor has left the drop target.
- The drop operation has been cancelled.
- The data is about to be dropped.
You cannot modify any field of the event object in this method.
drop()
In the drop method, the data is being dropped. The data field of the event object contains the data as a Java object. For more details on data type and Java object mapping, please refer to the table in the section "Creating Drag Sources." In the word jumbles program, you check the data type and then set the text to the label. Even in this method, you can cancel a drop operation by setting the detail field of the event object to DND.DROP_NONE.
You have now completed the word jumbles program. With the knowledge gained during development of this simple program, you are ready to create more complex applications. In the next section, you learn how to build an easy-to-use bookmark organizer by taking advantage of SWT drag and drop.
The Bookmark Organizer
In this section, you build a bookmark organizer as shown in Figure 16-2. The bookmark organizer allows you to reorganize Internet Explorer's bookmarks. To use the program, first you need to export your IE bookmarks as an HTML file. The bookmark organizer can load all the bookmarks contained in the exported HTML file. You can then reorganize your bookmarks by drag and drop — i.e., you can copy or move a bookmark to any folder through drag and drop.
Figure 16-2
Constructing the UI
First, construct the basic UI objects and implement the data parsing and saving mechanism.
/** * Represents a bookmark. * */ class Bookmark { public String name; public String href; public String addDate; public String lastVisited; public String lastModified; } /** * */ public class BookmarkOrganizer { private static String folderLinePrefix = "
<a href="">(.*)<"); // the pattern used to extract HREF, DATE and other information. private static Pattern urlPattern = Pattern.compile(""(.*)".*"(.*)".*"(.*)".*"(.*)">(.*)<"); private static String KEY_ADD_DATE = "ADD_DATE"; private static String KEY_HREF = "HREF"; private static String KEY_LAST_VISITED = "LAST_VISITED"; private static String KEY_LAST_MODIFIED = "LAST_MODIFIED"; Display display = new Display(); Shell shell = new Shell(display); Tree tree; Label label; TreeItem rootItem; Image iconRoot = new Image(display, "icons/icon.gif"); Image iconFolder = new Image(display, "icons/folder.gif"); Image iconURL = new Image(display, "icons/file.gif"); TreeItem dragSourceItem; public BookmarkOrganizer() { shell.setText("Bookmark organizer"); shell.setLayout(new GridLayout(1, true)); ToolBar toolBar = new ToolBar(shell, SWT.FLAT); ToolItem itemOpen = new ToolItem(toolBar, SWT.PUSH); itemOpen.setText("Load"); itemOpen.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { FileDialog dialog = new FileDialog(shell, SWT.OPEN); String file = dialog.open(); if (file != null) { // removes existing items. TreeItem[] items = rootItem.getItems(); for (int i = 0; i < items.length; i++) items[i].dispose(); loadBookmark(new File(file), rootItem); setStatus("Bookmarks loaded successfully"); } } }); ToolItem itemSave = new ToolItem(toolBar, SWT.PUSH); itemSave.setText("Save as"); itemSave.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { FileDialog dialog = new FileDialog(shell, SWT.SAVE); String file = dialog.open(); if (file != null) { try { BufferedWriter writer = new BufferedWriter(new FileWriter(file)); saveBookmark(writer, rootItem); writer.close(); setStatus( "Bookmarks saved successfully to file: " + file); } catch (IOException e) { e.printStackTrace(); } } } }); tree = new Tree(shell, SWT.BORDER); tree.setLayoutData(new GridData(GridData.FILL_BOTH)); rootItem = new TreeItem(tree, SWT.NULL); rootItem.setText("BOOKMARKS"); rootItem.setImage(iconRoot); label = new Label(shell, SWT.BORDER); label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); tree.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { TreeItem item = (TreeItem) e.item; Bookmark bookmark = (Bookmark) item.getData(); if (bookmark != null) { setStatus(bookmark.href); } else if (item.getData(KEY_ADD_DATE) != null) { // folder. setStatus("Folder: " + item.getText()); } } }); // Specifies drag and drop support here .... shell.setSize(400, 300); shell.open(); // Sets up the event loop. while (!shell.isDisposed()) { if (!display.readAndDispatch()) { // If no more entries in event queue display.sleep(); } } display.dispose(); } /** * Writes the bookmark(s) into the given buffered writer. * @param writer * @param item * @throws IOException */ private void saveBookmark(BufferedWriter writer, TreeItem item) throws IOException { ... } /** * Loads the bookmarks from the specified file. * This method parses the specified file line by line to extract all folders * and urls contained in the file and wrap them into tree items. * @param file * @param rootItem */ private void loadBookmark(File file, TreeItem rootItem) { TreeItem parent = rootItem; try { BufferedReader reader = new BufferedReader(new FileReader(file)); String line = null; while ((line = reader.readLine()) != null) { line = line.trim(); if (line.startsWith(folderLinePrefix)) { // a folder. // using regex to find any folder contained in the line. Matcher matcher = folderPattern.matcher(line); if (matcher.find()) { String addDate = matcher.group(1); String name = matcher.group(2); TreeItem item = new TreeItem(parent, SWT.NULL); item.setText(name); item.setData(KEY_ADD_DATE, addDate); item.setImage(iconFolder); parent = item; } } else if (line.startsWith(urlLinePrefix)) { // a url // using regex to find any URL contained in the line. Matcher matcher = urlPattern.matcher(line); if (matcher.find()) { Bookmark bookmark = new Bookmark(); bookmark.href = matcher.group(1); bookmark.addDate = matcher.group(2); bookmark.lastVisited = matcher.group(3); bookmark.lastModified = matcher.group(4); bookmark.name = matcher.group(5); TreeItem item = new TreeItem(parent, SWT.NULL); item.setText(bookmark.name); item.setData(bookmark); item.setImage(iconURL); } } else if (line.equals("</a>
<a href="">")) { // folder boundary. parent = parent.getParentItem(); } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void setStatus(String message) { label.setText(message); } public static void main(String[] args) { new BookmarkOrganizer(); } } </a>
<a href="">First, declare the tree to be used to display bookmarks. Then add the toolbar and toolbar items to the shell. One toolbar item is used to load bookmark information from a file, and the other one saves the bookmarks on the tree into a file. A tree control is created so that bookmarks can be displayed on it. The loadBookmark method creates a tree item for each bookmark folder and bookmark when loading bookmark information from the file. The Bookmark class is used to represent a book. An instance of Bookmark is associated with its tree item through the setData method of the TreeItem class.</a>
<a href="">Let's take a moment to think of a way to implement the drag-and-drop mechanism. Our goal here is to enable the user to drag a bookmark (represented by a tree item) to any of the folders. Remember that only a control can be used to create a drag source or a drop target, so we cannot directly create drag sources or drop targets based on tree items because TreeItem is not a subclass of Control. The only possible candidate here is the tree control itself. Clearly, we need to create a drag source and a drop target based on the exact same control — the tree control.</a>
<a href="">Then you determine the proper format of data being transferred. Obviously, the data to be transferred is a Bookmark object. As you learned in previous sections, only three transfer types are available in the SWT DND package: TextTransfer, RTFTransfer, and FileTransfer. None of the existing transfer classes is capable of directly transferring an instance of the Bookmark object. There are two possible ways to overcome this:</a>
- <a href="">"Embedding" an instance of Bookmark in a string. For example, for a bookmark {"Eclipse", " http://www.eclipse.org ", ADD_DATE=" 1045048972", LAST_VISIT="1083069965" LAST_MODIFIED="1045048972"}, we can embed it in a string object as: Eclipse| http://www.eclipse.org |1045048972|1083069965. Both the drag source listener and the drop target listener should understand the string format.</a>
- <a href="">Creating our own transfer type by extending the ByteArrayTransfer class.</a>
<a href="">Both approaches can do the job. In the following section, you use the latter method.</a>
<a href="">Creating a Custom Transfer Type</a>
<a href="">Create a class named BookmarkTransfer to handle Bookmark object transfer:</a>
<a href=""> public class BookmarkTransfer extends ByteArrayTransfer { private static final String BOOKMARK_TRANSFER_NAME = "BOOKMARK"; private static final int BOOKMARK_TRANSFER_ID = registerType(BOOKMARK_TRANSFER_NAME); private static final BookmarkTransfer instance = new BookmarkTransfer(); public static BookmarkTransfer getInstance() { return instance; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#getTypeIds() */ protected int[] getTypeIds() { return new int[] { BOOKMARK_TRANSFER_ID }; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#getTypeNames() */ protected String[] getTypeNames() { return new String[] { BOOKMARK_TRANSFER_NAME }; } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#javaToNative(java.lang.Object, * org.eclipse.swt.dnd.TransferData) */ protected void javaToNative(Object object, TransferData transferData) { if (object == null || !(object instanceof Bookmark)) return; Bookmark bookmark = (Bookmark) object; if (isSupportedType(transferData)) { try { // Writes data to a byte array. ByteArrayOutputStream stream = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(stream); out.writeUTF(bookmark.name); out.writeUTF(bookmark.href); out.writeUTF(bookmark.addDate); out.writeUTF(bookmark.lastVisited); out.writeUTF(bookmark.lastModified); out.close(); super.javaToNative(stream.toByteArray(), transferData); } catch (IOException e) { e.printStackTrace(); } } } /* * (non-Javadoc) * * @see org.eclipse.swt.dnd.Transfer#nativeToJava(TransferData) */ protected Object nativeToJava(TransferData transferData) { if (isSupportedType(transferData)) { byte[] raw = (byte[]) super.nativeToJava(transferData); if (raw == null) return null; Bookmark bookmark = new Bookmark(); try { ByteArrayInputStream stream = new ByteArrayInputStream(raw); DataInputStream in = new DataInputStream(stream); bookmark.name = in.readUTF(); bookmark.href = in.readUTF(); bookmark.addDate = in.readUTF(); bookmark.lastVisited = in.readUTF(); bookmark.lastModified = in.readUTF(); in.close(); } catch (IOException e) { e.printStackTrace(); return null; } return bookmark; } else { return null; } } } </a>
<a href="">The getTypeIds method returns the platform-specific IDs of the data types and can be converted using this transfer agent, and the getTypesNames method returns the names of the data types supported by the transfer agent. The javaToNative method in the preceding class is used to convert a Java object to a platform-specific representation. First, you write the Bookmark object into a byte array. Then you call the javaToNative method of the ByteArrayTransfer class, which is capable of converting a Java byte array into a platform-specific representation and writes the native code to the TransferData object. In this way, you don't have to care about the platform-specific data transformation; those methods that are defined in the ByteArrayTransfer class (which is the superclass for all transfer types such as TextTransfer, RTFTransfer, and FileTransfer) do all the dirty work. The nativeToJava method coverts the native data representation into a Bookmark object.</a>
<a href="">Now, we can equip the tree control with drag-and-drop functionality.</a>
<a href="">Adding Drag-and-Drop Support</a>
<a href="">First, create a drag source on the tree control:</a>
<a href=""> TreeItem dragSourceItem; ... final DragSource dragSource = new DragSource(tree, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK); dragSource.setTransfer(new Transfer[] { BookmarkTransfer.getInstance()}); dragSource.addDragListener(new DragSourceAdapter() { public void dragStart(DragSourceEvent event) { TreeItem[] selection = tree.getSelection(); // Only a URL bookmark can be dragged. if (selection.length > 0 && selection[0].getData() != null) { event.doit = true; dragSourceItem = selection[0]; } else { event.doit = false; } }; public void dragSetData(DragSourceEvent event) { if (BookmarkTransfer .getInstance() .isSupportedType(event.dataType)) event.data = dragSourceItem.getData(); } public void dragFinished(DragSourceEvent event) { if (event.detail == DND.DROP_MOVE) dragSourceItem.dispose(); dragSourceItem = null; } }); </a>
<a href="">The drag source is created with BookmarkTransfer as its only supported transfer type. A class member named dragSourceItem is used to record the dragged item. Items other than bookmark items, such as folder items, are not allowed to be dragged, as they are restricted in the dragStart method. The dragSetData method simply verifies the requested data type and sends the Bookmark object associated with the dragged item. If the drop operation is the move operation, you remove the dragged item by calling its dispose method.</a>
<a href="">After setting the drag source, you need to create a drop target based on the tree control:</a>
<a href=""> final DropTarget dropTarget = new DropTarget(tree, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK); dropTarget.setTransfer(new Transfer[] { BookmarkTransfer.getInstance()}); dropTarget.addDropListener(new DropTargetAdapter() { public void dragOver(DropTargetEvent event) { event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL | DND.FEEDBACK_SELECT; } public void dropAccept(DropTargetEvent event) { // can only drop into to a folder if (event.item == null || ((TreeItem) event.item).getData() != null) event.detail = DND.DROP_NONE; } public void drop(DropTargetEvent event) { try { if (event.data == null) { event.detail = DND.DROP_NONE; return; } TreeItem item = new TreeItem((TreeItem) event.item, SWT.NULL); Bookmark bookmark = (Bookmark) event.data; item.setText(bookmark.name); item.setImage(iconURL); item.setData(bookmark); } catch (RuntimeException e) { e.printStackTrace(); } } }); </a>
<a href="">Like the drag source, the drop target supports the BookmarkTransfer type only. A drop target listener is added after the drop target is created. The feedback field of the event object is configured in the dropOver method to have the tree control exhibiting visual effects such as selection and expansion. The dropAccept method specifies that only a folder item can accept a bookmark drop. Finally, the drop method performs the actual drop. Notice that you use a try-catch statement to catch all exceptions. In the current SWT release, DND wraps all event listener code in an exception block. If an event listener throws an exception, no error will be reported and the DND operation is set to DND.DROP_NONE. I advise you to wrap your complex code in drag-and-drop listeners in exception blocks so that you can easily figure out what happens in case of error.</a>
<a href="">Now, the bookmark organizer is completed. You can load your bookmarks, organize them, and save the results in a new file.</a>
<a href=""> </a>
<a href="">Using the Clipboard</a>
<a href="">Drag and drop allows the user to transfer data from an application to itself or to other applications instantly. Sometimes, it is desirable to perform the transfer operation at a later point in time. In this case, you can use the clipboard as the temporary holder for the transfer data.</a>
<a href="">Putting Data on the Clipboard</a>
<a href="">The clipboard provides a virtual space to place data in multiple formats. For example, the following code places data in plain text format as well as rich text format:</a>
<a href=""> ToolBar toolBar = new ToolBar(shell, SWT.FLAT); ToolItem itemCopy = new ToolItem(toolBar, SWT.PUSH); ToolItem itemPaste = new ToolItem(toolBar, SWT.PUSH); itemCopy.setText("Copy"); itemPaste.setText("Paste"); itemCopy.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { Clipboard clipboard = new Clipboard(display); String plainText = "Hello World"; String rtfText = "{\rtf1\b Hello World}"; TextTransfer textTransfer = TextTransfer.getInstance(); RTFTransfer rftTransfer = RTFTransfer.getInstance(); clipboard.setContents(new String[]{plainText, rtfText}, new Transfer[]{textTransfer, rftTransfer}); clipboard.dispose(); } }); </a>
<a href="">When the item labeled "Copy" is hit, a Clipboard object is created with the following constructor:</a>
<a href=""> public Clipboard(Display display) </a>
<a href="">The data to be placed on the clipboard is prepared in two formats. The setContents of the Clipboard class is used to place the data onto the clipboard:</a>
<a href=""> public void setContents(Object[] data, Transfer[] dataTypes) </a>
<a href="">Once the data is placed on the clipboard, all previous data available on the clipboard is cleared. Because the data is available in multiple formats, the target application may choose the format it supports best. For example, when you hit the Copy tool item and do a paste in Microsoft Word, you see the text "Hello World" displayed in bold. This is because Microsoft Word gets the data in rich text format. Now, if you open the Notepad application and do a paste, you will see that the text "Hello World" does not appear in bold because Notepad is not able to display RTF text and it gets data in plain text format only.</a>
<a href="">On all systems except GTK, the data you place on the clipboard lasts even after your application is closed.</a>
<a href="">Because a Clipboard object allocates native system resources, you have to dispose of it after use.</a>
<a href="">Getting Data from the Clipboard</a>
<a href="">In the preceding section, you learned how to put data on the clipboard. Now you learn how to get data from the clipboard. Because the data on the clipboard may be available in multiple formats, you need to check the availability of your desired format. If the format is supported, you can get the data in that format directly. If the desired format is not supported, you may have to consider using an alternate format.</a>
<a href="">For example, the follow code checks the availability of the RTF format and gets the data in both plain text format and RTF format:</a>
<a href=""> itemPaste.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { Clipboard clipboard = new Clipboard(display); TransferData[] transferDatas = clipboard.getAvailableTypes(); for(int i=0; i<]transferDatas.length; i++) { // Checks whether RTF format is available. if(RTFTransfer.getInstance().isSupportedType(transferDatas[i])) { System.out.println("Data is available in RTF format"); break; } } String plainText = (String)clipboard.getContents(TextTransfer.getInstance()); String rtfText = (String)clipboard.getContents(RTFTransfer.getInstance()); System.out.println("PLAIN: " + plainText + " " + "RTF: " + rtfText); clipboard.dispose(); } }); </a>
<a href="">The getAvailableTypes method returns available data formats in an array:</a>
<a href=""> public TransferData[] getAvailableTypes() </a>
<a href="">To check whether a format is supported, you need use the Transfer.isSupportedType method, as shown in the preceding code.</a>
<a href="">You use the getContents method to get the data in a specified format:</a>
<a href=""> public Object getContents(Transfer transfer) </a>
<a href="">The getContents method returns the data in the given type or null if no data of the type is available.</a>
<a href=""> </a>
<a href="">Summary</a>
<a href="">In this chapter, the SWT drag-and-drop mechanism was discussed. A drag source provides data in a DND process, while a drop target consumes the data. Drag source listeners and drop target listeners are used extensively to perform various actions in DND. If none of the existing data types is suitable for your data transfer, you can create your own data transfer type. You learned that the clipboard is a handy tool for holding data temporarily and then transferring it at a later point in time. DND and the clipboard are particularly useful for text editing applications. In such applications, you can persist the editing by saving to disks or printing to printers. In the next chapter, you learn about SWT printing support in detail.</a>
<a href=""> </a>
<a href="">Chapter 17 Printing</a>
Категории