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 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(), "", ""); // 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 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 true if finish is allowed, false * 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(); }