| /* |
| * Copyright 2000-2014 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.ui.messages; |
| |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.ui.Gray; |
| import com.intellij.ui.JBColor; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.ui.UIUtil; |
| import org.jdesktop.swingx.graphics.GraphicsUtilities; |
| import org.jdesktop.swingx.graphics.ShadowRenderer; |
| |
| import javax.swing.*; |
| import javax.swing.event.HyperlinkEvent; |
| import javax.swing.event.HyperlinkListener; |
| import java.awt.*; |
| import java.awt.event.*; |
| import java.awt.geom.Rectangle2D; |
| import java.awt.image.BufferedImage; |
| import java.io.IOException; |
| import java.net.URISyntaxException; |
| |
| /** |
| * Created by Denis Fokin |
| */ |
| public class SheetController { |
| |
| private static final Logger LOG = Logger.getInstance(SheetController.class); |
| private static final int SHEET_MINIMUM_HEIGHT = 143; |
| private static String fontName = "Lucida Grande"; |
| private static Font regularFont = new Font(fontName, Font.PLAIN, 10); |
| private static Font boldFont = new Font(fontName, Font.BOLD, 12).deriveFont(Font.BOLD); |
| private final DialogWrapper.DoNotAskOption myDoNotAskOption; |
| private JCheckBox doNotAskCheckBox; |
| private boolean myDoNotAskResult; |
| |
| private BufferedImage myShadowImage; |
| |
| private JButton[] buttons; |
| private JButton myDefaultButton; |
| private JButton myFocusedButton; |
| |
| public static int SHADOW_BORDER = 5; |
| |
| private static int RIGHT_OFFSET = 10 - SHADOW_BORDER; |
| private static int BOTTOM_SHEET_PADDING = 10; |
| |
| private final static int TOP_SHEET_PADDING = 20; |
| private final static int GAP_BETWEEN_LINES = 10; |
| |
| private final static int LEFT_SHEET_PADDING = 35; |
| private final static int LEFT_SHEET_OFFSET = 120; |
| |
| private static int GAP_BETWEEN_BUTTONS = 5; |
| |
| private static String SPACE_OR_LINE_SEPARATOR_PATTERN = "[\\s" + System.getProperty("line.separator") + "]+"; |
| |
| // SHEET |
| public int SHEET_WIDTH = 400; |
| |
| public int SHEET_HEIGHT = 143; |
| |
| // SHEET + shadow |
| int SHEET_NC_WIDTH = SHEET_WIDTH + SHADOW_BORDER * 2; |
| int SHEET_NC_HEIGHT = SHEET_HEIGHT + SHADOW_BORDER ; |
| |
| |
| private Icon myIcon = UIUtil.getInformationIcon(); |
| |
| private String myResult; |
| private JPanel mySheetPanel; |
| private SheetMessage mySheetMessage; |
| |
| private JEditorPane messageTextPane = new JEditorPane(); |
| private Dimension messageArea = new Dimension(250, Short.MAX_VALUE); |
| |
| SheetController(final SheetMessage sheetMessage, |
| final String title, |
| final String message, |
| final Icon icon, |
| final String[] buttonTitles, |
| final String defaultButtonTitle, |
| final DialogWrapper.DoNotAskOption doNotAskOption, |
| final String focusedButton) { |
| if (icon != null) { |
| myIcon = icon; |
| } |
| |
| myDoNotAskOption = doNotAskOption; |
| myDoNotAskResult = (doNotAskOption != null) && !doNotAskOption.isToBeShown(); |
| mySheetMessage = sheetMessage; |
| buttons = new JButton[buttonTitles.length]; |
| |
| myResult = null; |
| |
| for (int i = 0; i < buttons.length; i++) { |
| String buttonTitle = buttonTitles[i]; |
| |
| buttons[i] = new JButton(); |
| buttons[i].setOpaque(false); |
| handleMnemonics(i, buttonTitle); |
| |
| if (buttonTitle.equals(defaultButtonTitle)) { |
| myDefaultButton = buttons[i]; |
| } |
| if (buttonTitle.equals(focusedButton)) { |
| myFocusedButton = buttons[i]; |
| } |
| } |
| |
| if (myFocusedButton == null) { |
| myFocusedButton = myDefaultButton; |
| } |
| |
| if (myResult == null) { |
| myResult = Messages.CANCEL_BUTTON; |
| } |
| |
| mySheetPanel = createSheetPanel(title, message, buttons); |
| |
| initShadowImage(); |
| } |
| |
| private void initShadowImage() { |
| |
| final BufferedImage mySheetStencil = GraphicsUtilities.createCompatibleTranslucentImage(SHEET_WIDTH, SHEET_HEIGHT); |
| |
| Graphics2D g2 = mySheetStencil.createGraphics(); |
| g2.setColor(new JBColor(Gray._255, Gray._0)); |
| g2.fillRect(0, 0, SHEET_WIDTH, SHEET_HEIGHT); |
| g2.dispose(); |
| |
| ShadowRenderer renderer = new ShadowRenderer(); |
| renderer.setSize(SHADOW_BORDER); |
| renderer.setOpacity(.80f); |
| renderer.setColor(new JBColor(JBColor.BLACK, Gray._10)); |
| myShadowImage = renderer.createShadow(mySheetStencil); |
| } |
| |
| private void handleMnemonics(int i, String buttonTitle) { |
| buttons[i].setName(buttonTitle); |
| buttons[i].setText(buttonTitle); |
| setMnemonicsFromChar('&', buttons[i]); |
| setMnemonicsFromChar('_', buttons[i]); |
| } |
| |
| private static void setMnemonicsFromChar(char mnemonicChar, JButton button) { |
| String buttonTitle = button.getText(); |
| if (buttonTitle.indexOf(mnemonicChar) != -1) { |
| button.setMnemonic(buttonTitle.charAt(buttonTitle.indexOf(mnemonicChar) + 1)); |
| button.setText(buttonTitle.replace(Character.toString(mnemonicChar), "")); |
| } |
| } |
| |
| void requestFocus() { |
| final JComponent focusedComponent = (myDoNotAskOption == null) ? myFocusedButton : doNotAskCheckBox; |
| if (focusedComponent == null) return; // it might be we have only one button. it is a default one in that case |
| if (SystemInfo.isAppleJvm) { |
| focusedComponent.requestFocus(); |
| } else { |
| focusedComponent.requestFocusInWindow(); |
| } |
| } |
| |
| JPanel getPanel(final JDialog w) { |
| w.getRootPane().setDefaultButton(myDefaultButton); |
| |
| |
| ActionListener actionListener = new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (e.getSource() instanceof JButton) { |
| myResult = ((JButton)e.getSource()).getName(); |
| } |
| mySheetMessage.startAnimation(false); |
| } |
| }; |
| |
| mySheetPanel.registerKeyboardAction(actionListener, |
| KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), |
| JComponent.WHEN_IN_FOCUSED_WINDOW); |
| |
| for (JButton button: buttons) { |
| button.addActionListener(actionListener); |
| } |
| return mySheetPanel; |
| } |
| |
| private static float getSheetAlpha() { |
| return .95f; |
| } |
| |
| private JPanel createSheetPanel(String title, String message, JButton[] buttons) { |
| JPanel sheetPanel = new JPanel() { |
| @Override |
| protected void paintComponent(Graphics g2d) { |
| final Graphics2D g = (Graphics2D) g2d.create(); |
| super.paintComponent(g); |
| g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getSheetAlpha())); |
| |
| g.setColor(new JBColor(Gray._230, UIUtil.getPanelBackground())); |
| Rectangle2D dialog = new Rectangle2D.Double(SHADOW_BORDER, 0, SHEET_WIDTH, SHEET_HEIGHT); |
| |
| paintShadow(g); |
| // draw the sheet background |
| if (UIUtil.isUnderDarcula()) { |
| g.fillRoundRect((int)dialog.getX(), (int)dialog.getY() - 5, (int)dialog.getWidth(), (int)(5 + dialog.getHeight()), 5, 5); |
| } else { |
| //todo make bottom corners |
| g.fill(dialog); |
| } |
| paintShadowFromParent(g); |
| } |
| |
| }; |
| |
| sheetPanel.setOpaque(false); |
| sheetPanel.setLayout(null); |
| |
| JPanel ico = new JPanel() { |
| @Override |
| protected void paintComponent(Graphics g) { |
| super.paintComponent(g); |
| myIcon.paintIcon(this, g, 0, 0); |
| } |
| }; |
| |
| |
| JEditorPane headerLabel = new JEditorPane(); |
| |
| |
| |
| headerLabel.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); |
| headerLabel.setFont(boldFont); |
| headerLabel.setEditable(false); |
| |
| headerLabel.setContentType("text/html"); |
| headerLabel.setSize(250, Short.MAX_VALUE); |
| headerLabel.setText(title); |
| headerLabel.setSize(250, headerLabel.getPreferredSize().height); |
| |
| headerLabel.setOpaque(false); |
| headerLabel.setFocusable(false); |
| |
| sheetPanel.add(headerLabel); |
| |
| headerLabel.repaint(); |
| |
| messageTextPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE); |
| messageTextPane.setFont(regularFont); |
| messageTextPane.setEditable(false); |
| |
| messageTextPane.setContentType("text/html"); |
| |
| messageTextPane.addHyperlinkListener(new HyperlinkListener() { |
| public void hyperlinkUpdate(HyperlinkEvent he) { |
| if(he.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { |
| if(Desktop.isDesktopSupported()) { |
| try { |
| Desktop.getDesktop().browse(he.getURL().toURI()); |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| } |
| catch (URISyntaxException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| } |
| }); |
| |
| FontMetrics fontMetrics = mySheetMessage.getFontMetrics(regularFont); |
| |
| int widestWordWidth = 250; |
| |
| String [] words = (message == null) ? ArrayUtil.EMPTY_STRING_ARRAY : message.split(SPACE_OR_LINE_SEPARATOR_PATTERN); |
| |
| for (String word : words) { |
| widestWordWidth = Math.max(fontMetrics.stringWidth(word), widestWordWidth); |
| } |
| |
| messageTextPane.setSize(widestWordWidth, Short.MAX_VALUE); |
| messageTextPane.setText(handleBreaks(message)); |
| messageArea.setSize(widestWordWidth, messageTextPane.getPreferredSize().height); |
| |
| SHEET_WIDTH = Math.max(LEFT_SHEET_OFFSET + widestWordWidth + RIGHT_OFFSET, SHEET_WIDTH); |
| messageTextPane.setSize(messageArea); |
| |
| messageTextPane.setOpaque(false); |
| messageTextPane.setFocusable(false); |
| |
| sheetPanel.add(messageTextPane); |
| |
| messageTextPane.repaint(); |
| |
| ico.setOpaque(false); |
| ico.setSize(new Dimension(AllIcons.Logo_welcomeScreen.getIconWidth(), AllIcons.Logo_welcomeScreen.getIconHeight())); |
| ico.setLocation(LEFT_SHEET_PADDING, TOP_SHEET_PADDING); |
| sheetPanel.add(ico); |
| headerLabel.setLocation(LEFT_SHEET_OFFSET, TOP_SHEET_PADDING); |
| messageTextPane.setLocation(LEFT_SHEET_OFFSET, TOP_SHEET_PADDING + headerLabel.getPreferredSize().height + GAP_BETWEEN_LINES); |
| |
| SHEET_HEIGHT = TOP_SHEET_PADDING + headerLabel.getPreferredSize().height + GAP_BETWEEN_LINES + messageArea.height + GAP_BETWEEN_LINES; |
| |
| if (myDoNotAskOption != null) { |
| layoutDoNotAskCheckbox(sheetPanel); |
| } |
| |
| layoutWithAbsoluteLayout(buttons, sheetPanel); |
| |
| SHEET_HEIGHT += BOTTOM_SHEET_PADDING; |
| |
| if (SHEET_HEIGHT < SHEET_MINIMUM_HEIGHT) { |
| SHEET_HEIGHT = SHEET_MINIMUM_HEIGHT; |
| shiftButtonsToTheBottom(SHEET_MINIMUM_HEIGHT - SHEET_HEIGHT); |
| } |
| |
| sheetPanel.setFocusCycleRoot(true); |
| |
| recalculateShadow(); |
| |
| sheetPanel.setSize(SHEET_NC_WIDTH, SHEET_NC_HEIGHT); |
| |
| return sheetPanel; |
| } |
| |
| private static String handleBreaks(final String message) { |
| return message.replaceAll("(\r\n|\n)", "<br/>"); |
| } |
| |
| private void shiftButtonsToTheBottom(int shiftDistance) { |
| for (JButton b : buttons) { |
| b.setLocation(b.getX(), b.getY() + shiftDistance); |
| } |
| } |
| |
| private void recalculateShadow() { |
| SHEET_NC_WIDTH = SHEET_WIDTH + SHADOW_BORDER * 2; |
| SHEET_NC_HEIGHT = SHEET_HEIGHT + SHADOW_BORDER; |
| } |
| |
| private void layoutWithAbsoluteLayout(JButton[] buttons, JPanel sheetPanel) { |
| layoutButtons(buttons, sheetPanel); |
| } |
| |
| private void paintShadow(Graphics2D g2d) { |
| g2d.setBackground(new JBColor(new Color(255, 255, 255, 0), new Color(110, 110, 110, 0))); |
| g2d.clearRect(0, 0, SHEET_NC_WIDTH, SHEET_HEIGHT); |
| g2d.drawImage(myShadowImage, 0, -SHADOW_BORDER, null); |
| g2d.clearRect(SHADOW_BORDER, 0, SHEET_WIDTH, SHEET_HEIGHT); |
| } |
| |
| |
| private static float getShadowAlpha() { |
| return ((UIUtil.isUnderDarcula())) ? .85f : .35f; |
| } |
| |
| private void paintShadowFromParent(Graphics2D g2d) { |
| g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getShadowAlpha())); |
| g2d.drawImage(myShadowImage, 0, - SHEET_HEIGHT - SHADOW_BORDER, null); |
| } |
| |
| private void layoutButtons(final JButton[] buttons, JPanel panel) { |
| |
| //int widestButtonWidth = 0; |
| int buttonWidth = 0; |
| SHEET_HEIGHT += GAP_BETWEEN_LINES; |
| |
| for (int i = 0; i < buttons.length; i ++) { |
| buttons[i].repaint(); |
| buttons[i].setSize(buttons[i].getPreferredSize()); |
| buttonWidth += buttons[i].getWidth(); |
| if (i == buttons.length - 1) break; |
| buttonWidth += GAP_BETWEEN_BUTTONS; |
| } |
| |
| int buttonsRowWidth = LEFT_SHEET_OFFSET + buttonWidth + RIGHT_OFFSET; |
| |
| // update the pane if the sheet is going to be wider |
| messageTextPane.setSize(Math.max(messageTextPane.getWidth(), buttonWidth), messageTextPane.getHeight()); |
| |
| SHEET_WIDTH = Math.max(buttonsRowWidth, SHEET_WIDTH); |
| |
| int buttonShift = RIGHT_OFFSET; |
| |
| for (JButton button : buttons) { |
| Dimension size = button.getSize(); |
| buttonShift += size.width; |
| button.setBounds(SHEET_WIDTH - buttonShift, |
| SHEET_HEIGHT, |
| size.width, size.height); |
| panel.add(button); |
| buttonShift += GAP_BETWEEN_BUTTONS; |
| } |
| |
| SHEET_HEIGHT += buttons[0].getHeight(); |
| } |
| |
| private void layoutDoNotAskCheckbox(JPanel sheetPanel) { |
| doNotAskCheckBox = new JCheckBox(myDoNotAskOption.getDoNotShowMessage(), !myDoNotAskOption.isToBeShown()); |
| doNotAskCheckBox.addItemListener(new ItemListener() { |
| @Override |
| public void itemStateChanged(ItemEvent e) { |
| myDoNotAskResult = e.getStateChange() == ItemEvent.SELECTED; |
| } |
| }); |
| doNotAskCheckBox.repaint(); |
| doNotAskCheckBox.setSize(doNotAskCheckBox.getPreferredSize()); |
| |
| doNotAskCheckBox.setLocation(LEFT_SHEET_OFFSET, SHEET_HEIGHT); |
| sheetPanel.add(doNotAskCheckBox); |
| |
| SHEET_HEIGHT += doNotAskCheckBox.getHeight(); |
| } |
| |
| /** |
| * This method is used to show an image during message showing |
| * @return image to show |
| */ |
| Image getStaticImage() { |
| final JFrame myOffScreenFrame = new JFrame() ; |
| myOffScreenFrame.add(mySheetPanel); |
| myOffScreenFrame.getRootPane().setDefaultButton(myDefaultButton); |
| |
| final BufferedImage image = (SystemInfo.isJavaVersionAtLeast("1.7")) ? |
| UIUtil.createImage(SHEET_NC_WIDTH, SHEET_NC_HEIGHT, BufferedImage.TYPE_INT_ARGB) : |
| GraphicsUtilities.createCompatibleTranslucentImage(SHEET_NC_WIDTH, SHEET_NC_HEIGHT); |
| |
| Graphics g = image.createGraphics(); |
| mySheetPanel.paint(g); |
| |
| g.dispose(); |
| |
| myOffScreenFrame.dispose(); |
| return image; |
| } |
| |
| public boolean getDoNotAskResult () { |
| return myDoNotAskResult; |
| } |
| |
| public String getResult() { |
| return myResult; |
| } |
| } |