Line 0
Link Here
|
|
|
1 |
/* |
2 |
* Sun Public License Notice |
3 |
* |
4 |
* The contents of this file are subject to the Sun Public License |
5 |
* Version 1.0 (the "License"). You may not use this file except in |
6 |
* compliance with the License. A copy of the License is available at |
7 |
* http://www.sun.com/ |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
13 |
|
14 |
package org.openide.text; |
15 |
|
16 |
import java.awt.*; |
17 |
import java.awt.event.*; |
18 |
import java.awt.font.*; |
19 |
import java.awt.datatransfer.*; |
20 |
import java.awt.dnd.*; |
21 |
import java.beans.*; |
22 |
import java.io.*; |
23 |
import java.net.*; |
24 |
import javax.swing.*; |
25 |
import javax.swing.plaf.*; |
26 |
import javax.swing.text.*; |
27 |
import javax.swing.event.*; |
28 |
import javax.swing.border.Border; |
29 |
import javax.swing.plaf.UIResource; |
30 |
import javax.swing.Timer; |
31 |
|
32 |
import org.openide.windows.TopComponent; |
33 |
|
34 |
|
35 |
/* A transfer handler for the editor component. This seems necessary because |
36 |
* the NetBeans editor doesn't inherit from the Swing plaf.basic package, |
37 |
* so it's missing a bunch of drag & drop behavior. |
38 |
* <p> |
39 |
* This code is basically a merged version of text-related code in |
40 |
* javax.swing.plaf.basic: BasicTextUI, BasicTransferable, ... |
41 |
* I had to copy it since it has package protected access in |
42 |
* javax.swing.plaf.basic. |
43 |
* <p> |
44 |
* <b>There is one important difference</b>. In order to allow the DESTINATION |
45 |
* to decide if the transferable should be moved or copied (e.g. if the |
46 |
* destination is the same document, move, if it's the clipboard palette, |
47 |
* copy), there's a global flag that can be set which basically turns off |
48 |
* moving when a drag is in progress. Yup, this is a bit of a hack, but |
49 |
* I couldn't find a better way. With Swing, the copy-vs-move decision is |
50 |
* made when the drag is -started-, and at that point we don't know yet |
51 |
* where you're going to drop. The docs for TransferHandler exportAsDrag says: |
52 |
* <blockquote> action - the transfer action initially requested; this should |
53 |
* be a value of either <code>COPY</code> or <code>MOVE</code>; |
54 |
* the value may be changed during the course of the drag operation |
55 |
* </blockquote>. |
56 |
* However, it does not say HOW you can change the action, and from looking |
57 |
* at the code, I suspect it cannot be done, since the action is passed |
58 |
* to a gesture listener that has private access. |
59 |
* <p> |
60 |
* @author Tor Norbye |
61 |
*/ |
62 |
|
63 |
public class TextTransferHandler extends TransferHandler implements UIResource { |
64 |
|
65 |
/** Flag which is only defined during a drag & drop operation. |
66 |
* Clients (typically drop zones) can set it to true to indicate |
67 |
* that the data being dragged should be copied, not moved. |
68 |
* For example, the clipboard viewer sets this when it handles |
69 |
* the import. That way, the exportDone method knows not to remove |
70 |
* the text being placed on the clipboard from the document, since |
71 |
* text dragging (without modified keys) defaults to moving, not |
72 |
* copying. And we don't want to disallow copying in getSourceActions, |
73 |
* since dragging text from one place in the document to another |
74 |
* SHOULD be moved, not copied. */ |
75 |
public static boolean dontRemove = false; |
76 |
|
77 |
|
78 |
private JTextComponent exportComp; |
79 |
private boolean shouldRemove; |
80 |
private int p0; |
81 |
private int p1; |
82 |
|
83 |
/** |
84 |
* Try to find a flavor that can be used to import a Transferable. |
85 |
* The set of usable flavors are tried in the following order: |
86 |
* <ol> |
87 |
* <li>First, an attempt is made to find a flavor matching the content type |
88 |
* of the EditorKit for the component. |
89 |
* <li>Second, an attempt to find a text/plain flavor is made. |
90 |
* <li>Third, an attempt to find a flavor representing a String reference |
91 |
* in the same VM is made. |
92 |
* <li>Lastly, DataFlavor.stringFlavor is searched for. |
93 |
* </ol> |
94 |
*/ |
95 |
protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) { |
96 |
DataFlavor plainFlavor = null; |
97 |
DataFlavor refFlavor = null; |
98 |
DataFlavor stringFlavor = null; |
99 |
if (c instanceof JEditorPane) { |
100 |
for (int i = 0; i < flavors.length; i++) { |
101 |
String mime = flavors[i].getMimeType(); |
102 |
if (mime.startsWith(((JEditorPane)c).getEditorKit().getContentType())) { |
103 |
return flavors[i]; |
104 |
} else if (plainFlavor == null && mime.startsWith("text/plain")) { |
105 |
plainFlavor = flavors[i]; |
106 |
} else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") |
107 |
&& flavors[i].getRepresentationClass() == java.lang.String.class) { |
108 |
refFlavor = flavors[i]; |
109 |
} else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) { |
110 |
stringFlavor = flavors[i]; |
111 |
} |
112 |
} |
113 |
if (plainFlavor != null) { |
114 |
return plainFlavor; |
115 |
} else if (refFlavor != null) { |
116 |
return refFlavor; |
117 |
} else if (stringFlavor != null) { |
118 |
return stringFlavor; |
119 |
} |
120 |
return null; |
121 |
} |
122 |
|
123 |
for (int i = 0; i < flavors.length; i++) { |
124 |
String mime = flavors[i].getMimeType(); |
125 |
if (mime.startsWith("text/plain")) { |
126 |
return flavors[i]; |
127 |
} else if (refFlavor == null && mime.startsWith("application/x-java-jvm-local-objectref") |
128 |
&& flavors[i].getRepresentationClass() == java.lang.String.class) { |
129 |
refFlavor = flavors[i]; |
130 |
} else if (stringFlavor == null && flavors[i].equals(DataFlavor.stringFlavor)) { |
131 |
stringFlavor = flavors[i]; |
132 |
} |
133 |
} |
134 |
if (refFlavor != null) { |
135 |
return refFlavor; |
136 |
} else if (stringFlavor != null) { |
137 |
return stringFlavor; |
138 |
} |
139 |
return null; |
140 |
} |
141 |
|
142 |
/** |
143 |
* Import the given stream data into the text component. |
144 |
*/ |
145 |
protected void handleReaderImport(Reader in, JTextComponent c, boolean useRead) |
146 |
throws BadLocationException, IOException { |
147 |
if (useRead) { |
148 |
int startPosition = c.getSelectionStart(); |
149 |
int endPosition = c.getSelectionEnd(); |
150 |
int length = endPosition - startPosition; |
151 |
EditorKit kit = c.getUI().getEditorKit(c); |
152 |
Document doc = c.getDocument(); |
153 |
if (length > 0) { |
154 |
doc.remove(startPosition, length); |
155 |
} |
156 |
kit.read(in, doc, startPosition); |
157 |
} else { |
158 |
char[] buff = new char[1024]; |
159 |
int nch; |
160 |
boolean lastWasCR = false; |
161 |
int last; |
162 |
StringBuffer sbuff = null; |
163 |
|
164 |
// Read in a block at a time, mapping \r\n to \n, as well as single |
165 |
// \r to \n. |
166 |
while ((nch = in.read(buff, 0, buff.length)) != -1) { |
167 |
if (sbuff == null) { |
168 |
sbuff = new StringBuffer(nch); |
169 |
} |
170 |
last = 0; |
171 |
for(int counter = 0; counter < nch; counter++) { |
172 |
switch(buff[counter]) { |
173 |
case '\r': |
174 |
if (lastWasCR) { |
175 |
if (counter == 0) { |
176 |
sbuff.append('\n'); |
177 |
} else { |
178 |
buff[counter - 1] = '\n'; |
179 |
} |
180 |
} else { |
181 |
lastWasCR = true; |
182 |
} |
183 |
break; |
184 |
case '\n': |
185 |
if (lastWasCR) { |
186 |
if (counter > (last + 1)) { |
187 |
sbuff.append(buff, last, counter - last - 1); |
188 |
} |
189 |
// else nothing to do, can skip \r, next write will |
190 |
// write \n |
191 |
lastWasCR = false; |
192 |
last = counter; |
193 |
} |
194 |
break; |
195 |
default: |
196 |
if (lastWasCR) { |
197 |
if (counter == 0) { |
198 |
sbuff.append('\n'); |
199 |
} else { |
200 |
buff[counter - 1] = '\n'; |
201 |
} |
202 |
lastWasCR = false; |
203 |
} |
204 |
break; |
205 |
} |
206 |
} |
207 |
if (last < nch) { |
208 |
if (lastWasCR) { |
209 |
if (last < (nch - 1)) { |
210 |
sbuff.append(buff, last, nch - last - 1); |
211 |
} |
212 |
} else { |
213 |
sbuff.append(buff, last, nch - last); |
214 |
} |
215 |
} |
216 |
} |
217 |
if (lastWasCR) { |
218 |
sbuff.append('\n'); |
219 |
} |
220 |
c.replaceSelection(sbuff != null ? sbuff.toString() : ""); |
221 |
} |
222 |
} |
223 |
|
224 |
// --- TransferHandler methods ------------------------------------ |
225 |
|
226 |
/** |
227 |
* This is the type of transfer actions supported by the source. Some models are |
228 |
* not mutable, so a transfer operation of COPY only should |
229 |
* be advertised in that case. |
230 |
* |
231 |
* @param c The component holding the data to be transfered. This |
232 |
* argument is provided to enable sharing of TransferHandlers by |
233 |
* multiple components. |
234 |
* @return This is implemented to return NONE if the component is a JPasswordField |
235 |
* since exporting data via user gestures is not allowed. If the text component is |
236 |
* editable, COPY_OR_MOVE is returned, otherwise just COPY is allowed. |
237 |
*/ |
238 |
public int getSourceActions(JComponent c) { |
239 |
int actions = NONE; |
240 |
if (! (c instanceof JPasswordField)) { |
241 |
if (((JTextComponent)c).isEditable()) { |
242 |
actions = COPY_OR_MOVE; |
243 |
} else { |
244 |
actions = COPY; |
245 |
} |
246 |
} |
247 |
return actions; |
248 |
} |
249 |
|
250 |
/** |
251 |
* Create a Transferable to use as the source for a data transfer. |
252 |
* |
253 |
* @param comp The component holding the data to be transfered. This |
254 |
* argument is provided to enable sharing of TransferHandlers by |
255 |
* multiple components. |
256 |
* @return The representation of the data to be transfered. |
257 |
* |
258 |
*/ |
259 |
protected Transferable createTransferable(JComponent comp) { |
260 |
exportComp = (JTextComponent)comp; |
261 |
shouldRemove = true; |
262 |
dontRemove = false; |
263 |
p0 = exportComp.getSelectionStart(); |
264 |
p1 = exportComp.getSelectionEnd(); |
265 |
return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null; |
266 |
} |
267 |
|
268 |
/** |
269 |
* This method is called after data has been exported. This |
270 |
* method should remove the data that was transfered if the action |
271 |
* was MOVE. |
272 |
* |
273 |
* @param source The component that was the source of the data. |
274 |
* @param data The data that was transferred or possibly null |
275 |
* if the action is <code>NONE</code>. |
276 |
* @param action The actual action that was performed. |
277 |
*/ |
278 |
protected void exportDone(JComponent source, Transferable data, int action) { |
279 |
// only remove the text if shouldRemove has not been set to |
280 |
// false by importData and only if the action is a move |
281 |
if (shouldRemove && action == MOVE) { |
282 |
TextTransferable t = (TextTransferable)data; |
283 |
if (!dontRemove) { |
284 |
t.removeText(); |
285 |
} |
286 |
} |
287 |
|
288 |
exportComp = null; |
289 |
} |
290 |
|
291 |
/** |
292 |
* This method causes a transfer to a component from a clipboard or a |
293 |
* DND drop operation. The Transferable represents the data to be |
294 |
* imported into the component. |
295 |
* |
296 |
* @param comp The component to receive the transfer. This |
297 |
* argument is provided to enable sharing of TransferHandlers by |
298 |
* multiple components. |
299 |
* @param t The data to import |
300 |
* @return true if the data was inserted into the component, false otherwise. |
301 |
*/ |
302 |
public boolean importData(JComponent comp, Transferable t) { |
303 |
JTextComponent c = (JTextComponent)comp; |
304 |
|
305 |
// if we are importing to the same component that we exported from |
306 |
// then don't actually do anything if the drop location is inside |
307 |
// the drag location and set shouldRemove to false so that exportDone |
308 |
// knows not to remove any data |
309 |
if (c == exportComp && c.getCaretPosition() >= p0 && c.getCaretPosition() <= p1) { |
310 |
shouldRemove = false; |
311 |
return true; |
312 |
} |
313 |
|
314 |
boolean imported = false; |
315 |
DataFlavor importFlavor = getImportFlavor(t.getTransferDataFlavors(), c); |
316 |
if (importFlavor != null) { |
317 |
try { |
318 |
boolean useRead = false; |
319 |
if (comp instanceof JEditorPane) { |
320 |
JEditorPane ep = (JEditorPane)comp; |
321 |
if (!ep.getContentType().startsWith("text/plain") && |
322 |
importFlavor.getMimeType().startsWith(ep.getContentType())) { |
323 |
useRead = true; |
324 |
|
325 |
} |
326 |
|
327 |
try { |
328 |
// XXX Hacking the code clips transfer. |
329 |
Runnable r = (Runnable)t.getTransferData(CodeClipTransferData.CODE_CLIP_DATA_FLAVOR); |
330 |
if(r != null) { |
331 |
r.run(); |
332 |
} |
333 |
} catch(Exception e) { |
334 |
// e.printStackTrace(); |
335 |
} |
336 |
} |
337 |
Reader r = importFlavor.getReaderForText(t); |
338 |
handleReaderImport(r, c, useRead); |
339 |
imported = true; |
340 |
|
341 |
// #4946925 Trying to put the activation to the drop target. |
342 |
TopComponent tc = (TopComponent)SwingUtilities.getAncestorOfClass(TopComponent.class, c); |
343 |
if(tc != null) { |
344 |
tc.requestActive(); |
345 |
} |
346 |
} catch (UnsupportedFlavorException ufe) { |
347 |
} catch (BadLocationException ble) { |
348 |
} catch (IOException ioe) { |
349 |
} |
350 |
} |
351 |
return imported; |
352 |
} |
353 |
|
354 |
/** |
355 |
* This method indicates if a component would accept an import of the given |
356 |
* set of data flavors prior to actually attempting to import it. |
357 |
* |
358 |
* @param comp The component to receive the transfer. This |
359 |
* argument is provided to enable sharing of TransferHandlers by |
360 |
* multiple components. |
361 |
* @param flavors The data formats available |
362 |
* @return true if the data can be inserted into the component, false otherwise. |
363 |
*/ |
364 |
public boolean canImport(JComponent comp, DataFlavor[] flavors) { |
365 |
JTextComponent c = (JTextComponent)comp; |
366 |
if (!(c.isEditable() && c.isEnabled())) { |
367 |
return false; |
368 |
} |
369 |
return (getImportFlavor(flavors, c) != null); |
370 |
} |
371 |
|
372 |
/** |
373 |
* A possible implementation of the Transferable interface |
374 |
* for text components. For a JEditorPane with a rich set |
375 |
* of EditorKit implementations, conversions could be made |
376 |
* giving a wider set of formats. This is implemented to |
377 |
* offer up only the active content type and text/plain |
378 |
* (if that is not the active format) since that can be |
379 |
* extracted from other formats. |
380 |
*/ |
381 |
static class TextTransferable implements Transferable, UIResource { |
382 |
|
383 |
// begin copied from BasicTransferable |
384 |
protected String plainData = null; |
385 |
protected String htmlData = null; |
386 |
|
387 |
private static DataFlavor[] htmlFlavors; |
388 |
private static DataFlavor[] stringFlavors; |
389 |
private static DataFlavor[] plainFlavors; |
390 |
|
391 |
static { |
392 |
try { |
393 |
htmlFlavors = new DataFlavor[3]; |
394 |
htmlFlavors[0] = new DataFlavor("text/html;class=java.lang.String"); |
395 |
htmlFlavors[1] = new DataFlavor("text/html;class=java.io.Reader"); |
396 |
htmlFlavors[2] = new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"); |
397 |
|
398 |
plainFlavors = new DataFlavor[3]; |
399 |
plainFlavors[0] = new DataFlavor("text/plain;class=java.lang.String"); |
400 |
plainFlavors[1] = new DataFlavor("text/plain;class=java.io.Reader"); |
401 |
plainFlavors[2] = new DataFlavor("text/plain;charset=unicode;class=java.io.InputStream"); |
402 |
|
403 |
stringFlavors = new DataFlavor[2]; |
404 |
stringFlavors[0] = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType+";class=java.lang.String"); |
405 |
stringFlavors[1] = DataFlavor.stringFlavor; |
406 |
|
407 |
} catch (ClassNotFoundException cle) { |
408 |
System.err.println("error initializing javax.swing.plaf.basic.BasicTranserable"); |
409 |
} |
410 |
} |
411 |
|
412 |
/** |
413 |
* Returns an array of DataFlavor objects indicating the flavors the data |
414 |
* can be provided in. The array should be ordered according to preference |
415 |
* for providing the data (from most richly descriptive to least descriptive). |
416 |
* @return an array of data flavors in which this data can be transferred |
417 |
*/ |
418 |
public DataFlavor[] getTransferDataFlavors() { |
419 |
DataFlavor[] richerFlavors = getRicherFlavors(); |
420 |
int nRicher = (richerFlavors != null) ? richerFlavors.length : 0; |
421 |
int nHTML = (isHTMLSupported()) ? htmlFlavors.length : 0; |
422 |
int nPlain = (isPlainSupported()) ? plainFlavors.length: 0; |
423 |
int nString = (isPlainSupported()) ? stringFlavors.length : 0; |
424 |
int nFlavors = nRicher + nHTML + nPlain + nString; |
425 |
DataFlavor[] flavors = new DataFlavor[nFlavors]; |
426 |
|
427 |
// fill in the array |
428 |
int nDone = 0; |
429 |
if (nRicher > 0) { |
430 |
System.arraycopy(richerFlavors, 0, flavors, nDone, nRicher); |
431 |
nDone += nRicher; |
432 |
} |
433 |
if (nHTML > 0) { |
434 |
System.arraycopy(htmlFlavors, 0, flavors, nDone, nHTML); |
435 |
nDone += nHTML; |
436 |
} |
437 |
if (nPlain > 0) { |
438 |
System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain); |
439 |
nDone += nPlain; |
440 |
} |
441 |
if (nString > 0) { |
442 |
System.arraycopy(stringFlavors, 0, flavors, nDone, nString); |
443 |
nDone += nString; |
444 |
} |
445 |
return flavors; |
446 |
} |
447 |
|
448 |
/** |
449 |
* Returns whether or not the specified data flavor is supported for |
450 |
* this object. |
451 |
* @param flavor the requested flavor for the data |
452 |
* @return boolean indicating whether or not the data flavor is supported |
453 |
*/ |
454 |
public boolean isDataFlavorSupported(DataFlavor flavor) { |
455 |
DataFlavor[] flavors = getTransferDataFlavors(); |
456 |
for (int i = 0; i < flavors.length; i++) { |
457 |
if (flavors[i].equals(flavor)) { |
458 |
return true; |
459 |
} |
460 |
} |
461 |
return false; |
462 |
} |
463 |
|
464 |
/** |
465 |
* Returns an object which represents the data to be transferred. The class |
466 |
* of the object returned is defined by the representation class of the flavor. |
467 |
* |
468 |
* @param flavor the requested flavor for the data |
469 |
* @see DataFlavor#getRepresentationClass |
470 |
* @exception IOException if the data is no longer available |
471 |
* in the requested flavor. |
472 |
* @exception UnsupportedFlavorException if the requested data flavor is |
473 |
* not supported. |
474 |
*/ |
475 |
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { |
476 |
DataFlavor[] richerFlavors = getRicherFlavors(); |
477 |
if (isRicherFlavor(flavor)) { |
478 |
return getRicherData(flavor); |
479 |
} else if (isHTMLFlavor(flavor)) { |
480 |
String data = getHTMLData(); |
481 |
data = (data == null) ? "" : data; |
482 |
if (String.class.equals(flavor.getRepresentationClass())) { |
483 |
return data; |
484 |
} else if (Reader.class.equals(flavor.getRepresentationClass())) { |
485 |
return new StringReader(data); |
486 |
} else if (InputStream.class.equals(flavor.getRepresentationClass())) { |
487 |
return new StringBufferInputStream(data); |
488 |
} |
489 |
// fall through to unsupported |
490 |
} else if (isPlainFlavor(flavor)) { |
491 |
String data = getPlainData(); |
492 |
data = (data == null) ? "" : data; |
493 |
if (String.class.equals(flavor.getRepresentationClass())) { |
494 |
return data; |
495 |
} else if (Reader.class.equals(flavor.getRepresentationClass())) { |
496 |
return new StringReader(data); |
497 |
} else if (InputStream.class.equals(flavor.getRepresentationClass())) { |
498 |
return new StringBufferInputStream(data); |
499 |
} |
500 |
// fall through to unsupported |
501 |
|
502 |
} else if (isStringFlavor(flavor)) { |
503 |
String data = getPlainData(); |
504 |
data = (data == null) ? "" : data; |
505 |
return data; |
506 |
} |
507 |
throw new UnsupportedFlavorException(flavor); |
508 |
} |
509 |
|
510 |
// --- richer subclass flavors ---------------------------------------------- |
511 |
|
512 |
protected boolean isRicherFlavor(DataFlavor flavor) { |
513 |
DataFlavor[] richerFlavors = getRicherFlavors(); |
514 |
int nFlavors = (richerFlavors != null) ? richerFlavors.length : 0; |
515 |
for (int i = 0; i < nFlavors; i++) { |
516 |
if (richerFlavors[i].equals(flavor)) { |
517 |
return true; |
518 |
} |
519 |
} |
520 |
return false; |
521 |
} |
522 |
|
523 |
// --- html flavors ---------------------------------------------------------- |
524 |
|
525 |
/** |
526 |
* Returns whether or not the specified data flavor is an HTML flavor that |
527 |
* is supported. |
528 |
* @param flavor the requested flavor for the data |
529 |
* @return boolean indicating whether or not the data flavor is supported |
530 |
*/ |
531 |
protected boolean isHTMLFlavor(DataFlavor flavor) { |
532 |
DataFlavor[] flavors = htmlFlavors; |
533 |
for (int i = 0; i < flavors.length; i++) { |
534 |
if (flavors[i].equals(flavor)) { |
535 |
return true; |
536 |
} |
537 |
} |
538 |
return false; |
539 |
} |
540 |
|
541 |
/** |
542 |
* Should the HTML flavors be offered? If so, the method |
543 |
* getHTMLData should be implemented to provide something reasonable. |
544 |
*/ |
545 |
protected boolean isHTMLSupported() { |
546 |
return htmlData != null; |
547 |
} |
548 |
|
549 |
/** |
550 |
* Fetch the data in a text/html format |
551 |
*/ |
552 |
protected String getHTMLData() { |
553 |
return htmlData; |
554 |
} |
555 |
|
556 |
// --- plain text flavors ---------------------------------------------------- |
557 |
|
558 |
/** |
559 |
* Returns whether or not the specified data flavor is an plain flavor that |
560 |
* is supported. |
561 |
* @param flavor the requested flavor for the data |
562 |
* @return boolean indicating whether or not the data flavor is supported |
563 |
*/ |
564 |
protected boolean isPlainFlavor(DataFlavor flavor) { |
565 |
DataFlavor[] flavors = plainFlavors; |
566 |
for (int i = 0; i < flavors.length; i++) { |
567 |
if (flavors[i].equals(flavor)) { |
568 |
return true; |
569 |
} |
570 |
} |
571 |
return false; |
572 |
} |
573 |
|
574 |
/** |
575 |
* Should the plain text flavors be offered? If so, the method |
576 |
* getPlainData should be implemented to provide something reasonable. |
577 |
*/ |
578 |
protected boolean isPlainSupported() { |
579 |
return plainData != null; |
580 |
} |
581 |
|
582 |
/** |
583 |
* Fetch the data in a text/plain format. |
584 |
*/ |
585 |
protected String getPlainData() { |
586 |
return plainData; |
587 |
} |
588 |
|
589 |
// --- string flavorss -------------------------------------------------------- |
590 |
|
591 |
/** |
592 |
* Returns whether or not the specified data flavor is a String flavor that |
593 |
* is supported. |
594 |
* @param flavor the requested flavor for the data |
595 |
* @return boolean indicating whether or not the data flavor is supported |
596 |
*/ |
597 |
protected boolean isStringFlavor(DataFlavor flavor) { |
598 |
DataFlavor[] flavors = stringFlavors; |
599 |
for (int i = 0; i < flavors.length; i++) { |
600 |
if (flavors[i].equals(flavor)) { |
601 |
return true; |
602 |
} |
603 |
} |
604 |
return false; |
605 |
} |
606 |
|
607 |
// end copied from BasicTransferable |
608 |
|
609 |
|
610 |
TextTransferable(JTextComponent c, int start, int end) { |
611 |
this.c = c; |
612 |
|
613 |
Document doc = c.getDocument(); |
614 |
|
615 |
try { |
616 |
p0 = doc.createPosition(start); |
617 |
p1 = doc.createPosition(end); |
618 |
|
619 |
plainData = c.getSelectedText(); |
620 |
|
621 |
if (c instanceof JEditorPane) { |
622 |
JEditorPane ep = (JEditorPane)c; |
623 |
|
624 |
mimeType = ep.getContentType(); |
625 |
|
626 |
if (mimeType.startsWith("text/plain")) { |
627 |
return; |
628 |
} |
629 |
|
630 |
StringWriter sw = new StringWriter(p1.getOffset() - p0.getOffset()); |
631 |
ep.getEditorKit().write(sw, doc, p0.getOffset(), p1.getOffset() - p0.getOffset()); |
632 |
|
633 |
if (mimeType.startsWith("text/html")) { |
634 |
htmlData = sw.toString(); |
635 |
} else { |
636 |
richText = sw.toString(); |
637 |
} |
638 |
} |
639 |
} catch (BadLocationException ble) { |
640 |
} catch (IOException ioe) { |
641 |
} |
642 |
} |
643 |
|
644 |
void removeText() { |
645 |
if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) { |
646 |
try { |
647 |
Document doc = c.getDocument(); |
648 |
doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset()); |
649 |
} catch (BadLocationException e) { |
650 |
} |
651 |
} |
652 |
} |
653 |
|
654 |
// ---- EditorKit other than plain or HTML text ----------------------- |
655 |
|
656 |
/** |
657 |
* If the EditorKit is not for text/plain or text/html, that format |
658 |
* is supported through the "richer flavors" part of BasicTransferable. |
659 |
*/ |
660 |
protected DataFlavor[] getRicherFlavors() { |
661 |
if (richText == null) { |
662 |
return null; |
663 |
} |
664 |
|
665 |
try { |
666 |
DataFlavor[] flavors = new DataFlavor[3]; |
667 |
flavors[0] = new DataFlavor(mimeType + ";class=java.lang.String"); |
668 |
flavors[1] = new DataFlavor(mimeType + ";class=java.io.Reader"); |
669 |
flavors[2] = new DataFlavor(mimeType + ";class=java.io.InputStream;charset=unicode"); |
670 |
return flavors; |
671 |
} catch (ClassNotFoundException cle) { |
672 |
// fall through to unsupported (should not happen) |
673 |
} |
674 |
|
675 |
return null; |
676 |
} |
677 |
|
678 |
/** |
679 |
* The only richer format supported is the file list flavor |
680 |
*/ |
681 |
protected Object getRicherData(DataFlavor flavor) throws UnsupportedFlavorException { |
682 |
if (richText == null) { |
683 |
return null; |
684 |
} |
685 |
|
686 |
if (String.class.equals(flavor.getRepresentationClass())) { |
687 |
return richText; |
688 |
} else if (Reader.class.equals(flavor.getRepresentationClass())) { |
689 |
return new StringReader(richText); |
690 |
} else if (InputStream.class.equals(flavor.getRepresentationClass())) { |
691 |
return new StringBufferInputStream(richText); |
692 |
} |
693 |
throw new UnsupportedFlavorException(flavor); |
694 |
} |
695 |
|
696 |
Position p0; |
697 |
Position p1; |
698 |
String mimeType; |
699 |
String richText; |
700 |
JTextComponent c; |
701 |
} |
702 |
|
703 |
// XXX Hack to enable poping up a dialog when DnD of code clip. |
704 |
public static class CodeClipTransferData extends StringSelection { |
705 |
|
706 |
// XXX Fake DataFlavor.. for cheating to retrieve the callback. |
707 |
public static final DataFlavor CODE_CLIP_DATA_FLAVOR |
708 |
= new DataFlavor(CodeClipTransferData.class, CodeClipTransferData.class.getName()); // TEMP |
709 |
|
710 |
// XXX Callback to provide the popup. |
711 |
private Runnable callback; |
712 |
// XXX We need to manipulate the data in this class (the superclass is private). |
713 |
private String data; |
714 |
|
715 |
|
716 |
public CodeClipTransferData(String data) { |
717 |
super(data); // Just fake. |
718 |
this.data = data; |
719 |
} |
720 |
|
721 |
|
722 |
public void setCallback(Runnable callback) { |
723 |
this.callback = callback; |
724 |
} |
725 |
|
726 |
public void resetData(String data) { |
727 |
this.data = data; |
728 |
} |
729 |
|
730 |
// XXX Overriden to manipulate with our data field and provide the hacking callback. |
731 |
public Object getTransferData(DataFlavor flavor) |
732 |
throws UnsupportedFlavorException, IOException { |
733 |
if(flavor == CODE_CLIP_DATA_FLAVOR) { |
734 |
return callback; |
735 |
} |
736 |
|
737 |
// JCK Test StringSelection0007: if 'flavor' is null, throw NPE |
738 |
if (flavor.equals(DataFlavor.stringFlavor)) { |
739 |
return (Object)data; |
740 |
} else if (flavor.equals(DataFlavor.plainTextFlavor)) { // deprecated |
741 |
return new StringReader(data); |
742 |
} else { |
743 |
throw new UnsupportedFlavorException(flavor); |
744 |
} |
745 |
} |
746 |
|
747 |
} |
748 |
} |