/* * File : SystemTraySWT.java * Created : 2 avr. 2004 * By : Olivier * * Azureus - a Java Bittorrent client * * 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. * * 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 ( see the LICENSE file ). * * 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 org.gudy.azureus2.ui.systray; import java.util.Locale; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.download.DownloadManagerStats; import org.gudy.azureus2.core3.global.GlobalManager; import org.gudy.azureus2.core3.global.GlobalManagerStats; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.internat.MessageText.MessageTextListener; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.plugins.ui.config.ConfigSection; import org.gudy.azureus2.ui.common.util.MenuItemManager; import org.gudy.azureus2.ui.swt.Alerts; import org.gudy.azureus2.ui.swt.MenuBuildUtils; import org.gudy.azureus2.ui.swt.Messages; import org.gudy.azureus2.ui.swt.Utils; import org.gudy.azureus2.ui.swt.mainwindow.SWTThread; import org.gudy.azureus2.ui.swt.mainwindow.SelectableSpeedMenu; import org.gudy.azureus2.ui.swt.views.utils.ManagerUtils; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.core.AzureusCoreRunningListener; import com.aelitis.azureus.core.tag.TagDownload; import com.aelitis.azureus.core.tag.TagManager; import com.aelitis.azureus.core.tag.TagManagerFactory; import com.aelitis.azureus.core.tag.TagType; import com.aelitis.azureus.ui.UIFunctions; import com.aelitis.azureus.ui.UIFunctionsManager; import com.aelitis.azureus.ui.common.updater.UIUpdatableAlways; import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT; import com.aelitis.azureus.ui.swt.UIFunctionsSWT; import com.aelitis.azureus.ui.swt.imageloader.ImageLoader; /** * @author Olivier Chalouhi * */ public class SystemTraySWT implements UIUpdatableAlways, MessageTextListener { private static SystemTraySWT singleton; public static synchronized SystemTraySWT getTray() { if ( singleton == null ){ singleton = new SystemTraySWT(); } return( singleton ); } protected static AzureusCore core = null; Display display; UIFunctionsSWT uiFunctions; Tray tray; TrayItem trayItem; Menu menu; Image imgAzureus; Image imgAzureusGray; Image imgAzureusWhite; protected GlobalManager gm = null; private String seedingKeyVal; private String downloadingKeyVal; private String etaKeyVal; private String dlAbbrKeyVal; private String ulAbbrKeyVal; private String alertsKeyVal; long interval = 0; protected boolean enableTooltip; protected boolean enableTooltipNextETA; private SystemTraySWT() { AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() { public void azureusCoreRunning(AzureusCore core) { SystemTraySWT.core = core; gm = core.getGlobalManager(); } }); COConfigurationManager.addAndFireParameterListener( "ui.systray.tooltip.enable", new ParameterListener() { public void parameterChanged(String parameterName) { enableTooltip = COConfigurationManager.getBooleanParameter(parameterName); if (enableTooltip) { MessageText.addAndFireListener(SystemTraySWT.this); interval=0; } else { MessageText.removeListener(SystemTraySWT.this); if (trayItem != null && !trayItem.isDisposed()) { trayItem.setToolTipText(null); } } } }); COConfigurationManager.addAndFireParameterListener( "ui.systray.tooltip.next.eta.enable", new ParameterListener() { public void parameterChanged(String parameterName) { enableTooltipNextETA = COConfigurationManager.getBooleanParameter(parameterName); interval=0; } }); uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT(); display = SWTThread.getInstance().getDisplay(); tray = display.getSystemTray(); trayItem = new TrayItem(tray, SWT.NULL); ImageLoader imageLoader = ImageLoader.getInstance(); if (Constants.isOSX) { imgAzureusGray = imageLoader.getImage("azureus_grey"); imgAzureusWhite = imageLoader.getImage("azureus_white"); trayItem.setImage(imgAzureusGray); } else { imgAzureus = imageLoader.getImage("azureus"); trayItem.setImage(imgAzureus); } trayItem.setVisible(true); menu = new Menu(uiFunctions.getMainShell(), SWT.POP_UP); menu.addMenuListener(new MenuListener() { public void menuShown(MenuEvent _menu) {} public void menuHidden(MenuEvent _menu) { if(Constants.isOSX) { trayItem.setImage(imgAzureusGray); } } }); MenuBuildUtils.addMaintenanceListenerForMenu(menu, new MenuBuildUtils.MenuBuilder() { public void buildMenu(Menu menu, MenuEvent menuEvent) { fillMenu(menu); } }); trayItem.addListener(SWT.DefaultSelection, new Listener() { public void handleEvent(Event arg0) { showMainWindow(); } }); trayItem.addListener(SWT.Selection, new Listener() { long lastTime = 0; public void handleEvent(Event arg0) { // Bug in Windows (seems to have started around SWT 3.3 Release // Candidates) where double click isn't interpreted as DefaultSelection // Since we "know" SWT.Selection is actually a mouse down, check // if two mouse downs happen in a short timespan and fake a // DefaultSelection if (Constants.isWindows) { long now = SystemTime.getCurrentTime(); if (now - lastTime < 200) { showMainWindow(); } else { lastTime = now; } } else if (Constants.isOSX) { trayItem.setImage(imgAzureusWhite); menu.setVisible(true); } } }); trayItem.addListener(SWT.MenuDetect, new Listener() { public void handleEvent(Event arg0) { menu.setVisible(true); } }); uiFunctions.getUIUpdater().addUpdater(this); } public void fillMenu(final Menu menu) { final MenuItem itemShow = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemShow, "SystemTray.menu.show"); new MenuItem(menu, SWT.SEPARATOR); final MenuItem itemAddTorrent = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemAddTorrent, "menu.open.torrent"); new MenuItem(menu, SWT.SEPARATOR); final MenuItem itemCloseAll = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemCloseAll, "SystemTray.menu.closealldownloadbars"); final MenuItem itemShowGlobalTransferBar = new MenuItem(menu, SWT.CHECK); Messages.setLanguageText(itemShowGlobalTransferBar, "SystemTray.menu.open_global_transfer_bar"); new MenuItem(menu, SWT.SEPARATOR); org.gudy.azureus2.plugins.ui.menus.MenuItem[] menu_items; menu_items = MenuItemManager.getInstance().getAllAsArray("systray"); if (menu_items.length > 0) { MenuBuildUtils.addPluginMenuItems(uiFunctions.getMainShell(), menu_items, menu, true, true, MenuBuildUtils.BASIC_MENU_ITEM_CONTROLLER); new MenuItem(menu, SWT.SEPARATOR); } createUploadLimitMenu(menu); createDownloadLimitMenu(menu); new MenuItem(menu, SWT.SEPARATOR); final MenuItem itemStartAll = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemStartAll, "SystemTray.menu.startalltransfers"); final MenuItem itemStopAll = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemStopAll, "SystemTray.menu.stopalltransfers"); final MenuItem itemPause = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemPause, "SystemTray.menu.pausetransfers"); final MenuItem itemResume = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemResume, "SystemTray.menu.resumetransfers"); new MenuItem(menu, SWT.SEPARATOR); final Menu optionsMenu = new Menu(menu.getShell(), SWT.DROP_DOWN); final MenuItem optionsItem = new MenuItem(menu, SWT.CASCADE); Messages.setLanguageText( optionsItem, "tray.options" ); optionsItem.setMenu(optionsMenu); final MenuItem itemShowToolTip = new MenuItem(optionsMenu, SWT.CHECK); Messages.setLanguageText(itemShowToolTip,"show.tooltip.label"); final MenuItem itemMoreOptions = new MenuItem(optionsMenu, SWT.PUSH); Messages.setLanguageText(itemMoreOptions,"label.more.dot"); new MenuItem(menu, SWT.SEPARATOR); final MenuItem itemExit = new MenuItem(menu, SWT.NULL); Messages.setLanguageText(itemExit, "SystemTray.menu.exit"); itemShow.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { showMainWindow(); } }); itemAddTorrent.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { uiFunctions.openTorrentWindow(); } }); itemStartAll.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { if (gm == null) { return; } gm.startAllDownloads(); } }); itemStopAll.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { ManagerUtils.asyncStopAll(); } }); itemPause.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { ManagerUtils.asyncPause(); } }); itemResume.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { if (gm == null) { return; } gm.resumeDownloads(); } }); itemPause.setEnabled(gm != null && gm.canPauseDownloads()); itemResume.setEnabled(gm != null && gm.canResumeDownloads()); itemCloseAll.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { uiFunctions.closeDownloadBars(); } }); itemShowGlobalTransferBar.setSelection(uiFunctions.isGlobalTransferBarShown()); itemShowGlobalTransferBar.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { if (uiFunctions.isGlobalTransferBarShown()) { uiFunctions.closeGlobalTransferBar(); } else { uiFunctions.showGlobalTransferBar(); } } }); itemShowToolTip.setSelection(enableTooltip); itemShowToolTip.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { COConfigurationManager.setParameter( "ui.systray.tooltip.enable", itemShowToolTip.getSelection()); } }); itemMoreOptions.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { UIFunctions uif = UIFunctionsManager.getUIFunctions(); if (uif != null) { uif.openView(UIFunctions.VIEW_CONFIG, ConfigSection.SECTION_INTERFACE ); } } }); itemExit.addListener(SWT.Selection, new Listener() { public void handleEvent(Event arg0) { // User got a stack overflow (all SWT code) because of this dispose, // so execute it outside of the selection trigger and hope it doesn't // overflow there. Utils.execSWTThreadLater(0, new AERunnable() { public void runSupport() { uiFunctions.dispose(false, false); } }); } }); } /** * Creates the global upload limit context menu item * @param parent The system tray contextual menu */ private final void createUploadLimitMenu(final Menu parent) { final MenuItem uploadSpeedItem = new MenuItem(parent, SWT.CASCADE); uploadSpeedItem.setText(MessageText.getString("GeneralView.label.maxuploadspeed")); final Menu uploadSpeedMenu = new Menu(uiFunctions.getMainShell(), SWT.DROP_DOWN); uploadSpeedMenu.addListener(SWT.Show, new Listener() { public void handleEvent(Event event) { SelectableSpeedMenu.generateMenuItems(uploadSpeedMenu, core, gm, true); } }); uploadSpeedItem.setMenu(uploadSpeedMenu); } /** * Creates the global download limit context menu item * @param parent The system tray contextual menu */ private final void createDownloadLimitMenu(final Menu parent) { final MenuItem downloadSpeedItem = new MenuItem(parent, SWT.CASCADE); downloadSpeedItem.setText(MessageText.getString("GeneralView.label.maxdownloadspeed")); final Menu downloadSpeedMenu = new Menu(uiFunctions.getMainShell(), SWT.DROP_DOWN); downloadSpeedMenu.addListener(SWT.Show, new Listener() { public void handleEvent(Event event) { SelectableSpeedMenu.generateMenuItems(downloadSpeedMenu, core, gm, false); } }); downloadSpeedItem.setMenu(downloadSpeedMenu); } public void dispose() { uiFunctions.getUIUpdater().removeUpdater(this); Utils.execSWTThread(new AERunnable() { public void runSupport() { if (trayItem != null && !trayItem.isDisposed()) { trayItem.dispose(); } ImageLoader imageLoader = ImageLoader.getInstance(); if (Constants.isOSX) { imageLoader.releaseImage("azureus_grey"); imageLoader.releaseImage("azureus_white"); } else { imageLoader.releaseImage("azureus"); } } }); synchronized( SystemTraySWT.class ){ singleton = null; } } // @see com.aelitis.azureus.ui.common.updater.UIUpdatable#updateUI() public void updateUI() { if (interval++ % 10 > 0) { return; } if (trayItem.isDisposed()) { uiFunctions.getUIUpdater().removeUpdater(this); return; } if (core == null || !core.isStarted()) { return; } if (enableTooltip) { GlobalManagerStats stats = gm.getStats(); StringBuffer toolTip = new StringBuffer(); int seeding = 0; int downloading = 0; DownloadManager next_download = null; long next_download_eta = Long.MAX_VALUE; TagManager tm = TagManagerFactory.getTagManager(); if ( tm != null && tm.isEnabled()){ TagType tt = tm.getTagType( TagType.TT_DOWNLOAD_STATE ); if ( tt != null ){ TagDownload dl_tag = (TagDownload)tt.getTag( 1 ); downloading = dl_tag.getTaggedCount(); seeding = tt.getTag( 2 ).getTaggedCount(); if ( enableTooltipNextETA && downloading > 0 ){ for ( DownloadManager dl: dl_tag.getTaggedDownloads()){ DownloadManagerStats dl_stats = dl.getStats(); long eta = dl_stats.getSmoothedETA(); if ( eta < next_download_eta ){ next_download_eta = eta; next_download = dl; } } } } }else{ // OMG this must be slow on 10k lists /* List<?> managers = gm.getDownloadManagers(); for (int i = 0; i < managers.size(); i++) { DownloadManager manager = (DownloadManager) managers.get(i); int state = manager.getState(); if (state == DownloadManager.STATE_DOWNLOADING) downloading++; if (state == DownloadManager.STATE_SEEDING) seeding++; } */ } String seeding_text = seedingKeyVal.replaceAll("%1", "" + seeding); String downloading_text = downloadingKeyVal.replaceAll("%1", "" + downloading); toolTip.append(seeding_text).append(downloading_text).append("\n"); if ( next_download != null ){ String dl_name = next_download.getDisplayName(); if ( dl_name.length() > 80 ){ dl_name = dl_name.substring( 0, 77 ) + "..."; } dl_name = dl_name.replaceAll( "&", "&&" ); toolTip.append( " " ); toolTip.append( dl_name ); toolTip.append( ": " ); toolTip.append( etaKeyVal ); toolTip.append( "=" ); toolTip.append( DisplayFormatters.formatETA( next_download_eta )); toolTip.append( "\n" ); } toolTip.append(dlAbbrKeyVal).append(" "); toolTip.append(DisplayFormatters.formatDataProtByteCountToKiBEtcPerSec( stats.getDataReceiveRate(), stats.getProtocolReceiveRate())); toolTip.append(", ").append(ulAbbrKeyVal).append(" "); toolTip.append(DisplayFormatters.formatDataProtByteCountToKiBEtcPerSec( stats.getDataSendRate(), stats.getProtocolSendRate())); int alerts = Alerts.getUnviewedLogAlertCount(); if ( alerts > 0 ){ toolTip.append( "\n" ); toolTip.append( alertsKeyVal.replaceAll("%1", "" + alerts)); } trayItem.setToolTipText(toolTip.toString()); } //Why should we refresh the image? it never changes ... //and is a memory bottleneck for some non-obvious reasons. //trayItem.setImage(ImageRepository.getImage("azureus")); trayItem.setVisible(true); } private void showMainWindow() { uiFunctions.bringToFront(false); } public void updateLanguage() { if (menu != null) { Messages.updateLanguageForControl(menu); } updateUI(); } // @see com.aelitis.azureus.ui.common.updater.UIUpdatable#getUpdateUIName() public String getUpdateUIName() { return "SystemTraySWT"; } public void localeChanged(Locale oldLocale, Locale newLocale) { seedingKeyVal = MessageText.getString("SystemTray.tooltip.seeding"); downloadingKeyVal = MessageText.getString("SystemTray.tooltip.downloading"); if (!downloadingKeyVal.startsWith(" ")) { downloadingKeyVal = " " + downloadingKeyVal; } etaKeyVal = MessageText.getString("TableColumn.header.eta" ); dlAbbrKeyVal = MessageText.getString("ConfigView.download.abbreviated"); ulAbbrKeyVal = MessageText.getString("ConfigView.upload.abbreviated"); alertsKeyVal = MessageText.getString("label.alertnum"); } }