1/* 2 * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25package sun.jvm.hotspot.ui; 26 27import java.awt.*; 28import java.awt.event.*; 29import java.io.*; 30import java.net.*; 31import java.util.*; 32import javax.swing.*; 33import javax.swing.text.BadLocationException; 34 35/** Panel supporting loading of and scrolling through source code. 36 Contains convenience routines for implementing the Editor 37 interface. */ 38 39public class SourceCodePanel extends JPanel { 40 private JTextArea source; 41 private RowHeader header; 42 private String filename; 43 // Amount of white space between edges, line numbers and icons 44 private static final int LINE_NO_SPACE = 4; 45 // Size of icons in resources directory 46 private static final int ICON_SIZE = 12; 47 // Icons used in panel drawing 48 private static Icon topFrameCurLine; 49 private static Icon lowerFrameCurLine; 50 private static Icon breakpoint; 51 // State 52 private int highlightedLine = -1; 53 private Set/*<Integer>*/ breakpoints = new HashSet(); // Zero-based lines internally 54 // Parent Editor container and EditorCommands object for setting breakpoints 55 private EditorCommands comm; 56 private Editor parent; 57 58 /** Support for displaying icons and line numbers in row header of 59 scroll pane */ 60 class RowHeader extends JPanel { 61 private JViewport view; 62 private boolean showLineNumbers; 63 private int width; 64 private int rowHeight; 65 private boolean initted; 66 67 public RowHeader() { 68 super(); 69 initted = true; 70 addHierarchyBoundsListener(new HierarchyBoundsAdapter() { 71 public void ancestorResized(HierarchyEvent e) { 72 recomputeSize(); 73 } 74 }); 75 } 76 77 public void paint(Graphics g) { 78 super.paint(g); 79 if (getShowLineNumbers()) { 80 // Visible region of header panel, in coordinate system of the 81 // panel, is provided by clip bounds of Graphics object. This 82 // is used to figure out which line numbers to draw. 83 Rectangle clip = g.getClipBounds(); 84 // To avoid missing lines, round down starting line number and 85 // round up ending line number 86 int start = clip.y / rowHeight; 87 int end = start + (clip.height + (rowHeight - 1)) / rowHeight; 88 // Draw these line numbers, right justified to look better 89 FontMetrics fm = getFontMetrics(getFont()); 90 int ascent = fm.getMaxAscent(); // Causes proper alignment -- trial-and-error 91 for (int i = start; i <= end; i++) { 92 // Line numbers are 1-based 93 String str = Integer.toString(i + 1); 94 int strWidth = GraphicsUtilities.getStringWidth(str, fm); 95 g.drawString(str, width - strWidth - LINE_NO_SPACE, ascent + rowHeight * i); 96 97 // Draw breakpoint if necessary 98 if (breakpoints.contains(new Integer(i))) { 99 breakpoint.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); 100 } 101 102 // Draw current line icon if necessary 103 if (i == highlightedLine) { 104 // FIXME: use correct icon (not always topmost frame) 105 topFrameCurLine.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); 106 } 107 } 108 } 109 } 110 111 public boolean getShowLineNumbers() { 112 return showLineNumbers; 113 } 114 115 public void setShowLineNumbers(boolean val) { 116 if (val != showLineNumbers) { 117 showLineNumbers = val; 118 recomputeSize(); 119 // Force re-layout 120 invalidate(); 121 validate(); 122 } 123 } 124 125 public void setFont(Font f) { 126 super.setFont(f); 127 rowHeight = getFontMetrics(f).getHeight(); 128 recomputeSize(); 129 } 130 131 void setViewport(JViewport view) { 132 this.view = view; 133 } 134 135 void recomputeSize() { 136 if (!initted) return; 137 if (view == null) return; 138 width = ICON_SIZE + 2 * LINE_NO_SPACE; 139 try { 140 int numLines = 1 + source.getLineOfOffset(source.getDocument().getEndPosition().getOffset() - 1); 141 String str = Integer.toString(numLines); 142 if (getShowLineNumbers()) { 143 // Compute width based on whether we are drawing line numbers 144 width += GraphicsUtilities.getStringWidth(str, getFontMetrics(getFont())) + LINE_NO_SPACE; 145 } 146 // FIXME: add on width for all icons (breakpoint, current line, 147 // current line in caller frame) 148 Dimension d = new Dimension(width, numLines * getFontMetrics(getFont()).getHeight()); 149 setSize(d); 150 setPreferredSize(d); 151 } catch (BadLocationException e) { 152 e.printStackTrace(); 153 } 154 } 155 } 156 157 public SourceCodePanel() { 158 maybeLoadIcons(); 159 160 // Build user interface 161 setLayout(new BorderLayout()); 162 source = new JTextArea(); 163 source.setEditable(false); 164 source.getCaret().setVisible(true); 165 header = new RowHeader(); 166 header.setShowLineNumbers(true); 167 JScrollPane scroller = new JScrollPane(source); 168 JViewport rowView = new JViewport(); 169 rowView.setView(header); 170 header.setViewport(rowView); 171 rowView.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); 172 scroller.setRowHeader(rowView); 173 add(scroller, BorderLayout.CENTER); 174 // Reset font now that header and source are present 175 setFont(getFont()); 176 177 source.addFocusListener(new FocusAdapter() { 178 public void focusGained(FocusEvent e) { 179 source.getCaret().setVisible(true); 180 } 181 }); 182 183 source.addKeyListener(new KeyAdapter() { 184 public void keyPressed(KeyEvent e) { 185 if (e.getKeyCode() == KeyEvent.VK_F9) { 186 int lineNo = getCurrentLineNumber(); 187 // Only the debugger can figure out whether we are setting 188 // or clearing a breakpoint, since it has the debug 189 // information available and knows whether we're on a 190 // valid line 191 comm.toggleBreakpointAtLine(parent, lineNo); 192 } 193 } 194 }); 195 196 } 197 198 public void setFont(Font f) { 199 super.setFont(f); 200 if (source != null) { 201 source.setFont(f); 202 } 203 if (header != null) { 204 header.setFont(f); 205 } 206 } 207 208 public boolean getShowLineNumbers() { 209 return header.getShowLineNumbers(); 210 } 211 212 public void setShowLineNumbers(boolean val) { 213 header.setShowLineNumbers(val); 214 } 215 216 public boolean openFile(String filename) { 217 try { 218 this.filename = filename; 219 File file = new File(filename); 220 int len = (int) file.length(); 221 StringBuffer buf = new StringBuffer(len); // Approximation 222 char[] tmp = new char[4096]; 223 FileReader in = new FileReader(file); 224 int res = 0; 225 do { 226 res = in.read(tmp, 0, tmp.length); 227 if (res >= 0) { 228 buf.append(tmp, 0, res); 229 } 230 } while (res != -1); 231 in.close(); 232 String text = buf.toString(); 233 source.setText(text); 234 header.recomputeSize(); 235 return true; 236 } catch (IOException e) { 237 return false; 238 } 239 } 240 241 public String getSourceFileName() { 242 return filename; 243 } 244 245 /** Line number is one-based */ 246 public int getCurrentLineNumber() { 247 try { 248 return 1 + source.getLineOfOffset(source.getCaretPosition()); 249 } catch (BadLocationException e) { 250 return 0; 251 } 252 } 253 254 /** Line number is one-based */ 255 public void showLineNumber(int lineNo) { 256 try { 257 int offset = source.getLineStartOffset(lineNo - 1); 258 Rectangle rect = source.modelToView(offset); 259 if (rect == null) { 260 return; 261 } 262 source.scrollRectToVisible(rect); 263 } catch (BadLocationException e) { 264 e.printStackTrace(); 265 } 266 } 267 268 /** Line number is one-based */ 269 public void highlightLineNumber(int lineNo) { 270 highlightedLine = lineNo - 1; 271 } 272 273 public void showBreakpointAtLine(int lineNo) { breakpoints.add(new Integer(lineNo - 1)); repaint(); } 274 public boolean hasBreakpointAtLine(int lineNo){ return breakpoints.contains(new Integer(lineNo - 1)); } 275 public void clearBreakpointAtLine(int lineNo) { breakpoints.remove(new Integer(lineNo - 1)); repaint(); } 276 public void clearBreakpoints() { breakpoints.clear(); repaint(); } 277 278 public void setEditorCommands(EditorCommands comm, Editor parent) { 279 this.comm = comm; 280 this.parent = parent; 281 } 282 283 public void requestFocus() { 284 source.requestFocus(); 285 } 286 287 //---------------------------------------------------------------------- 288 // Internals only below this point 289 // 290 291 private void maybeLoadIcons() { 292 if (topFrameCurLine == null) { 293 topFrameCurLine = loadIcon("resources/arrow.png"); 294 lowerFrameCurLine = loadIcon("resources/triangle.png"); 295 breakpoint = loadIcon("resources/breakpoint.png"); 296 } 297 } 298 299 private Icon loadIcon(String which) { 300 URL url = getClass().getResource(which); 301 return new ImageIcon(url); 302 } 303} 304