package rmblworx.tools.timey.gui;
import java.awt.AWTException;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import rmblworx.tools.timey.event.TimeyEvent;
import rmblworx.tools.timey.event.TimeyEventListener;
import rmblworx.tools.timey.gui.config.ConfigManager;
/*
* Copyright 2014-2015 Christian Raue
* MIT License http://opensource.org/licenses/mit-license.php
*/
/**
* Controller für die Timey-GUI.
* @author Christian Raue {@literal <christian.raue@gmail.com>}
*/
public class TimeyController extends Controller implements TimeyEventListener {
/**
* Intervall für blinkendes Tray-Symbol in ms.
*/
private static final int TRAY_ICON_BLINK_INTERVAL = 500;
/**
* Fenster der Anwendung.
*/
private Stage stage;
/**
* Ob die Anwendung erstmalig minimiert wurde.
*/
private boolean minimizedFirstTime = true;
/**
* Ob das Tray-Symbol blinken soll.
*/
private boolean trayIconBlinking = false;
@FXML
private ResourceBundle resources;
@FXML
private TabPane timeyTabs;
@FXML
private StopwatchController stopwatchController;
@FXML
private CountdownController countdownController;
@FXML
private AlarmController alarmController;
@FXML
private OptionsController optionsController;
@FXML
private void initialize() {
addEmbeddedController(stopwatchController);
addEmbeddedController(countdownController);
addEmbeddedController(alarmController);
addEmbeddedController(optionsController);
// zuletzt geöffneten Tab aktivieren
timeyTabs.getSelectionModel().select(ConfigManager.getCurrentConfig().getActiveTab());
timeyTabs.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
public void changed(final ObservableValue<? extends Number> property, final Number oldValue, final Number newValue) {
ConfigManager.getCurrentConfig().setActiveTab(newValue.intValue());
}
});
Platform.runLater(new Runnable() {
public void run() {
createTrayIcon();
}
});
final TimeyEventListener eventListener = this;
Platform.runLater(new Runnable() {
public void run() {
getGuiHelper().getFacade().addEventListener(eventListener);
}
});
}
public void setStage(final Stage stage) {
this.stage = stage;
}
/**
* Erzeugt das Symbol im System-Tray.
*/
private void createTrayIcon() {
if (!SystemTray.isSupported()) {
return;
}
Platform.setImplicitExit(false);
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
public void handle(final WindowEvent event) {
if (ConfigManager.getCurrentConfig().isMinimizeToTray()) {
hide();
} else {
exit();
}
}
});
stage.iconifiedProperty().addListener(new ChangeListener<Boolean>() {
public void changed(final ObservableValue<? extends Boolean> property, final Boolean oldValue, final Boolean newValue) {
if (Boolean.TRUE.equals(newValue)) {
hide();
}
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final Image clock = Toolkit.getDefaultToolkit().getImage(getClass().getResource("img/clock.png"));
final Image clockWarning = Toolkit.getDefaultToolkit().getImage(getClass().getResource("img/clock_error.png"));
final TrayIcon trayIcon = new TrayIcon(clock, resources.getString("application.title"), createTrayMenu());
new Timer().schedule(new TimerTask() {
public void run() {
// aufhören zu blinken, nachdem Fenster wieder sichtbar gemacht wurde
if (isAppVisible()) {
trayIconBlinking = false;
}
trayIcon.setImage(trayIconBlinking && trayIcon.getImage() == clock ? clockWarning : clock);
}
}, 0, TRAY_ICON_BLINK_INTERVAL);
addTrayListeners(trayIcon);
try {
SystemTray.getSystemTray().add(trayIcon);
getGuiHelper().setTrayIcon(trayIcon);
} catch (final AWTException e) {
e.printStackTrace();
}
}
});
}
/**
* Erzeugt das Menü fürs System-Tray.
* @return Popup-Menü
*/
private PopupMenu createTrayMenu() {
final PopupMenu popup = new PopupMenu();
final MenuItem showItem = new MenuItem(resources.getString("trayMenu.show.label"));
showItem.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent event) {
show();
}
});
popup.add(showItem);
final MenuItem closeItem = new MenuItem(resources.getString("trayMenu.close.label"));
closeItem.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent event) {
exit();
}
});
popup.add(closeItem);
return popup;
}
/**
* Ergänzt das Symbol im System-Tray um notwendige EventListener.
* @param trayIcon Symbol im System-Tray
*/
private void addTrayListeners(final TrayIcon trayIcon) {
trayIcon.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent event) {
show();
}
});
trayIcon.addMouseListener(new MouseListener() {
public void mouseClicked(final MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
if (stage.isIconified()) {
show();
} else {
hide();
}
}
}
public void mouseReleased(final MouseEvent event) {
}
public void mousePressed(final MouseEvent event) {
}
public void mouseExited(final MouseEvent event) {
}
public void mouseEntered(final MouseEvent event) {
}
});
}
/**
* Blendet beim erstmaligen Minimieren der Anwendung einen entsprechenden Hinweis ein.
*/
private void showProgramIsMinimizedMessage() {
if (minimizedFirstTime) {
getGuiHelper().showTrayMessage(resources.getString("trayMenu.appMinimized.title"),
resources.getString("trayMenu.appMinimized.text"));
minimizedFirstTime = false;
}
}
/**
* Blendet die Anwendung aus.
*/
private void hide() {
Platform.runLater(new Runnable() {
public void run() {
if (SystemTray.isSupported() && ConfigManager.getCurrentConfig().isMinimizeToTray()) {
stage.hide();
stage.setIconified(true);
showProgramIsMinimizedMessage();
}
}
});
}
/**
* Blendet die Anwendung ein.
*/
private void show() {
Platform.runLater(new Runnable() {
public void run() {
stage.show();
stage.toFront();
stage.setIconified(false);
trayIconBlinking = false;
}
});
}
/**
* @return ob das Fenster der Anwendung direkt sichtbar ist
*/
private boolean isAppVisible() {
return !stage.isIconified() && stage.isFocused();
}
/**
* Beendet die Anwendung.
*/
private void exit() {
Platform.exit();
}
/**
* {@inheritDoc}
*/
@Override
public final void handleEvent(final TimeyEvent event) {
// blinken, falls Fenster bei Eintreten des Ereignisses nicht sichtbar ist
trayIconBlinking = !isAppVisible();
}
}