/**
* 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;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import com.rapidminer.gui.license.LicenseTools;
import com.rapidminer.license.License;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.Tools;
import com.rapidminer.tools.container.Pair;
import com.rapidminer.tools.plugin.Plugin;
/**
* The splash screen is displayed during start up of RapidMiner. It displays the logo and the some
* start information. The product logo should have a size of approximately 270 times 70 pixels.
*
* @author Ingo Mierswa, Nils Woehler
*/
public class SplashScreen extends JPanel implements ActionListener {
private static final Font FONT_SANS_SERIF_11 = new Font("SansSerif", java.awt.Font.PLAIN, 11);
private static final Font FONT_SANS_SERIF_BOLD_11 = new Font("SansSerif", java.awt.Font.BOLD, 11);
private static final Font FONT_OPEN_SANS_15 = new Font("Open Sans", java.awt.Font.PLAIN, 15);
private static final Font FONT_OPEN_SANS_LIGHT_60 = new Font("Open Sans Light", java.awt.Font.PLAIN, 60);
private static final int EXTENSION_GAP = 400;
private static final float EXTENSION_FADE_TIME = 1000;
private static final int MAX_NUMBER_EXTENSION_ICONS_X = 4;
private static final float MAX_NUMBER_EXTENSION_ICONS_Y = 2f;
private static final int MAX_NUMBER_EXTENSION_ICONS = (int) (MAX_NUMBER_EXTENSION_ICONS_X
* MAX_NUMBER_EXTENSION_ICONS_Y);
private static final long serialVersionUID = -1525644776910410809L;
private static final Paint MAIN_PAINT = new Color(96, 96, 96);
private static final Paint WHITE_PAINT = Color.WHITE;
public static Image backgroundImage = null;
private static final int MARGIN = 10;
private static final String PROPERTY_FILE = "splash_infos.properties";
static {
try {
if (backgroundImage == null) {
URL url = Tools.getResource("splashscreen_background.png");
if (url != null) {
backgroundImage = ImageIO.read(url);
}
}
} catch (IOException e) {
LogService.getRoot().log(Level.WARNING,
"com.rapidminer.gui.tools.SplashScreen.loading_images_for_splash_screen_error");
}
}
private transient Image productLogo;
private Properties properties;
private JFrame splashScreenFrame = new JFrame();
private String message = "Starting...";
private Timer animationTimer;
private List<Runnable> animationRenderers = new LinkedList<>();
private List<Pair<BufferedImage, Long>> extensionIcons = Collections
.synchronizedList(new LinkedList<Pair<BufferedImage, Long>>());
private long lastExtensionAdd = 0;
private License license;
private String productEdition;
private String productName;
public SplashScreen(String productVersion, Image productLogo) {
this(productLogo, createDefaultProperties(productVersion));
}
public SplashScreen(String productVersion, Image productLogo, URL propertyFile) {
this(productLogo, createProperties(productVersion, propertyFile));
}
public SplashScreen(Image productLogo, Properties properties) {
this.properties = properties;
this.productLogo = productLogo;
this.productName = I18N.getGUIMessage("gui.splash.product_name");
splashScreenFrame = new JFrame(properties.getProperty("name"));
splashScreenFrame.getContentPane().add(this);
SwingTools.setFrameIcon(splashScreenFrame);
splashScreenFrame.setUndecorated(true);
if (backgroundImage != null) {
splashScreenFrame.setSize(backgroundImage.getWidth(this), backgroundImage.getHeight(this));
} else {
splashScreenFrame.setSize(550, 400);
}
splashScreenFrame.setLocationRelativeTo(null);
animationTimer = new Timer(10, this);
animationTimer.setRepeats(true);
animationTimer.start();
}
private static Properties createDefaultProperties(String productVersion) {
return createProperties(productVersion, Tools.getResource(PROPERTY_FILE));
}
private static Properties createProperties(String productVersion, URL propertyFile) {
Properties properties = new Properties();
if (propertyFile != null) {
try {
InputStream in = propertyFile.openStream();
properties.load(in);
in.close();
} catch (Exception e) {
LogService.getRoot().log(Level.SEVERE,
"com.rapidminer.gui.tools.dialogs.SplashScreen.reading_splash_screen_error", e.getMessage());
}
}
properties.setProperty("version", productVersion);
return properties;
}
public void showSplashScreen() {
splashScreenFrame.setVisible(true);
}
public JFrame getSplashScreenFrame() {
return splashScreenFrame;
}
public void dispose() {
splashScreenFrame.dispose();
splashScreenFrame = null;
animationTimer.stop();
}
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
drawMain(g2d);
g2d.setColor(Color.black);
g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
// draw extensions
List<Pair<BufferedImage, Long>> currentExtensionIcons = getSynchronizedExtensionIcons();
int size = currentExtensionIcons.size();
if (size > 0) {
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.translate(435, 340);
g2d.scale(0.5, 0.5);
long currentTimeMillis = System.currentTimeMillis();
int numberToShow = 0;
for (Pair<BufferedImage, Long> pair : currentExtensionIcons) {
if (currentTimeMillis > pair.getSecond()) {
numberToShow++;
}
}
// now paint other icons
int shiftX = 51;
int shiftY = 51;
for (int i = 0; i < numberToShow; i++) {
if (numberToShow > i + MAX_NUMBER_EXTENSION_ICONS) {
// then we have to fade out again
Pair<BufferedImage, Long> pair = currentExtensionIcons.get(i + MAX_NUMBER_EXTENSION_ICONS);
float min = Math.min((currentTimeMillis - pair.getSecond()) / EXTENSION_FADE_TIME, 1f);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1 - min));
} else {
// fade in
Pair<BufferedImage, Long> pair = currentExtensionIcons.get(i);
float min = Math.min((currentTimeMillis - pair.getSecond()) / EXTENSION_FADE_TIME, 1f);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, min));
}
int x = i % MAX_NUMBER_EXTENSION_ICONS_X * shiftX;
int y = (int) (Math.floor(i / MAX_NUMBER_EXTENSION_ICONS_X) % MAX_NUMBER_EXTENSION_ICONS_Y) * shiftY;
g2d.drawImage(currentExtensionIcons.get(i).getFirst(), null, x, y);
}
}
g2d.dispose();
}
public void drawMain(Graphics2D g) {
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setPaint(MAIN_PAINT);
g.fillRect(0, 0, getWidth(), getHeight());
if (backgroundImage != null) {
g.drawImage(backgroundImage, 0, 0, this);
}
if (productLogo != null) {
g.drawImage(productLogo, getWidth() / 2 - productLogo.getWidth(this) / 2, 90, this);
}
int y = 355;
if (message != null) {
g.setFont(FONT_SANS_SERIF_BOLD_11);
drawString(g, message, y);
y += 15;
}
// draw product name and version
{
g.setFont(FONT_OPEN_SANS_LIGHT_60);
g.setPaint(WHITE_PAINT);
FontMetrics fm = getFontMetrics(g.getFont());
int x_product = (getSize().width - fm.stringWidth(productName)) / 2;
int y_product = (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(" ");
builder.append(properties.getProperty("version"));
String version = builder.toString();
int x_version = x_product + fm.stringWidth(productName);
g.setFont(FONT_OPEN_SANS_15);
fm = getFontMetrics(g.getFont());
int y_version = y_product + fm.getHeight();
x_version -= fm.stringWidth(version);
g.drawString(version, x_version, y_version);
g.setPaint(MAIN_PAINT);
}
// draw bottom text
if (license != null) {
g.setFont(FONT_SANS_SERIF_11);
StringBuilder builder = new StringBuilder();
builder.append(productEdition);
if (license.getLicenseUser().getName() != null) {
builder.append(" ");
builder.append(I18N.getGUILabel("registered_to", license.getLicenseUser().getName()));
}
drawString(g, builder.toString(), y);
y += 15;
} else {
y += 15;
}
g.setFont(FONT_SANS_SERIF_11);
drawString(g, properties.getProperty("copyright"), y);
}
private void drawString(Graphics2D g, String text, int height) {
if (text == null) {
return;
}
float xPos = MARGIN;
float yPos = height;
g.drawString(text, xPos, yPos);
}
public void setMessage(String message) {
this.message = message;
}
public void setProperty(String key, String value) {
properties.setProperty(key, value);
}
public void addExtension(Plugin plugin) {
ImageIcon extensionIcon = plugin.getExtensionIcon();
if (extensionIcon != null) {
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis < lastExtensionAdd + EXTENSION_GAP) {
currentTimeMillis = lastExtensionAdd + EXTENSION_GAP;
}
lastExtensionAdd = currentTimeMillis;
BufferedImage bufferedImage = new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = bufferedImage.createGraphics();
graphics.drawImage(extensionIcon.getImage(), 0, 0, null);
synchronized (extensionIcons) {
extensionIcons.add(new Pair<>(bufferedImage, currentTimeMillis));
}
}
}
private List<Pair<BufferedImage, Long>> getSynchronizedExtensionIcons() {
synchronized (extensionIcons) {
return new ArrayList<>(extensionIcons);
}
}
public void addAnimationRenderer(Runnable runable) {
this.animationRenderers.add(runable);
}
@Override
/**
* This method is used for being repainted for splash animation.
*/
public void actionPerformed(ActionEvent e) {
List<Runnable> copiedAnimationRenderers = new LinkedList<>(animationRenderers);
for (Runnable runnable : copiedAnimationRenderers) {
runnable.run();
}
repaint();
}
/**
* @param license
* the currently active license
*/
public void setLicense(License license) {
this.license = license;
this.productEdition = I18N.getGUILabel("license_edition", LicenseTools.translateProductEdition(license));
}
}