/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.ui.components.richtooltip;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.text.AttributedString;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import org.pushingpixels.flamingo.internal.utils.FlamingoUtilities;
/**
*
* @author Mathieu Bastian
*/
class BasicRichTooltipPanelUI extends RichTooltipPanelUI {
/**
* The associated tooltip panel.
*/
protected JRichTooltipPanel richTooltipPanel;
protected JLabel titleLabel;
protected java.util.List<JLabel> descriptionLabels;
protected JLabel mainImageLabel;
protected JSeparator footerSeparator;
protected JLabel footerImageLabel;
protected java.util.List<JLabel> footerLabels;
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
*/
public static ComponentUI createUI(JComponent c) {
return new BasicRichTooltipPanelUI();
}
public BasicRichTooltipPanelUI() {
this.descriptionLabels = new ArrayList<JLabel>();
this.footerLabels = new ArrayList<JLabel>();
}
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent)
*/
@Override
public void installUI(JComponent c) {
this.richTooltipPanel = (JRichTooltipPanel) c;
super.installUI(this.richTooltipPanel);
installDefaults();
installComponents();
installListeners();
this.richTooltipPanel.setLayout(createLayoutManager());
}
/*
* (non-Javadoc)
*
* @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent)
*/
@Override
public void uninstallUI(JComponent c) {
uninstallListeners();
uninstallComponents();
uninstallDefaults();
super.uninstallUI(this.richTooltipPanel);
}
/**
* Installs default settings for the associated rich tooltip panel.
*/
protected void installDefaults() {
Border b = this.richTooltipPanel.getBorder();
if (b == null || b instanceof UIResource) {
Border toSet = UIManager.getBorder("RichTooltipPanel.border");
if (toSet == null)
toSet = new BorderUIResource.CompoundBorderUIResource(
new LineBorder(FlamingoUtilities.getBorderColor()),
new EmptyBorder(2, 4, 3, 4));
this.richTooltipPanel.setBorder(toSet);
}
LookAndFeel.installProperty(this.richTooltipPanel, "opaque",
Boolean.TRUE);
}
/**
* Installs listeners on the associated rich tooltip panel.
*/
protected void installListeners() {
}
/**
* Installs components on the associated rich tooltip panel.
*/
protected void installComponents() {
}
/**
* Uninstalls default settings from the associated rich tooltip panel.
*/
protected void uninstallDefaults() {
LookAndFeel.uninstallBorder(this.richTooltipPanel);
}
/**
* Uninstalls listeners from the associated rich tooltip panel.
*/
protected void uninstallListeners() {
}
/**
* Uninstalls subcomponents from the associated rich tooltip panel.
*/
protected void uninstallComponents() {
this.removeExistingComponents();
}
@Override
public void update(Graphics g, JComponent c) {
this.paintBackground(g);
this.paint(g, c);
}
protected void paintBackground(Graphics g) {
Color main = FlamingoUtilities.getColor(Color.gray,
"Label.disabledForeground").brighter();
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(new GradientPaint(0, 0, FlamingoUtilities.getLighterColor(
main, 0.9), 0, this.richTooltipPanel.getHeight(),
FlamingoUtilities.getLighterColor(main, 0.4)));
g2d.fillRect(0, 0, this.richTooltipPanel.getWidth(),
this.richTooltipPanel.getHeight());
g2d.setFont(FlamingoUtilities.getFont(this.richTooltipPanel,
"Ribbon.font", "Button.font", "Panel.font"));
g2d.dispose();
}
@Override
public void paint(Graphics g, JComponent c) {
}
protected LayoutManager createLayoutManager() {
return new RichTooltipPanelLayout();
}
protected class RichTooltipPanelLayout implements LayoutManager {
@Override
public void addLayoutComponent(String name, Component comp) {
}
@Override
public void removeLayoutComponent(Component comp) {
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return this.preferredLayoutSize(parent);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
Insets ins = parent.getInsets();
int gap = getLayoutGap();
Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
"Button.font", "Panel.font");
// the main text gets 200 pixels. The width is defined
// by this and the presence of the main text.
// The height is defined based on the width and the
// text broken into multiline paragraphs
int descTextWidth = getDescriptionTextWidth();
int width = ins.left + 2 * gap + descTextWidth + ins.right;
RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
FontRenderContext frc = new FontRenderContext(
new AffineTransform(), true, false);
if (tooltipInfo.getMainImage() != null) {
width += tooltipInfo.getMainImage().getWidth(null);
}
int fontHeight = parent.getFontMetrics(font).getHeight();
int height = ins.top;
// The title label
height += fontHeight + gap;
// The description text
int descriptionTextHeight = 0;
for (String descText : tooltipInfo.getDescriptionSections()) {
AttributedString attributedDescription = new AttributedString(
descText);
attributedDescription.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
while (true) {
TextLayout tl = lineBreakMeasurer.nextLayout(descTextWidth);
if (tl == null)
break;
descriptionTextHeight += fontHeight;
}
// add an empty line after the paragraph
descriptionTextHeight += fontHeight;
}
// remove the empty line after the last paragraph
descriptionTextHeight -= fontHeight;
if (tooltipInfo.getMainImage() != null) {
height += Math.max(descriptionTextHeight, new JLabel(
new ImageIcon(tooltipInfo.getMainImage()))
.getPreferredSize().height);
} else {
height += descriptionTextHeight;
}
if ((tooltipInfo.getFooterImage() != null)
|| (tooltipInfo.getFooterSections().size() > 0)) {
height += gap;
// The footer separator
height += new JSeparator(JSeparator.HORIZONTAL)
.getPreferredSize().height;
height += gap;
int footerTextHeight = 0;
int availableWidth = descTextWidth;
if (tooltipInfo.getFooterImage() != null) {
availableWidth -= tooltipInfo.getFooterImage().getWidth(
null);
}
if (tooltipInfo.getMainImage() != null) {
availableWidth += tooltipInfo.getMainImage().getWidth(null);
}
for (String footerText : tooltipInfo.getFooterSections()) {
AttributedString attributedDescription = new AttributedString(
footerText);
attributedDescription
.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
while (true) {
TextLayout tl = lineBreakMeasurer
.nextLayout(availableWidth);
if (tl == null)
break;
footerTextHeight += fontHeight;
}
// add an empty line after the paragraph
footerTextHeight += fontHeight;
}
// remove the empty line after the last paragraph
footerTextHeight -= fontHeight;
if (tooltipInfo.getFooterImage() != null) {
height += Math.max(footerTextHeight, new JLabel(
new ImageIcon(tooltipInfo.getFooterImage()))
.getPreferredSize().height);
} else {
height += footerTextHeight;
}
}
height += ins.bottom;
return new Dimension(width, height);
}
@Override
public void layoutContainer(Container parent) {
removeExistingComponents();
Font font = FlamingoUtilities.getFont(parent, "Ribbon.font",
"Button.font", "Panel.font");
Insets ins = richTooltipPanel.getInsets();
int y = ins.top;
RichTooltip tooltipInfo = richTooltipPanel.getTooltipInfo();
FontRenderContext frc = new FontRenderContext(
new AffineTransform(), true, false);
int gap = getLayoutGap();
int fontHeight = parent.getFontMetrics(font).getHeight();
// The title label
titleLabel = new JLabel(tooltipInfo.getTitle());
titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD));
richTooltipPanel.add(titleLabel);
titleLabel.setBounds(ins.left, y,
titleLabel.getPreferredSize().width, fontHeight);
y += titleLabel.getHeight() + gap;
// The main image
int x = ins.left;
if (tooltipInfo.getMainImage() != null) {
mainImageLabel = new JLabel(new ImageIcon(tooltipInfo
.getMainImage()));
richTooltipPanel.add(mainImageLabel);
mainImageLabel.setBounds(x, y, mainImageLabel
.getPreferredSize().width, mainImageLabel
.getPreferredSize().height);
x += mainImageLabel.getWidth();
}
x += 2 * gap;
// The description text
int descLabelWidth = parent.getWidth() - x - ins.right;
for (String descText : tooltipInfo.getDescriptionSections()) {
AttributedString attributedDescription = new AttributedString(
descText);
attributedDescription.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
int currOffset = 0;
while (true) {
TextLayout tl = lineBreakMeasurer
.nextLayout(descLabelWidth);
if (tl == null)
break;
int charCount = tl.getCharacterCount();
String line = descText.substring(currOffset, currOffset
+ charCount);
JLabel descLabel = new JLabel(line);
descriptionLabels.add(descLabel);
richTooltipPanel.add(descLabel);
descLabel.setBounds(x, y,
descLabel.getPreferredSize().width, fontHeight);
y += descLabel.getHeight();
currOffset += charCount;
}
// add an empty line after the paragraph
y += titleLabel.getHeight();
}
// remove the empty line after the last paragraph
y -= titleLabel.getHeight();
if (mainImageLabel != null) {
y = Math.max(y, mainImageLabel.getY()
+ mainImageLabel.getHeight());
}
if ((tooltipInfo.getFooterImage() != null)
|| (tooltipInfo.getFooterSections().size() > 0)) {
y += gap;
// The footer separator
footerSeparator = new JSeparator(JSeparator.HORIZONTAL);
richTooltipPanel.add(footerSeparator);
footerSeparator.setBounds(ins.left, y, parent.getWidth()
- ins.left - ins.right, footerSeparator
.getPreferredSize().height);
y += footerSeparator.getHeight() + gap;
// The footer image
x = ins.left;
if (tooltipInfo.getFooterImage() != null) {
footerImageLabel = new JLabel(new ImageIcon(tooltipInfo
.getFooterImage()));
richTooltipPanel.add(footerImageLabel);
footerImageLabel.setBounds(x, y, footerImageLabel
.getPreferredSize().width, footerImageLabel
.getPreferredSize().height);
x += footerImageLabel.getWidth() + 2 * gap;
}
// The footer text
int footerLabelWidth = parent.getWidth() - x - ins.right;
for (String footerText : tooltipInfo.getFooterSections()) {
AttributedString attributedDescription = new AttributedString(
footerText);
attributedDescription
.addAttribute(TextAttribute.FONT, font);
LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(
attributedDescription.getIterator(), frc);
int currOffset = 0;
while (true) {
TextLayout tl = lineBreakMeasurer
.nextLayout(footerLabelWidth);
if (tl == null)
break;
int charCount = tl.getCharacterCount();
String line = footerText.substring(currOffset,
currOffset + charCount);
JLabel footerLabel = new JLabel(line);
footerLabels.add(footerLabel);
richTooltipPanel.add(footerLabel);
footerLabel.setBounds(x, y, footerLabel
.getPreferredSize().width, fontHeight);
y += footerLabel.getHeight();
currOffset += charCount;
}
// add an empty line after the paragraph
y += titleLabel.getHeight();
}
// remove the empty line after the last paragraph
y -= titleLabel.getHeight();
}
}
}
protected int getDescriptionTextWidth() {
return 200;
}
protected int getLayoutGap() {
return 4;
}
protected void removeExistingComponents() {
if (this.titleLabel != null) {
this.richTooltipPanel.remove(this.titleLabel);
}
if (this.mainImageLabel != null) {
this.richTooltipPanel.remove(this.mainImageLabel);
}
for (JLabel label : this.descriptionLabels)
this.richTooltipPanel.remove(label);
if (this.footerSeparator != null) {
this.richTooltipPanel.remove(this.footerSeparator);
}
if (this.footerImageLabel != null) {
this.richTooltipPanel.remove(this.footerImageLabel);
}
for (JLabel label : this.footerLabels)
this.richTooltipPanel.remove(label);
}
}