1 23 24 package org.gjt.sp.jedit.textarea; 25 26 import java.awt.*; 28 import java.awt.event.*; 29 import javax.swing.*; 30 import javax.swing.border.*; 31 import javax.swing.event.*; 32 import org.gjt.sp.jedit.GUIUtilities; 33 import org.gjt.sp.jedit.buffer.JEditBuffer; 34 import org.gjt.sp.util.Log; 35 37 52 public class Gutter extends JComponent implements SwingConstants 53 { 54 60 public static final int LOWEST_LAYER = Integer.MIN_VALUE; 61 62 67 public static final int DEFAULT_LAYER = 0; 68 69 73 public static final int HIGHEST_LAYER = Integer.MAX_VALUE; 74 76 public Gutter(TextArea textArea) 78 { 79 this.textArea = textArea; 80 81 setAutoscrolls(true); 82 setOpaque(true); 83 setRequestFocusEnabled(false); 84 85 extensionMgr = new ExtensionManager(); 86 87 mouseHandler = new MouseHandler(); 88 addMouseListener(mouseHandler); 89 addMouseMotionListener(mouseHandler); 90 91 updateBorder(); 92 } 94 public void paintComponent(Graphics _gfx) 96 { 97 Graphics2D gfx = (Graphics2D)_gfx; 98 99 Rectangle clip = gfx.getClipBounds(); 101 gfx.setColor(getBackground()); 102 gfx.fillRect(clip.x, clip.y, clip.width, clip.height); 103 104 if (textArea.getBuffer().isLoading()) 106 return; 107 108 int lineHeight = textArea.getPainter().getFontMetrics() 109 .getHeight(); 110 111 if(lineHeight == 0) 112 return; 113 114 int firstLine = clip.y / lineHeight; 115 int lastLine = (clip.y + clip.height - 1) / lineHeight; 116 117 if(lastLine - firstLine > textArea.getVisibleLines()) 118 { 119 Log.log(Log.ERROR,this,"BUG: firstLine=" + firstLine); 120 Log.log(Log.ERROR,this," lastLine=" + lastLine); 121 Log.log(Log.ERROR,this," visibleLines=" + textArea.getVisibleLines()); 122 Log.log(Log.ERROR,this," height=" + getHeight()); 123 Log.log(Log.ERROR,this," painter.height=" + textArea.getPainter().getHeight()); 124 Log.log(Log.ERROR,this," clip.y=" + clip.y); 125 Log.log(Log.ERROR,this," clip.height=" + clip.height); 126 Log.log(Log.ERROR,this," lineHeight=" + lineHeight); 127 } 128 129 int y = clip.y - clip.y % lineHeight; 130 131 extensionMgr.paintScreenLineRange(textArea,gfx, 132 firstLine,lastLine,y,lineHeight); 133 134 for (int line = firstLine; line <= lastLine; 135 line++, y += lineHeight) 136 { 137 paintLine(gfx,line,y); 138 } 139 } 141 148 public void addExtension(TextAreaExtension extension) 149 { 150 extensionMgr.addExtension(DEFAULT_LAYER,extension); 151 repaint(); 152 } 154 163 public void addExtension(int layer, TextAreaExtension extension) 164 { 165 extensionMgr.addExtension(layer,extension); 166 repaint(); 167 } 169 176 public void removeExtension(TextAreaExtension extension) 177 { 178 extensionMgr.removeExtension(extension); 179 repaint(); 180 } 182 188 public TextAreaExtension[] getExtensions() 189 { 190 return extensionMgr.getExtensions(); 191 } 193 198 public String getToolTipText(MouseEvent evt) 199 { 200 if(textArea.getBuffer().isLoading()) 201 return null; 202 203 return extensionMgr.getToolTipText(evt.getX(),evt.getY()); 204 } 206 215 public void setBorder(int width, Color color1, Color color2, Color color3) 216 { 217 borderWidth = width; 218 219 focusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3), 220 new MatteBorder(0,0,0,width,color1)); 221 noFocusBorder = new CompoundBorder(new MatteBorder(0,0,0,width,color3), 222 new MatteBorder(0,0,0,width,color2)); 223 updateBorder(); 224 } 226 230 public void updateBorder() 231 { 232 if (textArea.hasFocus()) 233 setBorder(focusBorder); 234 else 235 setBorder(noFocusBorder); 236 } 238 244 public void setBorder(Border border) 245 { 246 super.setBorder(border); 247 248 if (border == null) 249 { 250 collapsedSize.width = 0; 251 collapsedSize.height = 0; 252 } 253 else 254 { 255 Insets insets = border.getBorderInsets(this); 256 collapsedSize.width = FOLD_MARKER_SIZE + insets.right; 257 collapsedSize.height = gutterSize.height 258 = insets.top + insets.bottom; 259 gutterSize.width = FOLD_MARKER_SIZE + insets.right 260 + fm.stringWidth("12345"); 261 } 262 263 revalidate(); 264 } 266 272 public void setFont(Font font) 273 { 274 super.setFont(font); 275 276 fm = getFontMetrics(font); 277 278 baseline = fm.getAscent(); 279 280 Border border = getBorder(); 281 if(border != null) 282 { 283 gutterSize.width = FOLD_MARKER_SIZE 284 + border.getBorderInsets(this).right 285 + fm.stringWidth("12345"); 286 revalidate(); 287 } 288 } 290 292 297 public Color getHighlightedForeground() 298 { 299 return intervalHighlight; 300 } 302 public void setHighlightedForeground(Color highlight) 304 { 305 intervalHighlight = highlight; 306 } 308 public Color getCurrentLineForeground() 310 { 311 return currentLineHighlight; 312 } 314 public void setCurrentLineForeground(Color highlight) 316 { 317 currentLineHighlight = highlight; 318 } 320 public Color getFoldColor() 322 { 323 return foldColor; 324 } 326 public void setFoldColor(Color foldColor) 328 { 329 this.foldColor = foldColor; 330 } 332 337 public Dimension getPreferredSize() 338 { 339 if (expanded) 340 return gutterSize; 341 else 342 return collapsedSize; 343 } 345 public Dimension getMinimumSize() 347 { 348 return getPreferredSize(); 349 } 351 356 public int getLineNumberAlignment() 357 { 358 return alignment; 359 } 361 366 public void setLineNumberAlignment(int alignment) 367 { 368 if (this.alignment == alignment) return; 369 370 this.alignment = alignment; 371 372 repaint(); 373 } 375 380 public boolean isExpanded() 381 { 382 return expanded; 383 } 385 392 public void setExpanded(boolean expanded) 393 { 394 if (this.expanded == expanded) return; 395 396 this.expanded = expanded; 397 398 textArea.revalidate(); 399 } 401 405 public void toggleExpanded() 406 { 407 setExpanded(!expanded); 408 } 410 416 public int getHighlightInterval() 417 { 418 return interval; 419 } 421 427 public void setHighlightInterval(int interval) 428 { 429 if (interval <= 1) interval = 0; 430 this.interval = interval; 431 repaint(); 432 } 434 public boolean isCurrentLineHighlightEnabled() 436 { 437 return currentLineHighlightEnabled; 438 } 440 public void setCurrentLineHighlightEnabled(boolean enabled) 442 { 443 if (currentLineHighlightEnabled == enabled) return; 444 445 currentLineHighlightEnabled = enabled; 446 447 repaint(); 448 } 450 455 public final Color getStructureHighlightColor() 456 { 457 return structureHighlightColor; 458 } 460 466 public final void setStructureHighlightColor(Color structureHighlightColor) 467 { 468 this.structureHighlightColor = structureHighlightColor; 469 repaint(); 470 } 472 477 public final boolean isStructureHighlightEnabled() 478 { 479 return structureHighlight; 480 } 482 489 public final void setStructureHighlightEnabled(boolean structureHighlight) 490 { 491 this.structureHighlight = structureHighlight; 492 repaint(); 493 } 495 public void setMouseActionsProvider(MouseActionsProvider mouseActionsProvider) 496 { 497 mouseHandler.mouseActions = mouseActionsProvider; 498 } 499 501 503 private static final int FOLD_MARKER_SIZE = 12; 505 506 private final TextArea textArea; 507 private MouseHandler mouseHandler; 508 private ExtensionManager extensionMgr; 509 510 private int baseline; 511 512 private Dimension gutterSize = new Dimension(0,0); 513 private Dimension collapsedSize = new Dimension(0,0); 514 515 private Color intervalHighlight; 516 private Color currentLineHighlight; 517 private Color foldColor; 518 519 private FontMetrics fm; 520 521 private int alignment; 522 523 private int interval; 524 private boolean currentLineHighlightEnabled; 525 private boolean expanded; 526 527 private boolean structureHighlight; 528 private Color structureHighlightColor; 529 530 private int borderWidth; 531 private Border focusBorder, noFocusBorder; 532 534 private void paintLine(Graphics2D gfx, int line, int y) 536 { 537 JEditBuffer buffer = textArea.getBuffer(); 538 if(buffer.isLoading()) 539 return; 540 541 int lineHeight = textArea.getPainter().getFontMetrics() 542 .getHeight(); 543 544 ChunkCache.LineInfo info = textArea.chunkCache.getLineInfo(line); 545 int physicalLine = info.physicalLine; 546 547 if(physicalLine == -1) 549 return; 550 551 if(info.firstSubregion && buffer.isFoldStart(physicalLine)) 553 { 554 int _y = y + lineHeight / 2; 555 gfx.setColor(foldColor); 556 if(textArea.displayManager 557 .isLineVisible(physicalLine + 1)) 558 { 559 gfx.drawLine(1,_y - 3,10,_y - 3); 560 gfx.drawLine(2,_y - 2,9,_y - 2); 561 gfx.drawLine(3,_y - 1,8,_y - 1); 562 gfx.drawLine(4,_y,7,_y); 563 gfx.drawLine(5,_y + 1,6,_y + 1); 564 } 565 else 566 { 567 gfx.drawLine(4,_y - 5,4,_y + 4); 568 gfx.drawLine(5,_y - 4,5,_y + 3); 569 gfx.drawLine(6,_y - 3,6,_y + 2); 570 gfx.drawLine(7,_y - 2,7,_y + 1); 571 gfx.drawLine(8,_y - 1,8,_y); 572 } 573 } 574 else if(info.lastSubregion && buffer.isFoldEnd(physicalLine)) 575 { 576 gfx.setColor(foldColor); 577 int _y = y + lineHeight / 2; 578 gfx.drawLine(4,_y,4,_y + 3); 579 gfx.drawLine(4,_y + 3,7,_y + 3); 580 } else if(structureHighlight) 583 { 584 StructureMatcher.Match match = textArea.getStructureMatch(); 585 int caretLine = textArea.getCaretLine(); 586 587 if(textArea.isStructureHighlightVisible() 588 && physicalLine >= Math.min(caretLine,match.startLine) 589 && physicalLine <= Math.max(caretLine,match.startLine)) 590 { 591 int caretScreenLine; 592 if(caretLine > textArea.getLastPhysicalLine()) 593 caretScreenLine = Integer.MAX_VALUE; 594 else if(textArea.displayManager.isLineVisible( 595 textArea.getCaretLine())) 596 { 597 caretScreenLine = textArea 598 .getScreenLineOfOffset( 599 textArea.getCaretPosition()); 600 } 601 else 602 { 603 caretScreenLine = -1; 604 } 605 606 int structScreenLine; 607 if(match.startLine > textArea.getLastPhysicalLine()) 608 structScreenLine = Integer.MAX_VALUE; 609 else if(textArea.displayManager.isLineVisible( 610 match.startLine)) 611 { 612 structScreenLine = textArea 613 .getScreenLineOfOffset( 614 match.start); 615 } 616 else 617 { 618 structScreenLine = -1; 619 } 620 621 if(caretScreenLine > structScreenLine) 622 { 623 int tmp = caretScreenLine; 624 caretScreenLine = structScreenLine; 625 structScreenLine = tmp; 626 } 627 628 gfx.setColor(structureHighlightColor); 629 if(structScreenLine == caretScreenLine) 630 { 631 } 633 else if(line == caretScreenLine) 635 { 636 gfx.fillRect(5, 637 y 638 + lineHeight / 2, 639 5, 640 2); 641 gfx.fillRect(5, 642 y 643 + lineHeight / 2, 644 2, 645 lineHeight - lineHeight / 2); 646 } 647 else if(line == structScreenLine) 649 { 650 gfx.fillRect(5, 651 y, 652 2, 653 lineHeight / 2); 654 gfx.fillRect(5, 655 y + lineHeight / 2, 656 5, 657 2); 658 } 659 else if(line > caretScreenLine 661 && line < structScreenLine) 662 { 663 gfx.fillRect(5, 664 y, 665 2, 666 lineHeight); 667 } 668 } 669 } 671 if(info.firstSubregion && expanded) 673 { 674 String number = Integer.toString(physicalLine + 1); 675 676 int offset; 677 switch (alignment) 678 { 679 case RIGHT: 680 offset = gutterSize.width - collapsedSize.width 681 - (fm.stringWidth(number) + 1); 682 break; 683 case CENTER: 684 offset = ((gutterSize.width - collapsedSize.width) 685 - fm.stringWidth(number)) / 2; 686 break; 687 case LEFT: default: 688 offset = 0; 689 break; 690 } 691 692 if (physicalLine == textArea.getCaretLine() && currentLineHighlightEnabled) 693 { 694 gfx.setColor(currentLineHighlight); 695 } 696 else if (interval > 1 && (line 697 + textArea.getFirstLine() + 1) 698 % interval == 0) 699 gfx.setColor(intervalHighlight); 700 else 701 gfx.setColor(getForeground()); 702 703 gfx.drawString(number, FOLD_MARKER_SIZE + offset, 704 baseline + y); 705 } } 708 710 class MouseHandler extends MouseInputAdapter 712 { 713 MouseActionsProvider mouseActions; 714 boolean drag; 715 int toolTipInitialDelay, toolTipReshowDelay; 716 717 public void mouseEntered(MouseEvent e) 719 { 720 ToolTipManager ttm = ToolTipManager.sharedInstance(); 721 toolTipInitialDelay = ttm.getInitialDelay(); 722 toolTipReshowDelay = ttm.getReshowDelay(); 723 ttm.setInitialDelay(0); 724 ttm.setReshowDelay(0); 725 } 727 public void mouseExited(MouseEvent evt) 729 { 730 ToolTipManager ttm = ToolTipManager.sharedInstance(); 731 ttm.setInitialDelay(toolTipInitialDelay); 732 ttm.setReshowDelay(toolTipReshowDelay); 733 } 735 public void mousePressed(MouseEvent e) 737 { 738 textArea.requestFocus(); 739 740 if(GUIUtilities.isPopupTrigger(e) 741 || e.getX() >= getWidth() - borderWidth * 2) 742 { 743 e.translatePoint(-getWidth(),0); 744 textArea.mouseHandler.mousePressed(e); 745 drag = true; 746 } 747 else 748 { 749 JEditBuffer buffer = textArea.getBuffer(); 750 751 int screenLine = e.getY() / textArea.getPainter() 752 .getFontMetrics().getHeight(); 753 754 int line = textArea.chunkCache.getLineInfo(screenLine) 755 .physicalLine; 756 757 if(line == -1) 758 return; 759 760 String defaultAction; 762 String variant; 763 if(buffer.isFoldStart(line)) 764 { 765 defaultAction = "toggle-fold"; 766 variant = "fold"; 767 } 768 else if(structureHighlight 769 && textArea.isStructureHighlightVisible() 770 && textArea.lineInStructureScope(line)) 771 { 772 defaultAction = "match-struct"; 773 variant = "struct"; 774 } 775 else 776 return; 777 778 String action = null; 779 780 if (mouseActions != null) 781 action = mouseActions.getActionForEvent( 782 e,variant); 783 784 if(action == null) 785 action = defaultAction; 786 788 StructureMatcher.Match match = textArea 790 .getStructureMatch(); 791 792 if(action.equals("select-fold")) 793 { 794 textArea.displayManager.expandFold(line,true); 795 textArea.selectFold(line); 796 } 797 else if(action.equals("narrow-fold")) 798 { 799 int[] lines = buffer.getFoldAtLine(line); 800 textArea.displayManager.narrow(lines[0],lines[1]); 801 } 802 else if(action.startsWith("toggle-fold")) 803 { 804 if(textArea.displayManager 805 .isLineVisible(line + 1)) 806 { 807 textArea.collapseFold(line); 808 } 809 else 810 { 811 if(action.endsWith("-fully")) 812 { 813 textArea.displayManager 814 .expandFold(line, 815 true); 816 } 817 else 818 { 819 textArea.displayManager 820 .expandFold(line, 821 false); 822 } 823 } 824 } 825 else if(action.equals("match-struct")) 826 { 827 if(match != null) 828 textArea.setCaretPosition(match.end); 829 } 830 else if(action.equals("select-struct")) 831 { 832 if(match != null) 833 { 834 match.matcher.selectMatch( 835 textArea); 836 } 837 } 838 else if(action.equals("narrow-struct")) 839 { 840 if(match != null) 841 { 842 int start = Math.min( 843 match.startLine, 844 textArea.getCaretLine()); 845 int end = Math.max( 846 match.endLine, 847 textArea.getCaretLine()); 848 textArea.displayManager.narrow(start,end); 849 } 850 } } 852 } 854 public void mouseDragged(MouseEvent e) 856 { 857 if(drag ) 858 { 859 e.translatePoint(-getWidth(),0); 860 textArea.mouseHandler.mouseDragged(e); 861 } 862 } 864 public void mouseReleased(MouseEvent e) 866 { 867 if(drag && e.getX() >= getWidth() - borderWidth * 2) 868 { 869 e.translatePoint(-getWidth(),0); 870 textArea.mouseHandler.mouseReleased(e); 871 } 872 873 drag = false; 874 } } } 877 | Popular Tags |