Skip to content

Commit

Permalink
NEXU-71
Browse files Browse the repository at this point in the history
  • Loading branch information
jean authored and jean committed Jan 11, 2017
1 parent 4635981 commit 7423819
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 61 deletions.
1 change: 0 additions & 1 deletion .gitignore
Expand Up @@ -11,7 +11,6 @@ target
.mtj.tmp/

# Package Files #
*.jar
*.war
database-web.xml
pom.xml.versionsBackup
Expand Down
Binary file not shown.
Binary file added nexu-standalone/lib/SystemTray-3.0.RC6.jar
Binary file not shown.
3 changes: 3 additions & 0 deletions nexu-standalone/lib/original_fie_locations.txt
@@ -0,0 +1,3 @@
https://files2.dorkbox.com/SystemTray_get/system_tray_RC6_javaFX.jar
https://files2.dorkbox.com/SystemTray_get/system_tray_RC6_src.zip

7 changes: 7 additions & 0 deletions nexu-standalone/pom.xml
Expand Up @@ -60,6 +60,13 @@
<artifactId>freemarker</artifactId>
<version>2.3.22</version>
</dependency>
<dependency>
<groupId>com.dorkbox</groupId>
<artifactId>SystemTray</artifactId>
<version>3.0.RC6</version>
<scope>system</scope>
<systemPath>${basedir}/lib/SystemTray-3.0.RC6.jar</systemPath>
</dependency>
</dependencies>

<properties>
Expand Down
171 changes: 111 additions & 60 deletions nexu-standalone/src/main/java/lu/nowina/nexu/SystrayMenu.java
@@ -1,5 +1,5 @@
/**
* © Nowina Solutions, 2015-2015
* © Nowina Solutions, 2015-2017
*
* Concédée sous licence EUPL, version 1.1 ou – dès leur approbation par la Commission européenne - versions ultérieures de l’EUPL (la «Licence»).
* Vous ne pouvez utiliser la présente œuvre que conformément à la Licence.
Expand All @@ -13,13 +13,8 @@
*/
package lu.nowina.nexu;

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.net.URL;
import java.util.List;
import java.util.ResourceBundle;

import org.slf4j.Logger;
Expand All @@ -28,72 +23,128 @@
import javafx.application.Platform;
import lu.nowina.nexu.api.NexuAPI;
import lu.nowina.nexu.api.SystrayMenuItem;
import lu.nowina.nexu.api.flow.FutureOperationInvocation;
import lu.nowina.nexu.api.flow.OperationFactory;
import lu.nowina.nexu.api.flow.OperationResult;
import lu.nowina.nexu.generic.DatabaseWebLoader;
import lu.nowina.nexu.systray.SystrayMenuInitializer;
import lu.nowina.nexu.view.core.NonBlockingUIOperation;

