PageRenderTime 124ms CodeModel.GetById 87ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/TextAreaTransferHandler.java

#
Java | 502 lines | 379 code | 54 blank | 69 comment | 65 complexity | 111084fe9d17a1a472eb92b812f139e9 MD5 | raw file
  1/*
  2 * TextAreaTransferHandler.java - Drag and drop support
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2004 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.textarea;
 24
 25//{{{ Imports
 26import org.gjt.sp.jedit.*;
 27import org.gjt.sp.jedit.bufferset.BufferSetManager;
 28import org.gjt.sp.jedit.browser.VFSBrowser;
 29import org.gjt.sp.jedit.io.FileVFS;
 30import org.gjt.sp.jedit.io.VFS;
 31import org.gjt.sp.jedit.io.VFSManager;
 32import org.gjt.sp.util.Log;
 33import org.gjt.sp.util.WorkRequest;
 34
 35import javax.swing.*;
 36import java.awt.datatransfer.DataFlavor;
 37import java.awt.datatransfer.StringSelection;
 38import java.awt.datatransfer.Transferable;
 39import java.io.File;
 40import java.net.URI;
 41import java.util.List;
 42//}}}
 43
 44/**
 45 * @author Slava Pestov
 46 * @version $Id: TextAreaTransferHandler.java 19698 2011-07-23 12:22:01Z shlomy $
 47 */
 48public class TextAreaTransferHandler extends TransferHandler
 49{
 50	/* I assume that there can be only one drag operation at the time */
 51	private static JEditTextArea dragSource;
 52	private static boolean compoundEdit;
 53	private static boolean sameTextArea;
 54	private static int insertPos;
 55	private static int insertOffset;
 56	
 57	// 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.
 58	/*
 59	protected static DataFlavor	textURIlistDataFlavor = null;
 60	
 61	static {
 62		try {
 63			textURIlistDataFlavor = new DataFlavor("text/uri-list;representationclass=java.lang.String");
 64		} catch (ClassNotFoundException e) {
 65			throw new RuntimeException("Cannot create DataFlavor. This should not happen.",e);
 66		}
 67	}
 68	*/
 69
 70	//{{{ createTransferable
 71	@Override
 72	protected Transferable createTransferable(JComponent c)
 73	{
 74		Log.log(Log.DEBUG,this,"createTransferable()");
 75		JEditTextArea textArea = (JEditTextArea)c;
 76		if(textArea.getSelectionCount() == 0)
 77			return null;
 78		else
 79		{
 80			dragSource = textArea;
 81			return new TextAreaSelection(textArea);
 82		}
 83	} //}}}
 84
 85	//{{{ getSourceActions
 86	@Override
 87	public int getSourceActions(JComponent c)
 88	{
 89		return COPY_OR_MOVE;
 90	} //}}}
 91
 92	//{{{ importData
 93	@Override
 94	public boolean importData(JComponent c, Transferable t)
 95	{
 96		Log.log(Log.DEBUG,this,"Import data");
 97//		Log.log(Log.DEBUG,this,"Import data: t.isDataFlavorSupported("+textURIlistDataFlavor+")="+t.isDataFlavorSupported(textURIlistDataFlavor)+".");
 98		if(!canImport(c,t.getTransferDataFlavors()))
 99			return false;
100
101		boolean returnValue;
102
103		try
104		{
105			if(t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
106			{
107				returnValue = importFile(c,t);
108			}
109			else
110			{
111				DataFlavor uriListStringDataFlavor = null;
112				DataFlavor[] dataFlavors = t.getTransferDataFlavors();
113
114				for (DataFlavor dataFlavor : dataFlavors)
115				{
116					if (isUriList(dataFlavor))
117					{
118						uriListStringDataFlavor = dataFlavor;
119						break;
120					}
121				}
122				
123				if (uriListStringDataFlavor != null &&t.isDataFlavorSupported(uriListStringDataFlavor))
124				{
125					returnValue = importURIList(c,t,uriListStringDataFlavor);
126				}
127				else
128				{
129					returnValue = importText(c,t);
130				}
131			}
132		}
133		catch(Exception e)
134		{
135			Log.log(Log.ERROR,this,e);
136			returnValue = false;
137		}
138
139		GUIUtilities.getView(c).toFront();
140		GUIUtilities.getView(c).requestFocus();
141		c.requestFocus();
142
143		return returnValue;
144	} //}}}
145
146	//{{{ importFile
147	private boolean importFile(JComponent c, Transferable t)
148		throws Exception
149	{
150		Log.log(Log.DEBUG,this,"=> File list");
151		EditPane editPane = (EditPane)
152			GUIUtilities.getComponentParent(
153			c,EditPane.class);
154		View view = editPane.getView();
155		Buffer buffer = null;
156
157		// per the Java API, javaFileListFlavor guarantees that a 
158		// List<File> will be returned.  So suppress warning for this
159		// statement.  We know what we're doing.
160		@SuppressWarnings("unchecked")
161		List<File> data = (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor);
162
163		boolean browsedDirectory = false;
164		BufferSetManager bufferSetManager = jEdit.getBufferSetManager();
165		for (File file : data)
166		{
167			if (file.isDirectory())
168			{
169				if (!browsedDirectory)
170				{
171					VFSBrowser.browseDirectory(view, file.getPath());
172					browsedDirectory = true;
173				}
174				continue;
175			}
176			Buffer _buffer = jEdit.openFile(editPane, file.getPath());
177			if (_buffer != null)
178			{
179				buffer = _buffer;
180				bufferSetManager.addBuffer(editPane, buffer);
181			}
182		}
183
184		if(buffer != null)
185			editPane.setBuffer(buffer);
186		view.toFront();
187		view.requestFocus();
188		editPane.requestFocus();
189
190		return true;
191	} //}}}
192
193	//{{{ importURIList
194	private boolean importURIList(JComponent c, Transferable t,DataFlavor uriListStringDataFlavor)
195		throws Exception
196	{
197		String str = (String) t.getTransferData(uriListStringDataFlavor);
198
199		Log.log(Log.DEBUG,this,"=> URIList \""+str+ '\"');
200		EditPane editPane = (EditPane) GUIUtilities.getComponentParent(c, EditPane.class);
201		View view = editPane.getView();
202		JEditTextArea textArea = (JEditTextArea) c;
203		if (dragSource == null)
204		{
205			boolean found = false;
206			String[] components = str.split("\r\n");
207
208			boolean browsedDirectory = false;
209			for (int i = 0;i<components.length;i++)
210			{
211				String str0 = components[i];
212
213				// gnome-commander adds a 0 byte at the end of the file name, discard it
214				int len = str0.length();
215				if (len > 0 && (int)str0.charAt(len - 1) == 0)
216					str0 = str0.substring(0, len - 1);
217				if (str0.length() > 0)
218				{
219					URI uri = new URI(str0); // this handles the URI-decoding
220
221					if ("file".equals(uri.getScheme()))
222					{
223						File file = new File(uri.getPath());
224						if (file.isDirectory())
225						{
226							if (!browsedDirectory)
227							{
228								VFSBrowser.browseDirectory(view, file.getPath());
229								browsedDirectory = true;
230							}
231						}
232						else
233						{
234							VFSManager.runInAWTThread(new DraggedURLLoader(textArea,uri.getPath()));
235						}
236						found = true;
237					}
238					else
239					{
240						Log.log(Log.DEBUG,this,"I do not know how to handle this URI "+uri+", ignoring.");
241					}
242				}
243				else
244				{
245					// This should be the last component, because every URI in the list is terminated with a "\r\n", even the last one.
246					if (i!=components.length-1)
247					{
248						Log.log(Log.DEBUG,this,"Odd: there is an empty line in the uri list which is not the last line.");
249					}
250				}
251			}
252
253			if (found)
254			{
255				return true;
256			}
257		}
258		
259		return true;
260	} //}}}
261	
262	//{{{ importText
263	private boolean importText(JComponent c, Transferable t)
264		throws Exception
265	{
266		String str = (String)t.getTransferData(
267			DataFlavor.stringFlavor);
268		str = str.trim();
269		Log.log(Log.DEBUG,this,"=> String \""+str+ '\"');
270		
271		JEditTextArea textArea = (JEditTextArea)c;
272		if (dragSource == null)
273		{
274			boolean found = false;
275			String[] components = str.split("\n");
276
277			for (String str0 : components)
278			{
279				// Only examine the string for a URL if it came from
280				// outside of jEdit.
281				VFS vfs = VFSManager.getVFSForPath(str0);
282				if (!(vfs instanceof FileVFS) || str.startsWith("file://"))
283				{
284//					str = str.replace('\n',' ').replace('\r',' ').trim();
285					if (str0.startsWith("file://"))
286					{
287						str0 = str0.substring(7);
288					}
289
290					VFSManager.runInWorkThread(new DraggedURLLoader(textArea, str0));
291				}
292				found = true;
293
294			}
295			
296			if (found)
297				return true;
298		}
299
300		if(dragSource != null
301			&& textArea.getBuffer()
302			== dragSource.getBuffer())
303		{
304			compoundEdit = true;
305			textArea.getBuffer().beginCompoundEdit();
306		}
307		
308		
309		sameTextArea = textArea == dragSource;
310
311		int caret = textArea.getCaretPosition();
312		Selection s = textArea.getSelectionAtOffset(caret);
313
314		/* if user drops into the same
315		selection where they started, do
316		nothing. */
317		if(s != null)
318		{
319			if(sameTextArea)
320				return false;
321			/* if user drops into a selection,
322			replace selection */
323			int startPos = s.start;
324			textArea.setSelectedText(s,str);
325			textArea.setSelection(new Selection.Range(startPos,startPos+str.length()));
326		}
327		/* otherwise just insert the text */
328		else
329		{
330			if (sameTextArea)
331			{
332				insertPos = caret;
333				insertOffset = 0;
334				Selection[] selections = textArea.getSelection();
335				for (Selection selection : selections)
336				{
337					if (selection.end < insertPos + insertOffset)
338						insertOffset -= selection.end - selection.start;
339				}
340			}
341			else
342			{
343				textArea.getBuffer().insert(caret,str);
344				textArea.setSelection(new Selection.Range(caret,caret+str.length()));
345			}
346		}
347		textArea.scrollToCaret(true);
348
349		return true;
350	} //}}}
351
352	//{{{ exportDone() method
353	@Override
354	protected void exportDone(JComponent c, Transferable t,
355		int action)
356	{
357		Log.log(Log.DEBUG,this,"Export done");
358
359		JEditTextArea textArea = (JEditTextArea)c;
360
361		try
362		{
363			// This happens if importData returns false. For example if you drop into the Selection
364			if (action == NONE)
365			{
366				Log.log(Log.DEBUG,this,"Export impossible");
367				return;
368			}
369
370			if(t == null)
371			{
372				Log.log(Log.DEBUG,this,"=> Null transferrable");
373				textArea.selectNone();
374			}
375			else if(t.isDataFlavorSupported(
376				DataFlavor.stringFlavor))
377			{
378				Log.log(Log.DEBUG,this,"=> String");
379				if (sameTextArea)
380				{
381					if(action == MOVE)
382					{
383						textArea.setSelectedText(null,false);
384						insertPos += insertOffset;
385					} 
386					try
387					{
388						String str = (String)t.getTransferData(DataFlavor.stringFlavor);
389						textArea.getBuffer().insert(insertPos,str);
390						textArea.setSelection(new Selection.Range(insertPos,insertPos+str.length()));
391					}
392					catch(Exception e)
393					{
394						Log.log(Log.DEBUG,this,"exportDone in sameTextArea");
395						Log.log(Log.DEBUG,this,e);
396					}
397				}
398				else
399				{
400					if(action == MOVE)
401						textArea.setSelectedText(null,false);
402					else
403						textArea.selectNone();
404				}
405			}
406		}
407		finally
408		{
409			if(compoundEdit)
410			{
411				compoundEdit = false;
412				textArea.getBuffer().endCompoundEdit();
413			}
414		}
415
416		dragSource = null;
417	} //}}}
418
419	//{{{ isUriList() method
420	private boolean isUriList(DataFlavor flavor)
421	{
422		return ("text".equals(flavor.getPrimaryType()) &&
423			"uri-list".equals(flavor.getSubType()) &&
424			flavor.getRepresentationClass() == String.class);
425	} //}}}
426
427	//{{{ canImport() methods
428	@Override
429	public boolean canImport(TransferSupport support)
430	{
431		if (dragSource != null)
432			return true;
433		else
434		{
435			support.setDropAction(COPY);
436			return super.canImport(support);
437		}
438	}
439
440	@Override
441	public boolean canImport(JComponent c, DataFlavor[] flavors)
442	{
443		JEditTextArea textArea = (JEditTextArea)c;
444
445		// correctly handle text flavor + file list flavor
446		// + text area read only, do an or of all flags
447		boolean returnValue = false;
448
449		for (DataFlavor flavor : flavors)
450		{
451			if (flavor.equals(DataFlavor.javaFileListFlavor) ||
452				isUriList(flavor))
453			{
454				returnValue = true;
455				break;
456			} else if (flavor.equals(
457				DataFlavor.stringFlavor))
458			{
459				if (textArea.isEditable())
460				{
461					returnValue = true;
462					break;
463				}
464			}
465		}
466
467		Log.log(Log.DEBUG,this,"canImport() returning "
468			+ returnValue);
469		return returnValue;
470	} //}}}
471
472	//{{{ TextAreaSelection class
473	private static class TextAreaSelection extends StringSelection
474	{
475		final JEditTextArea textArea;
476
477		TextAreaSelection(JEditTextArea textArea)
478		{
479			super(textArea.getSelectedText());
480			this.textArea = textArea;
481		}
482	} //}}}
483
484	//{{{ DraggedURLLoader class
485	private static class DraggedURLLoader extends WorkRequest
486	{
487		private final JEditTextArea textArea;
488		private final String url;
489		
490		DraggedURLLoader(JEditTextArea textArea, String url)
491		{
492			this.textArea = textArea;
493			this.url = url;
494		}
495		public void run()
496		{
497			EditPane editPane = EditPane.get(textArea);
498			jEdit.openFile(editPane,url);
499		}
500	} //}}}
501
502}