|
33 | 33 | import org.fife.ui.rsyntaxtextarea.Theme;
|
34 | 34 | import org.fife.ui.rsyntaxtextarea.Token;
|
35 | 35 | import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip;
|
| 36 | +import org.fife.ui.rtextarea.RTextArea; |
36 | 37 | import org.fife.ui.rtextarea.RUndoManager;
|
| 38 | + |
37 | 39 | import processing.app.*;
|
38 | 40 |
|
39 | 41 | import javax.swing.*;
|
| 42 | +import javax.swing.event.EventListenerList; |
40 | 43 | import javax.swing.event.HyperlinkEvent;
|
| 44 | +import javax.swing.event.HyperlinkListener; |
41 | 45 | import javax.swing.text.BadLocationException;
|
42 | 46 | import javax.swing.text.Document;
|
43 | 47 | import javax.swing.text.Segment;
|
44 | 48 | import javax.swing.undo.UndoManager;
|
| 49 | + |
45 | 50 | import java.awt.*;
|
46 | 51 | import java.awt.event.KeyEvent;
|
| 52 | +import java.awt.event.MouseEvent; |
47 | 53 | import java.io.File;
|
48 | 54 | import java.io.FileInputStream;
|
49 | 55 | import java.io.IOException;
|
| 56 | +import java.net.MalformedURLException; |
| 57 | +import java.net.URL; |
50 | 58 | import java.util.HashSet;
|
51 | 59 | import java.util.Map;
|
52 | 60 | import java.util.Set;
|
@@ -228,6 +236,11 @@ protected JPopupMenu createPopupMenu() {
|
228 | 236 | protected void configurePopupMenu(JPopupMenu popupMenu) {
|
229 | 237 | super.configurePopupMenu(popupMenu);
|
230 | 238 | }
|
| 239 | + |
| 240 | + @Override |
| 241 | + protected RTAMouseListener createMouseListener() { |
| 242 | + return new SketchTextAreaMouseListener(this); |
| 243 | + } |
231 | 244 |
|
232 | 245 | public void getTextLine(int line, Segment segment) {
|
233 | 246 | try {
|
@@ -296,5 +309,167 @@ public HyperlinkEvent execute() {
|
296 | 309 | return null;
|
297 | 310 | }
|
298 | 311 | }
|
| 312 | + |
| 313 | + |
| 314 | + /** |
| 315 | + * Handles http hyperlinks. |
| 316 | + * NOTE (@Ricardo JL Rufino): Workaround to enable hyperlinks by default: https://github.com/bobbylight/RSyntaxTextArea/issues/119 |
| 317 | + */ |
| 318 | + private class SketchTextAreaMouseListener extends RTextAreaMutableCaretEvent { |
| 319 | + |
| 320 | + private Insets insets; |
| 321 | + private boolean isScanningForLinks; |
| 322 | + private int hoveredOverLinkOffset = -1; |
| 323 | + |
| 324 | + protected SketchTextAreaMouseListener(RTextArea textArea) { |
| 325 | + super(textArea); |
| 326 | + insets = new Insets(0, 0, 0, 0); |
| 327 | + } |
| 328 | + |
| 329 | + /** |
| 330 | + * Notifies all listeners that have registered interest for notification |
| 331 | + * on this event type. The listener list is processed last to first. |
| 332 | + * |
| 333 | + * @param e The event to fire. |
| 334 | + * @see EventListenerList |
| 335 | + */ |
| 336 | + private void fireHyperlinkUpdate(HyperlinkEvent e) { |
| 337 | + // Guaranteed to return a non-null array |
| 338 | + Object[] listeners = listenerList.getListenerList(); |
| 339 | + // Process the listeners last to first, notifying |
| 340 | + // those that are interested in this event |
| 341 | + for (int i = listeners.length-2; i>=0; i-=2) { |
| 342 | + if (listeners[i]==HyperlinkListener.class) { |
| 343 | + ((HyperlinkListener)listeners[i+1]).hyperlinkUpdate(e); |
| 344 | + } |
| 345 | + } |
| 346 | + } |
| 347 | + |
| 348 | + private HyperlinkEvent createHyperlinkEvent(MouseEvent e) { |
| 349 | + HyperlinkEvent he = null; |
| 350 | + |
| 351 | + Token t = viewToToken(e.getPoint()); |
| 352 | + if (t!=null) { |
| 353 | + // Copy token, viewToModel() unfortunately modifies Token |
| 354 | + t = new TokenImpl(t); |
| 355 | + } |
| 356 | + |
| 357 | + if (t != null && t.isHyperlink()) { |
| 358 | + URL url = null; |
| 359 | + String desc = null; |
| 360 | + try { |
| 361 | + String temp = t.getLexeme(); |
| 362 | + // URI's need "http://" prefix for web URL's to work. |
| 363 | + if (temp.startsWith("www.")) { |
| 364 | + temp = "http://" + temp; |
| 365 | + } |
| 366 | + url = new URL(temp); |
| 367 | + } catch (MalformedURLException mue) { |
| 368 | + desc = mue.getMessage(); |
| 369 | + } |
| 370 | + he = new HyperlinkEvent(SketchTextArea.this, HyperlinkEvent.EventType.ACTIVATED, url, desc); |
| 371 | + } |
| 372 | + |
| 373 | + return he; |
| 374 | + } |
| 375 | + |
| 376 | + @Override |
| 377 | + public void mouseClicked(MouseEvent e) { |
| 378 | + if (getHyperlinksEnabled()) { |
| 379 | + HyperlinkEvent he = createHyperlinkEvent(e); |
| 380 | + if (he!=null) { |
| 381 | + fireHyperlinkUpdate(he); |
| 382 | + } |
| 383 | + } |
| 384 | + } |
| 385 | + |
| 386 | + @Override |
| 387 | + public void mouseMoved(MouseEvent e) { |
| 388 | + |
| 389 | + super.mouseMoved(e); |
| 390 | + |
| 391 | + if (!getHyperlinksEnabled()) { |
| 392 | + return; |
| 393 | + } |
| 394 | + |
| 395 | +// LinkGenerator linkGenerator = getLinkGenerator(); |
| 396 | + |
| 397 | + // GitHub issue RSyntaxTextArea/#25 - links identified at "edges" of editor |
| 398 | + // should not be activated if mouse is in margin insets. |
| 399 | + insets = getInsets(insets); |
| 400 | + if (insets!=null) { |
| 401 | + int x = e.getX(); |
| 402 | + int y = e.getY(); |
| 403 | + if (x<=insets.left || y<insets.top) { |
| 404 | + if (isScanningForLinks) { |
| 405 | + stopScanningForLinks(); |
| 406 | + } |
| 407 | + return; |
| 408 | + } |
| 409 | + } |
| 410 | + |
| 411 | + isScanningForLinks = true; |
| 412 | + Token t = viewToToken(e.getPoint()); |
| 413 | + if (t!=null) { |
| 414 | + // Copy token, viewToModel() unfortunately modifies Token |
| 415 | + t = new TokenImpl(t); |
| 416 | + } |
| 417 | + Cursor c2 = null; |
| 418 | + if (t!=null && t.isHyperlink()) { |
| 419 | + if (hoveredOverLinkOffset==-1 || |
| 420 | + hoveredOverLinkOffset!=t.getOffset()) { |
| 421 | + hoveredOverLinkOffset = t.getOffset(); |
| 422 | + repaint(); |
| 423 | + } |
| 424 | + c2 = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); |
| 425 | + } |
| 426 | +// else if (t!=null && linkGenerator!=null) { |
| 427 | +// int offs = viewToModel(e.getPoint()); |
| 428 | +// LinkGeneratorResult newResult = linkGenerator. |
| 429 | +// isLinkAtOffset(SketchTextArea.this, offs); |
| 430 | +// if (newResult!=null) { |
| 431 | +// // Repaint if we're at a new link now. |
| 432 | +// if (linkGeneratorResult==null || |
| 433 | +// !equal(newResult, linkGeneratorResult)) { |
| 434 | +// repaint(); |
| 435 | +// } |
| 436 | +// linkGeneratorResult = newResult; |
| 437 | +// hoveredOverLinkOffset = t.getOffset(); |
| 438 | +// c2 = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); |
| 439 | +// } |
| 440 | +// else { |
| 441 | +// // Repaint if we've moved off of a link. |
| 442 | +// if (linkGeneratorResult!=null) { |
| 443 | +// repaint(); |
| 444 | +// } |
| 445 | +// c2 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); |
| 446 | +// hoveredOverLinkOffset = -1; |
| 447 | +// linkGeneratorResult = null; |
| 448 | +// } |
| 449 | +// } |
| 450 | + else { |
| 451 | + c2 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); |
| 452 | + hoveredOverLinkOffset = -1; |
| 453 | + // linkGeneratorResult = null; |
| 454 | + } |
| 455 | + if (getCursor()!=c2) { |
| 456 | + setCursor(c2); |
| 457 | + // TODO: Repaint just the affected line(s). |
| 458 | + repaint(); // Link either left or went into. |
| 459 | + } |
| 460 | + } |
| 461 | + |
| 462 | + private void stopScanningForLinks() { |
| 463 | + if (isScanningForLinks) { |
| 464 | + Cursor c = getCursor(); |
| 465 | + isScanningForLinks = false; |
| 466 | + if (c != null && c.getType() == Cursor.HAND_CURSOR) { |
| 467 | + setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); |
| 468 | + repaint(); // TODO: Repaint just the affected line. |
| 469 | + } |
| 470 | + } |
| 471 | + } |
| 472 | + |
| 473 | + } |
299 | 474 |
|
300 | 475 | }
|
0 commit comments