public class SystrayMenu {

private static final Logger LOGGER = LoggerFactory.getLogger(SystrayMenu.class.getName());

private final TrayIcon trayIcon;

public SystrayMenu(OperationFactory operationFactory, DatabaseWebLoader webLoader, NexuAPI api, UserPreferences prefs) {
if (SystemTray.isSupported()) {
final ResourceBundle resources = ResourceBundle.getBundle("bundles/nexu");
final PopupMenu popup = new PopupMenu();

final MenuItem aboutItem = new MenuItem(resources.getString("systray.menu.about"));
aboutItem.addActionListener((l) -> about(operationFactory, api, webLoader, resources));
popup.add(aboutItem);

final MenuItem preferencesItem = new MenuItem(resources.getString("systray.menu.preferences"));
preferencesItem.addActionListener((l) -> preferences(operationFactory, api, prefs));
popup.add(preferencesItem);

for(final SystrayMenuItem menuItem : api.getExtensionSystrayMenuItems()) {
final MenuItem mi = new MenuItem(menuItem.getLabel());
mi.addActionListener((l) -> menuItem.getFutureOperationInvocation().call(operationFactory));
popup.add(mi);
}

final MenuItem exitItem = new MenuItem(resources.getString("systray.menu.exit"));
exitItem.addActionListener((l) -> exit());
popup.add(exitItem);
final ResourceBundle resources = ResourceBundle.getBundle("bundles/nexu");

final List<SystrayMenuItem> extensionSystrayMenuItems = api.getExtensionSystrayMenuItems();
final SystrayMenuItem[] systrayMenuItems = new SystrayMenuItem[extensionSystrayMenuItems.size() + 3];

final Image image = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/tray-icon.png"));
trayIcon = new TrayIcon(image, api.getAppConfig().getApplicationName(), popup);
trayIcon.setImageAutoSize(true);
try {
SystemTray.getSystemTray().add(trayIcon);
} catch (AWTException e) {
LOGGER.error("Cannot add TrayIcon", e);
systrayMenuItems[0] = createAboutSystrayMenuItem(operationFactory, api, webLoader, resources);
systrayMenuItems[1] = createPreferencesSystrayMenuItem(operationFactory, api, prefs, resources);

int i = 2;
for(final SystrayMenuItem systrayMenuItem : extensionSystrayMenuItems) {
systrayMenuItems[i++] = systrayMenuItem;
}

systrayMenuItems[i] = createExitSystrayMenuItem(resources);

final String tooltip = api.getAppConfig().getApplicationName();
final URL trayIconURL = this.getClass().getResource("/tray-icon.png");
try {
switch(api.getEnvironmentInfo().getOs()) {
case WINDOWS:
case MACOSX:
// Use reflection to avoid wrong initialization issues
Class.forName("lu.nowina.nexu.systray.AWTSystrayMenuInitializer")
.asSubclass(SystrayMenuInitializer.class).newInstance()
.init(tooltip, trayIconURL, operationFactory, systrayMenuItems);
break;
case LINUX:
// Use reflection to avoid wrong initialization issues
Class.forName("lu.nowina.nexu.systray.DorkboxSystrayMenuInitializer")
.asSubclass(SystrayMenuInitializer.class).newInstance()
.init(tooltip, trayIconURL, operationFactory, systrayMenuItems);
break;
case NOT_RECOGNIZED:
LOGGER.warn("System tray is currently not supported for NOT_RECOGNIZED OS.");
break;
default:
throw new IllegalArgumentException("Unhandled value: " + api.getEnvironmentInfo().getOs());
}
} else {
trayIcon = null;
LOGGER.error("System tray is currently not supported.");
} catch (InstantiationException e) {
LOGGER.error("Cannot initialize systray menu", e);
} catch (IllegalAccessException e) {
LOGGER.error("Cannot initialize systray menu", e);
} catch (ClassNotFoundException e) {
LOGGER.error("Cannot initialize systray menu", e);
}
}

private void about(final OperationFactory operationFactory, final NexuAPI api, final DatabaseWebLoader webLoader,
final ResourceBundle resources) {
operationFactory.getOperation(NonBlockingUIOperation.class, "/fxml/about.fxml",
api.getAppConfig().getApplicationName(), api.getAppConfig().getApplicationVersion(), webLoader,
resources).perform();

private SystrayMenuItem createAboutSystrayMenuItem(final OperationFactory operationFactory, final NexuAPI api,
final DatabaseWebLoader webLoader, final ResourceBundle resources) {
return new SystrayMenuItem() {
@Override
public String getLabel() {
return resources.getString("systray.menu.about");
}

@Override
public FutureOperationInvocation<Void> getFutureOperationInvocation() {
return new FutureOperationInvocation<Void>() {
@Override
public OperationResult<Void> call(OperationFactory operationFactory) {
return operationFactory.getOperation(NonBlockingUIOperation.class, "/fxml/about.fxml",
api.getAppConfig().getApplicationName(), api.getAppConfig().getApplicationVersion(),
webLoader, resources).perform();
}
};
}
};
}

private void preferences(final OperationFactory operationFactory, final NexuAPI api, final UserPreferences prefs) {
final ProxyConfigurer proxyConfigurer = new ProxyConfigurer(api.getAppConfig(), prefs);

operationFactory.getOperation(NonBlockingUIOperation.class, "/fxml/preferences.fxml",
proxyConfigurer, prefs, !api.getAppConfig().isUserPreferencesEditable()).perform();

private SystrayMenuItem createPreferencesSystrayMenuItem(final OperationFactory operationFactory,
final NexuAPI api, final UserPreferences prefs, final ResourceBundle resources) {
return new SystrayMenuItem() {
@Override
public String getLabel() {
return resources.getString("systray.menu.preferences");
}

@Override
public FutureOperationInvocation<Void> getFutureOperationInvocation() {
return new FutureOperationInvocation<Void>() {
@Override
public OperationResult<Void> call(OperationFactory operationFactory) {
final ProxyConfigurer proxyConfigurer = new ProxyConfigurer(api.getAppConfig(), prefs);

return operationFactory.getOperation(NonBlockingUIOperation.class, "/fxml/preferences.fxml",
proxyConfigurer, prefs, !api.getAppConfig().isUserPreferencesEditable()).perform();
}
};
}
};
}

private void exit() {
LOGGER.info("Exiting...");
if(trayIcon != null) {
SystemTray.getSystemTray().remove(trayIcon);
}
Platform.exit();

private SystrayMenuItem createExitSystrayMenuItem(final ResourceBundle resources) {
return new SystrayMenuItem() {
@Override
public String getLabel() {
return resources.getString("systray.menu.exit");
}

@Override
public FutureOperationInvocation<Void> getFutureOperationInvocation() {
return new FutureOperationInvocation<Void>() {
@Override
public OperationResult<Void> call(OperationFactory operationFactory) {
LOGGER.info("Exiting...");
Platform.exit();
return new OperationResult<Void>((Void) null);
}
};
}
};
}
}
@@ -0,0 +1,70 @@
/**
* © Nowina Solutions, 2015-2017
*
* Concédée sous licence EUPL, version 1.1 ou – dès leur approbation par la Commission européenne - versions ultérieures de l’EUPL (la «Licence»).
* Vous ne pouvez utiliser la présente œuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://ec.europa.eu/idabc/eupl5
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous la Licence est distribué «en l’état»,
* SANS GARANTIES OU CONDITIONS QUELLES QU’ELLES SOIENT, expresses ou implicites.
* Consultez la Licence pour les autorisations et les restrictions linguistiques spécifiques relevant de la Licence.
*/
package lu.nowina.nexu.systray;

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.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import lu.nowina.nexu.api.SystrayMenuItem;
import lu.nowina.nexu.api.flow.OperationFactory;

/**
* Implementation of {@link SystrayMenuInitializer} using AWT.
*
* @author Jean Lepropre (jean.lepropre@nowina.lu)
*/
public class AWTSystrayMenuInitializer implements SystrayMenuInitializer {

private static final Logger LOGGER = LoggerFactory.getLogger(AWTSystrayMenuInitializer.class.getName());

public AWTSystrayMenuInitializer() {
super();
}

@Override
public void init(final String tooltip, final URL trayIconURL, final OperationFactory operationFactory,
final SystrayMenuItem... systrayMenuItems) {
if (SystemTray.isSupported()) {
final PopupMenu popup = new PopupMenu();

for(final SystrayMenuItem systrayMenuItem : systrayMenuItems) {
final MenuItem mi = new MenuItem(systrayMenuItem.getLabel());
mi.addActionListener((l) -> systrayMenuItem.getFutureOperationInvocation().call(operationFactory));
popup.add(mi);
}

final Image image = Toolkit.getDefaultToolkit().getImage(trayIconURL);
final TrayIcon trayIcon = new TrayIcon(image, tooltip, popup);
trayIcon.setImageAutoSize(true);

try {
SystemTray.getSystemTray().add(trayIcon);
} catch (final AWTException e) {
LOGGER.error("Cannot add TrayIcon", e);
}
} else {
LOGGER.error("System tray is currently not supported.");
}
}

}
@@ -0,0 +1,59 @@
/**
* © Nowina Solutions, 2015-2017
*
* Concédée sous licence EUPL, version 1.1 ou – dès leur approbation par la Commission européenne - versions ultérieures de l’EUPL (la «Licence»).
* Vous ne pouvez utiliser la présente œuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://ec.europa.eu/idabc/eupl5
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous la Licence est distribué «en l’état»,
* SANS GARANTIES OU CONDITIONS QUELLES QU’ELLES SOIENT, expresses ou implicites.
* Consultez la Licence pour les autorisations et les restrictions linguistiques spécifiques relevant de la Licence.
*/
package lu.nowina.nexu.systray;

import java.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import lu.nowina.nexu.api.SystrayMenuItem;
import lu.nowina.nexu.api.flow.OperationFactory;

/**
* Implementation of {@link SystrayMenuInitializer} using
* <a href="https://github.com/dorkbox/SystemTray">SystemTray from Dorkbox</a>.
*
* @author Jean Lepropre (jean.lepropre@nowina.lu)
*/
public class DorkboxSystrayMenuInitializer implements SystrayMenuInitializer {

private static final Logger LOGGER = LoggerFactory.getLogger(DorkboxSystrayMenuInitializer.class.getName());

public DorkboxSystrayMenuInitializer() {
super();
}

@Override
public void init(final String tooltip, final URL trayIconURL, final OperationFactory operationFactory,
final SystrayMenuItem... systrayMenuItems) {
final SystemTray systemTray = SystemTray.getNative();
if (systemTray == null) {
LOGGER.warn("System tray is currently not supported.");
return;
}

systemTray.setImage(trayIconURL);

final Menu menu = systemTray.getMenu();
for(final SystrayMenuItem systrayMenuItem : systrayMenuItems) {
menu.add(new MenuItem(systrayMenuItem.getLabel(),
(e) -> systrayMenuItem.getFutureOperationInvocation().call(operationFactory)));
}
}

}

0 comments on commit 7423819

Please sign in to comment.