// Tray.java // (C) 2008-2012 by David Wieditz; d.wieditz@gmx.de // (C) 2008 by Florian Richter; Florian_Richter@gmx.de // first published 13.07.2008 on http://yacy.net // // This is a part of YaCy, a peer-to-peer based web search engine // // $LastChangedDate$ // $LastChangedRevision$ // $LastChangedBy$ // // LICENSE // // 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package net.yacy.gui; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.Color; import java.awt.Graphics2D; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import net.yacy.cora.util.ConcurrentLog; import net.yacy.gui.framework.Browser; import net.yacy.kelondro.util.OS; import net.yacy.search.Switchboard; import net.yacy.search.SwitchboardConstants; public final class Tray { private Switchboard sb; private TrayIcon ti = null; private String trayLabel; final private static boolean deutsch = System.getProperty("user.language","").equals("de"); final private static boolean french = System.getProperty("user.language","").equals("fr"); // states private boolean isShown = false; private boolean appIsReady = false; private boolean menuEnabled = true; private BufferedImage[] progressIcons = null; private final String iconPath; private MenuItem menuItemHeadline = null; private MenuItem menuItemSearch = null; private MenuItem menuItemAdministration = null; private MenuItem menuItemTerminate = null; public Tray(final Switchboard sb_par) { this.sb = sb_par; this.menuEnabled = sb.getConfigBool(SwitchboardConstants.TRAY_MENU_ENABLED, true); this.trayLabel = sb.getConfig(SwitchboardConstants.TRAY_ICON_LABEL, "YaCy"); this.iconPath = sb.getAppPath().toString() + "/addon/YaCy_TrayIcon.png".replace("/", File.separator); if (useTray()) { try { System.setProperty("java.awt.headless", "false"); // we have to switch off headless mode, else all will fail if (SystemTray.isSupported()) { ActionListener al = new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { doubleClickAction(); } }; ImageIO.setUseCache(false); // do not write a cache to disc; keep in RAM final Image trayIcon = ImageIO.read(new File(iconPath)); // 128x128 final Image progressBooting = ImageIO.read(new File(sb.getAppPath().toString() + "/addon/progress_booting.png".replace("/", File.separator))); // 128x28 final BufferedImage progress = getProgressImage(); this.progressIcons = new BufferedImage[4]; for (int i = 0; i < 4; i++) { this.progressIcons[i] = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); Graphics2D h = this.progressIcons[i].createGraphics(); h.setBackground(Color.BLACK); h.clearRect(0, 0, 128, 128); h.drawImage(trayIcon, 0, 0, 128, 128 - progress.getHeight(), null); h.drawImage(progress.getSubimage(i * 7, 0, 128, progress.getHeight() / 2), 0, 128 - progress.getHeight() / 2, null); h.drawImage(progressBooting, 0, 128 - progress.getHeight(), null); h.dispose(); } final PopupMenu menu = (menuEnabled) ? getPopupMenu() : null; ti = new TrayIcon(trayIcon, trayLabel, menu); if (OS.isMacArchitecture) setDockIcon(trayIcon); ti.setImageAutoSize(true); ti.addActionListener(al); SystemTray.getSystemTray().add(ti); isShown = true; ti.setToolTip(startupMessage()); new TrayAnimation().start(); } else { System.setProperty("java.awt.headless", "true"); } } catch (final Exception e) { System.setProperty("java.awt.headless", "true"); } } } private boolean useTray() { final boolean trayIconEnabled = sb.getConfigBool(SwitchboardConstants.TRAY_ICON_ENABLED, false); final boolean trayIconForced = sb.getConfigBool(SwitchboardConstants.TRAY_ICON_FORCED, false); return trayIconEnabled && (OS.isWindows || OS.isMacArchitecture || trayIconForced); } private class TrayAnimation extends Thread { public TrayAnimation() { super(TrayAnimation.class.getSimpleName()); } int ic = 0; @Override public void run() { while (!Tray.this.appIsReady) { Tray.this.ti.setImage(Tray.this.progressIcons[ic]); if (OS.isMacArchitecture) setDockIcon(Tray.this.progressIcons[ic]); ic++; if (ic >= 4) ic = 0; try {Thread.sleep(80);} catch (InterruptedException e) {break;} } try { ImageIO.setUseCache(false); // do not write a cache to disc; keep in RAM Image trayIcon = ImageIO.read(new File(Tray.this.iconPath)); ti.setImage(trayIcon); ti.setToolTip(readyMessage()); setDockIcon(trayIcon); } catch (IOException e) { ConcurrentLog.logException(e); } Tray.this.progressIcons = null; } } private static void setDockIcon(Image icon) { if (!OS.isMacArchitecture) return; try { Toolkits.setDockIconImage.invoke(Toolkits.applicationInstance, icon); } catch (Throwable e) {} // same as: Application.getApplication().setDockIconImage(i); } /** * set all functions available */ public void setReady() { appIsReady = true; if (useTray()) { try { ImageIO.setUseCache(false); // do not write a cache to disc; keep in RAM Image trayIcon = ImageIO.read(new File(this.iconPath)); if (ti != null) { ti.setImage(trayIcon); ti.setToolTip(readyMessage()); } setDockIcon(trayIcon); } catch (IOException e) { ConcurrentLog.logException(e); } if (this.menuItemHeadline != null) this.menuItemHeadline.setLabel(readyMessage()); if (this.menuItemSearch != null) this.menuItemSearch.setEnabled(true); if (this.menuItemAdministration != null) this.menuItemAdministration.setEnabled(true); if (this.menuItemTerminate != null) this.menuItemTerminate.setEnabled(true); } } public void setShutdown() { if (useTray()) { try { if (ti != null) { ImageIO.setUseCache(false); // do not write a cache to disc; keep in RAM Image trayIcon = ImageIO.read(new File(this.iconPath)); // 128x128 final Image progressShutdown = ImageIO.read(new File(sb.getAppPath().toString() + "/addon/progress_shutdown.png".replace("/", File.separator))); // 128x28 final BufferedImage progress = getProgressImage(); BufferedImage shutdownIcon; shutdownIcon = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB); Graphics2D h = shutdownIcon.createGraphics(); h.setBackground(Color.BLACK); h.clearRect(0, 0, 128, 128); h.drawImage(trayIcon, 0, 0, 128, 128 - progress.getHeight(), null); h.drawImage(progress.getSubimage(0, 0, 128, progress.getHeight() / 2), 0, 128 - progress.getHeight() / 2, null); h.drawImage(progressShutdown, 0, 128 - progress.getHeight(), null); h.dispose(); ti.setImage(shutdownIcon); setDockIcon(shutdownIcon); ti.setToolTip(shutdownMessage()); } } catch (IOException e) { ConcurrentLog.logException(e); } if (this.menuItemHeadline != null) this.menuItemHeadline.setLabel(shutdownMessage()); if (this.menuItemSearch != null) this.menuItemSearch.setEnabled(false); if (this.menuItemAdministration != null) this.menuItemAdministration.setEnabled(false); if (this.menuItemTerminate != null) this.menuItemTerminate.setEnabled(false); } } private BufferedImage getProgressImage() throws IOException { final String progressPath = sb.getAppPath().toString() + "/addon/progressbar.png".replace("/", File.separator); ImageIO.setUseCache(false); // do not write a cache to disc; keep in RAM Image progress_raw = ImageIO.read(new File(progressPath)); // 149x56 BufferedImage progress = new BufferedImage(149, 56, BufferedImage.TYPE_INT_ARGB); Graphics2D progressg = progress.createGraphics(); progressg.drawImage(progress_raw, 0, 0, null); progressg.dispose(); return progress; } public void remove() { if (isShown){ SystemTray.getSystemTray().remove(ti); ti = null; isShown = false; } } private String startupMessage() { if (deutsch) return "YaCy startet, bitte warten..."; else if (french) return "YaCy est en cours de démarrage, veuillez patienter..."; else return "YaCy is starting, please wait..."; } private String readyMessage() { if (deutsch) return "YaCy laeuft unter http://localhost:" + sb.getLocalPort(); else if(french) return "YaCy est en cours d'exécution à l'adresse http://localhost:" + sb.getLocalPort(); return "YaCy is running at http://localhost:" + sb.getLocalPort(); } private String shutdownMessage() { if (deutsch) return "YaCy wird beendet, bitte warten..."; else if(french) return "YaCy est en cours d'arrêt, veuillez patienter..."; return "YaCy will shut down, please wait..."; } private void doubleClickAction() { if (!appIsReady) { String label = startupMessage(); ti.displayMessage("YaCy", label, TrayIcon.MessageType.INFO); } else { openBrowserPage(""); } } /** * * @param browserPopUpPage relative path to the webserver root */ private void openBrowserPage(final String browserPopUpPage) { if(!menuEnabled) return; // no need for https, because we are on localhost Browser.openBrowser("http://localhost:" + sb.getConfig(SwitchboardConstants.SERVER_PORT, "8090") + "/" + browserPopUpPage); } private PopupMenu getPopupMenu() { String label; PopupMenu menu = new PopupMenu(); // Headline this.menuItemHeadline = new MenuItem(startupMessage()); this.menuItemHeadline.setEnabled(false); //this.menuItemHeadline.setFont(this.menuItemHeadline.getFont().deriveFont(Font.BOLD)); // does not work because getFont() returns null; menu.add(this.menuItemHeadline); // Separator menu.addSeparator(); // YaCy Search if (deutsch) label = "YaCy Suche"; else if (french) label = "Recherche YaCy"; else label = "YaCy Search"; this.menuItemSearch = new MenuItem(label); this.menuItemSearch.setEnabled(false); this.menuItemSearch.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { openBrowserPage("index.html"); } }); menu.add(this.menuItemSearch); // Peer Administration if (deutsch) label = "Administration"; else if (french) label = "Administration"; else label = "Administration"; this.menuItemAdministration = new MenuItem(label); this.menuItemAdministration.setEnabled(false); this.menuItemAdministration.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { openBrowserPage("Status.html"); } }); menu.add(this.menuItemAdministration); // Separator menu.addSeparator(); // Quit if(deutsch) label = "YaCy Beenden"; else if(french) label = "Arrêter YaCy"; else label = "Shutdown YaCy"; this.menuItemTerminate = new MenuItem(label); this.menuItemTerminate.setEnabled(false); this.menuItemTerminate.addActionListener(new ActionListener() { @Override public void actionPerformed(final ActionEvent e) { sb.terminate("shutdown from tray"); } }); menu.add(this.menuItemTerminate); return menu; } }