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