/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * For information about the authors of this project Have a look * at the AUTHORS file in the root of this project. */ package net.sourceforge.fullsync.ui; import java.io.IOException; import java.util.concurrent.ScheduledThreadPoolExecutor; import net.sourceforge.fullsync.ExceptionHandler; import net.sourceforge.fullsync.Preferences; import net.sourceforge.fullsync.ProfileManager; import net.sourceforge.fullsync.Synchronizer; import net.sourceforge.fullsync.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.Shell; public class GuiController implements Runnable { private static GuiController singleton; private final Preferences preferences; private final ProfileManager profileManager; private final Synchronizer synchronizer; private ExceptionHandler oldExceptionHandler; private Display display; private ImageRepository imageRepository; private FontRepository fontRepository; private Shell mainShell; private MainWindow mainWindow; private SystemTrayItem systemTrayItem; private final ScheduledThreadPoolExecutor executorService; private GuiController(Preferences preferences, ProfileManager profileManager, Synchronizer synchronizer) { this.preferences = preferences; this.profileManager = profileManager; this.synchronizer = synchronizer; executorService = new ScheduledThreadPoolExecutor(1); } private void createMainShell(boolean minimized) { try { mainShell = new Shell(display); mainWindow = new MainWindow(mainShell, SWT.NULL, this); mainShell.setLayout(new FillLayout()); Rectangle shellBounds = mainShell.computeTrim(0, 0, mainWindow.getSize().x, mainWindow.getSize().y); mainShell.setSize(shellBounds.width, shellBounds.height); mainShell.setText("FullSync"); //$NON-NLS-1$ mainShell.setImage(getImage("fullsync48.png")); //$NON-NLS-1$ restoreWindowState(shellBounds); if (minimized) { mainShell.setVisible(false); } } catch (Exception e) { ExceptionHandler.reportException(e); } } private void restoreWindowState(final Rectangle shellBounds) { mainShell.setVisible(true); Rectangle wb = preferences.getWindowBounds(); boolean maximized = preferences.getWindowMaximized(); boolean minimized = preferences.getWindowMinimized(); Rectangle r = display.getBounds(); if ((wb.width > 0) && (wb.height > 0) && r.contains(wb.x, wb.y) && r.contains(wb.x + wb.width, wb.y + wb.height)) { mainShell.setBounds(wb); } if (minimized) { mainShell.setMinimized(true); } if (maximized) { mainShell.setMaximized(true); } } public void setMainShellVisible(boolean visible) { mainShell.setVisible(visible); mainShell.setMinimized(!visible); } public Shell getMainShell() { return mainShell; } public MainWindow getMainWindow() { return mainWindow; } public Preferences getPreferences() { return preferences; } public ProfileManager getProfileManager() { return profileManager; } public Synchronizer getSynchronizer() { return synchronizer; } public Display getDisplay() { return display; } public Image getImage(String imageName) { return imageRepository.getImage(imageName); } public void startGui(boolean minimized) { Display.setAppName("FullSync"); display = Display.getDefault(); imageRepository = new ImageRepository(display); fontRepository = new FontRepository(display); createMainShell(minimized); systemTrayItem = new SystemTrayItem(this); oldExceptionHandler = ExceptionHandler.registerExceptionHandler(new ExceptionHandler() { @Override protected void doReportException(final String message, final Throwable exception) { exception.printStackTrace(); display.syncExec(() -> new ExceptionDialog(mainShell, message, exception)); } }); createWelcomeScreen(); } @Override public void run() { while (!mainShell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void closeGui() { // TODO before closing anything we need to find out whether there are operations // currently running / windows open that should/may not be closed // Close the application, but give him a chance to // confirm his action first if ((profileManager.getNextScheduleTask() != null) && preferences.confirmExit()) { MessageBox mb = new MessageBox(mainShell, SWT.ICON_WARNING | SWT.YES | SWT.NO); mb.setText(Messages.getString("GuiController.Confirmation")); //$NON-NLS-1$ mb.setMessage(Messages.getString("GuiController.Do_You_Want_To_Quit") + "\n" //$NON-NLS-1$ //$NON-NLS-2$ + Messages.getString("GuiController.Schedule_is_stopped")); //$NON-NLS-1$ // check whether the user really wants to close if (mb.open() != SWT.YES) { return; } } profileManager.disconnectRemote(); synchronizer.disconnectRemote(); storeWindowState(); disposeGui(); } private void storeWindowState() { boolean maximized = mainShell.getMaximized(); boolean minimized = mainShell.getMinimized(); if (!maximized) { preferences.setWindowBounds(mainShell.getBounds()); } preferences.setWindowMaximized(maximized); preferences.setWindowMinimized(minimized); preferences.save(); } public void disposeGui() { ExceptionHandler.registerExceptionHandler(oldExceptionHandler); if ((mainShell != null) && !mainShell.isDisposed()) { mainShell.dispose(); } if (imageRepository != null) { imageRepository.dispose(); } if (fontRepository != null) { fontRepository.dispose(); } if ((systemTrayItem != null) && !systemTrayItem.isDisposed()) { systemTrayItem.dispose(); } if ((display != null) && !display.isDisposed()) { display.dispose(); } } // TODO the busy cursor should be applied only to the window that is busy // difficulty: getShell() can only be accessed by the display thread :-/ public void showBusyCursor(final boolean show) { display.asyncExec(() -> { try { Cursor cursor = show ? display.getSystemCursor(SWT.CURSOR_WAIT) : null; Shell[] shells = display.getShells(); for (Shell shell : shells) { shell.setCursor(cursor); } } catch (Exception ex) { ExceptionHandler.reportException(ex); } }); } public static GuiController getInstance() { return singleton; } public static GuiController initialize(Preferences preferences, ProfileManager profileManager, Synchronizer synchronizer) { singleton = new GuiController(preferences, profileManager, synchronizer); return singleton; } public static void launchProgram(final String uri) { if (System.getProperty("os.name").toLowerCase().indexOf("linux") > -1) { Thread t = new Thread(() -> { try { Process p = Runtime.getRuntime().exec(new String[] { "xdg-open", uri }); p.waitFor(); } catch (IOException | InterruptedException e) { ExceptionHandler.reportException("Error opening " + uri + ".", e); } }); // set this thread as a daemon to avoid hanging the FullSync shutdown // this might happen if xdg-open opens the browser directly and the // browser is still running t.setDaemon(true); t.start(); } else { try { Program.launch(uri); } catch (Exception e) { ExceptionHandler.reportException("Error opening " + uri + ".", e); } } } public Font getFont(String name, int height, int style) { return fontRepository.getFont(name, height, style); } private void createWelcomeScreen() { if ((null != System.getProperty("net.sourceforge.fullsync.skipWelcomeScreen", null)) || preferences.getSkipWelcomeScreen()) { return; } if (!preferences.getLastVersion().equals(Util.getFullSyncVersion())) { // update the stored version number preferences.save(); try { new WelcomeScreen(getMainShell()); } catch (Exception e) { ExceptionHandler.reportException(e); } } } public static void backgroundExec(AsyncUIUpdate job) { GuiController gc = getInstance(); gc.executorService.execute(new ExecuteBackgroundJob(job, gc.display)); } }