1 31 32 package org.antlr.works.ate; 33 34 import org.antlr.works.ate.breakpoint.ATEBreakpointEntity; 35 import org.antlr.works.ate.folding.ATEFoldingEntity; 36 import org.antlr.works.ate.syntax.misc.ATELine; 37 import org.antlr.works.utils.IconManager; 38 39 import javax.swing.*; 40 import javax.swing.text.BadLocationException ; 41 import java.awt.*; 42 import java.awt.event.MouseAdapter ; 43 import java.awt.event.MouseEvent ; 44 import java.awt.event.MouseMotionAdapter ; 45 import java.util.ArrayList ; 46 import java.util.HashSet ; 47 import java.util.List ; 48 import java.util.Set ; 49 50 public class ATEGutter extends JComponent { 51 52 private static final int BREAKPOINT_WIDTH = 9; 53 private static final int BREAKPOINT_HEIGHT = 9; 54 private static final int FOLDING_ICON_WIDTH = 9; 55 private static final int FOLDING_ICON_HEIGHT = 9; 56 57 private static final int OFFSET_FROM_TEXT = 2; 58 59 private static final Color BACKGROUND_COLOR = new Color(240,240,240); 60 private static final Stroke FOLDING_DASHED_STROKE = new BasicStroke(0.0f, BasicStroke.CAP_BUTT, 61 BasicStroke.JOIN_MITER, 1.0f, new float[] { 1.0f}, 0.0f); 62 private static final Font LINE_NUMBER_FONT = new Font("Courier", Font.PLAIN, 12); 63 64 private ATEPanel textEditor; 65 66 private List <BreakpointInfo> breakpoints = new ArrayList <BreakpointInfo>(); 67 68 private List <FoldingInfo> foldingInfos = new ArrayList <FoldingInfo>(); 69 private boolean foldingEnabled = false; 70 71 private FontMetrics lineNumberMetrics; 72 private int offsetForLineNumber; 73 private boolean lineNumberEnabled; 74 75 private transient Image collapseDown; 76 private transient Image collapseUp; 77 private transient Image collapse; 78 private transient Image expand; 79 private transient Image delimiter; 80 private transient Image delimiterUp; 81 private transient Image delimiterDown; 82 83 public ATEGutter(ATEPanel textEditor) { 84 this.textEditor = textEditor; 85 86 collapseDown = IconManager.shared().getIconCollapseDown().getImage(); 87 collapseUp = IconManager.shared().getIconCollapseUp().getImage(); 88 collapse = IconManager.shared().getIconCollapse().getImage(); 89 expand = IconManager.shared().getIconExpand().getImage(); 90 delimiter = IconManager.shared().getIconDelimiter().getImage(); 91 delimiterUp = IconManager.shared().getIconDelimiterUp().getImage(); 92 delimiterDown = IconManager.shared().getIconDelimiterDown().getImage(); 93 94 addMouseListener(new MyMouseAdapter()); 95 addMouseMotionListener(new MyMouseMotionAdapter()); 96 } 97 98 public void setFoldingEnabled(boolean foldingEnabled) { 99 } 102 103 public void setLineNumberEnabled(boolean lineNumberEnabled) { 104 this.lineNumberEnabled = lineNumberEnabled; 105 } 106 107 public void markDirty() { 108 repaint(); 109 } 110 111 public Set <Integer > getBreakpoints() { 112 Set <Integer > set = new HashSet <Integer >(); 114 for (BreakpointInfo info : breakpoints) { 115 if (info.entity.breakpointEntityIsBreakpoint()) 116 set.add(info.entity.breakpointEntityLine()); 117 } 118 return set; 119 } 120 121 protected void toggleBreakpoint(BreakpointInfo info) { 122 if(info == null) 123 return; 124 125 info.entity.breakpointEntitySetBreakpoint(!info.entity.breakpointEntityIsBreakpoint()); 126 repaint(); 127 } 128 129 protected void toggleFolding(FoldingInfo info) { 130 if(info == null || !info.entity.foldingEntityCanBeCollapsed()) 131 return; 132 133 textEditor.foldingManager.toggleFolding(info.entity); 134 markDirty(); 135 } 136 137 protected int getLineYPixelPosition(int indexInText) { 138 try { 139 Rectangle r = textEditor.textPane.modelToView(indexInText); 140 return r.y + r.height / 2; 141 } catch (BadLocationException e) { 142 return -1; 143 } 144 } 145 146 public void updateInfo(Rectangle clip) { 147 148 151 152 int startIndex = textEditor.textPane.viewToModel(new Point(clip.x, clip.y)); 153 int endIndex = textEditor.textPane.viewToModel(new Point(clip.x+clip.width, clip.y+clip.height)); 154 155 breakpoints.clear(); 156 if(textEditor.breakpointManager != null) { 157 List <? extends ATEBreakpointEntity> entities = textEditor.breakpointManager.getBreakpointEntities(); 158 for (ATEBreakpointEntity entity : entities) { 159 int index = entity.breakpointEntityIndex(); 160 if (index >= startIndex && index <= endIndex) { 161 int y = getLineYPixelPosition(entity.breakpointEntityIndex()); 162 Rectangle r = new Rectangle(offsetForLineNumber, y - BREAKPOINT_HEIGHT / 2, BREAKPOINT_WIDTH, BREAKPOINT_HEIGHT); 163 breakpoints.add(new BreakpointInfo(entity, r)); 164 } 165 } 166 } 167 168 foldingInfos.clear(); 169 if(textEditor.foldingManager != null) { 170 List <ATEFoldingEntity> entities = textEditor.foldingManager.getFoldingEntities(); 171 for (ATEFoldingEntity entity : entities) { 172 int entityStartIndex = entity.foldingEntityGetStartIndex(); 173 int entityEndIndex = entity.foldingEntityGetEndIndex(); 174 if (!(entityStartIndex > endIndex || entityEndIndex < startIndex)) { 175 int top_y = getLineYPixelPosition(entity.foldingEntityGetStartIndex()); 176 int bottom_y = getLineYPixelPosition(entity.foldingEntityGetEndIndex()); 177 178 Point top = new Point(getWidth() - getOffsetFromText(), top_y); 179 Point bottom = new Point(getWidth() - getOffsetFromText(), bottom_y); 180 foldingInfos.add(new FoldingInfo(entity, top, bottom)); 181 } 182 } 183 } 184 185 } 186 187 public void updateSize() { 188 if(lineNumberMetrics == null) { 189 lineNumberMetrics = textEditor.textPane.getFontMetrics(LINE_NUMBER_FONT); 190 } 191 192 offsetForLineNumber = 0; 193 if(lineNumberEnabled) { 194 List <ATELine> lines = textEditor.getLines(); 195 if(lines != null) { 196 offsetForLineNumber = lineNumberMetrics.stringWidth(String.valueOf(lines.size())); 197 } 198 } 199 } 200 201 public BreakpointInfo getBreakpointInfoAtPoint(Point p) { 202 for (BreakpointInfo info : breakpoints) { 203 if (info.contains(p)) 204 return info; 205 } 206 return null; 207 } 208 209 public FoldingInfo getFoldingInfoAtPoint(Point p) { 210 for (FoldingInfo info : foldingInfos) { 211 if (info.contains(p)) 212 return info; 213 } 214 return null; 215 } 216 217 public int getOffsetFromText() { 218 return OFFSET_FROM_TEXT+FOLDING_ICON_WIDTH/2; 219 } 220 221 public void paintComponent(Graphics g) { 222 Rectangle r = g.getClipBounds(); 223 224 updateInfo(r); 225 updateSize(); 226 227 paintGutter(g, r); 228 paintFolding((Graphics2D)g, r); 229 paintBreakpoints((Graphics2D)g, r); 230 231 if(lineNumberEnabled) { 232 paintLineNumbers((Graphics2D)g, r); 233 } 234 } 235 236 private void paintGutter(Graphics g, Rectangle clip) { 237 g.setColor(textEditor.textPane.getBackground()); 238 g.fillRect(clip.x+clip.width-getOffsetFromText(), clip.y, getOffsetFromText(), clip.height); 239 240 g.setColor(BACKGROUND_COLOR); 241 g.fillRect(clip.x, clip.y, clip.width-getOffsetFromText(), clip.height); 242 243 g.setColor(Color.lightGray); 244 g.drawLine(clip.x+clip.width-getOffsetFromText(), clip.y, clip.x+clip.width-getOffsetFromText(), clip.y+clip.height); 245 } 246 247 private void paintBreakpoints(Graphics2D g, Rectangle clip) { 248 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); 249 250 g.setColor(Color.red); 251 for (BreakpointInfo info : breakpoints) { 252 if (info.entity.breakpointEntityIsBreakpoint()) { 253 Rectangle r = info.r; 254 if (clip.intersects(r)) { 255 g.fillArc(r.x, r.y, r.width, r.height, 0, 360); 256 } 257 } 258 } 259 } 260 261 private void paintLineNumbers(Graphics2D g, Rectangle clip) { 262 g.setColor(Color.black); 263 g.setFont(LINE_NUMBER_FONT); 264 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); 265 266 int lineCount = textEditor.getLines().size(); 267 int lineHeight = textEditor.textPane.getFontMetrics(textEditor.textPane.getFont()).getHeight(); 268 int number = Math.max(0, (Math.round(clip.y / lineHeight) - 1)); 269 int y = number*lineHeight; 270 while(number <= lineCount && y-lineHeight <= clip.getY()+clip.getHeight()) { 271 String s = String.valueOf(number++); 272 g.drawString(s, offsetForLineNumber-lineNumberMetrics.stringWidth(s), y - 4); 273 y += lineHeight; 274 } 275 } 276 277 private void paintFolding(Graphics2D g, Rectangle clip) { 278 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF); 280 281 for (FoldingInfo info : foldingInfos) { 282 if (!clip.intersects(info.top_r) && !clip.intersects(info.bottom_r)) continue; 283 284 Point top = info.top; 285 Point bottom = info.bottom; 286 if (foldingEnabled && info.entity.foldingEntityCanBeCollapsed()) { 287 if (info.entity.foldingEntityIsExpanded()) { 288 drawFoldingLine(g, top, bottom); 289 290 if (top.equals(bottom)) { 291 drawCenteredImageAtPoint(g, collapse, top); 292 } else { 293 drawCenteredImageAtPoint(g, collapseUp, top); 294 drawCenteredImageAtPoint(g, collapseDown, bottom); 295 } 296 } else { 297 drawCenteredImageAtPoint(g, expand, top); 298 } 299 } else { 300 drawFoldingLine(g, top, bottom); 301 302 if (top.equals(bottom)) { 303 drawCenteredImageAtPoint(g, delimiter, top); 304 } else { 305 drawCenteredImageAtPoint(g, delimiterUp, top); 306 drawCenteredImageAtPoint(g, delimiterDown, bottom); 307 } 308 } 309 } 310 } 311 312 private void drawFoldingLine(Graphics2D g, Point top, Point bottom) { 313 g.setColor(Color.white); 314 g.drawLine(top.x, top.y, bottom.x, bottom.y); 315 316 Stroke s = g.getStroke(); 317 g.setStroke(FOLDING_DASHED_STROKE); 318 g.setColor(Color.black); 319 g.drawLine(top.x, top.y, bottom.x, bottom.y); 320 g.setStroke(s); 321 } 322 323 public Dimension getPreferredSize() { 324 Dimension d = textEditor.textPane.getSize(); 325 d.width = 25+offsetForLineNumber; 326 return d; 327 } 328 329 public static void drawCenteredImageAtPoint(Graphics g, Image image, Point p) { 330 g.drawImage(image, p.x-image.getWidth(null)/2, p.y-image.getHeight(null)/2, null); 331 } 332 333 protected static class BreakpointInfo { 334 public ATEBreakpointEntity entity; 335 public Rectangle r; 336 337 public BreakpointInfo(ATEBreakpointEntity entity, Rectangle r) { 338 this.entity = entity; 339 this.r = r; 340 } 341 342 public boolean contains(Point p) { 343 return r.contains(p); 344 } 345 } 346 347 protected static class FoldingInfo { 348 public ATEFoldingEntity entity; 349 350 public Point top; 351 public Point bottom; 352 353 public Rectangle top_r; 354 public Rectangle bottom_r; 355 356 public FoldingInfo(ATEFoldingEntity entity, Point top, Point bottom) { 357 this.entity = entity; 358 this.top = top; 359 this.bottom = bottom; 360 this.top_r = new Rectangle(top.x-FOLDING_ICON_WIDTH/2, top.y-FOLDING_ICON_HEIGHT/2, FOLDING_ICON_WIDTH, FOLDING_ICON_HEIGHT); 361 this.bottom_r = new Rectangle(bottom.x-FOLDING_ICON_WIDTH/2, bottom.y-FOLDING_ICON_HEIGHT/2, FOLDING_ICON_WIDTH, FOLDING_ICON_HEIGHT); 362 } 363 364 public boolean contains(Point p) { 365 if(entity.foldingEntityIsExpanded()) 366 return top_r.contains(p) || bottom_r.contains(p); 367 else 368 return top_r.contains(p); 369 } 370 } 371 372 protected class MyMouseAdapter extends MouseAdapter { 373 public void mousePressed(MouseEvent e) { 374 toggleBreakpoint(getBreakpointInfoAtPoint(e.getPoint())); 375 toggleFolding(getFoldingInfoAtPoint(e.getPoint())); 376 } 377 378 public void mouseExited(MouseEvent e) { 379 setCursor(Cursor.getDefaultCursor()); 380 } 381 } 382 383 protected class MyMouseMotionAdapter extends MouseMotionAdapter { 384 public void mouseMoved(MouseEvent e) { 385 if(!foldingEnabled) 386 return; 387 388 FoldingInfo info = getFoldingInfoAtPoint(e.getPoint()); 389 if(info != null && info.entity.foldingEntityCanBeCollapsed()) 390 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 391 else 392 setCursor(Cursor.getDefaultCursor()); 393 } 394 } 395 } 396 | Popular Tags |