Path: blob/master/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/SourceCodePanel.java
41161 views
/*1* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324package sun.jvm.hotspot.ui;2526import java.awt.*;27import java.awt.event.*;28import java.awt.geom.Rectangle2D;29import java.io.*;30import java.net.*;31import java.util.*;32import javax.swing.*;33import javax.swing.text.BadLocationException;3435/** Panel supporting loading of and scrolling through source code.36Contains convenience routines for implementing the Editor37interface. */3839public class SourceCodePanel extends JPanel {40private JTextArea source;41private RowHeader header;42private String filename;43// Amount of white space between edges, line numbers and icons44private static final int LINE_NO_SPACE = 4;45// Size of icons in resources directory46private static final int ICON_SIZE = 12;47// Icons used in panel drawing48private static Icon topFrameCurLine;49private static Icon lowerFrameCurLine;50private static Icon breakpoint;51// State52private int highlightedLine = -1;53private Set<Integer> breakpoints = new HashSet<>(); // Zero-based lines internally54// Parent Editor container and EditorCommands object for setting breakpoints55private EditorCommands comm;56private Editor parent;5758/** Support for displaying icons and line numbers in row header of59scroll pane */60class RowHeader extends JPanel {61private JViewport view;62private boolean showLineNumbers;63private int width;64private int rowHeight;65private boolean initted;6667public RowHeader() {68super();69initted = true;70addHierarchyBoundsListener(new HierarchyBoundsAdapter() {71public void ancestorResized(HierarchyEvent e) {72recomputeSize();73}74});75}7677public void paint(Graphics g) {78super.paint(g);79if (getShowLineNumbers()) {80// Visible region of header panel, in coordinate system of the81// panel, is provided by clip bounds of Graphics object. This82// is used to figure out which line numbers to draw.83Rectangle clip = g.getClipBounds();84// To avoid missing lines, round down starting line number and85// round up ending line number86int start = clip.y / rowHeight;87int end = start + (clip.height + (rowHeight - 1)) / rowHeight;88// Draw these line numbers, right justified to look better89FontMetrics fm = getFontMetrics(getFont());90int ascent = fm.getMaxAscent(); // Causes proper alignment -- trial-and-error91for (int i = start; i <= end; i++) {92// Line numbers are 1-based93String str = Integer.toString(i + 1);94int strWidth = GraphicsUtilities.getStringWidth(str, fm);95g.drawString(str, width - strWidth - LINE_NO_SPACE, ascent + rowHeight * i);9697// Draw breakpoint if necessary98if (breakpoints.contains(i)) {99breakpoint.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i);100}101102// Draw current line icon if necessary103if (i == highlightedLine) {104// FIXME: use correct icon (not always topmost frame)105topFrameCurLine.paintIcon(this, g, LINE_NO_SPACE, rowHeight * i);106}107}108}109}110111public boolean getShowLineNumbers() {112return showLineNumbers;113}114115public void setShowLineNumbers(boolean val) {116if (val != showLineNumbers) {117showLineNumbers = val;118recomputeSize();119// Force re-layout120invalidate();121validate();122}123}124125public void setFont(Font f) {126super.setFont(f);127rowHeight = getFontMetrics(f).getHeight();128recomputeSize();129}130131void setViewport(JViewport view) {132this.view = view;133}134135void recomputeSize() {136if (!initted) return;137if (view == null) return;138width = ICON_SIZE + 2 * LINE_NO_SPACE;139try {140int numLines = 1 + source.getLineOfOffset(source.getDocument().getEndPosition().getOffset() - 1);141String str = Integer.toString(numLines);142if (getShowLineNumbers()) {143// Compute width based on whether we are drawing line numbers144width += 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)148Dimension d = new Dimension(width, numLines * getFontMetrics(getFont()).getHeight());149setSize(d);150setPreferredSize(d);151} catch (BadLocationException e) {152e.printStackTrace();153}154}155}156157public SourceCodePanel() {158maybeLoadIcons();159160// Build user interface161setLayout(new BorderLayout());162source = new JTextArea();163source.setEditable(false);164source.getCaret().setVisible(true);165header = new RowHeader();166header.setShowLineNumbers(true);167JScrollPane scroller = new JScrollPane(source);168JViewport rowView = new JViewport();169rowView.setView(header);170header.setViewport(rowView);171rowView.setScrollMode(JViewport.SIMPLE_SCROLL_MODE);172scroller.setRowHeader(rowView);173add(scroller, BorderLayout.CENTER);174// Reset font now that header and source are present175setFont(getFont());176177source.addFocusListener(new FocusAdapter() {178public void focusGained(FocusEvent e) {179source.getCaret().setVisible(true);180}181});182183source.addKeyListener(new KeyAdapter() {184public void keyPressed(KeyEvent e) {185if (e.getKeyCode() == KeyEvent.VK_F9) {186int lineNo = getCurrentLineNumber();187// Only the debugger can figure out whether we are setting188// or clearing a breakpoint, since it has the debug189// information available and knows whether we're on a190// valid line191comm.toggleBreakpointAtLine(parent, lineNo);192}193}194});195196}197198public void setFont(Font f) {199super.setFont(f);200if (source != null) {201source.setFont(f);202}203if (header != null) {204header.setFont(f);205}206}207208public boolean getShowLineNumbers() {209return header.getShowLineNumbers();210}211212public void setShowLineNumbers(boolean val) {213header.setShowLineNumbers(val);214}215216public boolean openFile(String filename) {217try {218this.filename = filename;219File file = new File(filename);220int len = (int) file.length();221StringBuilder buf = new StringBuilder(len); // Approximation222char[] tmp = new char[4096];223FileReader in = new FileReader(file);224int res = 0;225do {226res = in.read(tmp, 0, tmp.length);227if (res >= 0) {228buf.append(tmp, 0, res);229}230} while (res != -1);231in.close();232String text = buf.toString();233source.setText(text);234header.recomputeSize();235return true;236} catch (IOException e) {237return false;238}239}240241public String getSourceFileName() {242return filename;243}244245/** Line number is one-based */246public int getCurrentLineNumber() {247try {248return 1 + source.getLineOfOffset(source.getCaretPosition());249} catch (BadLocationException e) {250return 0;251}252}253254/** Line number is one-based */255public void showLineNumber(int lineNo) {256try {257int offset = source.getLineStartOffset(lineNo - 1);258Rectangle2D rect2d = source.modelToView2D(offset);259if (rect2d == null) {260return;261}262Rectangle rect = new Rectangle((int) rect2d.getX(), (int) rect2d.getY(),263(int) rect2d.getWidth(), (int) rect2d.getHeight());264source.scrollRectToVisible(rect);265} catch (BadLocationException e) {266e.printStackTrace();267}268}269270/** Line number is one-based */271public void highlightLineNumber(int lineNo) {272highlightedLine = lineNo - 1;273}274275public void showBreakpointAtLine(int lineNo) { breakpoints.add(lineNo - 1); repaint(); }276public boolean hasBreakpointAtLine(int lineNo){ return breakpoints.contains(lineNo - 1); }277public void clearBreakpointAtLine(int lineNo) { breakpoints.remove(lineNo - 1); repaint(); }278public void clearBreakpoints() { breakpoints.clear(); repaint(); }279280public void setEditorCommands(EditorCommands comm, Editor parent) {281this.comm = comm;282this.parent = parent;283}284285public void requestFocus() {286source.requestFocus();287}288289//----------------------------------------------------------------------290// Internals only below this point291//292293private void maybeLoadIcons() {294if (topFrameCurLine == null) {295topFrameCurLine = loadIcon("resources/arrow.png");296lowerFrameCurLine = loadIcon("resources/triangle.png");297breakpoint = loadIcon("resources/breakpoint.png");298}299}300301private Icon loadIcon(String which) {302URL url = getClass().getResource(which);303return new ImageIcon(url);304}305}306307308