/* * Created on 12 May 2007 * Created by Allan Crooks * Copyright (C) 2007 Aelitis, All Rights Reserved. * * 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. * * AELITIS, SAS au capital de 46,603.30 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ package org.gudy.azureus2.ui.swt.minibar; import java.util.Iterator; import org.eclipse.swt.SWT; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.widgets.*; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.util.AERunnable; import org.gudy.azureus2.core3.util.Constants; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.DisplayFormatters; import org.gudy.azureus2.ui.common.util.MenuItemManager; import org.gudy.azureus2.ui.swt.MenuBuildUtils; import org.gudy.azureus2.ui.swt.components.DoubleBufferedLabel; import org.gudy.azureus2.ui.swt.mainwindow.Colors; import com.aelitis.azureus.ui.swt.imageloader.ImageLoader; /** * @author Allan Crooks * */ public abstract class MiniBar implements MenuBuildUtils.MenuBuilder { // // These are components which are related to the bar itself. // protected Label lDrag; protected MiniBar stucked; protected Monitor[] screens; protected int xPressed, yPressed; protected boolean moving; protected int hSize; protected Shell splash; protected MiniBarManager manager; private Font bold_font = null; // // These are components used during the construction of the bar. // private boolean constructing = false; private boolean constructed = false; private Menu menu; private MouseMoveListener mMoveListener; private MouseListener mListener; private int xSize; private boolean separateDataProt; private float width_multiplier = 1.0f; protected MiniBar(MiniBarManager manager) { this.manager = manager; setPrebuildValues(); this.separateDataProt = DisplayFormatters.isDataProtSeparate(); } private void setPrebuildValues() { this.constructing = false; this.constructed = false; this.xSize = 0; this.hSize = -1; } // // These methods provide a construction API for subclasses. // private void assertConstructing() { if (!this.constructing) throw new RuntimeException("not constructing!"); } private Font createBoldFont(Font original) { FontData[] font_data = original.getFontData(); for (int i=0; i<font_data.length; i++) { font_data[i].setStyle(font_data[i].getStyle() | SWT.BOLD); } return new Font(original.getDevice(), font_data); } protected final void createGap(int width) { width = (int)(width * width_multiplier ); // We create a label just so we can attach the menu to it. assertConstructing(); Label result = new Label(splash, SWT.NONE); result.setBackground(Colors.blues[Colors.BLUES_LIGHTEST]); result.setForeground(Colors.blues[Colors.BLUES_DARKEST]); result.setText(""); result.addMouseListener(this.mListener); result.addMouseMoveListener(this.mMoveListener); result.setLocation(this.xSize, 0); result.setSize(width, hSize); result.setMenu(this.menu); this.xSize += width; } protected final Label createFixedTextLabel(String msg_key, boolean add_colon, boolean bold) { assertConstructing(); Label result = new Label(splash, SWT.NONE); result.setBackground(Colors.blues[Colors.BLUES_LIGHTEST]); result.setForeground(Colors.blues[Colors.BLUES_DARKEST]); result.setText(MessageText.getString(msg_key) + ((add_colon) ? ":" : "")); if (bold) { if (this.bold_font == null) { this.bold_font = createBoldFont(result.getFont()); } result.setFont(this.bold_font); } result.addMouseListener(this.mListener); result.addMouseMoveListener(this.mMoveListener); result.pack(); result.setLocation(this.xSize, 0); result.setMenu(this.menu); if (this.hSize == -1) { int hSizeText = result.getSize().y; int hSizeImage = this.lDrag.getSize().y; this.hSize = hSizeText > hSizeImage ? hSizeText : hSizeImage; } this.xSize += result.getSize().x + 3; return( result ); } protected final DoubleBufferedLabel createDataLabel(int width, boolean centered) { width = (int)(width * width_multiplier ); assertConstructing(); DoubleBufferedLabel result = new DoubleBufferedLabel(splash, (centered ? SWT.CENTER : SWT.NULL) | SWT.DOUBLE_BUFFERED ); result.setBackground(Colors.blues[Colors.BLUES_LIGHTEST]); result.setText(""); result.addMouseListener(this.mListener); result.addMouseMoveListener(this.mMoveListener); if (this.hSize == -1) { throw new RuntimeException("must add fixed text label first!"); } result.setSize(width, hSize); result.setLocation(this.xSize, 0); result.setMenu(this.menu); this.xSize += width + 3; return result; } protected final DoubleBufferedLabel createDataLabel(int width) { return createDataLabel(width, false); } protected final DoubleBufferedLabel createSpeedLabel() { return createDataLabel(separateDataProt ? 110 : 65, separateDataProt); } protected void updateSpeedLabel(DoubleBufferedLabel label, long data_rate, long protocol_rate) { if (separateDataProt) { label.setText(DisplayFormatters.formatDataProtByteCountToKiBEtcPerSec(data_rate, protocol_rate)); } else { label.setText(DisplayFormatters.formatByteCountToKiBEtcPerSec(data_rate+protocol_rate)); } } protected final ProgressBar createProgressBar(int min, int max, int width, final ProgressBarText pbt) { width = (int)(width * width_multiplier ); final ProgressBar result = new ProgressBar(splash, SWT.SMOOTH); result.setBackground(Colors.blues[Colors.BLUES_LIGHTEST]); result.setForeground(Colors.blues[Colors.BLUES_MIDLIGHT]); result.setMinimum(min); result.setMaximum(max); result.addMouseListener(this.mListener); result.addMouseMoveListener(this.mMoveListener); if (this.hSize == -1) { throw new RuntimeException("must add fixed text label first!"); } result.setSize(width, hSize); result.setLocation(this.xSize, 0); result.setMenu(this.menu); this.xSize += width + 3; // Add a listener to display text on the progress bar. if (pbt != null) { result.addListener(SWT.Paint, new Listener() { public void handleEvent(Event event) { Color old = event.gc.getForeground(); event.gc.setForeground(Colors.black); int char_width = event.gc.getFontMetrics().getAverageCharWidth(); String pb_text = pbt.convert(result.getSelection()); event.gc.drawText(pb_text, (result.getSize().x - pb_text.length() * char_width )/2, -1, true); event.gc.setForeground(old); } }); } return result; } protected final ProgressBar createPercentProgressBar(int width) { return createProgressBar(0, 1000, width, new ProgressBarText() { public String convert(int value) { return DisplayFormatters.formatPercentFromThousands(value); } }); } protected static interface ProgressBarText { public String convert(int value); } // // These methods define the main MiniBar behaviour. // public final void construct(final Shell main) { if (this.constructed) { throw new RuntimeException("already constructed!"); } this.constructing = true; this.stucked = null; this.splash = org.gudy.azureus2.ui.swt.components.shell.ShellFactory .createShell(SWT.ON_TOP); int trans = COConfigurationManager.getIntParameter( "Bar Transparency" ); if ( trans > 0 && trans <= 100 ){ int alpha = (int)((255*(100.0f-trans))/100); splash.setAlpha( alpha ); } manager.register(this); final DisposeListener mainDisposeListener; main.addDisposeListener(mainDisposeListener = new DisposeListener() { public void widgetDisposed(DisposeEvent event) { close(); } }); // cleanup dangling references splash.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { if (main.isDisposed()) return; main.removeDisposeListener(mainDisposeListener); } }); screens = main.getDisplay().getMonitors(); build(); } private void build() { lDrag = new Label(splash, SWT.NULL); int testTextHeight = 0; try{ GC gc = new GC(lDrag); gc.setFont(splash.getDisplay().getSystemFont()); Point textSize = gc.textExtent( "Vuze Rocks innit: 100 MB/sec"); int textWidth = textSize.x; testTextHeight = textSize.y; if ( textWidth > 139 ){ // 139 is what parg gets normally on Windows width_multiplier = textWidth/139.0f; } gc.dispose(); }catch( Throwable e ){ Debug.out( e); } if(!Constants.isOSX) { lDrag.setImage(ImageLoader.getInstance().getImage("dragger")); lDrag.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { ImageLoader.getInstance().releaseImage("dragger"); } }); } lDrag.pack(); int yPad = 0; Point lDragSize = lDrag.getSize(); this.xSize = lDragSize.x + 3; if ( lDragSize.y < testTextHeight ){ yPad = ( testTextHeight - lDragSize.y )/2; } lDrag.setLocation(0, yPad); this.mListener = new MouseAdapter() { int old_alpha; public void mouseDown(MouseEvent e) { xPressed = e.x; yPressed = e.y; moving = true; old_alpha = splash.getAlpha(); if ( old_alpha != 255 ){ splash.setAlpha( 255 ); } //System.out.println("Position : " + xPressed + " , " + yPressed); } public void mouseUp(MouseEvent e) { moving = false; if ( old_alpha != 255 ){ splash.setAlpha( old_alpha ); } } }; this.mMoveListener = new MouseMoveListener() { public void mouseMove(MouseEvent e) { if (moving) { int dX = xPressed - e.x; int dY = yPressed - e.y; //System.out.println("dX,dY : " + dX + " , " + dY); Point currentLoc = splash.getLocation(); currentLoc.x -= dX; currentLoc.y -= dY; setSnapLocation(currentLoc); //System.out.println("Position : " + xPressed + " , " + yPressed); } } }; splash.setBackground(Colors.blues[Colors.BLUES_LIGHTEST]); splash.setForeground(Colors.blues[Colors.BLUES_DARKEST]); splash.addMouseListener(mListener); splash.addMouseMoveListener(mMoveListener); lDrag.addMouseListener(mListener); lDrag.addMouseMoveListener(mMoveListener); this.menu = new Menu(splash, SWT.POP_UP); MenuBuildUtils.addMaintenanceListenerForMenu(menu, this); this.beginConstruction(); splash.addListener(SWT.Deiconify, new Listener() { public void handleEvent(Event e) { splash.setVisible(true); splash.setActive(); } }); splash.setSize(xSize + 3, hSize + 2); // Tidy up construction variables. this.mListener = null; this.mMoveListener = null; this.menu = null; // Avoid doing a refresh on construction. this.refresh(); this.constructing = false; this.constructed = true; // Allow subclasses to determine the initial position of the splash object. Point point = this.getInitialLocation(); if (point != null) {splash.setLocation(point);} splash.setVisible(true); } public void buildMenu(Menu menu) { org.gudy.azureus2.plugins.ui.menus.MenuItem[] menu_items; Object plugin_context_obj = this.getPluginMenuContextObject(); Object[] plugin_context_obj_arg = null; if (plugin_context_obj != null) { plugin_context_obj_arg = new Object[]{plugin_context_obj}; } String[] plugin_menu_ids = this.getPluginMenuIdentifiers(plugin_context_obj); if (plugin_menu_ids != null) { menu_items = MenuItemManager.getInstance().getAllAsArray(plugin_menu_ids); if (menu_items.length > 0) { MenuBuildUtils.addPluginMenuItems(splash, menu_items, menu, true, true, // This will retrieve the plugin download object for associated menus. new MenuBuildUtils.MenuItemPluginMenuControllerImpl(plugin_context_obj_arg) ); new MenuItem(menu, SWT.SEPARATOR); } } MenuItem itemClose = new MenuItem(menu,SWT.NULL); itemClose.setText(MessageText.getString("wizard.close")); itemClose.addListener(SWT.Selection,new Listener() { public void handleEvent(Event e) { close(); } }); } protected final void setSnapLocation(Point currentLoc) { Rectangle dim = new Rectangle(currentLoc.x,currentLoc.y,splash.getBounds().width,splash.getBounds().height); // find the screen we mostly reside in int topIntersectArea = 0; int bestScreen = 0; for(int i=0;i<screens.length;i++) { Rectangle curScreen = screens[i].getClientArea(); curScreen.intersect(dim); int area = curScreen.width*curScreen.height; if(area > topIntersectArea) { bestScreen = i; topIntersectArea = area; } } Rectangle screen = screens[bestScreen].getClientArea(); if (currentLoc.x-screen.x < 10) currentLoc.x = screen.x; else if (currentLoc.x-screen.x > screen.width - dim.width - 10) currentLoc.x = screen.x + screen.width - dim.width; if (currentLoc.y-screen.y < 10) currentLoc.y = screen.y; MiniBar mw = this; int height = 0; while (mw != null) { Shell s = mw.getShell(); if (s.isDisposed()) mw = null; else { height += s.getBounds().height - 1; mw = mw.getStucked(); if (mw == this) mw = null; } } if (currentLoc.y-screen.y > screen.height - height - 10) currentLoc.y = screen.y + screen.height - height; MiniBarManager g_manager = MiniBarManager.getManager(); try { g_manager.getMiniBarMonitor().enter(); if (g_manager.countMiniBars() > 1) { Iterator itr = g_manager.getMiniBarIterator(); while (itr.hasNext()) { MiniBar downloadBar = (MiniBar) itr.next(); Point location = downloadBar.getShell().getLocation(); // isn't the height always 10? // Gudy : No it depends on your system font. location.y += downloadBar.getShell().getBounds().height; //Stucking to someone else if (downloadBar != this && downloadBar.getStucked() == null || downloadBar.getStucked() == this) { if (Math.abs(location.x - currentLoc.x) < 10 && location.y - currentLoc.y < 10 && location.y - currentLoc.y > 0) { downloadBar.setStucked(this); currentLoc.x = location.x; currentLoc.y = location.y - 1; } } //Un-stucking from someone if (downloadBar != this && downloadBar.getStucked() == this) { if (Math.abs(location.x - currentLoc.x) > 10 || Math.abs(location.y - currentLoc.y) > 10) downloadBar.setStucked(null); } } } } finally { g_manager.getMiniBarMonitor().exit(); } splash.setLocation(currentLoc); MiniBar mwCurrent = this; while (mwCurrent != null) { currentLoc.y += mwCurrent.getShell().getBounds().height - 1; MiniBar mwChild = mwCurrent.getStucked(); if (mwChild != null && mwChild != this) { Shell s = mwChild.getShell(); if (s.isDisposed()) { mwCurrent.setStucked(null); mwCurrent = null; } else { mwCurrent = mwChild; mwCurrent.getShell().setLocation(currentLoc); } } else mwCurrent = null; } } // // These methods define the management of MiniBars. // public Shell getShell() { return this.splash; } public void setVisible(boolean visible) { splash.setVisible(visible); } public final boolean hasSameContext(MiniBar m) { return this.hasContext(m.getContextObject()); } public final boolean hasContext(Object context) { Object my_context = this.getContextObject(); if (my_context == null) { return context == null; } else { return my_context.equals(context); } } public MiniBar getStucked() { return this.stucked; } public void setStucked(MiniBar mw) { this.stucked = mw; } // Have to be in the SWT thread to do this. public final void forceSaveLocation() { if (!splash.isDisposed()) { this.storeLastLocation(splash.getLocation()); } } public final void close() { if (!splash.isDisposed()) { Display display = splash.getDisplay(); if (display != null && !display.isDisposed()) { display.asyncExec(new AERunnable() { public void runSupport() {dispose();} }); } } manager.unregister(this); } public void dispose() { if (!splash.isDisposed()) { this.forceSaveLocation(); splash.dispose(); } if (bold_font != null && !bold_font.isDisposed()) {bold_font.dispose();} } public final void refresh() { if (splash.isDisposed()) {return;} refresh0(); } // // Subclass methods. // protected abstract void refresh0(); protected abstract void beginConstruction(); protected abstract Object getContextObject(); public String[] getPluginMenuIdentifiers(Object context) { return null; } public Object getPluginMenuContextObject() { return null; } protected Point getInitialLocation() { return null; } protected void storeLastLocation(Point point) { // Do nothing. } }