package com.kartoflane.superluminal2.utils; import java.io.File; import javax.swing.JOptionPane; import net.vhati.modmanager.core.FTLUtilities; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolTip; import com.kartoflane.superluminal2.Superluminal; import com.kartoflane.superluminal2.components.interfaces.Action; import com.kartoflane.superluminal2.ui.LoadingDialog; /** * This class contains methods that are used to show customizable dialogs * and UI prompts to the user. * * @author kartoFlane * */ public class UIUtils { /** * Displays a Swing message dialog.<br> * Used when the user has downloaded incorrect version of the editor for their system/architecture. */ public static void showSwingDialog(String title, String message) { JOptionPane.showMessageDialog(null, message, title, JOptionPane.INFORMATION_MESSAGE); } /** * Displays a simple dialog to inform the user about an error. * * @param parentShell * a shell which will be the parent of the dialog. May be null. * @param title * the title of the dialog window, or null for default value:<br> * <code>APP_NAME - Error</code> * @param message * the message that will be displayed to the user. Must not be null. * */ public static void showErrorDialog(Shell parentShell, String title, String message) { boolean dispose = false; if (title == null) title = Superluminal.APP_NAME + " - Error"; if (message == null) throw new IllegalArgumentException("Message must not be null."); if (parentShell == null) { parentShell = new Shell(UIUtils.getDisplay()); dispose = true; } MessageBox box = new MessageBox(parentShell, SWT.ICON_ERROR | SWT.OK); box.setText(title); box.setMessage(message); box.open(); if (dispose) parentShell.dispose(); } /** * Displays a simple dialog to warn the user about something. * * @param parentShell * a shell which will be the parent of the dialog. May be null. * @param title * the title of the dialog window, or null for default value:<br> * <code>APP_NAME - Warning</code> * @param message * the message that will be displayed to the user. Must not be null. * */ public static void showWarningDialog(Shell parentShell, String title, String message) { boolean dispose = false; if (title == null) title = Superluminal.APP_NAME + " - Warning"; if (message == null) throw new IllegalArgumentException("Message must not be null."); if (parentShell == null) { parentShell = new Shell(UIUtils.getDisplay()); dispose = true; } MessageBox box = new MessageBox(parentShell, SWT.ICON_WARNING | SWT.OK); box.setText(title); box.setMessage(message); box.open(); if (dispose) parentShell.dispose(); } /** * Displays a simple dialog to inform the user about something. * * @param parentShell * a shell which will be the parent of the dialog. May be null. * @param title * the title of the dialog window, or null for default value:<br> * <code>APP_NAME - Information</code> * @param message * the message that will be displayed to the user. Must not be null. * */ public static void showInfoDialog(Shell parentShell, String title, String message) { boolean dispose = false; if (title == null) title = Superluminal.APP_NAME + " - Information"; if (message == null) throw new IllegalArgumentException("Message must not be null."); if (parentShell == null) { parentShell = new Shell(UIUtils.getDisplay()); dispose = true; } MessageBox box = new MessageBox(parentShell, SWT.ICON_INFORMATION | SWT.OK); box.setText(title); box.setMessage(message); box.open(); if (dispose) parentShell.dispose(); } /** * Displays a dialog prompting the user for a yes/no response. * * @param parentShell * a shell which will be the parent of the dialog. May be null. * @param title * the title of the dialog window. Must not be null. * @param message * the message that will be displayed to the user. Must not be null. * * @return true if the user selected "Yes", false otherwise. */ public static boolean showYesNoDialog(Shell parentShell, String title, String message) { boolean dispose = false; if (title == null) throw new IllegalArgumentException("Title must not be null."); if (message == null) throw new IllegalArgumentException("Message must not be null."); if (parentShell == null) { parentShell = new Shell(UIUtils.getDisplay()); dispose = true; } MessageBox box = new MessageBox(parentShell, SWT.ICON_INFORMATION | SWT.YES | SWT.NO); box.setText(title); box.setMessage(message); boolean result = box.open() == SWT.YES; if (dispose) parentShell.dispose(); return result; } /** * Displays a dialog prompting the user for a yes/no/cancel response. * * @param parentShell * a shell which will be the parent of the dialog. May be null. * @param title * the title of the dialog window. Must not be null. * @param message * the message that will be displayed to the user. Must not be null. * * @return int value equal to {@link SWT.YES} is the user selected "Yes", {@link SWT.NO} is the user selected "No", * and {@link SWT.CANCEL} is the user selected "Cancel", */ public static int showYesNoCancelDialog(Shell parentShell, String title, String message) { boolean dispose = false; if (title == null) throw new IllegalArgumentException("Title must not be null."); if (message == null) throw new IllegalArgumentException("Message must not be null."); if (parentShell == null) { parentShell = new Shell(UIUtils.getDisplay()); dispose = true; } MessageBox box = new MessageBox(parentShell, SWT.ICON_INFORMATION | SWT.YES | SWT.NO | SWT.CANCEL); box.setText(title); box.setMessage(message); int result = box.open(); if (dispose) parentShell.dispose(); return result; } /** * Prompts the user to select a directory. * * @param parentShell * the parent shell. Must not be null. * @param title * title of the dialog window. Must not be null. * @param message * description of the purpose of the dialog. Must not be null. * @param defaultPath * the path that the dialog will initially show when it is opened. * May be null for the system's default path. * @return the selected directory, or null if not selected. */ public static File promptForDirectory(Shell parentShell, String title, String message, String defaultPath) { File result = null; DirectoryDialog dialog = new DirectoryDialog(parentShell); dialog.setFilterPath(defaultPath); dialog.setText(title); dialog.setMessage(message); String path = dialog.open(); if (path == null) { // User aborted selection // Nothing to do here } else { result = new File(path); } return result; } /** * Prompts the user to select a file for the purpose of saving. * * @param parentShell * the parent shell. Must not be null. * @param title * title of the dialog window. Must not be null. * @param defaultPath * the path that the dialog will initially show when it is opened. * May be null for the system's default path. * @param extensions * an array of file extensions the user is allowed to select. May be null.<br> * Usage: <code>new String[] { "*.txt" }</code> * @return the selected file, or null if not selected. */ public static File promptForSaveFile(Shell parentShell, String title, String defaultPath, String[] extensions) { File result = null; FileDialog dialog = new FileDialog(parentShell, SWT.SAVE); dialog.setFilterExtensions(extensions); dialog.setFilterPath(defaultPath); dialog.setFileName(defaultPath); dialog.setText(title); dialog.setOverwrite(true); String path = dialog.open(); if (path == null) { // User aborted selection // Nothing to do here } else { result = new File(path); } return result; } /** * Prompts the user to select a file for the purpose of loading. * * @param parentShell * the parent shell. Must not be null. * @param title * title of the dialog window. Must not be null. * @param defaultPath * the path that the dialog will initially show when it is opened. * May be null for the system's default path. * @param extensions * an array of file extensions the user is allowed to select. May be null.<br> * Usage: <code>new String[] { "*.txt" }</code> * @return the selected file, or null if not selected. */ public static File promptForLoadFile(Shell parentShell, String title, String defaultPath, String[] extensions) { File result = null; FileDialog dialog = new FileDialog(parentShell, SWT.OPEN); dialog.setFilterExtensions(extensions); dialog.setFilterPath(defaultPath); dialog.setFileName(defaultPath); dialog.setText(title); String path = dialog.open(); if (path == null) { // User aborted selection // Nothing to do here } else { result = new File(path); } return result; } /** * Modally prompts the user for the FTL resources dir. * * @param parentShell * parent for the SWT dialog * * @author Vhati - original method wth Swing dialogs * @author kartoFlane - modified to work with SWT dialogs */ public static File promptForDatsDir(Shell parentShell) { File result = null; String message = ""; message += "You will now be prompted to locate FTL manually.\n"; message += "Select '(FTL dir)/resources/data.dat'.\n"; message += "Or 'FTL.app', if you're on OSX."; MessageBox box = new MessageBox(parentShell, SWT.ICON_INFORMATION | SWT.OK); box.setText("Find FTL"); box.setMessage(message); FileDialog fd = new FileDialog(parentShell, SWT.OPEN); fd.setText("Find data.dat or FTL.app"); fd.setFilterExtensions(new String[] { "*.dat", "*.app" }); fd.setFilterNames(new String[] { "FTL Data File - (FTL dir)/resources/data.dat", "FTL Application Bundle" }); String filePath = fd.open(); if (filePath == null) { // User aborted selection // Nothing to do here } else { File f = new File(filePath); if (f.getName().equals("data.dat")) { result = f.getParentFile(); } else if (f.getName().endsWith(".app")) { File contentsPath = new File(f, "Contents"); if (contentsPath.exists() && contentsPath.isDirectory() && new File(contentsPath, "Resources").exists()) result = new File(contentsPath, "Resources"); // TODO test whether this works on OSX } } if (result != null && FTLUtilities.isDatsDirValid(result)) { return result; } return null; } /** * Displays a simple dialog with an indeterminate progress bar in the UI thread, while * executing the given task in another thread, and waiting for it to finish.<br> * <br> * Note that SWT is a single-threaded library, so you <b>cannot create or modify * UI widgets</b> via this method.<br> * <br> * Usage: * * <pre> * showLoadDialog(shell, title, message, new Action() { * public void execute() { * // your code here... * } * }); * </pre> * * @param parentShell * the shell which will be the dialog's parent. Must not be null. * @param title * the title of the dialog window, or null for default value:<br> * <code>APP_NAME - Loading...</code> * @param message * a brief message that will be displayed above the progress bar, or null for default value:<br> * <code>Loading, please wait...</code> * @param task * the task that is to be performed in the background, or null to make the method return immediately. * * @throws IllegalArgumentException * when the parent shell is null. */ public static void showLoadDialog(Shell parentShell, String title, String message, final Action task) throws IllegalArgumentException { if (task == null) return; if (parentShell == null) throw new IllegalArgumentException("Parent shell must not be null."); final LoadingDialog dialog = new LoadingDialog(parentShell, title, message); Thread loadThread = new Thread() { @Override public void run() { try { task.execute(); } finally { dialog.dispose(); } } }; loadThread.start(); dialog.open(); } /** * @param c * the control to which the tooltip will be added * @param message * tooltip's message * * @see #addTooltip(Control, String, String) */ public static void addTooltip(Control c, String message) { addTooltip(c, "", message); } /** * Adds a tooltip to the given control.<br> * The tooltip will appear once the user hovers over the control, and will remain * visible until the user moves the cursor away. * * @param c * the control to which the tooltip will be added * @param tooltipText * tooltip's title * @param tooltipMessage * tooltip's message */ public static void addTooltip(final Control c, String tooltipText, String tooltipMessage) { final ToolTip tip = new ToolTip(c.getShell(), SWT.NONE); tip.setText(tooltipText); tip.setMessage(tooltipMessage); tip.setAutoHide(false); c.addListener(SWT.MouseHover, new Listener() { public void handleEvent(Event e) { Point p = c.toDisplay(e.x, e.y); tip.setLocation(p.x, p.y + 20); tip.setVisible(true); } }); c.addListener(SWT.MouseExit, new Listener() { public void handleEvent(Event e) { tip.setVisible(false); } }); c.addListener(SWT.Dispose, new Listener() { public void handleEvent(Event e) { if (!tip.isDisposed()) tip.setVisible(false); tip.dispose(); } }); } /** * Adds hotkey text to the given menu item.<br> * This method is used to avoid a bug with GTK version of SWT; adding * accelerator text to the menu item on Linux systems also changes the * accelerator itself. * * @param mntm * menu item to which the hotkey will be added * @param hotkeyText * the text to be displayed as accelerator * * @see MenuItem#setAccelerator(int) * @see MenuItem#setText(String) */ public static void addHotkeyText(MenuItem mntm, String hotkeyText) { // Bug with SWT-GTK: MenuItem.setText() changes the widget's accelerator, // contrary to the Javadoc. The accelerator consumes the key event that triggers // it, therefore the application is never informed about the event. // (Text after \t is the accelerator text) // Fix is to use brackets instead of \t (ugly workaround) String os = System.getProperty("os.name").toLowerCase(); if (os.contains("linux") || os.contains("nix")) { mntm.setText(String.format("%s [%s]", mntm.getText(), hotkeyText)); } else { mntm.setText(mntm.getText() + "\t" + hotkeyText); } } public static Display getDisplay() { Display display = Display.getCurrent(); // Can sometimes return null if (display == null) display = Display.getDefault(); return display; } }