/* * 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.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Insets; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.logging.Level; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.border.Border; import org.jdesktop.swingx.util.GraphicsUtilities; import com.sun.swingset3.utilities.ArrowIcon; /** * * @author Amy Fowler */ // This is more complicated thn it needs to be because it was an attempt to // create a seamless control where the label was integrated into the buttons; // this integration is temporarily turned off due to visual problems with focus // and enabled state of the buttons public class SnippetNavigator extends JPanel { private String NO_SNIPPET; private SnippetMap snippetMap; private JLabel statusLabel; private JButton prevButton; private JButton nextButton; private int arrowSize = 8; private int overlap = 0; private int labelPad = 6; private boolean needLabelBorder = true; public SnippetNavigator(SnippetMap snippetMap) { this.snippetMap = snippetMap; snippetMap.addPropertyChangeListener(new SnippetHighlightListener()); setLayout(null); NO_SNIPPET = getString("CodeViewer.snippets.noCodeHighlighted", "No Code highlight selected"); statusLabel = new JLabel(NO_SNIPPET); statusLabel.setHorizontalAlignment(JLabel.CENTER); add(statusLabel); prevButton = (JButton) add(new NavButton()); prevButton.setDefaultCapable(false); prevButton.setVisible(false); nextButton = (JButton) add(new NavButton()); nextButton.setDefaultCapable(false); nextButton.setVisible(false); applyDefaults(); } protected String getString(String key, String fallback) { String value = fallback; String bundleName = getClass().getPackage().getName()+".resources.CodeViewer"; ResourceBundle bundle = ResourceBundle.getBundle(bundleName); try { value = bundle != null? bundle.getString(key) : fallback; } catch (MissingResourceException e) { CodeViewer.logger.log(Level.WARNING, "missing String resource " + key + "; using fallback \"" +fallback + "\""); } return value; } @Override public void doLayout() { Dimension size = getSize(); Insets insets = getInsets(); Dimension labelSize = statusLabel.getPreferredSize(); if (prevButton.isVisible()) { if (needLabelBorder) { matchLabelBorderToButtons(); } Dimension buttonSize = prevButton.getPreferredSize(); prevButton.setBounds(insets.left, insets.top, buttonSize.width, size.height - insets.top - insets.bottom); statusLabel.setBounds(insets.left + buttonSize.width - overlap, insets.top, labelSize.width + (2 * overlap) + labelPad, size.height - insets.top - insets.bottom); nextButton.setBounds(size.width - buttonSize.width, insets.top, buttonSize.width, size.height - insets.top - insets.bottom); } else { statusLabel.setBounds(insets.left, insets.top, size.width - insets.left - insets.right, size.height - insets.top - insets.bottom); } } @Override public Dimension getPreferredSize() { Dimension prefSize; Insets insets = getInsets(); Dimension buttonSize = prevButton.getPreferredSize(); Dimension labelSize = statusLabel.getPreferredSize(); if (prevButton.isVisible()) { prefSize = new Dimension(buttonSize.width * 2 + labelSize.width + labelPad + insets.left + insets.right, buttonSize.height + insets.top + insets.bottom); } else { prefSize = new Dimension(labelSize.width + insets.left + insets.right, buttonSize.height + insets.top + insets.bottom); } return prefSize; } @Override public void updateUI() { super.updateUI(); applyDefaults(); } protected void applyDefaults() { if (prevButton != null) { Color arrowColor = UIManager.getColor("Label.foreground"); Color inactiveColor = UIManager.getColor("Label.disabledText"); Dimension buttonSize = new Dimension(arrowSize + 12 + overlap, arrowSize + 12); prevButton.setIcon(new ArrowIcon(ArrowIcon.WEST, arrowSize, arrowColor)); prevButton.setDisabledIcon(new ArrowIcon(ArrowIcon.WEST, arrowSize, inactiveColor)); prevButton.setPreferredSize(buttonSize); nextButton.setIcon(new ArrowIcon(ArrowIcon.EAST, arrowSize, arrowColor)); nextButton.setDisabledIcon(new ArrowIcon(ArrowIcon.EAST, arrowSize, inactiveColor)); nextButton.setPreferredSize(buttonSize); statusLabel.setOpaque(true); statusLabel.setFont(UIManager.getFont("Label.font").deriveFont(12f)); //needLabelBorder = true; needLabelBorder = false; } } // this doesn't get called because label integration is disabled private void matchLabelBorderToButtons() { Insets insets = new Insets(4, 6, 4, 6); Dimension buttonSize = prevButton.getPreferredSize(); prevButton.setSize(buttonSize); BufferedImage image = GraphicsUtilities. createCompatibleTranslucentImage( buttonSize.width, buttonSize.height); prevButton.paint(image.getGraphics()); BufferedImage labelTopImage = GraphicsUtilities. createCompatibleTranslucentImage( buttonSize.width - insets.left - insets.right, insets.top); labelTopImage.getGraphics().drawImage(image, 0, 0, buttonSize.width - insets.left - insets.right, insets.top, insets.left, 0, buttonSize.width - insets.right, insets.top, null, null); BufferedImage labelBottomImage = GraphicsUtilities.createCompatibleTranslucentImage(buttonSize.width - insets.left - insets.right, insets.bottom); labelBottomImage.getGraphics().drawImage(image, 0, 0, buttonSize.width - insets.left - insets.right, insets.top, insets.left, buttonSize.height - insets.bottom, buttonSize.width - insets.right, buttonSize.height, null, null); statusLabel.setBorder(new CenterLabelBorder(labelTopImage, labelBottomImage)); needLabelBorder = false; } public void setNavigatePreviousAction(Action action) { setButtonAction(prevButton, action); } public void setNavigateNextAction(Action action) { setButtonAction(nextButton, action); } private void setButtonAction(JButton button, Action action) { Icon icon = button.getIcon(); button.setAction(action); button.setHideActionText(true); button.setIcon(icon); // icon gets obliterated when action set! } private class SnippetHighlightListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { String propertyName = e.getPropertyName(); if (propertyName.equals("currentSet")) { String key = (String) e.getNewValue(); setComponentState(key); } else if (propertyName.equals("currentSnippet")) { setComponentState(snippetMap.getCurrentSet()); } } private void setComponentState(String currentKey) { if (currentKey == null) { statusLabel.setText(NO_SNIPPET); } else { String place = "<html><b>" + snippetMap.getIndexForSnippet(snippetMap.getCurrentSnippet()) + "</b>" + " of " + snippetMap.getSnippetCountForSet(currentKey) + "</html>"; statusLabel.setText(place); } boolean moreThanOne = snippetMap.getSnippetCountForSet(currentKey) > 1; prevButton.setVisible(moreThanOne); nextButton.setVisible(moreThanOne); } } private class NavButton extends JButton { public NavButton() { super(); } protected void paintComponent(Graphics g) { // need to make sure overlapping label paints after buttons otherwise // the buttons paint over the label when they gain focus (which is really // a Swing bug, as the clip should be properly set based on zorder // within the container) super.paintComponent(g); statusLabel.repaint(); } } private class CenterLabelBorder implements Border { private Image topImage; private Image bottomImage; public CenterLabelBorder(Image topImage, Image bottomImage) { this.topImage = topImage; this.bottomImage = bottomImage; } public Insets getBorderInsets(Component c) { return new Insets(topImage.getHeight(null), 0, bottomImage.getHeight(null), 0); } public boolean isBorderOpaque() { return false; } public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { if (prevButton.isVisible()) { int xx = x; int imageWidth = topImage.getWidth(c); while (xx < x + w) { g.drawImage(topImage, xx, y, c); xx += imageWidth; } xx = x; imageWidth = bottomImage.getWidth(c); while (xx < x + w) { g.drawImage(bottomImage, xx, y + h - bottomImage.getHeight(c), c); xx += imageWidth; } } } } }