KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > antlr > works > ate > ATEGutter


1 /*
2
3 [The "BSD licence"]
4 Copyright (c) 2005 Jean Bovet
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products
17 derived from this software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 */

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 JavaDoc;
41 import java.awt.*;
42 import java.awt.event.MouseAdapter JavaDoc;
43 import java.awt.event.MouseEvent JavaDoc;
44 import java.awt.event.MouseMotionAdapter JavaDoc;
45 import java.util.ArrayList JavaDoc;
46 import java.util.HashSet JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.Set JavaDoc;
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 JavaDoc<BreakpointInfo> breakpoints = new ArrayList JavaDoc<BreakpointInfo>();
67
68     private List JavaDoc<FoldingInfo> foldingInfos = new ArrayList JavaDoc<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         // @todo currently disabled
100
//this.foldingEnabled = foldingEnabled;
101
}
102
103     public void setLineNumberEnabled(boolean lineNumberEnabled) {
104         this.lineNumberEnabled = lineNumberEnabled;
105     }
106
107     public void markDirty() {
108         repaint();
109     }
110
111     public Set JavaDoc<Integer JavaDoc> getBreakpoints() {
112         // Returns a set containing all lines which contains a breakpoint
113
Set JavaDoc<Integer JavaDoc> set = new HashSet JavaDoc<Integer JavaDoc>();
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 JavaDoc e) {
142             return -1;
143         }
144     }
145
146     public void updateInfo(Rectangle clip) {
147
148         /** Make sure we are only updating objects in the current visible range
149          *
150          */

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 JavaDoc<? 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 JavaDoc<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 JavaDoc<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 JavaDoc 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         // Do not alias otherwise the dotted line between collapsed icon doesn't show up really well
279
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 JavaDoc {
373         public void mousePressed(MouseEvent JavaDoc e) {
374             toggleBreakpoint(getBreakpointInfoAtPoint(e.getPoint()));
375             toggleFolding(getFoldingInfoAtPoint(e.getPoint()));
376         }
377
378         public void mouseExited(MouseEvent JavaDoc e) {
379             setCursor(Cursor.getDefaultCursor());
380         }
381     }
382
383     protected class MyMouseMotionAdapter extends MouseMotionAdapter JavaDoc {
384         public void mouseMoved(MouseEvent JavaDoc 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