/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.actions;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.ClickListener;
import com.intellij.ui.UI;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Properties;
/**
* @author Konstantin Bulenkov
*/
@SuppressWarnings("SSBasedInspection")
public class AboutDialog extends JDialog {
public AboutDialog(Window owner) {
super(owner);
init(owner);
}
private void init(Window window) {
ApplicationInfoEx appInfo = (ApplicationInfoEx)ApplicationInfo.getInstance();
JPanel mainPanel = new JPanel(new BorderLayout());
final JComponent closeListenerOwner;
Icon image = IconLoader.getIcon(appInfo.getAboutImageUrl());
final InfoSurface infoSurface= new InfoSurface(image);
infoSurface.setPreferredSize(new Dimension(image.getIconWidth(), image.getIconHeight()));
mainPanel.add(infoSurface, BorderLayout.NORTH);
closeListenerOwner = infoSurface;
setUndecorated(true);
setContentPane(mainPanel);
final Ref<Long> showTime = Ref.create(System.currentTimeMillis());
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_ESCAPE && e.getModifiers() == 0) {
dispose();
}
else if (infoSurface != null) {
if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_META) {
showTime.set(System.currentTimeMillis());
e.consume();
}
else if ((code == KeyEvent.VK_C && (e.isControlDown() || e.isMetaDown()))
|| (!SystemInfo.isMac && code == KeyEvent.VK_INSERT && e.isControlDown())) {
copyInfoToClipboard(infoSurface.getText());
showTime.set(System.currentTimeMillis());
e.consume();
}
}
}
});
//final long delta = Patches.APPLE_BUG_ID_3716865 ? 100 : 0;
final long delta = 500; //reproducible on Windows too
addWindowFocusListener(new WindowFocusListener() {
@Override
public void windowGainedFocus(WindowEvent e) {
}
@Override
public void windowLostFocus(WindowEvent e) {
long eventTime = System.currentTimeMillis();
if (eventTime - showTime.get() > delta && e.getOppositeWindow() != e.getWindow()) {
dispose();
}
else {
IdeFocusManager.getGlobalInstance().requestFocus(AboutDialog.this, true);
}
}
});
new ClickListener() {
@Override
public boolean onClick(MouseEvent event, int clickCount) {
dispose();
return true;
}
}.installOn(closeListenerOwner);
pack();
setLocationRelativeTo(window);
}
private static void copyInfoToClipboard(String text) {
try {
CopyPasteManager.getInstance().setContents(new StringSelection(text));
}
catch (Exception ignore) {
}
}
private static class InfoSurface extends JPanel {
final Color col;
final Color linkCol;
private final Icon myImage;
private Font myFont;
private Font myBoldFont;
private final List<AboutBoxLine> myLines = new ArrayList<AboutBoxLine>();
private StringBuilder myInfo = new StringBuilder();
private static class Link {
private final Rectangle rectangle;
private final String url;
private Link(Rectangle rectangle, String url) {
this.rectangle = rectangle;
this.url = url;
}
}
private Link myActiveLink;
private final List<Link> myLinks = new ArrayList<Link>();
public InfoSurface(Icon image) {
myImage = image;
setOpaque(false);
col = Color.white;
final ApplicationInfoImpl ideInfo = (ApplicationInfoImpl)ApplicationInfoEx.getInstanceEx();
linkCol = ideInfo.getAboutLinkColor() != null ? ideInfo.getAboutLinkColor() : UI.getColor("link.foreground");
setBackground(col);
Calendar cal = ideInfo.getBuildDate();
myLines.add(new AboutBoxLine(ideInfo.getFullApplicationName(), true, null));
appendLast();
String buildInfo = IdeBundle.message("aboutbox.build.number", ideInfo.getBuild().asString());
String buildDate = "";
if (ideInfo.getBuild().isSnapshot()) {
buildDate = new SimpleDateFormat("HH:mm, ").format(cal.getTime());
}
buildDate += DateFormatUtil.formatAboutDialogDate(cal.getTime());
buildInfo += IdeBundle.message("aboutbox.build.date", buildDate);
myLines.add(new AboutBoxLine(buildInfo));
appendLast();
myLines.add(new AboutBoxLine(""));
myLines.add(new AboutBoxLine(""));
final Properties properties = System.getProperties();
myLines.add(new AboutBoxLine(IdeBundle.message("aboutbox.jdk", properties.getProperty("java.version", "unknown")), true, null));
appendLast();
myLines.add(new AboutBoxLine(IdeBundle.message("aboutbox.vm", properties.getProperty("java.vm.name", "unknown"),
properties.getProperty("java.vendor", "unknown"))));
appendLast();
myLines.add(new AboutBoxLine(""));
myLines.add(new AboutBoxLine("Based on ").keepWithNext());
myLines.add(new AboutBoxLine("IntelliJ IDEA Community Edition", true, "http://jetbrains.org"));
myLines.add(new AboutBoxLine(""));
myLines.add(new AboutBoxLine(""));
myLines.add(new AboutBoxLine("Powered by ").keepWithNext());
myLines.add(new AboutBoxLine("consulo.io", true, "http://consulo.io"));
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
if (myActiveLink != null) {
event.consume();
BrowserUtil.browse(myActiveLink.url);
}
}
});
addMouseMotionListener(new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent event) {
boolean hadLink = (myActiveLink != null);
myActiveLink = null;
for (Link link : myLinks) {
if (link.rectangle.contains(event.getPoint())) {
myActiveLink = link;
if (!hadLink) {
setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
break;
}
}
if (hadLink && myActiveLink == null) {
setCursor(Cursor.getDefaultCursor());
}
}
});
}
private void appendLast() {
myInfo.append(myLines.get(myLines.size() - 1).getText()).append("\n");
}
@Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
Graphics2D g2 = (Graphics2D)g;
UIUtil.applyRenderingHints(g);
Font labelFont = UIUtil.getLabelFont();
for (int labelSize = 10; labelSize != 6; labelSize -= 1) {
myLinks.clear();
g2.setPaint(col);
myImage.paintIcon(this, g2, 0, 0);
g2.setColor(col);
TextRenderer renderer = new TextRenderer(0, 145, 398, 120, g2);
UIUtil.setupComposite(g2);
myFont = labelFont.deriveFont(Font.PLAIN, JBUI.scaleFontSize(labelSize));
myBoldFont = labelFont.deriveFont(Font.BOLD, JBUI.scaleFontSize(labelSize + 1));
try {
renderer.render(JBUI.scale(30), 0, myLines);
break;
}
catch (TextRenderer.OverflowException ignore) {
}
}
ApplicationInfo appInfo = ApplicationInfo.getInstance();
Rectangle aboutLogoRect = appInfo.getAboutLogoRect();
if (aboutLogoRect != null) {
myLinks.add(new Link(aboutLogoRect, appInfo.getCompanyURL()));
}
}
public String getText() {
return myInfo.toString();
}
public class TextRenderer {
private final int xBase;
private final int yBase;
private final int w;
private final int h;
private final Graphics2D g2;
private int x = 0;
private int y = 0;
private FontMetrics fontmetrics;
private int fontAscent;
private int fontHeight;
private Font font;
public class OverflowException extends Exception {
}
public TextRenderer(final int xBase, final int yBase, final int w, final int h, final Graphics2D g2) {
this.xBase = JBUI.scale(xBase);
this.yBase = JBUI.scale(yBase);
this.w = JBUI.scale(w);
this.h = JBUI.scale(h);
this.g2 = g2;
if (SystemInfo.isWindows) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
}
}
public void render(int indentX, int indentY, List<AboutBoxLine> lines) throws OverflowException {
x = indentX;
y = indentY;
ApplicationInfoEx appInfo = (ApplicationInfoEx)ApplicationInfo.getInstance();
for (AboutBoxLine line : lines) {
final String s = line.getText();
setFont(line.isBold() ? myBoldFont : myFont);
if (line.getUrl() != null) {
g2.setColor(linkCol);
FontMetrics metrics = g2.getFontMetrics(font);
myLinks.add(new Link(new Rectangle(x, yBase + y - fontAscent, metrics.stringWidth(s), fontHeight), line.getUrl()));
}
else {
g2.setColor(appInfo.getAboutForeground());
}
renderString(s, indentX);
if (!line.isKeepWithNext() && !line.equals(lines.get(lines.size()-1))) {
lineFeed(indentX, s);
}
}
}
private void renderString(final String s, final int indentX) throws OverflowException {
final List<String> words = StringUtil.split(s, " ");
for (String word : words) {
int wordWidth = fontmetrics.stringWidth(word);
if (x + wordWidth >= w) {
lineFeed(indentX, word);
}
else {
char c = ' ';
final int cW = fontmetrics.charWidth(c);
if (x + cW < w) {
g2.drawChars(new char[]{c}, 0, 1, xBase + x, yBase + y);
x += cW;
}
}
renderWord(word, indentX);
}
}
private void renderWord(final String s, final int indentX) throws OverflowException {
for (int j = 0; j != s.length(); ++j) {
final char c = s.charAt(j);
final int cW = fontmetrics.charWidth(c);
if (x + cW >= w) {
lineFeed(indentX, s);
}
g2.drawChars(new char[]{c}, 0, 1, xBase + x, yBase + y);
x += cW;
}
}
private void lineFeed(int indent, final String s) throws OverflowException {
x = indent;
if (s.length() == 0) {
y += fontHeight / 3;
}
else {
y += fontHeight;
}
if (y >= h) {
throw new OverflowException();
}
}
private void setFont(Font font) {
this.font = font;
fontmetrics = g2.getFontMetrics(font);
g2.setFont(font);
fontAscent = fontmetrics.getAscent();
fontHeight = fontmetrics.getHeight();
}
}
}
private static class AboutBoxLine {
private final String myText;
private final boolean myBold;
private final String myUrl;
private boolean myKeepWithNext;
public AboutBoxLine(final String text, final boolean bold, final String url) {
myText = text;
myBold = bold;
myUrl = url;
}
public AboutBoxLine(final String text) {
myText = text;
myBold = false;
myUrl = null;
}
public String getText() {
return myText;
}
public boolean isBold() {
return myBold;
}
public String getUrl() {
return myUrl;
}
public boolean isKeepWithNext() {
return myKeepWithNext;
}
public AboutBoxLine keepWithNext() {
myKeepWithNext = true;
return this;
}
}
}