/****************************************************************************** * Copyright © 2013-2016 The Nxt Core Developers. * * * * See the AUTHORS.txt, DEVELOPER-AGREEMENT.txt and LICENSE.txt files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * Nxt software, including this file, may be copied, modified, propagated, * * or distributed except according to the terms contained in the LICENSE.txt * * file. * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ package nxt.env; import nxt.Block; import nxt.Constants; import nxt.Db; import nxt.Generator; import nxt.Nxt; import nxt.http.API; import nxt.peer.Peers; import nxt.util.Convert; import nxt.util.Logger; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.io.IOException; import java.nio.file.Paths; import java.text.DateFormat; import java.util.Collection; import java.util.Date; import java.util.Locale; public class DesktopSystemTray { public static final int DELAY = 1000; private SystemTray tray; private final JFrame wrapper = new JFrame(); private JDialog statusDialog; private JPanel statusPanel; private ImageIcon imageIcon; private TrayIcon trayIcon; private MenuItem openWallet; private MenuItem viewLog; private SystemTrayDataProvider dataProvider; private DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM, Locale.getDefault()); void createAndShowGUI() { if (!SystemTray.isSupported()) { Logger.logInfoMessage("SystemTray is not supported"); return; } final PopupMenu popup = new PopupMenu(); imageIcon = new ImageIcon("html/ui/img/nxt-icon-32x32.png", "tray icon"); trayIcon = new TrayIcon(imageIcon.getImage()); trayIcon.setImageAutoSize(true); tray = SystemTray.getSystemTray(); MenuItem shutdown = new MenuItem("Shutdown"); openWallet = new MenuItem("Open Wallet"); if (!Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { openWallet.setEnabled(false); } viewLog = new MenuItem("View Log File"); if (!Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) { viewLog.setEnabled(false); } MenuItem status = new MenuItem("Status"); popup.add(status); popup.add(viewLog); popup.addSeparator(); popup.add(openWallet); popup.addSeparator(); popup.add(shutdown); trayIcon.setPopupMenu(popup); trayIcon.setToolTip("Initializing"); try { tray.add(trayIcon); } catch (AWTException e) { Logger.logInfoMessage("TrayIcon could not be added", e); return; } trayIcon.addActionListener(e -> displayStatus()); openWallet.addActionListener(e -> { try { Desktop.getDesktop().browse(dataProvider.getWallet()); } catch (IOException ex) { Logger.logInfoMessage("Cannot open wallet", ex); } }); viewLog.addActionListener(e -> { try { Desktop.getDesktop().open(dataProvider.getLogFile()); } catch (IOException ex) { Logger.logInfoMessage("Cannot view log", ex); } }); status.addActionListener(e -> displayStatus()); shutdown.addActionListener(e -> { if(JOptionPane.showConfirmDialog (null, "Are you sure ?", "Confirm Shutdown", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { Logger.logInfoMessage("Shutdown requested by System Tray"); System.exit(0); // Implicitly invokes shutdown using the shutdown hook } }); ActionListener statusUpdater = evt -> { if (statusDialog == null || !statusDialog.isVisible()) { return; } displayStatus(); }; new Timer(DELAY, statusUpdater).start(); } private void displayStatus() { Block lastBlock = Nxt.getBlockchain().getLastBlock(); Collection<Generator> allGenerators = Generator.getAllGenerators(); StringBuilder generators = new StringBuilder(); for (Generator generator : allGenerators) { generators.append(Convert.rsAccount(generator.getAccountId())).append(' '); } Object optionPaneBackground = UIManager.get("OptionPane.background"); UIManager.put("OptionPane.background", Color.WHITE); Object panelBackground = UIManager.get("Panel.background"); UIManager.put("Panel.background", Color.WHITE); Object textFieldBackground = UIManager.get("TextField.background"); UIManager.put("TextField.background", Color.WHITE); Container statusPanelParent = null; if (statusDialog != null && statusPanel != null) { statusPanelParent = statusPanel.getParent(); statusPanelParent.remove(statusPanel); } statusPanel = new JPanel(); statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.Y_AXIS)); addLabelRow(statusPanel, "Installation"); addDataRow(statusPanel, "Application", Nxt.APPLICATION); addDataRow(statusPanel, "Version", Nxt.VERSION); addDataRow(statusPanel, "Network", (Constants.isTestnet) ? "TestNet" : "MainNet"); addDataRow(statusPanel, "Working offline", "" + Constants.isOffline); addDataRow(statusPanel, "Wallet", String.valueOf(API.getBrowserUri())); addDataRow(statusPanel, "Peer port", String.valueOf(Peers.getDefaultPeerPort())); addDataRow(statusPanel, "Program folder", String.valueOf(Paths.get(".").toAbsolutePath().getParent())); addDataRow(statusPanel, "User folder", String.valueOf(Paths.get(Nxt.getUserHomeDir()).toAbsolutePath())); addDataRow(statusPanel, "Database URL", Db.db == null ? "unavailable" : Db.db.getUrl()); addEmptyRow(statusPanel); if (lastBlock != null) { addLabelRow(statusPanel, "Last Block"); addDataRow(statusPanel, "Height", String.valueOf(lastBlock.getHeight())); addDataRow(statusPanel, "Timestamp", String.valueOf(lastBlock.getTimestamp())); addDataRow(statusPanel, "Time", String.valueOf(new Date(Convert.fromEpochTime(lastBlock.getTimestamp())))); addDataRow(statusPanel, "Seconds passed", String.valueOf(Nxt.getEpochTime() - lastBlock.getTimestamp())); addDataRow(statusPanel, "Forging", String.valueOf(allGenerators.size() > 0)); if (allGenerators.size() > 0) { addDataRow(statusPanel, "Forging accounts", generators.toString()); } } addEmptyRow(statusPanel); addLabelRow(statusPanel, "Environment"); addDataRow(statusPanel, "Number of peers", String.valueOf(Peers.getAllPeers().size())); addDataRow(statusPanel, "Available processors", String.valueOf(Runtime.getRuntime().availableProcessors())); addDataRow(statusPanel, "Max memory", humanReadableByteCount(Runtime.getRuntime().maxMemory())); addDataRow(statusPanel, "Total memory", humanReadableByteCount(Runtime.getRuntime().totalMemory())); addDataRow(statusPanel, "Free memory", humanReadableByteCount(Runtime.getRuntime().freeMemory())); addDataRow(statusPanel, "Process id", Nxt.getProcessId()); addEmptyRow(statusPanel); addDataRow(statusPanel, "Updated", dateFormat.format(new Date())); if (statusDialog == null || !statusDialog.isVisible()) { JOptionPane pane = new JOptionPane(statusPanel, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, imageIcon); statusDialog = pane.createDialog(wrapper, "NXT Server Status"); statusDialog.setVisible(true); statusDialog.dispose(); } else { if (statusPanelParent != null) { statusPanelParent.add(statusPanel); statusPanelParent.revalidate(); } statusDialog.getContentPane().validate(); statusDialog.getContentPane().repaint(); EventQueue.invokeLater(statusDialog::toFront); } UIManager.put("OptionPane.background", optionPaneBackground); UIManager.put("Panel.background", panelBackground); UIManager.put("TextField.background", textFieldBackground); } private void addDataRow(JPanel parent, String text, String value) { JPanel rowPanel = new JPanel(); if (!"".equals(value)) { rowPanel.add(Box.createRigidArea(new Dimension(20, 0))); } rowPanel.setLayout(new BoxLayout(rowPanel, BoxLayout.X_AXIS)); if (!"".equals(text) && !"".equals(value)) { text += ':'; } JLabel textLabel = new JLabel(text); // textLabel.setFont(textLabel.getFont().deriveFont(Font.BOLD)); rowPanel.add(textLabel); rowPanel.add(Box.createRigidArea(new Dimension(140 - textLabel.getPreferredSize().width, 0))); JTextField valueField = new JTextField(value); valueField.setEditable(false); valueField.setBorder(BorderFactory.createEmptyBorder()); rowPanel.add(valueField); rowPanel.add(Box.createRigidArea(new Dimension(4, 0))); parent.add(rowPanel); parent.add(Box.createRigidArea(new Dimension(0, 4))); } private void addLabelRow(JPanel parent, String text) { addDataRow(parent, text, ""); } private void addEmptyRow(JPanel parent) { addLabelRow(parent, ""); } void setToolTip(final SystemTrayDataProvider dataProvider) { SwingUtilities.invokeLater(() -> { trayIcon.setToolTip(dataProvider.getToolTip()); openWallet.setEnabled(dataProvider.getWallet() != null && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)); viewLog.setEnabled(dataProvider.getWallet() != null); DesktopSystemTray.this.dataProvider = dataProvider; }); } void shutdown() { SwingUtilities.invokeLater(() -> tray.remove(trayIcon)); } public static String humanReadableByteCount(long bytes) { int unit = 1000; if (bytes < unit) { return bytes + " B"; } int exp = (int) (Math.log(bytes) / Math.log(unit)); String pre = "" + ("KMGTPE").charAt(exp-1); return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); } }