All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.swingset3.codeview.CodeViewer Maven / Gradle / Ivy

There is a newer version: 2.6.5
Show newest version
/*
 * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.sun.swingset3.codeview;

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.Highlighter;
import com.sun.swingset3.utilities.RoundedBorder;
import com.sun.swingset3.utilities.RoundedPanel;
import com.sun.swingset3.utilities.Utilities;

/**
 * GUI component for viewing a set of one or more Java source code files,
 * providing the user with the ability to easily highlight specific code fragments.
 * A tabbedpane is used to control which source code file is shown within the set
 * if more than one file is loaded.
 * 

* Example usage: *


 *    CodeViewer codeViewer = new CodeViewer();
 *    codeViewer.setSourceFiles(mySourceURLs);
 *    frame.add(codeViewer);
 * 
* *

* When loading the source code, this viewer will automatically parse the files for * any source fragments which are marked with "snippet" start/end tags that * are embedded within Java comments. The viewer will allow the user to highlight * these code snippets for easier inspection of specific code. *

* The text following immediately after the start tag will be used * as the key for that snippet. Multiple snippets may share the same * key, defining a "snippet set". Snippet sets may even span across * multiple source files. * The key for each snippet set is displayed in a combobox to allow the user to * select which snippet set should be highlighted. For example:

*


 *    ArrayList dogs = new ArrayList();
 *
 *    [other code...]
 *
 *    dogs.add("Labrador");
 *    dogs.add("Golden Retriever");
 *    dogs.add("Australian Shepherd");
 * 
* The above code would create a snippet set (containing 2 snippets) with the key * "Create dog array". *

* The viewer will allow the user to easily navigate across the currently highlighted * snippet set by pressing the navigation buttons or using accelerator keys. * * @author aim */ public class CodeViewer extends JPanel { public static final String SOURCES_JAVA = ".+\\.java"; public static final String SOURCES_TEXT = ".+\\.properties|.+\\.txt|.+\\.html|.+\\.xml"; public static final String SOURCES_IMAGES = ".+\\.jpg|.+\\.gif|.+\\.png"; private static final Color DEFAULT_HIGHLIGHT_COLOR = new Color(255,255,176); private static BufferedImage SNIPPET_GLYPH; private static String NO_SNIPPET_SELECTED; static final Logger logger = Logger.getLogger(CodeViewer.class.getName()); static { try { URL imageURL = CodeViewer.class.getResource("resources/images/snippetglyph.png"); SNIPPET_GLYPH = ImageIO.read(imageURL); } catch (Exception e) { System.err.println(e); } } // Cache all processed code files in case they are reloaded later private final Map codeCache = new HashMap(); private JComponent codeHighlightBar; private JComboBox snippetComboBox; private JComponent codePanel; private JLabel noCodeLabel; private JTabbedPane codeTabbedPane; private Color highlightColor; private Highlighter.HighlightPainter snippetPainter; private ResourceBundle bundle; // Current code file set private Map currentCodeFilesInfo; private List additionalSourceFiles; // Map of all snippets in current code file set private final SnippetMap snippetMap = new SnippetMap(); private Action firstSnippetAction; private Action nextSnippetAction; private Action previousSnippetAction; private Action lastSnippetAction; /** * Creates a new instance of CodeViewer */ public CodeViewer() { setHighlightColor(DEFAULT_HIGHLIGHT_COLOR); initActions(); setLayout(new BorderLayout()); codeHighlightBar = createCodeHighlightBar(); codeHighlightBar.setVisible(false); add(codeHighlightBar, BorderLayout.NORTH); codePanel = createCodePanel(); add(codePanel, BorderLayout.CENTER); applyDefaults(); } protected JComponent createCodeHighlightBar() { GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); JPanel bar = new JPanel(gridbag); bar.setBorder(new EmptyBorder(0, 0, 10, 0)); NO_SNIPPET_SELECTED = getString("CodeViewer.snippets.selectOne", "Select One"); JLabel snippetSetsLabel = new JLabel(getString("CodeViewer.snippets.highlightCode", "Highlight code to: ")); c.gridx = 0; c.gridy = 0; c.anchor = GridBagConstraints.WEST; c.weightx = 0; gridbag.addLayoutComponent(snippetSetsLabel, c); bar.add(snippetSetsLabel); snippetComboBox = new JComboBox(); snippetComboBox.setMaximumRowCount(20); snippetComboBox.setRenderer(new SnippetCellRenderer(snippetComboBox.getRenderer())); snippetComboBox.addActionListener(new SnippetActivator()); snippetSetsLabel.setLabelFor(snippetComboBox); c.gridx++; c.weightx = 1; gridbag.addLayoutComponent(snippetComboBox, c); bar.add(snippetComboBox); SnippetNavigator snippetNavigator = new SnippetNavigator(snippetMap); snippetNavigator.setNavigateNextAction(nextSnippetAction); snippetNavigator.setNavigatePreviousAction(previousSnippetAction); c.gridx++; c.anchor = GridBagConstraints.EAST; c.weightx = 0; gridbag.addLayoutComponent(snippetNavigator, c); bar.add(snippetNavigator); return bar; } protected JComponent createCodePanel() { JPanel panel = new RoundedPanel(new BorderLayout(), 10); panel.setBorder(new RoundedBorder(10)); noCodeLabel = new JLabel(getString("CodeViewer.noCodeLoaded", "no code loaded")); noCodeLabel.setHorizontalAlignment(JLabel.CENTER); panel.add(noCodeLabel, BorderLayout.CENTER); return panel; } @Override public void updateUI() { super.updateUI(); applyDefaults(); } protected void applyDefaults() { if (noCodeLabel != null) { noCodeLabel.setOpaque(false); noCodeLabel.setFont(UIManager.getFont("Label.font").deriveFont(24f)); noCodeLabel.setForeground( Utilities.deriveColorAlpha(UIManager.getColor("Label.foreground"), 110)); } if (codePanel != null) { Color base = UIManager.getColor("Panel.background"); codePanel.setBackground(Utilities.deriveColorHSB(base, 0, 0, -.06f)); } if (snippetComboBox != null) { // Now that the look and feel has changed, we need to wrap the new delegate snippetComboBox.setRenderer(new SnippetCellRenderer( new JComboBox().getRenderer())); } if (currentCodeFilesInfo != null) { Collection codeFiles = currentCodeFilesInfo.values(); for(CodeFileInfo cfi : codeFiles) { makeSelectionTransparent(cfi.textPane, 180); } } } private void makeSelectionTransparent(JEditorPane textPane, int alpha) { Color c = textPane.getSelectionColor(); textPane.setSelectionColor( new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha)); } protected String getString(String key, String fallback) { String value = fallback; if (bundle == null) { String bundleName = getClass().getPackage().getName()+".resources."+getClass().getSimpleName(); bundle = ResourceBundle.getBundle(bundleName); } try { value = bundle != null? bundle.getString(key) : fallback; } catch (MissingResourceException e) { logger.log(Level.WARNING, "missing String resource " + key + "; using fallback \"" +fallback + "\""); } return value; } protected void initActions() { firstSnippetAction = new FirstSnippetAction(); nextSnippetAction = new NextSnippetAction(); previousSnippetAction = new PreviousSnippetAction(); lastSnippetAction = new LastSnippetAction(); firstSnippetAction.setEnabled(false); nextSnippetAction.setEnabled(false); previousSnippetAction.setEnabled(false); lastSnippetAction.setEnabled(false); getActionMap().put("NextSnippet", nextSnippetAction); getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ctrl N"),"NextSnippet"); getActionMap().put("PreviousSnippet", previousSnippetAction); getInputMap(WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ctrl P"),"PreviousSnippet"); } public void setHighlightColor(Color highlight) { if (!highlight.equals(highlightColor)) { highlightColor = highlight; snippetPainter = new SnippetHighlighter.SnippetHighlightPainter(highlightColor); if (getCurrentSnippetKey() != null) { repaint(); } } } public Color getHighlightColor() { return highlightColor; } public void setSourceFiles(URL sourceFiles[]) { if (currentCodeFilesInfo != null && additionalSourceFiles != null && sourceFiles != null && currentCodeFilesInfo.size() + additionalSourceFiles.size() == sourceFiles.length) { List list = Arrays.asList(sourceFiles); if (list.containsAll(currentCodeFilesInfo.keySet()) && list.containsAll(additionalSourceFiles)) { // already loaded return; } } // clear everything clearAllSnippetHighlights(); snippetMap.clear(); if (sourceFiles == null) { // being reset to having no source files; need to clear everything currentCodeFilesInfo = null; additionalSourceFiles = null; configureCodePane(false); configureSnippetSetsComboBox(); } else { // Use LinkedHashMap to save source order currentCodeFilesInfo = new LinkedHashMap(); additionalSourceFiles = new ArrayList(); boolean needProcessing = false; for (URL sourceFile : sourceFiles) { if (sourceFile.getFile().matches(SOURCES_JAVA)) { // look in cache first to avoid unnecessary processing CodeFileInfo cachedFilesInfo = codeCache.get(sourceFile); currentCodeFilesInfo.put(sourceFile, cachedFilesInfo); if (cachedFilesInfo == null) { needProcessing = true; } } else { additionalSourceFiles.add(sourceFile); } } configureCodePane(true); if (needProcessing) { // Do it on a separate thread new SourceProcessor(currentCodeFilesInfo).execute(); } else { for (CodeFileInfo codeFileInfo : currentCodeFilesInfo.values()) { registerSnippets(codeFileInfo); createCodeFileTab(codeFileInfo); } createAdditionalTabs(); configureSnippetSetsComboBox(); } } } private void createAdditionalTabs() { JPanel pnImages = null; for (URL sourceFile : additionalSourceFiles) { String sourcePath = sourceFile.getPath(); int i = sourcePath.indexOf('!'); if (i >= 0) { sourcePath = sourcePath.substring(i + 1); } if (sourceFile.getFile().matches(SOURCES_IMAGES)) { if (pnImages == null) { pnImages = new JPanel(); pnImages.setLayout(new BoxLayout(pnImages, BoxLayout.Y_AXIS)); } JLabel label = new JLabel(); label.setIcon(new ImageIcon(sourceFile)); label.setBorder(new EmptyBorder(10, 0, 40, 0)); pnImages.add(new JLabel(sourcePath)); pnImages.add(label); } if (sourceFile.getFile().matches(SOURCES_TEXT)) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(sourceFile.openStream())); StringBuilder content = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { content.append(line).append('\n'); } JTextArea textArea = new JTextArea(content.toString()); Font font = textArea.getFont(); textArea.setEditable(false); textArea.setFont(new Font("Monospaced", font.getStyle(), font.getSize())); JScrollPane scrollPane = new JScrollPane(textArea); scrollPane.setBorder(null); codeTabbedPane.addTab(Utilities.getURLFileName(sourceFile), scrollPane); } catch (IOException e) { System.err.println(e); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { System.err.println(e); } } } } } if (pnImages != null) { JScrollPane scrollPane = new JScrollPane(pnImages); scrollPane.setBorder(null); codeTabbedPane.addTab(getString("CodeViewer.images", "Images"), scrollPane); } } private class SourceProcessor extends SwingWorker { private final Map codeFilesInfo; public SourceProcessor(Map codeFilesInfo) { this.codeFilesInfo = codeFilesInfo; } public Void doInBackground() { for (Map.Entry entry : codeFilesInfo.entrySet()) { // if not already fetched from cache, then process source code if (entry.getValue() == null) { entry.setValue(initializeCodeFileInfo(entry.getKey())); } // We don't publish intermediate to avoid tab mixing codeCache.put(entry.getKey(), entry.getValue()); } return null; } private CodeFileInfo initializeCodeFileInfo(URL sourceFile) { CodeFileInfo codeFileInfo = new CodeFileInfo(); codeFileInfo.url = sourceFile; codeFileInfo.styled = loadSourceCode(sourceFile); codeFileInfo.textPane = new JEditorPane(); codeFileInfo.textPane.setHighlighter(new SnippetHighlighter()); makeSelectionTransparent(codeFileInfo.textPane, 180); codeFileInfo.veneer = new CodeVeneer(codeFileInfo); Stacker layers = new Stacker(codeFileInfo.textPane); layers.add(codeFileInfo.veneer, JLayeredPane.POPUP_LAYER); codeFileInfo.textPane.setContentType("text/html"); codeFileInfo.textPane.setEditable(false); // HTML won't display correctly without this! codeFileInfo.textPane.setText(codeFileInfo.styled); codeFileInfo.textPane.setCaretPosition(0); // MUST parse AFTER textPane Document has been created to ensure // snippet offsets are relative to the editor pane's Document model codeFileInfo.snippets = SnippetParser.parse(codeFileInfo.textPane.getDocument()); return codeFileInfo; } protected void done() { try { // It's possible that by now another set of source files has been loaded. // so check first before adding the source tab;' if (currentCodeFilesInfo == codeFilesInfo) { for (CodeFileInfo codeFileInfo : currentCodeFilesInfo.values()) { registerSnippets(codeFileInfo); createCodeFileTab(codeFileInfo); } } else { logger.log(Level.FINEST, "source files changed before sources was processed."); } createAdditionalTabs(); configureSnippetSetsComboBox(); } catch (Exception ex) { System.err.println(ex); } } } // SourceProcessor // Called from Source Processing Thread in SwingWorker private void configureCodePane(boolean hasCodeFiles) { if (hasCodeFiles) { if (codeTabbedPane == null) { codeTabbedPane = new JTabbedPane(); codePanel.remove(noCodeLabel); codePanel.add(codeTabbedPane); revalidate(); } else { codeTabbedPane.removeAll(); } } else { // No code files if (codeTabbedPane != null) { codePanel.remove(codeTabbedPane); codeTabbedPane = null; codePanel.add(noCodeLabel); revalidate(); } } } private void createCodeFileTab(CodeFileInfo codeFileInfo) { JLayeredPane layeredPane = JLayeredPane.getLayeredPaneAbove(codeFileInfo.textPane); JScrollPane scrollPane = new JScrollPane(layeredPane); scrollPane.setBorder(null); JPanel tabPanel = new JPanel(); tabPanel.setLayout(new BorderLayout()); tabPanel.add(scrollPane, BorderLayout.CENTER); codeTabbedPane.addTab(Utilities.getURLFileName(codeFileInfo.url), tabPanel); } private void registerSnippets(CodeFileInfo codeFileInfo) { for(String snippetKey: codeFileInfo.snippets.keySet()) { List snippetCodeList = codeFileInfo.snippets.get(snippetKey); for(Snippet snippet: snippetCodeList) { snippetMap.add(snippetKey, codeFileInfo.url, snippet); } } } private void configureSnippetSetsComboBox() { TreeSet sortedSnippets = new TreeSet(snippetMap.keySet()); String snippetSetKeys[] = (String[])sortedSnippets.toArray(new String[0]); DefaultComboBoxModel snippetModel = new DefaultComboBoxModel(); for(String snippetKey : snippetSetKeys) { snippetModel.addElement(snippetKey); } snippetModel.insertElementAt(NO_SNIPPET_SELECTED, 0); snippetModel.setSelectedItem(NO_SNIPPET_SELECTED); snippetComboBox.setModel(snippetModel); codeHighlightBar.setVisible(snippetModel.getSize() > 1); } /** * Reads the java source file at the specified URL and returns an * HTML version stylized for display */ protected String loadSourceCode(URL sourceUrl) { InputStreamReader isr = null; CodeStyler cv = new CodeStyler(); String styledCode = "

";
        
        try {
            isr = new InputStreamReader(sourceUrl.openStream(), "UTF-8");
            BufferedReader reader = new BufferedReader(isr);
            
            // Read one line at a time, htmlizing using super-spiffy
            // html java code formating utility from www.CoolServlets.com
            String line = reader.readLine();
            while(line != null) {
                styledCode += cv.syntaxHighlight(line) + " \n ";
                line = reader.readLine();
            }
            styledCode += "
"; } catch (Exception ex) { ex.printStackTrace(); return "Could not load file from: " + sourceUrl; } finally { if (isr != null) { try { isr.close(); } catch (IOException e) { System.err.println(e); } } } return styledCode; } public void clearAllSnippetHighlights() { if (currentCodeFilesInfo != null) { snippetMap.setCurrentSet(null); for(CodeFileInfo code : currentCodeFilesInfo.values()) { if (code != null && code.textPane != null) { Highlighter highlighter = code.textPane.getHighlighter(); highlighter.removeAllHighlights(); code.textPane.repaint(); code.veneer.repaint(); } } } } public void highlightSnippetSet(String snippetKey) { clearAllSnippetHighlights(); snippetMap.setCurrentSet(snippetKey); URL files[] = snippetMap.getFilesForSet(snippetKey); CodeFileInfo firstCodeFileInfo = null; Snippet firstSnippet = null; for(URL file : files) { CodeFileInfo codeFileInfo = codeCache.get(file); Highlighter highlighter = codeFileInfo.textPane.getHighlighter(); // now add highlight for each snippet in this file associated // with the key Snippet snippets[] = snippetMap.getSnippetsForFile(snippetKey, file); if (firstCodeFileInfo == null) { firstCodeFileInfo = codeFileInfo; firstSnippet = snippets[0]; } for (Snippet snippet : snippets) { try { highlighter.addHighlight(snippet.startLine, snippet.endLine, snippetPainter ); codeFileInfo.veneer.repaint(); } catch (BadLocationException e) { e.printStackTrace(); } } } scrollToSnippet(firstCodeFileInfo, firstSnippet); snippetComboBox.setSelectedItem(snippetKey); } protected void scrollToSnippet(CodeFileInfo codeFileInfo, Snippet snippet) { if (!codeFileInfo.textPane.isShowing()) { // Need to switch tabs to source file with first snippet // remind: too brittle - need to find component some other way codeTabbedPane.setSelectedComponent( JLayeredPane.getLayeredPaneAbove(codeFileInfo.textPane).getParent().getParent().getParent()); } try { Rectangle r1 = codeFileInfo.textPane.modelToView(snippet.startLine); Rectangle r2 = codeFileInfo.textPane.modelToView(snippet.endLine); codeFileInfo.textPane.scrollRectToVisible( SwingUtilities.computeUnion(r1.x, r1.y, r1.width, r1.height, r2)); } catch (BadLocationException e) { System.err.println(e); } nextSnippetAction.setEnabled(snippetMap.nextSnippetExists()); previousSnippetAction.setEnabled(snippetMap.previousSnippetExists()); } protected String getCurrentSnippetKey() { String key = snippetMap.getCurrentSet(); return key != null? key : NO_SNIPPET_SELECTED; } protected Snippet getCurrentSnippet() { return snippetMap.getCurrentSnippet(); } protected void moveToFirstSnippet() { Snippet firstSnippet = snippetMap.firstSnippet(); if (firstSnippet != null) { CodeFileInfo codeFileInfo = codeCache.get(snippetMap.getFileForSnippet(firstSnippet)); scrollToSnippet(codeFileInfo, firstSnippet); } else { Toolkit.getDefaultToolkit().beep(); } } protected void moveToNextSnippet() { Snippet nextSnippet = snippetMap.nextSnippet(); if (nextSnippet != null) { CodeFileInfo codeFileInfo = codeCache.get(snippetMap.getFileForSnippet(nextSnippet)); scrollToSnippet(codeFileInfo, nextSnippet); } else { Toolkit.getDefaultToolkit().beep(); } } protected void moveToPreviousSnippet() { Snippet previousSnippet = snippetMap.previousSnippet(); if (previousSnippet != null) { CodeFileInfo codeFileInfo = codeCache.get(snippetMap.getFileForSnippet(previousSnippet)); scrollToSnippet(codeFileInfo, previousSnippet); } else { Toolkit.getDefaultToolkit().beep(); } } protected void moveToLastSnippet() { Snippet lastSnippet = snippetMap.lastSnippet(); if (lastSnippet != null) { CodeFileInfo codeFileInfo = codeCache.get(snippetMap.getFileForSnippet(lastSnippet)); scrollToSnippet(codeFileInfo, lastSnippet); } else { Toolkit.getDefaultToolkit().beep(); } } private class SnippetActivator implements ActionListener { public void actionPerformed(ActionEvent e) { String snippetKey = (String)snippetComboBox.getSelectedItem(); if (!snippetKey.equals(NO_SNIPPET_SELECTED)) { logger.log(Level.FINEST, "highlighting new snippet:"+snippetKey+"."); highlightSnippetSet(snippetKey); } else { clearAllSnippetHighlights(); } } } private abstract class SnippetAction extends AbstractAction { public SnippetAction(String name, String shortDescription) { super(name); putValue(AbstractAction.SHORT_DESCRIPTION, shortDescription); } } private class FirstSnippetAction extends SnippetAction { public FirstSnippetAction() { super("FirstSnippet", getString("CodeViewer.snippets.navigateFirst", "move to first code snippet within highlighted set")); } public void actionPerformed(ActionEvent e) { moveToFirstSnippet(); } } private class NextSnippetAction extends SnippetAction { public NextSnippetAction() { super("NextSnippet", getString("CodeViewer.snippets.navigateNext", "move to next code snippet within highlighted set")); } @Override public void actionPerformed(ActionEvent e) { moveToNextSnippet(); } } private class PreviousSnippetAction extends SnippetAction { public PreviousSnippetAction() { super("PreviousSnippet", getString("CodeViewer.snippets.navigatePrevious", "move to previous code fragment within highlighted set")); } @Override public void actionPerformed(ActionEvent e) { moveToPreviousSnippet(); } } private class LastSnippetAction extends SnippetAction { public LastSnippetAction() { super("LastSnippet", getString("CodeViewer.snippets.navigateLast", "move to last code snippet within highlighted set")); } public void actionPerformed(ActionEvent e) { moveToLastSnippet(); } } private class SnippetCellRenderer implements ListCellRenderer { private JLabel delegate; public SnippetCellRenderer(ListCellRenderer delegate) { this.delegate = (JLabel)delegate; } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel renderer = (JLabel)((ListCellRenderer)delegate).getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); int count = snippetMap.getSnippetCountForSet((String)value); Color foreground = renderer.getForeground(); Color countForeground = Utilities.deriveColorHSB(foreground, 0, 0, isSelected? .5f : .4f); String text = "" + value + "" + "" + (count > 0? " (" + count + (count > 1? " snippets)" : " snippet)") : "") + ""; renderer.setText(text); return renderer; } } private static class CodeFileInfo { public URL url; public String styled; public HashMap> snippets = new HashMap>(); public JEditorPane textPane; public JPanel veneer; } private static class Stacker extends JLayeredPane { private Component master; // dictates sizing, scrolling public Stacker(Component master) { this.master = master; setLayout(null); add(master, JLayeredPane.DEFAULT_LAYER); } public Dimension getPreferredSize() { return master.getPreferredSize(); } public void doLayout() { // ensure all layers are sized the same Dimension size = getSize(); Component layers[] = getComponents(); for(Component layer : layers) { layer.setBounds(0, 0, size.width, size.height); } } } private class CodeVeneer extends JPanel { private CodeFileInfo codeFileInfo; public CodeVeneer(CodeFileInfo codeFileInfo) { this.codeFileInfo = codeFileInfo; setOpaque(false); setLayout(null); } @Override protected void paintComponent(Graphics g) { String snippetKey = getCurrentSnippetKey(); if (snippetKey != NO_SNIPPET_SELECTED) { // Count total number of snippets for key int snippetTotal = 0; int snippetIndex = 0; List snippetList = null; URL files[] = snippetMap.getFilesForSet(snippetKey); for(URL file : files) { CodeFileInfo codeFileInfo = codeCache.get(file); if (this.codeFileInfo == codeFileInfo) { snippetList = codeFileInfo.snippets.get(snippetKey); snippetIndex = snippetTotal + 1; } snippetTotal += (codeFileInfo.snippets.get(snippetKey).size()); } if (snippetList != null) { Snippet currentSnippet = snippetMap.getCurrentSnippet(); CodeFileInfo currentSnippetCodeFileInfo = codeCache.get( snippetMap.getFileForSnippet(currentSnippet)); Font font = g.getFont(); g.setFont(font.deriveFont(10f)); FontMetrics metrics = g.getFontMetrics(); g.setColor(getHighlightColor()); Graphics2D g2Alpha = null; // cache composite for(Snippet snippet : snippetList) { Graphics2D g2 = (Graphics2D)g; try { if (currentSnippetCodeFileInfo != codeFileInfo || currentSnippet != snippet) { // if not painting the "current" snippet, then fade the glyph if (g2Alpha == null) { // first time, so create composite g2Alpha = (Graphics2D)g2.create(); g2Alpha.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.6f)); } g2 = g2Alpha; } Rectangle snipRect = codeFileInfo.textPane.modelToView(snippet.startLine); //String glyphLabel = snippetIndex++ + "/" + snippetTotal; String glyphLabel = "" + snippetIndex++; Rectangle labelRect = metrics.getStringBounds(glyphLabel, g2).getBounds(); g2.drawImage(SNIPPET_GLYPH, 0, snipRect.y, this); g2.setColor(Color.black); g2.drawString(glyphLabel, (SNIPPET_GLYPH.getWidth(this) - labelRect.width)/2, snipRect.y + (SNIPPET_GLYPH.getHeight(this) - labelRect.height)/2 + metrics.getAscent()); } catch (BadLocationException e) { System.err.println(e); } } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy