001: /*
002: * TextAreaTransferHandler.java - Drag and drop support
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2004 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.textarea;
024:
025: //{{{ Imports
026:
027: import org.gjt.sp.jedit.*;
028: import org.gjt.sp.jedit.browser.VFSBrowser;
029: import org.gjt.sp.jedit.io.FileVFS;
030: import org.gjt.sp.jedit.io.VFS;
031: import org.gjt.sp.jedit.io.VFSManager;
032: import org.gjt.sp.util.Log;
033: import org.gjt.sp.util.WorkRequest;
034:
035: import javax.swing.*;
036: import java.awt.datatransfer.DataFlavor;
037: import java.awt.datatransfer.StringSelection;
038: import java.awt.datatransfer.Transferable;
039: import java.io.File;
040: import java.net.URI;
041: import java.util.List;
042:
043: //}}}
044:
045: /**
046: * @author Slava Pestov
047: * @version $Id: TextAreaTransferHandler.java 10715 2007-09-22 21:26:39Z kpouer $
048: */
049: public class TextAreaTransferHandler extends TransferHandler {
050: /* I assume that there can be only one drag operation at the time */
051: private static JEditTextArea dragSource;
052: private static boolean compoundEdit;
053: private static boolean sameTextArea;
054: private static int insertPos;
055: private static int insertOffset;
056:
057: // Unfortunately, this does not work, as this DataFlavor is internally changed into another DataFlavor which does not match the intented DataFlavor anymore. :-( So, below, we are iterating.
058: /*
059: protected static DataFlavor textURIlistDataFlavor = null;
060:
061: static {
062: try {
063: textURIlistDataFlavor = new DataFlavor("text/uri-list;representationclass=java.lang.String");
064: } catch (ClassNotFoundException e) {
065: throw new RuntimeException("Cannot create DataFlavor. This should not happen.",e);
066: }
067: }
068: */
069:
070: //{{{ createTransferable
071: protected Transferable createTransferable(JComponent c) {
072: Log.log(Log.DEBUG, this , "createTransferable()");
073: JEditTextArea textArea = (JEditTextArea) c;
074: if (textArea.getSelectionCount() == 0)
075: return null;
076: else {
077: dragSource = textArea;
078: return new TextAreaSelection(textArea);
079: }
080: } //}}}
081:
082: //{{{ getSourceActions
083: public int getSourceActions(JComponent c) {
084: return COPY_OR_MOVE;
085: } //}}}
086:
087: //{{{ importData
088: public boolean importData(JComponent c, Transferable t) {
089: Log.log(Log.DEBUG, this , "Import data");
090: // Log.log(Log.DEBUG,this,"Import data: t.isDataFlavorSupported("+textURIlistDataFlavor+")="+t.isDataFlavorSupported(textURIlistDataFlavor)+".");
091: if (!canImport(c, t.getTransferDataFlavors()))
092: return false;
093:
094: boolean returnValue;
095:
096: try {
097: if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
098: returnValue = importFile(c, t);
099: } else {
100: DataFlavor uriListStringDataFlavor = null;
101: DataFlavor[] dataFlavors = t.getTransferDataFlavors();
102:
103: for (int i = 0; i < dataFlavors.length; i++) {
104: DataFlavor dataFlavor = dataFlavors[i];
105: if ("text".equals(dataFlavor.getPrimaryType())
106: && "uri-list".equals(dataFlavor
107: .getSubType())
108: && dataFlavor.getRepresentationClass() == String.class) {
109: uriListStringDataFlavor = dataFlavor;
110: break;
111: }
112: }
113:
114: if (uriListStringDataFlavor != null
115: && t
116: .isDataFlavorSupported(uriListStringDataFlavor)) {
117: returnValue = importURIList(c, t,
118: uriListStringDataFlavor);
119: } else {
120: returnValue = importText(c, t);
121: }
122: }
123: } catch (Exception e) {
124: Log.log(Log.ERROR, this , e);
125: returnValue = false;
126: }
127:
128: GUIUtilities.getView(c).toFront();
129: GUIUtilities.getView(c).requestFocus();
130: c.requestFocus();
131:
132: return returnValue;
133: } //}}}
134:
135: //{{{ importFile
136: private boolean importFile(JComponent c, Transferable t)
137: throws Exception {
138: Log.log(Log.DEBUG, this , "=> File list");
139: EditPane editPane = (EditPane) GUIUtilities.getComponentParent(
140: c, EditPane.class);
141: View view = editPane.getView();
142: Buffer buffer = null;
143:
144: List<File> data = (List<File>) t
145: .getTransferData(DataFlavor.javaFileListFlavor);
146:
147: boolean browsedDirectory = false;
148:
149: for (File file : data) {
150: if (file.isDirectory()) {
151: if (!browsedDirectory) {
152: VFSBrowser.browseDirectory(view, file.getPath());
153: browsedDirectory = true;
154: }
155: continue;
156: }
157: Buffer _buffer = jEdit.openFile(null, file.getPath());
158: if (_buffer != null)
159: buffer = _buffer;
160: }
161:
162: if (buffer != null)
163: editPane.setBuffer(buffer);
164: view.toFront();
165: view.requestFocus();
166: editPane.requestFocus();
167:
168: return true;
169: } //}}}
170:
171: //{{{ importURIList
172: private boolean importURIList(JComponent c, Transferable t,
173: DataFlavor uriListStringDataFlavor) throws Exception {
174: String str = (String) t
175: .getTransferData(uriListStringDataFlavor);
176:
177: Log.log(Log.DEBUG, this , "=> URIList \"" + str + '\"');
178: EditPane editPane = (EditPane) GUIUtilities.getComponentParent(
179: c, EditPane.class);
180: View view = editPane.getView();
181: JEditTextArea textArea = (JEditTextArea) c;
182: if (dragSource == null) {
183: boolean found = false;
184: String[] components = str.split("\r\n");
185:
186: boolean browsedDirectory = false;
187: for (int i = 0; i < components.length; i++) {
188: String str0 = components[i];
189:
190: if (str0.length() > 0) {
191: URI uri = new URI(str0); // this handles the URI-decoding
192:
193: if ("file".equals(uri.getScheme())) {
194: File file = new File(uri.getPath());
195: if (file.isDirectory()) {
196: if (!browsedDirectory) {
197: VFSBrowser.browseDirectory(view, file
198: .getPath());
199: browsedDirectory = true;
200: }
201: } else {
202: VFSManager
203: .runInWorkThread(new DraggedURLLoader(
204: textArea, uri.getPath()));
205: }
206: found = true;
207: } else {
208: Log.log(Log.DEBUG, this ,
209: "I do not know how to handle this URI "
210: + uri + ", ignoring.");
211: }
212: } else {
213: // This should be the last component, because every URI in the list is terminated with a "\r\n", even the last one.
214: if (i != components.length - 1) {
215: Log
216: .log(Log.DEBUG, this ,
217: "Odd: there is an empty line in the uri list which is not the last line.");
218: }
219: }
220: }
221:
222: if (found) {
223: return true;
224: }
225: }
226:
227: return true;
228: } //}}}
229:
230: //{{{ importText
231: private boolean importText(JComponent c, Transferable t)
232: throws Exception {
233: String str = (String) t
234: .getTransferData(DataFlavor.stringFlavor);
235: str = str.trim();
236: Log.log(Log.DEBUG, this , "=> String \"" + str + '\"');
237:
238: JEditTextArea textArea = (JEditTextArea) c;
239: if (dragSource == null) {
240: boolean found = false;
241: String[] components = str.split("\n");
242:
243: for (int i = 0; i < components.length; i++) {
244: String str0 = components[i];
245: // Only examine the string for a URL if it came from
246: // outside of jEdit.
247: VFS vfs = VFSManager.getVFSForPath(str0);
248: if (!(vfs instanceof FileVFS)
249: || str.startsWith("file://")) {
250:
251: // str = str.replace('\n',' ').replace('\r',' ').trim();
252: if (str0.startsWith("file://")) {
253: str0 = str0.substring(7);
254: }
255:
256: VFSManager.runInWorkThread(new DraggedURLLoader(
257: textArea, str0));
258: }
259: found = true;
260:
261: }
262:
263: if (found) {
264: return true;
265: }
266: }
267:
268: if (dragSource != null
269: && textArea.getBuffer() == dragSource.getBuffer()) {
270: compoundEdit = true;
271: textArea.getBuffer().beginCompoundEdit();
272: }
273:
274: sameTextArea = textArea == dragSource;
275:
276: int caret = textArea.getCaretPosition();
277: Selection s = textArea.getSelectionAtOffset(caret);
278:
279: /* if user drops into the same
280: selection where they started, do
281: nothing. */
282: if (s != null) {
283: if (sameTextArea)
284: return false;
285: /* if user drops into a selection,
286: replace selection */
287: int startPos = s.start;
288: textArea.setSelectedText(s, str);
289: textArea.setSelection(new Selection.Range(startPos,
290: startPos + str.length()));
291: }
292: /* otherwise just insert the text */
293: else {
294: if (sameTextArea) {
295: insertPos = caret;
296: insertOffset = 0;
297: Selection[] selections = textArea.getSelection();
298: for (int i = 0; i < selections.length; i++) {
299: if (selections[i].end < insertPos + insertOffset)
300: insertOffset -= selections[i].end
301: - selections[i].start;
302: }
303: } else {
304: textArea.getBuffer().insert(caret, str);
305: textArea.setSelection(new Selection.Range(caret, caret
306: + str.length()));
307: }
308: }
309: textArea.scrollToCaret(true);
310:
311: return true;
312: } //}}}
313:
314: //{{{ exportDone
315: protected void exportDone(JComponent c, Transferable t, int action) {
316: Log.log(Log.DEBUG, this , "Export done");
317:
318: JEditTextArea textArea = (JEditTextArea) c;
319:
320: try {
321: // This happens if importData returns false. For example if you drop into the Selection
322: if (action == NONE) {
323: Log.log(Log.DEBUG, this , "Export impossible");
324: return;
325: }
326:
327: if (t == null) {
328: Log.log(Log.DEBUG, this , "=> Null transferrable");
329: textArea.selectNone();
330: } else if (t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
331: Log.log(Log.DEBUG, this , "=> String");
332: if (sameTextArea) {
333: if (action == MOVE) {
334: textArea.setSelectedText(null, false);
335: insertPos += insertOffset;
336: }
337: try {
338: String str = (String) t
339: .getTransferData(DataFlavor.stringFlavor);
340: textArea.getBuffer().insert(insertPos, str);
341: textArea.setSelection(new Selection.Range(
342: insertPos, insertPos + str.length()));
343: } catch (Exception e) {
344: Log.log(Log.DEBUG, this ,
345: "exportDone in sameTextArea");
346: Log.log(Log.DEBUG, this , e);
347: }
348: } else {
349: if (action == MOVE)
350: textArea.setSelectedText(null, false);
351: else
352: textArea.selectNone();
353: }
354: }
355: } finally {
356: if (compoundEdit) {
357: compoundEdit = false;
358: textArea.getBuffer().endCompoundEdit();
359: }
360: }
361:
362: dragSource = null;
363: } //}}}
364:
365: //{{{ canImport
366: public boolean canImport(JComponent c, DataFlavor[] flavors) {
367: JEditTextArea textArea = (JEditTextArea) c;
368:
369: // correctly handle text flavor + file list flavor
370: // + text area read only, do an or of all flags
371: boolean returnValue = false;
372:
373: for (int i = 0; i < flavors.length; i++) {
374: if (flavors[i].equals(DataFlavor.javaFileListFlavor)) {
375: returnValue = true;
376: } else if (flavors[i].equals(DataFlavor.stringFlavor)) {
377: if (textArea.isEditable())
378: returnValue = true;
379: } else if (flavors[i].getRepresentationClass().equals(
380: java.io.InputStream.class)) {
381: //workaround for Ubuntu/Gnome/Nautilus import flavors, otherwise
382: //doesn't work on all Ubuntu installations
383: returnValue = true;
384: break;
385: }
386: }
387:
388: Log
389: .log(Log.DEBUG, this , "canImport() returning "
390: + returnValue);
391: return returnValue;
392: } //}}}
393:
394: //{{{ TextAreaSelection class
395: static class TextAreaSelection extends StringSelection {
396: JEditTextArea textArea;
397:
398: TextAreaSelection(JEditTextArea textArea) {
399: super (textArea.getSelectedText());
400: this .textArea = textArea;
401: }
402: } //}}}
403:
404: //{{{ DraggedURLLoader
405: static class DraggedURLLoader extends WorkRequest {
406: private JEditTextArea textArea;
407: private String url;
408:
409: DraggedURLLoader(JEditTextArea textArea, String url) {
410: this .textArea = textArea;
411: this .url = url;
412: }
413:
414: public void run() {
415: jEdit.openFile(textArea.getView(), url);
416: }
417: } //}}}
418:
419: }
|