/* * Syncany, www.syncany.org * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com> * * 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 3 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, see <http://www.gnu.org/licenses/>. */ package org.syncany.operations.gui; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.syncany.Client; import org.syncany.config.GuiEventBus; import org.syncany.config.LocalEventBus; import org.syncany.config.Logging; import org.syncany.config.UserConfig; import org.syncany.config.to.GuiConfigTO; import org.syncany.gui.tray.TrayIcon; import org.syncany.gui.tray.TrayIconFactory; import org.syncany.gui.tray.TrayIconTheme; import org.syncany.gui.tray.TrayIconType; import org.syncany.gui.util.I18n; import org.syncany.gui.util.SWTResourceManager; import org.syncany.operations.Operation; import org.syncany.operations.OperationResult; import org.syncany.operations.daemon.ControlServer.ControlCommand; import org.syncany.operations.daemon.DaemonOperation; import org.syncany.operations.daemon.messages.ExitGuiInternalEvent; import org.syncany.util.PidFileUtil; import com.google.common.eventbus.Subscribe; /** * @author Vincent Wiencek <vwiencek@gmail.com> * @author Philipp C. Heckel <philipp.heckel@gmail.com> */ public class GuiOperation extends Operation { private static final Logger logger = Logger.getLogger(GuiOperation.class.getSimpleName()); private static final String GUI_CONFIG_FILE = "gui.xml"; private static final String GUI_CONFIG_EXAMPLE_FILE = "gui-example.xml"; private GuiConfigTO guiConfig; private GuiEventBus eventBus; private GuiOperationOptions options; private Shell shell; private TrayIcon trayIcon; private boolean daemonStarted; private Thread daemonThread; private GuiEventBridge eventBridge; private GuiWebSocketClient webSocketClient; static { Logging.init(); UserConfig.init(); } public GuiOperation() { this(new GuiOperationOptions()); } public GuiOperation(GuiOperationOptions options) { super(null); this.options = options; } @Override public OperationResult execute() throws Exception { logger.log(Level.INFO, "Starting GUI operation ..."); loadOrCreateGuiConfig(); initEventBus(); initShutdownHook(); initDisplayWindow(); initInternationalization(); initTray(); startDaemon(); startDaemonClient(); startEventDispatchLoop(); return null; } private void loadOrCreateGuiConfig() { try { File configFile = new File(UserConfig.getUserConfigDir(), GUI_CONFIG_FILE); File configFileExample = new File(UserConfig.getUserConfigDir(), GUI_CONFIG_EXAMPLE_FILE); if (configFile.exists()) { guiConfig = GuiConfigTO.load(configFile); } else { // Write example config to daemon-example.xml, and default config to daemon.xml GuiConfigTO exampleGuiConfig = new GuiConfigTO(); exampleGuiConfig.setTray(TrayIconType.DEFAULT); GuiConfigTO.save(exampleGuiConfig, configFileExample); // Use default settings guiConfig = new GuiConfigTO(); } } catch (Exception e) { logger.log(Level.WARNING, "Cannot (re-)load config. Using default config.", e); guiConfig = new GuiConfigTO(); } } private void initEventBus() { eventBus = GuiEventBus.getInstance(); eventBus.register(this); } private void initShutdownHook() { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { logger.info("Releasing SWT Resources"); SWTResourceManager.dispose(); } }); } private void initDisplayWindow() { logger.log(Level.INFO, "SWT platform and version version: " + SWT.getPlatform() + " " + SWT.getVersion()); Display.setAppName("Syncany"); Display.setAppVersion(Client.getApplicationVersionFull()); shell = new Shell(); shell.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { System.exit(0); } }); } private void initInternationalization() { String intlPackage = I18n.class.getPackage().getName().replace(".", "/"); I18n.registerBundleName(intlPackage + "/i18n/messages"); } private void initTray() { TrayIconType type = (options.getTrayType() != null) ? options.getTrayType() : guiConfig.getTray(); TrayIconTheme theme = (options.getTrayTheme() != null) ? options.getTrayTheme() : guiConfig.getTheme(); trayIcon = TrayIconFactory.createTrayIcon(shell, type, theme); trayIcon.hashCode(); // Dummy call to avoid 'don't use' warning } public void startDaemon() { File daemonPidFile = new File(UserConfig.getUserConfigDir(), DaemonOperation.PID_FILE); boolean daemonRunning = PidFileUtil.isProcessRunning(daemonPidFile); if (!daemonRunning) { daemonThread = new Thread(new Runnable() { @Override public void run() { try { logger.log(Level.INFO, "Starting daemon in separate thread ..."); new DaemonOperation().execute(); logger.log(Level.INFO, "SHUTDOWN of daemon complete."); } catch (Exception e) { logger.log(Level.SEVERE, "Cannot start daemon or daemon execution failed.", e); } } }); daemonThread.start(); daemonStarted = true; } } private void startDaemonClient() { if (daemonStarted) { eventBridge = new GuiEventBridge(); eventBridge.start(); } else { webSocketClient = new GuiWebSocketClient(); webSocketClient.start(); } } public void startEventDispatchLoop() { Display display = Display.getDefault(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void disposeShell() { if (shell != null && !shell.isDisposed()) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { shell.dispose(); } }); } } public void stopDaemon() throws IOException, InterruptedException { if (daemonStarted) { LocalEventBus.getInstance().post(ControlCommand.SHUTDOWN); } } @Subscribe public void onExitGuiEventReceived(ExitGuiInternalEvent quitEvent) { try { stopDaemon(); } catch (IOException e) { logger.warning("Unable to stop daemon: " + e); } catch (InterruptedException e) { logger.warning("Unable to stop daemon: " + e); } disposeShell(); System.exit(0); } }