| /* |
| * 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); |
| } |
| } |