001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui.components;
042:
043: import java.awt.*;
044: import java.awt.datatransfer.Clipboard;
045: import java.awt.datatransfer.DataFlavor;
046: import java.awt.datatransfer.StringSelection;
047: import java.awt.datatransfer.Transferable;
048: import java.awt.event.ActionEvent;
049: import java.awt.event.ActionListener;
050: import java.awt.event.InputEvent;
051: import java.awt.event.MouseEvent;
052: import java.awt.event.MouseListener;
053: import java.io.IOException;
054: import java.io.StringWriter;
055: import java.io.Writer;
056: import java.net.URL;
057: import java.util.Hashtable;
058: import java.util.Map;
059: import java.util.ResourceBundle;
060: import javax.swing.*;
061: import javax.swing.event.HyperlinkEvent;
062: import javax.swing.event.HyperlinkListener;
063: import javax.swing.text.AttributeSet;
064: import javax.swing.text.BadLocationException;
065: import javax.swing.text.Element;
066: import javax.swing.text.StyleConstants;
067: import javax.swing.text.html.HTML;
068: import javax.swing.text.html.HTMLDocument;
069: import javax.swing.text.html.HTMLEditorKit;
070: import javax.swing.text.html.HTMLWriter;
071: import org.netbeans.lib.profiler.ui.UIUtils;
072:
073: /**
074: * @author Ian Formanek
075: * @author Jiri Sedlacek
076: */
077: public class HTMLTextArea extends JEditorPane implements
078: HyperlinkListener, MouseListener {
079: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
080:
081: /** Private Writer that extracts correctly formatted string from HTMLDocument */
082: private class ExtendedHTMLWriter extends HTMLWriter {
083: //~ Constructors ---------------------------------------------------------------------------------------------------------
084:
085: public ExtendedHTMLWriter(Writer w, HTMLDocument doc, int pos,
086: int len) {
087: super (w, doc, pos, len);
088: setLineLength(Integer.MAX_VALUE);
089: }
090:
091: //~ Methods --------------------------------------------------------------------------------------------------------------
092:
093: protected boolean isSupportedBreakFlowTag(AttributeSet attr) {
094: Object o = attr.getAttribute(StyleConstants.NameAttribute);
095:
096: if (o instanceof HTML.Tag) {
097: HTML.Tag tag = (HTML.Tag) o;
098:
099: if ((tag == HTML.Tag.HTML) || (tag == HTML.Tag.HEAD)
100: || (tag == HTML.Tag.BODY)
101: || (tag == HTML.Tag.HR)) {
102: return false;
103: }
104:
105: return (tag).breaksFlow();
106: }
107:
108: return false;
109: }
110:
111: protected void emptyTag(Element elem)
112: throws BadLocationException, IOException {
113: if (isSupportedBreakFlowTag(elem.getAttributes())) {
114: writeLineSeparator();
115: }
116:
117: if (matchNameAttribute(elem.getAttributes(),
118: HTML.Tag.CONTENT)) {
119: text(elem);
120: }
121: }
122:
123: protected void endTag(Element elem) throws IOException {
124: if (isSupportedBreakFlowTag(elem.getAttributes())) {
125: writeLineSeparator();
126: }
127: }
128:
129: protected void startTag(Element elem) throws IOException,
130: BadLocationException {
131: }
132: }
133:
134: // --- Private classes for copy/paste support --------------------------------
135: //
136: // NOTE: only vertical formatting is correctly copy/pasted,
137: // horizontal formatting (ul, li) is ignored.
138:
139: /** Private TransferHandler that copies correctly formatted string from HTMLDocument to system clipboard */
140: private class HTMLTextAreaTransferHandler extends TransferHandler {
141: //~ Methods --------------------------------------------------------------------------------------------------------------
142:
143: public void exportToClipboard(JComponent comp, Clipboard clip,
144: int action) {
145: try {
146: int selStart = getSelectionStart();
147: int selLength = getSelectionEnd() - selStart;
148:
149: StringWriter plainTextWriter = new StringWriter();
150:
151: try {
152: new ExtendedHTMLWriter(plainTextWriter,
153: (HTMLDocument) getDocument(), selStart,
154: selLength).write();
155: } catch (Exception e) {
156: }
157:
158: String plainText = NcrToUnicode.decode(plainTextWriter
159: .toString());
160: clip.setContents(new StringSelection(plainText), null);
161:
162: if (action == TransferHandler.MOVE) {
163: getDocument().remove(selStart, selLength);
164: }
165: } catch (BadLocationException ble) {
166: }
167: }
168: }
169:
170: /** Class for decoding strings from NCR to Unicode */
171: private static class NcrToUnicode {
172: //~ Static fields/initializers -------------------------------------------------------------------------------------------
173:
174: private static Map entities;
175:
176: //~ Methods --------------------------------------------------------------------------------------------------------------
177:
178: public static String decode(String str) {
179: StringBuffer ostr = new StringBuffer();
180: int i1 = 0;
181: int i2 = 0;
182:
183: while (i2 < str.length()) {
184: i1 = str.indexOf("&", i2); //NOI18N
185:
186: if (i1 == -1) {
187: ostr.append(str.substring(i2, str.length()));
188:
189: break;
190: }
191:
192: ostr.append(str.substring(i2, i1));
193: i2 = str.indexOf(";", i1); //NOI18N
194:
195: if (i2 == -1) {
196: ostr.append(str.substring(i1, str.length()));
197:
198: break;
199: }
200:
201: String tok = str.substring(i1 + 1, i2);
202:
203: if (tok.charAt(0) == '#') { //NOI18N
204:
205: if (tok.equals("#160")) { //NOI18N
206: ostr.append((String) getEntities().get("nbsp")); //NOI18N // Fixes Issue 92818, " " is resolved as " " before decoding, so redirecting back to " "
207: } else {
208: tok = tok.substring(1);
209:
210: try {
211: int radix = 10;
212:
213: if (tok.trim().charAt(0) == 'x') { //NOI18N
214: radix = 16;
215: tok = tok.substring(1, tok.length());
216: }
217:
218: ostr.append((char) Integer.parseInt(tok,
219: radix));
220: } catch (NumberFormatException exp) {
221: ostr.append('?'); //NOI18N
222: }
223: }
224: } else {
225: tok = (String) getEntities().get(tok);
226:
227: if (tok != null) {
228: ostr.append(tok);
229: } else {
230: ostr.append('?'); //NOI18N
231: }
232: }
233:
234: i2++;
235: }
236:
237: return ostr.toString();
238: }
239:
240: private static synchronized Map getEntities() {
241: if (entities == null) {
242: entities = new Hashtable();
243: //Quotation mark
244: entities.put("quot", "\""); //NOI18N
245: //Ampersand
246:
247: entities.put("amp", "\u0026"); //NOI18N
248: //Less than
249:
250: entities.put("lt", "\u003C"); //NOI18N
251: //Greater than
252:
253: entities.put("gt", "\u003E"); //NOI18N
254: //Nonbreaking space
255:
256: entities.put("nbsp", "\u0020"); //NOI18N // Fixes Issue 92818, "\u00A0" ( equivalent) is resolved as incorrect character, thus mapping to standard space
257: //Inverted exclamation point
258:
259: entities.put("iexcl", "\u00A1"); //NOI18N
260: //Cent sign
261:
262: entities.put("cent", "\u00A2"); //NOI18N
263: //Pound sign
264:
265: entities.put("pound", "\u00A3"); //NOI18N
266: //General currency sign
267:
268: entities.put("curren", "\u00A4"); //NOI18N
269: //Yen sign
270:
271: entities.put("yen", "\u00A5"); //NOI18N
272: //Broken vertical bar
273:
274: entities.put("brvbar", "\u00A6"); //NOI18N
275: //Section sign
276:
277: entities.put("sect", "\u00A7"); //NOI18N
278: //Umlaut
279:
280: entities.put("uml", "\u00A8"); //NOI18N
281: //Copyright
282:
283: entities.put("copy", "\u00A9"); //NOI18N
284: //Feminine ordinal
285:
286: entities.put("ordf", "\u00AA"); //NOI18N
287: //Left angle quote
288:
289: entities.put("laquo", "\u00AB"); //NOI18N
290: //Not sign
291:
292: entities.put("not", "\u00AC"); //NOI18N
293: //Soft hyphen
294:
295: entities.put("shy", "\u00AD"); //NOI18N
296: //Registered trademark
297:
298: entities.put("reg", "\u00AE"); //NOI18N
299: //Macron accent
300:
301: entities.put("macr", "\u00AF"); //NOI18N
302: //Degree sign
303:
304: entities.put("deg", "\u00B0"); //NOI18N
305: //Plus or minus
306:
307: entities.put("plusmn", "\u00B1"); //NOI18N
308: //Superscript 2
309:
310: entities.put("sup2", "\u00B2"); //NOI18N
311: //Superscript 3
312:
313: entities.put("sup3", "\u00B3"); //NOI18N
314: //Acute accent
315:
316: entities.put("acute", "\u00B4"); //NOI18N
317: //Micro sign (Greek mu)
318:
319: entities.put("micro", "\u00B5"); //NOI18N
320: //Paragraph sign
321:
322: entities.put("para", "\u00B6"); //NOI18N
323: //Middle dot
324:
325: entities.put("middot", "\u00B7"); //NOI18N
326: //Cedilla
327:
328: entities.put("cedil", "\u00B8"); //NOI18N
329: //Superscript 1
330:
331: entities.put("sup1", "\u00B9"); //NOI18N
332: //Masculine ordinal
333:
334: entities.put("ordm", "\u00BA"); //NOI18N
335: //Right angle quote
336:
337: entities.put("raquo", "\u00BB"); //NOI18N
338: //Fraction one-fourth
339:
340: entities.put("frac14", "\u00BC"); //NOI18N
341: //Fraction one-half
342:
343: entities.put("frac12", "\u00BD"); //NOI18N
344: //Fraction three-fourths
345:
346: entities.put("frac34", "\u00BE"); //NOI18N
347: //Inverted question mark
348:
349: entities.put("iquest", "\u00BF"); //NOI18N
350: //Capital A, grave accent
351:
352: entities.put("Agrave", "\u00C0"); //NOI18N
353: //Capital A, acute accent
354:
355: entities.put("Aacute", "\u00C1"); //NOI18N
356: //Capital A, circumflex accent
357:
358: entities.put("Acirc", "\u00C2"); //NOI18N
359: //Capital A, tilde
360:
361: entities.put("Atilde", "\u00C3"); //NOI18N
362: //Capital A, umlaut
363:
364: entities.put("Auml", "\u00C4"); //NOI18N
365: //Capital A, ring
366:
367: entities.put("Aring", "\u00C5"); //NOI18N
368: //Capital AE ligature
369:
370: entities.put("AElig", "\u00C6"); //NOI18N
371: //Capital C, cedilla
372:
373: entities.put("Ccedil", "\u00C7"); //NOI18N
374: //Capital E, grave accent
375:
376: entities.put("Egrave", "\u00C8"); //NOI18N
377: //Capital E, acute accent
378:
379: entities.put("Eacute", "\u00C9"); //NOI18N
380: //Capital E, circumflex accent
381:
382: entities.put("Ecirc", "\u00CA"); //NOI18N
383: //Capital E, umlaut
384:
385: entities.put("Euml", "\u00CB"); //NOI18N
386: //Capital I, grave accent
387:
388: entities.put("Igrave", "\u00CC"); //NOI18N
389: //Capital I, acute accent
390:
391: entities.put("Iacute", "\u00CD"); //NOI18N
392: //Capital I, circumflex accent
393:
394: entities.put("Icirc", "\u00CE"); //NOI18N
395: //Capital I, umlaut
396:
397: entities.put("Iuml", "\u00CF"); //NOI18N
398: //Capital eth, Icelandic
399:
400: entities.put("ETH", "\u00D0"); //NOI18N
401: //Capital N, tilde
402:
403: entities.put("Ntilde", "\u00D1"); //NOI18N
404: //Capital O, grave accent
405:
406: entities.put("Ograve", "\u00D2"); //NOI18N
407: //Capital O, acute accent
408:
409: entities.put("Oacute", "\u00D3"); //NOI18N
410: //Capital O, circumflex accent
411:
412: entities.put("Ocirc", "\u00D4"); //NOI18N
413: //Capital O, tilde
414:
415: entities.put("Otilde", "\u00D5"); //NOI18N
416: //Capital O, umlaut
417:
418: entities.put("Ouml", "\u00D6"); //NOI18N
419: //Multiply sign
420:
421: entities.put("times", "\u00D7"); //NOI18N
422: //Capital O, slash
423:
424: entities.put("Oslash", "\u00D8"); //NOI18N
425: //Capital U, grave accent
426:
427: entities.put("Ugrave", "\u00D9"); //NOI18N
428: //Capital U, acute accent
429:
430: entities.put("Uacute", "\u00DA"); //NOI18N
431: //Capital U, circumflex accent
432:
433: entities.put("Ucirc", "\u00DB"); //NOI18N
434: //Capital U, umlaut
435:
436: entities.put("Uuml", "\u00DC"); //NOI18N
437: //Capital Y, acute accent
438:
439: entities.put("Yacute", "\u00DD"); //NOI18N
440: //Capital thorn, Icelandic
441:
442: entities.put("THORN", "\u00DE"); //NOI18N
443: //Small sz ligature, German
444:
445: entities.put("szlig", "\u00DF"); //NOI18N
446: //Small a, grave accent
447:
448: entities.put("agrave", "\u00E0"); //NOI18N
449: //Small a, acute accent
450:
451: entities.put("aacute", "\u00E1"); //NOI18N
452: //Small a, circumflex accent
453:
454: entities.put("acirc", "\u00E2"); //NOI18N
455: //Small a, tilde
456:
457: entities.put("atilde", "\u00E3"); //NOI18N
458: //Small a, umlaut
459:
460: entities.put("auml", "\u00E4"); //NOI18N
461: //Small a, ring
462:
463: entities.put("aring", "\u00E5"); //NOI18N
464: //Small ae ligature
465:
466: entities.put("aelig", "\u00E6"); //NOI18N
467: //Small c, cedilla
468:
469: entities.put("ccedil", "\u00E7"); //NOI18N
470: //Small e, grave accent
471:
472: entities.put("egrave", "\u00E8"); //NOI18N
473: //Small e, acute accent
474:
475: entities.put("eacute", "\u00E9"); //NOI18N
476: //Small e, circumflex accent
477:
478: entities.put("ecirc", "\u00EA"); //NOI18N
479: //Small e, umlaut
480:
481: entities.put("euml", "\u00EB"); //NOI18N
482: //Small i, grave accent
483:
484: entities.put("igrave", "\u00EC"); //NOI18N
485: //Small i, acute accent
486:
487: entities.put("iacute", "\u00ED"); //NOI18N
488: //Small i, circumflex accent
489:
490: entities.put("icirc", "\u00EE"); //NOI18N
491: //Small i, umlaut
492:
493: entities.put("iuml", "\u00EF"); //NOI18N
494: //Small eth, Icelandic
495:
496: entities.put("eth", "\u00F0"); //NOI18N
497: //Small n, tilde
498:
499: entities.put("ntilde", "\u00F1"); //NOI18N
500: //Small o, grave accent
501:
502: entities.put("ograve", "\u00F2"); //NOI18N
503: //Small o, acute accent
504:
505: entities.put("oacute", "\u00F3"); //NOI18N
506: //Small o, circumflex accent
507:
508: entities.put("ocirc", "\u00F4"); //NOI18N
509: //Small o, tilde
510:
511: entities.put("otilde", "\u00F5"); //NOI18N
512: //Small o, umlaut
513:
514: entities.put("ouml", "\u00F6"); //NOI18N
515: //Division sign
516:
517: entities.put("divide", "\u00F7"); //NOI18N
518: //Small o, slash
519:
520: entities.put("oslash", "\u00F8"); //NOI18N
521: //Small u, grave accent
522:
523: entities.put("ugrave", "\u00F9"); //NOI18N
524: //Small u, acute accent
525:
526: entities.put("uacute", "\u00FA"); //NOI18N
527: //Small u, circumflex accent
528:
529: entities.put("ucirc", "\u00FB"); //NOI18N
530: //Small u, umlaut
531:
532: entities.put("uuml", "\u00FC"); //NOI18N
533: //Small y, acute accent
534:
535: entities.put("yacute", "\u00FD"); //NOI18N
536: //Small thorn, Icelandic
537:
538: entities.put("thorn", "\u00FE"); //NOI18N
539: //Small y, umlaut
540:
541: entities.put("yuml", "\u00FF"); //NOI18N
542: }
543:
544: return entities;
545: }
546: }
547:
548: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
549:
550: // -----
551: // I18N String constants
552: private static final ResourceBundle messages = ResourceBundle
553: .getBundle("org.netbeans.lib.profiler.ui.components.Bundle"); // NOI18N
554: private static final String CUT_STRING = messages
555: .getString("HTMLTextArea_CutString"); // NOI18N
556: private static final String COPY_STRING = messages
557: .getString("HTMLTextArea_CopyString"); // NOI18N
558: private static final String PASTE_STRING = messages
559: .getString("HTMLTextArea_PasteString"); // NOI18N
560: private static final String DELETE_STRING = messages
561: .getString("HTMLTextArea_DeleteString"); // NOI18N
562: private static final String SELECT_ALL_STRING = messages
563: .getString("HTMLTextArea_SelectAllString"); // NOI18N
564: // -----
565:
566: //~ Instance fields ----------------------------------------------------------------------------------------------------------
567:
568: private ActionListener popupListener;
569: private JMenuItem itemCopy;
570: private JMenuItem itemCut;
571: private JMenuItem itemDelete;
572: private JMenuItem itemPaste;
573: private JMenuItem itemSelectAll;
574:
575: // --- Popup menu support ----------------------------------------------------
576: private JPopupMenu popupMenu;
577: private String originalText;
578: private boolean showPopup = true;
579:
580: //~ Constructors -------------------------------------------------------------------------------------------------------------
581:
582: public HTMLTextArea() {
583: setEditorKit(new HTMLEditorKit());
584: setEditable(false);
585: setOpaque(true);
586: setAutoscrolls(true);
587: addHyperlinkListener(this );
588: setTransferHandler(new HTMLTextAreaTransferHandler());
589: setFont(UIManager.getFont("Label.font")); //NOI18N
590: setBackground(UIUtils.getProfilerResultsBackground());
591: addMouseListener(this );
592: }
593:
594: public HTMLTextArea(String text) {
595: this ();
596: setText(text);
597: }
598:
599: //~ Methods ------------------------------------------------------------------------------------------------------------------
600:
601: public void setForeground(Color color) {
602: super .setForeground(color);
603: setText(originalText);
604: }
605:
606: public void setShowPopup(boolean showPopup) {
607: this .showPopup = showPopup;
608: }
609:
610: public boolean getShowPopup() {
611: return showPopup;
612: }
613:
614: public void setText(String value) {
615: if (value == null) {
616: return;
617: }
618:
619: originalText = value;
620:
621: Font font = getFont();
622: Color textColor = getForeground();
623: value = value.replaceAll("\\n\\r|\\r\\n|\\n|\\r", "<br>"); //NOI18N
624: value = value.replaceAll("<code>", "<code style=\"font-size: "
625: + font.getSize() + "pt;\">"); //NOI18N
626:
627: String colorText = "rgb(" + textColor.getRed() + ","
628: + textColor.getGreen() + "," + textColor.getBlue()
629: + ")"; //NOI18N
630: super .setText("<html><body text=\"" + colorText
631: + "\" style=\"font-size: " + font.getSize()
632: + "pt; font-family: " + font.getName() + ";\">" + value
633: + "</body></html>"); //NOI18N
634: }
635:
636: public void deleteSelection() {
637: try {
638: getDocument().remove(getSelectionStart(),
639: getSelectionEnd() - getSelectionStart());
640: } catch (Exception ex) {
641: }
642:
643: ;
644: }
645:
646: public void hyperlinkUpdate(HyperlinkEvent e) {
647: if (!isEnabled()) {
648: return;
649: }
650:
651: if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
652: showURL(e.getURL());
653: } else if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
654: setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
655: } else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
656: setCursor(Cursor.getDefaultCursor());
657: }
658: }
659:
660: public void mouseClicked(MouseEvent e) {
661: if (e.getModifiers() == InputEvent.BUTTON3_MASK) {
662: if (isEnabled() && isFocusable() && showPopup) {
663: JPopupMenu popup = getPopupMenu();
664:
665: if (popup != null) {
666: updatePopupMenu();
667:
668: if (!hasFocus()) {
669: requestFocus(); // required for Select All functionality
670: }
671:
672: popup.show(this , e.getX(), e.getY());
673: }
674: }
675: }
676: }
677:
678: public void mouseEntered(MouseEvent e) {
679: }
680:
681: public void mouseExited(MouseEvent e) {
682: }
683:
684: public void mousePressed(MouseEvent e) {
685: }
686:
687: public void mouseReleased(MouseEvent e) {
688: }
689:
690: public void paste() {
691: try {
692: replaceSelection(Toolkit.getDefaultToolkit()
693: .getSystemClipboard().getContents(this )
694: .getTransferData(DataFlavor.stringFlavor)
695: .toString());
696: } catch (Exception ex) {
697: }
698:
699: ;
700: }
701:
702: protected JPopupMenu getPopupMenu() {
703: if (popupMenu == null) {
704: popupMenu = createPopupMenu();
705: }
706:
707: return popupMenu;
708: }
709:
710: protected JPopupMenu createPopupMenu() {
711: JPopupMenu popup = new JPopupMenu();
712:
713: popupListener = createPopupListener();
714:
715: itemCut = new JMenuItem(CUT_STRING);
716: itemCopy = new JMenuItem(COPY_STRING);
717: itemPaste = new JMenuItem(PASTE_STRING);
718: itemDelete = new JMenuItem(DELETE_STRING);
719: itemSelectAll = new JMenuItem(SELECT_ALL_STRING);
720:
721: itemCut.addActionListener(popupListener);
722: itemCopy.addActionListener(popupListener);
723: itemPaste.addActionListener(popupListener);
724: itemDelete.addActionListener(popupListener);
725: itemSelectAll.addActionListener(popupListener);
726:
727: popup.add(itemCut);
728: popup.add(itemCopy);
729: popup.add(itemPaste);
730: popup.add(itemDelete);
731: popup.addSeparator();
732: popup.add(itemSelectAll);
733:
734: return popup;
735: }
736:
737: protected void showURL(URL url) {
738: // override to react to URL clicks
739: }
740:
741: protected void updatePopupMenu() {
742: // Cut
743: itemCut.setEnabled(isEditable() && (getSelectedText() != null));
744:
745: // Copy
746: itemCopy.setEnabled(getSelectedText() != null);
747:
748: // Paste
749: try {
750: Transferable clipboardContent = Toolkit.getDefaultToolkit()
751: .getSystemClipboard().getContents(this );
752: itemPaste
753: .setEnabled(isEditable()
754: && (clipboardContent != null)
755: && clipboardContent
756: .isDataFlavorSupported(DataFlavor.stringFlavor));
757: } catch (Exception e) {
758: itemPaste.setEnabled(false);
759: }
760:
761: // Delete
762: if (isEditable()) {
763: itemDelete.setVisible(true);
764: itemDelete.setEnabled(getSelectedText() != null);
765: } else {
766: itemDelete.setVisible(false);
767: }
768:
769: // Select All
770: // always visible and enabled...
771: }
772:
773: private ActionListener createPopupListener() {
774: return new ActionListener() {
775: public void actionPerformed(ActionEvent e) {
776: if (e.getSource() == itemCut) {
777: cut();
778: } else if (e.getSource() == itemCopy) {
779: copy();
780: } else if (e.getSource() == itemPaste) {
781: paste();
782: } else if (e.getSource() == itemDelete) {
783: deleteSelection();
784: } else if (e.getSource() == itemSelectAll) {
785: selectAll();
786: }
787: }
788: };
789: }
790: }
|