/* * Copyright (c) 2001, 2002, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package sun.jvm.hotspot.ui; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; import javax.swing.text.BadLocationException; /** Panel supporting loading of and scrolling through source code. Contains convenience routines for implementing the Editor interface. */ public class SourceCodePanel extends JPanel { private JTextArea source; private RowHeader header; private String filename; // Amount of white space between edges, line numbers and icons private static final int LINE_NO_SPACE = 4; // Size of icons in resources directory private static final int ICON_SIZE = 12; // Icons used in panel drawing private static Icon topFrameCurLine; private static Icon lowerFrameCurLine; private static Icon breakpoint; // State private int highlightedLine = -1; private Set/*<Integer>*/ breakpoints = new HashSet(); // Zero-based lines internally // Parent Editor container and EditorCommands object for setting breakpoints private EditorCommands comm; private Editor parent; /** Support for displaying icons and line numbers in row header of scroll pane */ class RowHeader extends JPanel { private JViewport view; private boolean showLineNumbers; private int width; private int rowHeight; private boolean initted; public RowHeader() { super(); initted = true; addHierarchyBoundsListener(new HierarchyBoundsAdapter() { public void ancestorResized(HierarchyEvent e) { recomputeSize(); } }); } public void paint(Graphics g) { super.paint(g); if (getShowLineNumbers()) { // Visible region of header panel, in coordinate system of the // panel, is provided by clip bounds of Graphics object. This // is used to figure out which line numbers to draw. Rectangle clip = g.getClipBounds(); // To avoid missing lines, round down starting line number and // round up ending line number int start = clip.y / rowHeight; int end = start + (clip.height + (rowHeight - 1)) / rowHeight; // Draw these line numbers, right justified to look better FontMetrics fm = getFontMetrics(getFont()); int ascent = fm.getMaxAscent(); // Causes proper alignment -- trial-and-error for (int i = start; i <= end; i++) { // Line numbers are 1-based String str = Integer.toString(i + 1); int strWidth = GraphicsUtilities.getStringWidth(str, fm); g.drawString(str, width - strWidth - LINE_NO_SPACE, ascent + rowHeight * i); // Draw breakpoint if necessary if (breakpoints.contains(new Integer(i))) { breakpoint.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); } // Draw current line icon if necessary if (i == highlightedLine) { // FIXME: use correct icon (not always topmost frame) topFrameCurLine.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i); } } } } public boolean getShowLineNumbers() { return showLineNumbers; } public void setShowLineNumbers(boolean val) { if (val != showLineNumbers) { showLineNumbers = val; recomputeSize(); // Force re-layout invalidate(); validate(); } } public void setFont(Font f) { super.setFont(f); rowHeight = getFontMetrics(f).getHeight(); recomputeSize(); } void setViewport(JViewport view) { this.view = view; } void recomputeSize() { if (!initted) return; if (view == null) return; width = ICON_SIZE + 2 * LINE_NO_SPACE; try { int numLines = 1 + source.getLineOfOffset(source.getDocument().getEndPosition().getOffset() - 1); String str = Integer.toString(numLines); if (getShowLineNumbers()) { // Compute width based on whether we are drawing line numbers width += GraphicsUtilities.getStringWidth(str, getFontMetrics(getFont())) + LINE_NO_SPACE; } // FIXME: add on width for all icons (breakpoint, current line, // current line in caller frame) Dimension d = new Dimension(width, numLines * getFontMetrics(getFont()).getHeight()); setSize(d); setPreferredSize(d); } catch (BadLocationException e) { e.printStackTrace(); } } } public SourceCodePanel() { maybeLoadIcons(); // Build user interface setLayout(new BorderLayout()); source = new JTextArea(); source.setEditable(false); source.getCaret().setVisible(true); header = new RowHeader(); header.setShowLineNumbers(true); JScrollPane scroller = new JScrollPane(source); JViewport rowView = new JViewport(); rowView.setView(header); header.setViewport(rowView); rowView.setScrollMode(JViewport.SIMPLE_SCROLL_MODE); scroller.setRowHeader(rowView); add(scroller, BorderLayout.CENTER); // Reset font now that header and source are present setFont(getFont()); source.addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { source.getCaret().setVisible(true); } }); source.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_F9) { int lineNo = getCurrentLineNumber(); // Only the debugger can figure out whether we are setting // or clearing a breakpoint, since it has the debug // information available and knows whether we're on a // valid line comm.toggleBreakpointAtLine(parent, lineNo); } } }); } public void setFont(Font f) { super.setFont(f); if (source != null) { source.setFont(f); } if (header != null) { header.setFont(f); } } public boolean getShowLineNumbers() { return header.getShowLineNumbers(); } public void setShowLineNumbers(boolean val) { header.setShowLineNumbers(val); } public boolean openFile(String filename) { try { this.filename = filename; File file = new File(filename); int len = (int) file.length(); StringBuffer buf = new StringBuffer(len); // Approximation char[] tmp = new char[4096]; FileReader in = new FileReader(file); int res = 0; do { res = in.read(tmp, 0, tmp.length); if (res >= 0) { buf.append(tmp, 0, res); } } while (res != -1); in.close(); String text = buf.toString(); source.setText(text); header.recomputeSize(); return true; } catch (IOException e) { return false; } } public String getSourceFileName() { return filename; } /** Line number is one-based */ public int getCurrentLineNumber() { try { return 1 + source.getLineOfOffset(source.getCaretPosition()); } catch (BadLocationException e) { return 0; } } /** Line number is one-based */ public void showLineNumber(int lineNo) { try { int offset = source.getLineStartOffset(lineNo - 1); Rectangle rect = source.modelToView(offset); if (rect == null) { return; } source.scrollRectToVisible(rect); } catch (BadLocationException e) { e.printStackTrace(); } } /** Line number is one-based */ public void highlightLineNumber(int lineNo) { highlightedLine = lineNo - 1; } public void showBreakpointAtLine(int lineNo) { breakpoints.add(new Integer(lineNo - 1)); repaint(); } public boolean hasBreakpointAtLine(int lineNo){ return breakpoints.contains(new Integer(lineNo - 1)); } public void clearBreakpointAtLine(int lineNo) { breakpoints.remove(new Integer(lineNo - 1)); repaint(); } public void clearBreakpoints() { breakpoints.clear(); repaint(); } public void setEditorCommands(EditorCommands comm, Editor parent) { this.comm = comm; this.parent = parent; } public void requestFocus() { source.requestFocus(); } //---------------------------------------------------------------------- // Internals only below this point // private void maybeLoadIcons() { if (topFrameCurLine == null) { topFrameCurLine = loadIcon("resources/arrow.png"); lowerFrameCurLine = loadIcon("resources/triangle.png"); breakpoint = loadIcon("resources/breakpoint.png"); } } private Icon loadIcon(String which) { URL url = getClass().getResource(which); return new ImageIcon(url); } }