blob: 29cf53580b7031c428f1460123b531bc27f3c2b8 [file] [log] [blame]
/*
* 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);
}
}