package org.openslx.dozmod.gui.wizard; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Window; 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; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.SwingConstants; import org.openslx.dozmod.gui.Gui; import org.openslx.dozmod.gui.control.QLabel; import org.openslx.dozmod.gui.helper.GridManager; import org.openslx.dozmod.util.ResourceLoader; @SuppressWarnings("serial") public abstract class Wizard extends JDialog { private final QLabel titleLabel; private final QLabel messageLabel; 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; 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()); JPanel header = new JPanel(); header.setMinimumSize(Gui.getScaledDimension(0, 100)); header.setOpaque(true); header.setBackground(Color.WHITE); header.setLayout(new BoxLayout(header, BoxLayout.PAGE_AXIS)); header.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Labels in header titleLabel = new QLabel(""); messageLabel = new QLabel("<message>"); titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD)); titleLabel.setForeground(Color.BLACK); messageLabel.setForeground(Color.BLACK); messageLabel.setHorizontalTextPosition(SwingConstants.RIGHT); header.add(titleLabel); header.add(messageLabel); // Add header JPanel headerWrapper = new JPanel(); GridManager grid = new GridManager(headerWrapper, 1, false); grid.add(header).expand(true, false).fill(true, false); grid.add(new JSeparator()).expand(true, false).fill(true, false); grid.finish(false); getContentPane().add(headerWrapper, BorderLayout.PAGE_START); // 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("< Zurück"); btnNext = new JButton("Weiter >"); btnCancel = new JButton("Abbrechen"); btnFinish = new JButton("Fertigstellen"); 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(); } }); } @SuppressWarnings("deprecation") @Override public void show() { if (needsLayout) { needsLayout = false; pack(); if (!pages.isEmpty()) { showPage(0); } } super.show(); } private 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(); } 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 = ""; titleLabel.setText(pageTitle); messageLabel.setText(pageDesc); messageLabel.setIcon(ResourceLoader.getScaledIcon(page.getMessageIcon(), messageLabel.getHeight(), messageLabel)); messageLabel.setForeground(page.getMessageColor()); messageLabel.validate(); 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); } 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 (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 final 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 final 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(); } dispose(); } } protected final void doFinish() { if (isPostFinish) { postFinishPage.onPageLeave(); dispose(); 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) { dispose(); } else { isPostFinish = true; btnPrev.setVisible(false); btnNext.setVisible(false); btnFinish.setText("Schließen"); 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; } }