/*
* Copyright 2016 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor 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 Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.gui.utils;
import com.bric.util.JVM;
import pixelitor.gui.GlobalKeyboardWatch;
import pixelitor.gui.PixelitorWindow;
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.function.Predicate;
import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
/**
* A fluent interface for building JDialogs
*/
public class DialogBuilder {
public static final String DEFAULT_OK_TEXT = UIManager.getString("OptionPane.okButtonText");
public static final String DEFAULT_CANCEL_TEXT = UIManager.getString("OptionPane.cancelButtonText");
private String okText;
private String cancelText;
private boolean addOKButton = true;
private boolean addCancelButton = true;
private JComponent form;
private boolean addScrollBars;
private JFrame frameParent;
private JDialog dialogParent;
private String title;
private boolean reconfigureGlobalKeyWatch = true;
private boolean modal = true;
private Runnable okAction;
private Runnable cancelAction;
private Predicate<JDialog> validator;
public DialogBuilder() {
}
public DialogBuilder title(String s) {
this.title = s;
return this;
}
public DialogBuilder parent(Window window) {
if (window instanceof JFrame) {
frameParent = (JFrame) window;
} else if (window instanceof JDialog) {
dialogParent = (JDialog) window;
} else {
throw new IllegalStateException(window == null
? "null window"
: window.getClass().getName());
}
return this;
}
public DialogBuilder okText(String s) {
this.okText = s;
return this;
}
public DialogBuilder cancelText(String s) {
this.cancelText = s;
return this;
}
public DialogBuilder form(JComponent form) {
this.form = form;
return this;
}
public DialogBuilder notModal() {
this.modal = false;
return this;
}
public DialogBuilder withScrollbars(boolean b) {
this.addScrollBars = b;
return this;
}
public DialogBuilder noCancelButton() {
addCancelButton = false;
return this;
}
public DialogBuilder noOKButton() {
addOKButton = false;
return this;
}
public DialogBuilder noGlobalKeyChange() {
this.reconfigureGlobalKeyWatch = false;
return this;
}
public DialogBuilder okAction(Runnable a) {
this.okAction = a;
return this;
}
public DialogBuilder cancelAction(Runnable a) {
this.cancelAction = a;
return this;
}
public DialogBuilder validator(Predicate<JDialog> a) {
this.validator = a;
return this;
}
public JDialog show() {
assert form != null : "no form";
setupDefaults();
JDialog d;
if (frameParent != null) {
d = new JDialog(frameParent, title, modal);
} else if (dialogParent != null) {
d = new JDialog(dialogParent, title, modal);
} else {
PixelitorWindow pw = PixelitorWindow.getInstance();
d = new JDialog(pw, title, modal);
}
d.setLayout(new BorderLayout());
if (addScrollBars) {
JScrollPane scrollPane = new JScrollPane(form, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
d.add(scrollPane, BorderLayout.CENTER);
} else {
d.add(form, BorderLayout.CENTER);
}
JButton okButton = null;
if (addOKButton) {
okButton = new JButton(okText);
okButton.setName("ok");
okButton.addActionListener(e -> {
if (validator != null) {
if (!validator.test(d)) {
return;
}
}
closeDialog(d);
if (okAction != null) {
okAction.run();
}
});
d.getRootPane().setDefaultButton(okButton);
}
JButton cancelButton = null;
if (addCancelButton) {
cancelButton = new JButton(cancelText);
cancelButton.setName("cancel");
cancelButton.addActionListener(e -> cancelDialog(d, cancelAction));
}
JPanel southPanel = null;
if (addOKButton || addCancelButton) {
southPanel = new JPanel();
d.add(southPanel, BorderLayout.SOUTH);
}
if (addOKButton) {
if (addCancelButton) { // add both
if (JVM.isMac) {
southPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 5, 5));
southPanel.add(cancelButton);
southPanel.add(okButton);
} else {
southPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
southPanel.add(okButton);
southPanel.add(cancelButton);
}
} else { // only ok button
southPanel.add(okButton);
}
}
if (reconfigureGlobalKeyWatch) {
GlobalKeyboardWatch.setDialogActive(true);
}
// cancel when window is closed
d.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
d.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// the user pressed the X button...
cancelDialog(d, cancelAction);
}
});
// cancel when ESC is pressed
((JComponent) d.getContentPane()).registerKeyboardAction(e ->
cancelDialog(d, cancelAction),
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
d.pack();
GUIUtils.centerOnScreen(d);
d.setVisible(true);
return d;
}
private void setupDefaults() {
if (!addCancelButton && cancelAction == null) {
// an OK dialog can still be cancelled with Esc/X
cancelAction = okAction;
}
if (okText == null) {
okText = DEFAULT_OK_TEXT;
}
if (cancelText == null) {
cancelText = DEFAULT_CANCEL_TEXT;
}
}
private void cancelDialog(JDialog d, Runnable cancelAction) {
closeDialog(d);
if (cancelAction != null) {
cancelAction.run();
}
}
private void closeDialog(JDialog d) {
d.setVisible(false);
if (reconfigureGlobalKeyWatch) {
GlobalKeyboardWatch.setDialogActive(false);
}
d.dispose();
}
}