package org.openslx.dozmod.gui.wizard;
import org.openslx.dozmod.gui.Gui;
import org.openslx.dozmod.gui.helper.I18n;
import org.openslx.dozmod.gui.helper.StatusHeader;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
public abstract class Wizard extends JDialog {
/**
* Version for serialization.
*/
private static final long serialVersionUID = 1436816291272809418L;
private final StatusHeader header;
private final List<WizardPage> pages = new ArrayList<>();
private WizardPage postFinishPage = null;
private boolean isPostFinish = false;
private final JPanel contentPanel;
private int currentPage = -1;
private boolean needsLayout = true;
private boolean isCancelled = false;
// Reference if an out of order page is shown.
protected WizardPage outOfOrderPage = null;
private final JButton btnPrev;
private final JButton btnNext;
private final JButton btnCancel;
private final JButton btnFinish;
public Wizard(Window parent) {
super(parent, ModalityType.APPLICATION_MODAL);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setLayout(new BorderLayout());
this.header = new StatusHeader(getContentPane(), "<titel>", "<message>");
// Buttons in footer
JPanel footer = new JPanel();
footer.setLayout(new BoxLayout(footer, BoxLayout.LINE_AXIS));
footer.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
footer.add(Box.createHorizontalGlue());
btnPrev = new JButton(I18n.WIZARD.getString("Wizard.Button.prev.text"));
btnNext = new JButton(I18n.WIZARD.getString("Wizard.Button.next.text"));
btnCancel = new JButton(I18n.WIZARD.getString("Wizard.Button.cancel.text"));
btnFinish = new JButton(I18n.WIZARD.getString("Wizard.Button.finish.text.0"));
footer.add(btnPrev);
footer.add(btnNext);
footer.add(Box.createRigidArea(new Dimension(10, 10)));
footer.add(btnCancel);
footer.add(Box.createRigidArea(new Dimension(5, 5)));
footer.add(btnFinish);
add(footer, BorderLayout.PAGE_END);
// Add content panel
contentPanel = new JPanel();
contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.PAGE_AXIS));
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
add(contentPanel, BorderLayout.CENTER);
// Scale window with font size
setPreferredSize(Gui.getScaledDimension(550, 420));
setResizable(false);
pack();
Gui.centerShellOverShell(parent, this);
// Window events
addWindowListener(new WindowAdapter() {
@Override public void windowClosing(WindowEvent we) {
doCancel();
}
});
// Äkschns
btnNext.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
doNext();
}
});
btnPrev.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
doPrevious();
}
});
btnCancel.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
doCancel();
}
});
btnFinish.addActionListener(new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
doFinish();
}
});
}
@Override
public void setVisible(boolean visible) {
if (visible) {
if (needsLayout) {
needsLayout = false;
pack();
if (!pages.isEmpty()) {
showPage(0);
}
}
}
super.setVisible(visible);
}
protected void showPage(int index) {
if (currentPage != -1) {
WizardPage old = getPage(currentPage);
old.onPageLeave();
old.setVisible(false);
}
WizardPage page = getPage(index);
page.onPageEnter();
page.setVisible(true);
currentPage = index;
updateHeader(page);
updateButtons(page);
validate();
}
// Show an out of order page e.g. for converting an image
public void showOutOfOrderPage(WizardPage page) {
WizardPage old = getPage(currentPage);
outOfOrderPage = page;
old.onPageLeave();
old.setVisible(false);
page.onPageEnter();
page.setVisible(true);
updateHeader(page);
updateButtons(page);
validate();
}
// Show an out of order page e.g. for converting an image
/**
* @param newPage to return to.
* @param oldPage that should be hidden.
*/
public void returnAfterOutOfOrderPage(WizardPage newPage, WizardPage oldPage) {
oldPage.onPageLeave();
oldPage.setVisible(false);
newPage.onPageEnter();
newPage.setVisible(true);
updateHeader(newPage);
updateButtons(newPage);
validate();
}
void updateHeader(WizardPage page) {
if (!isPostFinish && (currentPage == -1 || getPage(currentPage) != page))
return;
String pageTitle = page.getTitle();
String pageDesc = page.getMessage();
if (pageTitle == null)
pageTitle = "Step " + currentPage;
if (pageDesc == null)
pageDesc = "";
header.updateHeader(pageTitle, pageDesc, page.getMessageIcon(), page.getMessageColor());
setTitle(getWindowTitle() + " - " + pageTitle);
}
public abstract String getWindowTitle();
protected final void addPage(WizardPage page) {
contentPanel.add(page);
if (!pages.isEmpty()) {
page.setVisible(false);
}
pages.add(page);
}
// For adding pages which are not in the normal linear flow
protected final void addOutOfOrderPage(WizardPage page) {
contentPanel.add(page);
page.setVisible(false);
}
protected final void removePages(List<WizardPage> currentPages) {
for (WizardPage i : currentPages) {
pages.remove(i);
}
}
protected void addSummaryPage(WizardPage page) {
postFinishPage = page;
}
void updateButtons(WizardPage page) {
if (isPostFinish) {
btnFinish.setEnabled(postFinishPage.isComplete());
return;
}
// State of finish button
boolean canFinish = true;
for (WizardPage p : pages) {
if (!p.isComplete()) {
canFinish = false;
break;
}
}
btnFinish.setEnabled(canFinish);
// State of next button
if (outOfOrderPage != null) {
btnNext.setEnabled(page.isComplete());
}
if (currentPage != -1 && getPage(currentPage) == page) {
btnNext.setEnabled(currentPage + 1 < pages.size() && page.isComplete());
btnPrev.setEnabled(currentPage > 0 && getPage(currentPage - 1).canComeBack);
}
}
/**
* Returns whether the page currently shown is the summary page.
*
* @return whether the page currently shown is the summary page.
*/
protected final boolean isSummaryPage() {
return isPostFinish;
}
/**
* Returns true if the wizard was cancelled. Mostly useful in onPageLeave to
* distinguish what's happening.
*
* @return true if the wizard was cancelled.
*/
public final boolean isCancelled() {
return isCancelled;
}
public void doNext() {
if (isPostFinish || !btnNext.isEnabled())
return;
if (currentPage + 1 < pages.size()) {
if (!getPage(currentPage).wantNextOrFinish())
return; // Page canceled the operation
showPage(currentPage + 1);
}
}
// protected void returnAfterOutOfOrderPage(int previousPage){
// showPage(previousPage);
// }
protected void doPrevious() {
if (isPostFinish || !btnPrev.isEnabled())
return;
if (currentPage > 0) {
showPage(currentPage - 1);
}
}
protected final void doCancel() {
if (onCancelRequest()) {
isCancelled = true;
if (currentPage != -1) {
getPage(currentPage).onPageLeave();
}
if (isPostFinish) {
postFinishPage.onPageLeave();
}
if (outOfOrderPage != null) {
outOfOrderPage.onPageLeave();
}
cleanup();
}
}
protected final void doFinish() {
if (isPostFinish) {
postFinishPage.onPageLeave();
cleanup();
return;
}
if (!btnFinish.isEnabled())
return;
if (currentPage != -1) {
if (!getPage(currentPage).wantNextOrFinish())
return;
}
if (wantFinish()) {
if (currentPage != -1) {
getPage(currentPage).onPageLeave();
}
postFinishPage = performFinish();
if (postFinishPage == null) {
cleanup();
} else {
isPostFinish = true;
btnPrev.setVisible(false);
btnNext.setVisible(false);
btnFinish.setText(I18n.WIZARD.getString("Wizard.Button.finish.text.1"));
postFinishPage.setVisible(false);
contentPanel.add(postFinishPage);
showPage(-1);
}
}
}
private WizardPage getPage(int index) {
if (isPostFinish && index == -1)
return postFinishPage;
return pages.get(index);
}
/*
* Callback to wizard implementation
*/
/**
* User clicked cancel or (X) - when returning false,
* wizard will stay open
*
* @return
*/
protected boolean onCancelRequest() {
return true;
}
/**
* Called when user clicks finish. Override to do final checks and take
* appropriate actions.
*
* @return <code>true</code> if finish is allowed, <code>false</code>
* otherwise
*/
protected boolean wantFinish() {
return true;
}
/**
* Called so the wizard can perform finishing steps. This is called after
* wantFinish() returned true and onPageLeave was called on the currently
* visible wizard step.
* If a wizard page is returned, it will serve as a summary page shown to
* the user within the wizard frame. The prev and next buttons will
* disappear, and the finish button will change to a close button.
*
* @return A wizard page that serves as a summary page, or null if the
* wizard should just close
*/
protected WizardPage performFinish() {
return null;
}
/**
* Called when this wizard is closed, either by aborting or
* by finishing it.
*/
private final void cleanup()
{
doCleanup();
dispose();
pages.clear();
postFinishPage = null;
outOfOrderPage = null;
}
/**
* Force all derived wizards to think about what
* references they might want to free when they're
* being closed.
*/
protected abstract void doCleanup();
}