/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program 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.
*
* This program 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 this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.tools.dialogs;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import com.rapidminer.gui.ApplicationFrame;
import com.rapidminer.gui.license.LicenseTools;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.VersionNumber;
import com.rapidminer.gui.tools.VersionNumber.VersionNumberExcpetion;
import com.rapidminer.gui.tools.components.LinkRemoteButton;
import com.rapidminer.license.License;
import com.rapidminer.license.StudioLicenseConstants;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.PlatformUtilities;
import com.rapidminer.tools.RMUrlHandler;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.plugin.Plugin;
/**
* This dialog displays some informations about the product. The product logo should have a size of
* approximately 270 times 70 pixels.
*
* @author Ingo Mierswa
*/
public class AboutBox extends JDialog {
private static final long serialVersionUID = -3889559376722324215L;
private static final int MAX_SHOWN_LINK_LENGTH = 50;
private static final String PROPERTY_FILE = "about_infos.properties";
private static final String DEFAULT_VENDOR = "RapidMiner";
private static final String DEFAULT_VENDOR_OLD = "Rapid-I";
public static Image backgroundImage = null;
public static Image backgroundImageWithoutLogo = null;
static {
URL url = Tools.getResource("about_background.png");
if (url != null) {
try {
backgroundImage = ImageIO.read(url);
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.tools.dialogs.AboutBox.loading_background_error");
}
}
url = Tools.getResource("about_background_wo_logo.png");
if (url != null) {
try {
backgroundImageWithoutLogo = ImageIO.read(url);
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.tools.dialogs.AboutBox.loading_background_error");
}
}
}
private final ContentPanel contentPanel;
private static class ContentPanel extends JPanel {
private static final String[] DISPLAYED_KEYS = new String[] { "copyright", "licensor", "license" };
private static final Font FONT_SANS_SERIF_11 = new Font("SansSerif", Font.PLAIN, 11);
private static final Font FONT_SANS_SERIF_BOLD_11 = new Font("SansSerif", Font.BOLD, 11);
private static final Font FONT_SANS_SERIF_BOLD_26 = new Font("SansSerif", Font.BOLD, 26);
private static final Font FONT_OPEN_SANS_15 = new Font("Open Sans", Font.PLAIN, 15);
private static final List<Font> FONTS_PRODUCT_NAME = new ArrayList<>(15);
static {
for (int size = 60; size >= 8; size -= 4) {
FONTS_PRODUCT_NAME.add(new Font("Open Sans Light", Font.PLAIN, size));
}
}
private static final long serialVersionUID = -1763842074674706654L;
private static final int LOGO_INSET_Y = 342;
private static final int LOGO_INSET_X = 10;
private static final int ADDITIONAL_LINE_HEIGHT = 15;
private static final Paint MAIN_PAINT = new Color(96, 96, 96);
private static final int MARGIN = 20;
private final Properties properties;
private transient Image productLogo;
public ContentPanel(Properties properties, Image productLogo) {
this.properties = properties;
this.productLogo = productLogo;
int width = 550;
int height = 400;
if (backgroundImage != null) {
width = backgroundImage.getWidth(this);
height = backgroundImage.getHeight(this);
}
// Add additional space, if we display more than two keys
int foundKeys = 0;
for (String key : DISPLAYED_KEYS) {
if (properties.containsKey(key)) {
foundKeys++;
if (foundKeys > 2) {
height += ADDITIONAL_LINE_HEIGHT;
}
}
}
setPreferredSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
drawMain(g2d);
g2d.dispose();
}
public void drawMain(Graphics2D g) {
g.setPaint(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
// draw the background image without RapidMiner branding if the vendor is not RM
if (properties.get("licensor") != null && !String.valueOf(properties.get("licensor")).contains(DEFAULT_VENDOR)
&& !String.valueOf(properties.get("licensor")).contains(DEFAULT_VENDOR_OLD)
&& backgroundImageWithoutLogo != null) {
g.drawImage(backgroundImageWithoutLogo, 0, 0, this);
} else if (backgroundImage != null) {
g.drawImage(backgroundImage, 0, 0, this);
}
g.setFont(FONT_SANS_SERIF_BOLD_26);
if (productLogo != null) {
g.drawImage(productLogo, LOGO_INSET_X, LOGO_INSET_Y, this);
}
String productName = removeLeadingRapidMinerString(properties.getProperty("name"));
g.setFont(FONTS_PRODUCT_NAME.get(FONTS_PRODUCT_NAME.size() - 1));
g.setPaint(Color.WHITE);
for (Font f : FONTS_PRODUCT_NAME) {
if (getFontMetrics(f).stringWidth(productName) <= getSize().width - 2 * MARGIN) {
g.setFont(f);
break;
}
}
FontMetrics fm = getFontMetrics(g.getFont());
int x_product = (getSize().width - fm.stringWidth(productName)) / 2;
int y_product = ((backgroundImage != null ? backgroundImage.getHeight(null) : getSize().height) - 70
- fm.getHeight()) / 2 + fm.getAscent();
g.drawString(productName, x_product, y_product);
StringBuilder builder = new StringBuilder();
builder.append(I18N.getGUILabel("version"));
builder.append(" ");
VersionNumber versionNumber = null;
if (properties.getProperty("version") != null) {
try {
versionNumber = new VersionNumber(properties.getProperty("version"));
} catch (VersionNumberExcpetion e) {
// nothing to do
}
}
builder.append(versionNumber != null ? versionNumber.getShortVersion() : I18N.getGUILabel("unknown_version"));
String version = builder.toString();
int x_version = x_product + fm.stringWidth(productName);
Rectangle2D bounds = getStringBounds(g, productName, x_product, y_product);
int y_version = (int) (bounds.getY() + bounds.getHeight());
g.setFont(FONT_OPEN_SANS_15);
fm = getFontMetrics(g.getFont());
x_version -= fm.stringWidth(version);
y_version += fm.getHeight();
g.drawString(version, x_version, y_version);
g.setPaint(MAIN_PAINT);
int y = 355;
g.setFont(FONT_SANS_SERIF_BOLD_11);
builder = new StringBuilder();
builder.append(properties.getProperty("name"));
builder.append(" ");
builder.append(properties.getProperty("version"));
String revision = properties.getProperty("revision");
if (revision != null) {
builder.append(" (rev: ");
builder.append(revision.substring(0, 6));
String platform = properties.getProperty("platform");
if (platform != null) {
builder.append(", platform: ");
builder.append(platform);
}
builder.append(")");
}
drawString(g, builder.toString(), y);
y += 15;
g.setFont(FONT_SANS_SERIF_11);
builder = new StringBuilder();
if (properties.getProperty("edition") != null) {
builder.append(properties.getProperty("edition"));
}
if (properties.getProperty("registered_to") != null) {
builder.append(" ");
builder.append(I18N.getGUILabel("registered_to", properties.getProperty("registered_to")));
}
String edition = builder.toString();
if (!edition.trim().isEmpty()) {
drawString(g, builder.toString(), y);
y += 15;
}
for (String key : DISPLAYED_KEYS) {
y = drawStringAndAdvance(g, properties.getProperty(key), y);
}
}
private Rectangle getStringBounds(Graphics2D g2, String str, float x, float y) {
FontRenderContext frc = g2.getFontRenderContext();
GlyphVector gv = g2.getFont().createGlyphVector(frc, str);
return gv.getPixelBounds(null, x, y);
}
private int drawStringAndAdvance(Graphics2D g, String string, int y) {
if (string == null) {
return y;
} else {
List<String> lines = new LinkedList<>();
String[] words = string.split("\\s+");
String current = "";
for (String word : words) {
if (current.length() + word.length() < 80) {
current += word + " ";
} else {
lines.add(current);
current = word + " ";
}
}
if (!current.isEmpty()) {
lines.add(current);
}
for (String line : lines) {
drawString(g, line, y);
y += 15;
}
return y;
}
}
private void drawString(Graphics2D g, String text, int y) {
drawString(g, text, y, MARGIN + (productLogo != null ? productLogo.getWidth(null) : 0));
}
private void drawString(Graphics2D g, String text, int y, int x) {
if (text == null) {
return;
}
float xPos = x;
float yPos = y;
g.drawString(text, xPos, yPos);
}
}
public AboutBox(Frame owner, String productName, String productVersion, String licensor, String url, String text,
boolean renderTextNextToLogo, Image productLogo) {
this(owner, createProperties(productName, productVersion, licensor, url, text, renderTextNextToLogo), productLogo);
}
public AboutBox(Frame owner, String productVersion, License license, Image productLogo) {
this(owner, createProperties(productVersion, license), productLogo);
}
public AboutBox(Frame owner, String productVersion, License license) {
this(owner, createProperties(productVersion, license), null);
}
public AboutBox(Frame owner, Properties properties, Image productLogo) {
super(owner, "About", true);
setResizable(false);
setLayout(new BorderLayout());
String name = properties.getProperty("name");
if (name != null) {
setTitle("About " + name);
}
contentPanel = new ContentPanel(properties, productLogo);
add(contentPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
final String url = properties.getProperty("url");
if (url != null) {
String shownURL = url;
if (shownURL.length() > MAX_SHOWN_LINK_LENGTH) {
shownURL = shownURL.substring(0, MAX_SHOWN_LINK_LENGTH) + "...";
}
buttonPanel.add(new LinkRemoteButton(new ResourceAction("link_action", url, shownURL) {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
RMUrlHandler.openInBrowser(url);
}
}));
}
ResourceAction closeAction = new ResourceAction("close") {
private static final long serialVersionUID = 1407089394491740308L;
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
};
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false), "CANCEL");
getRootPane().getActionMap().put("CANCEL", closeAction);
pack();
if (owner != null) {
setLocationRelativeTo(owner);
} else {
setLocationRelativeTo(ApplicationFrame.getApplicationFrame());
}
}
public static Properties createProperties(InputStream inputStream, String productVersion) {
Properties properties = new Properties();
if (inputStream != null) {
try {
properties.load(inputStream);
} catch (Exception e) {
LogService.getRoot().log(Level.SEVERE,
"com.rapidminer.gui.tools.dialogs.AboutBox.reading_splash_screen_error", e.getMessage());
}
}
properties.setProperty("version", productVersion);
Plugin.initAboutTexts(properties);
return properties;
}
private static Properties createProperties(String productVersion, License license) {
Properties properties = new Properties();
try {
URL propUrl = Tools.getResource(PROPERTY_FILE);
if (propUrl != null) {
InputStream in = propUrl.openStream();
properties.load(in);
in.close();
}
} catch (Exception e) {
LogService.getRoot().log(Level.SEVERE, "com.rapidminer.gui.tools.dialogs.AboutBox.reading_splash_screen_error",
e.getMessage());
}
properties.setProperty("name", LicenseTools.translateProductName(license));
properties.setProperty("version", productVersion);
if (StudioLicenseConstants.PRODUCT_ID.equals(license.getProductId())
&& PlatformUtilities.getReleaseRevision() != null) {
properties.setProperty("revision", PlatformUtilities.getReleaseRevision());
properties.setProperty("platform", PlatformUtilities.getReleasePlatform().toString());
}
properties.setProperty("edition",
I18N.getGUILabel("license_edition", LicenseTools.translateProductEdition(license)));
if (license.getLicenseUser().getName() != null) {
properties.setProperty("registered_to", license.getLicenseUser().getName());
}
Plugin.initAboutTexts(properties);
return properties;
}
private static Properties createProperties(String productName, String productVersion, String licensor, String url,
String text, boolean renderTextNextToLogo) {
Properties properties = new Properties();
properties.setProperty("name", productName);
properties.setProperty("version", productVersion);
properties.setProperty("licensor", licensor);
properties.setProperty("license", "Website: " + url);
properties.setProperty("more", text);
properties.setProperty("textNextToLogo", "" + renderTextNextToLogo);
properties.setProperty("url", url);
return properties;
}
/**
* This method removes a leading "RapidMiner " String from e.g. product names.
*/
private static String removeLeadingRapidMinerString(String productString) {
return productString.startsWith("RapidMiner ") ? productString.substring(11) : productString;
}
}