/*
* WizardPane.java 7 juin 07
*
* Sweet Home 3D, Copyright (c) 2007 Emmanuel PUYBARET / eTeks <info@eteks.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.eteks.sweethome3d.swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.Locale;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.BevelBorder;
import com.eteks.sweethome3d.model.UserPreferences;
import com.eteks.sweethome3d.tools.OperatingSystem;
import com.eteks.sweethome3d.viewcontroller.DialogView;
import com.eteks.sweethome3d.viewcontroller.View;
import com.eteks.sweethome3d.viewcontroller.WizardController;
/**
* Wizard pane.
* @author Emmanuel Puybaret
*/
public class WizardPane extends JOptionPane implements DialogView {
private final UserPreferences preferences;
private final WizardController controller;
private JButton backOptionButton;
private JButton nextFinishOptionButton;
private String defaultTitle;
private JDialog dialog;
/**
* Creates a wizard view controlled by <code>controller</code>.
*/
public WizardPane(UserPreferences preferences,
final WizardController controller) {
this.preferences = preferences;
this.controller = controller;
this.defaultTitle = preferences.getLocalizedString(WizardPane.class, "wizard.title");
setMessage(new JPanel(new BorderLayout(10, 0)));
createOptionButtons(preferences, controller);
setOptionType(DEFAULT_OPTION);
String cancelOption = preferences.getLocalizedString(WizardPane.class, "cancelOption");
// Make backOptionButton appear at left of nextFinishOptionButton
if (UIManager.getBoolean("OptionPane.isYesLast")
|| OperatingSystem.isMacOSX()) {
setOptions(new Object [] {this.nextFinishOptionButton, this.backOptionButton, cancelOption});
} else {
setOptions(new Object [] {this.backOptionButton, this.nextFinishOptionButton, cancelOption});
}
setInitialValue(this.nextFinishOptionButton);
// Update wizard pane content and icon
updateStepView(controller);
controller.addPropertyChangeListener(WizardController.Property.STEP_VIEW,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
updateStepView(controller);
}
});
updateStepIcon(controller);
controller.addPropertyChangeListener(WizardController.Property.STEP_ICON,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
updateStepIcon(controller);
}
});
}
private void createOptionButtons(UserPreferences preferences,
final WizardController controller) {
this.backOptionButton = new JButton(SwingTools.getLocalizedLabelText(preferences,
WizardPane.class, "backOptionButton.text"));
this.backOptionButton.setEnabled(controller.isBackStepEnabled());
controller.addPropertyChangeListener(WizardController.Property.BACK_STEP_ENABLED,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
backOptionButton.setEnabled(controller.isBackStepEnabled());
}
});
if (!OperatingSystem.isMacOSX()) {
this.backOptionButton.setMnemonic(
KeyStroke.getKeyStroke(preferences.getLocalizedString(
WizardPane.class, "backOptionButton.mnemonic")).getKeyCode());
}
this.nextFinishOptionButton = new JButton();
this.nextFinishOptionButton.setEnabled(controller.isNextStepEnabled());
controller.addPropertyChangeListener(WizardController.Property.NEXT_STEP_ENABLED,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
nextFinishOptionButton.setEnabled(controller.isNextStepEnabled());
}
});
// Update nextFinishButton text and mnemonic
updateNextFinishOptionButton(controller);
controller.addPropertyChangeListener(WizardController.Property.LAST_STEP,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
updateNextFinishOptionButton(controller);
}
});
// Add action listeners
this.backOptionButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
controller.goBackToPreviousStep();
}
});
this.nextFinishOptionButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
if (controller.isLastStep()) {
controller.finish();
setValue(nextFinishOptionButton);
if (dialog != null) {
dialog.setVisible(false);
}
} else {
controller.goToNextStep();
}
}
});
}
/**
* Sets whether this wizard view is displaying the last step or not.
*/
private void updateNextFinishOptionButton(WizardController controller) {
this.nextFinishOptionButton.setText(SwingTools.getLocalizedLabelText(this.preferences, WizardPane.class,
controller.isLastStep()
? "finishOptionButton.text"
: "nextOptionButton.text"));
if (!OperatingSystem.isMacOSX()) {
this.nextFinishOptionButton.setMnemonic(KeyStroke.getKeyStroke(
this.preferences.getLocalizedString(WizardPane.class,
controller.isLastStep()
? "finishOptionButton.mnemonic"
: "nextOptionButton.mnemonic")).getKeyCode());
}
}
/**
* Updates the step view displayed by this wizard view.
*/
private void updateStepView(WizardController controller) {
JPanel messagePanel = (JPanel)getMessage();
// Clean previous step view
Component previousStepView = ((BorderLayout)messagePanel.getLayout()).getLayoutComponent(BorderLayout.CENTER);
if (previousStepView != null) {
messagePanel.remove(previousStepView);
}
// Add new step view
View stepView = controller.getStepView();
if (stepView != null) {
messagePanel.add((JComponent)stepView, BorderLayout.CENTER);
}
if (this.dialog != null && !this.controller.isResizable()) {
this.dialog.pack();
}
}
/**
* Updates the step icon displayed by this wizard view.
*/
private void updateStepIcon(WizardController controller) {
JPanel messagePanel = (JPanel)getMessage();
Component previousStepIconLabel = ((BorderLayout)messagePanel.getLayout()).getLayoutComponent(BorderLayout.WEST);
if (previousStepIconLabel != null) {
// Clean previous icon label
messagePanel.remove(previousStepIconLabel);
}
// Add new icon
URL stepIcon = controller.getStepIcon();
if (stepIcon != null) {
JLabel iconLabel = new JLabel(new ImageIcon(stepIcon)) {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D)g;
// Paint a blue gradient behind icon
g2D.setPaint(new GradientPaint(0, 0, new Color(163, 168, 226),
0, getHeight(), new Color(80, 86, 158)));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
// Use a bevel border 1 pixel wide
iconLabel.setBorder(new BevelBorder(BevelBorder.LOWERED) {
@Override
public Insets getBorderInsets(Component c) {
return new Insets(1, 1, 1, 1);
}
@Override
protected void paintLoweredBevel(Component c, Graphics g, int x, int y,
int width, int height) {
Color oldColor = g.getColor();
g.translate(x, y);
g.setColor(getShadowInnerColor(c));
g.drawLine(0, 0, 0, height - 1);
g.drawLine(0, 0, width - 1, 0);
g.setColor(getHighlightInnerColor(c));
g.drawLine(0, height - 1, width - 1, height - 1);
g.drawLine(width - 1, 1, width - 1, height - 2);
g.translate(-x, -y);
g.setColor(oldColor);
}
});
// We don't use JOptionPane icon to let icon background spread in all height
messagePanel.add(iconLabel, BorderLayout.LINE_START);
}
}
/**
* Displays this wizard view in a modal dialog.
*/
public void displayView(View parentView) {
this.dialog = createDialog(SwingUtilities.getRootPane((JComponent)parentView),
this.controller.getTitle() != null
? this.controller.getTitle()
: this.defaultTitle);
this.controller.addPropertyChangeListener(WizardController.Property.TITLE,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
dialog.setTitle(controller.getTitle() != null
? controller.getTitle()
: defaultTitle);
}
});
this.dialog.applyComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));
this.dialog.setResizable(this.controller.isResizable());
this.controller.addPropertyChangeListener(WizardController.Property.RESIZABLE,
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent ev) {
dialog.setResizable(controller.isResizable());
}
});
// Pack again because resize decorations may have changed dialog preferred size
this.dialog.pack();
this.dialog.setMinimumSize(getSize());
this.dialog.setVisible(true);
this.dialog.dispose();
}
}