/****************************************************************************** * Copyright (c) 2008 g-Eclipse consortium * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Initial development of the original code was made for * project g-Eclipse founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributor(s): * Mathias Stuempert - initial API and implementation *****************************************************************************/ package eu.geclipse.ui.providers; import java.net.URL; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import eu.geclipse.ui.internal.Activator; public class ProgressTreeNode implements IProgressMonitor, Listener { /** * Internal class that handles UI updates. */ private static class ProgressNodeUpdater implements Runnable { /** * The progress tree node used to show the progress. */ private ProgressTreeNode node; /** * The last progress string. */ private String lastProgress; private boolean wasAlreadyDone; /** * Construct a new updater for the specified {@link ProgressTreeNode}. * * @param node The progress tree node showing the progress. */ public ProgressNodeUpdater( final ProgressTreeNode node ) { this.node = node; this.wasAlreadyDone = false; } /* (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { String progress = this.node.toString(); if ( ( progress != null ) && ! progress.equals( this.lastProgress ) ) { this.lastProgress = progress; this.node.getViewer().update( this.node, null ); } else if ( this.node.isDone() && ! this.wasAlreadyDone ) { this.wasAlreadyDone = true; this.node.getViewer().getTree().redraw(); } } /** * Reset the progress. */ public void reset() { this.lastProgress = null; } } private static Image emptyProgress; private static Image fullProgress; static { URL emptyURL = Activator.getDefault().getBundle() .getEntry( "icons/extras/progress_bar_empty.gif" ); //$NON-NLS-1$ emptyProgress = ImageDescriptor.createFromURL( emptyURL ).createImage(); URL fullURL = Activator.getDefault().getBundle() .getEntry( "icons/extras/progress_bar_full.gif" ); //$NON-NLS-1$ fullProgress = ImageDescriptor.createFromURL( fullURL ).createImage(); } protected boolean done; private int tWork; private double worked; private String taskName; private boolean canceled; private TreeViewer viewer; private ProgressNodeUpdater updater; public ProgressTreeNode( final TreeViewer viewer ) { this.tWork = 100; this.worked = 0; this.done = false; this.canceled = false; this.taskName = "Pending..."; this.updater = new ProgressNodeUpdater( this ); this.viewer = viewer; final Tree tree = viewer.getTree(); if ( ! tree.isDisposed() ) { Display display = tree.getDisplay(); display.syncExec( new Runnable() { public void run() { if ( !tree.isDisposed() ) { tree.addListener( SWT.MeasureItem, ProgressTreeNode.this ); tree.addListener( SWT.EraseItem, ProgressTreeNode.this ); tree.addListener( SWT.PaintItem, ProgressTreeNode.this ); } } } ); } } public void beginTask( final String name, final int totalWork ) { this.taskName = name; this.tWork = totalWork; this.worked = 0; this.done = false; this.canceled = false; update(); } public void done() { this.worked = this.tWork; this.done = true; update(); } public TreeViewer getViewer() { return this.viewer; } public void handleEvent( final Event event ) { if ( ( event.item instanceof TreeItem ) && ( event.index == 0 ) ) { Object data = ( ( TreeItem ) event.item ).getData(); if ( data == this ) { switch ( event.type ) { case SWT.MeasureItem: measureItem( event ); break; case SWT.EraseItem: eraseItem( event ); break; case SWT.PaintItem: paintItem( event ); break; } } } } public void internalWorked( final double work ) { this.worked += work; update(); } public boolean isCanceled() { return this.canceled; } /** * Determine if the job is done. * * @return True if the job is done. */ public boolean isDone() { return this.done; } public void setCanceled( final boolean value ) { this.canceled = value; update(); } public void setTaskName( final String name ) { this.taskName = name; update(); } @Override public String toString() { String resultString = this.taskName + " (" //$NON-NLS-1$ + String.valueOf( getProgressPercent() ) + ")"; //$NON-NLS-1$ return resultString; } public void subTask( final String name ) { setTaskName( name ); } public void worked( final int work ) { internalWorked( work ); } /** * Get the current progress in percent. * * @return The current progress. */ private int getProgressPercent() { return ( int ) Math.round( 100.*this.worked/this.tWork ); } /** * Convenience method for the SWT.EraseItem event. * * @param event The event triggering this erasure. */ private void eraseItem( @SuppressWarnings("unused") final Event event ) { // empty implementation } /** * Convenience method for the SWT.MeasureItem event. * * @param event The event triggering this measurement. */ private void measureItem( final Event event ) { int textHeight = event.gc.getFontMetrics().getHeight(); int imageWidth = emptyProgress.getBounds().width; int imageHeight = emptyProgress.getBounds().height; int textWidth = this.taskName == null ? 0 : event.gc.textExtent( this.taskName ).x; event.height = Math.max( textHeight, imageHeight ); event.width = textWidth + imageWidth + 2; } /** * Convenience method for the SWT.PaintItem event. * * @param event The event triggering this paint. */ protected void paintItem( final Event event ) { Display display = this.viewer.getTree().getDisplay(); Color black = display.getSystemColor( SWT.COLOR_BLACK ); event.gc.fillRectangle( event.x, event.y, event.width, event.height ); Color white = display.getSystemColor( SWT.COLOR_WHITE ); int progress = getProgressPercent(); int barwidth = fullProgress.getBounds().width; int barheight = fullProgress.getBounds().height; int bary = event.y + ( event.height - barheight ) / 2; int barp = barwidth * progress / 100; event.gc.drawImage( emptyProgress, barp, 0, barwidth-barp, barheight, event.x+barp+2, bary, barwidth-barp, barheight ); event.gc.drawImage( fullProgress, 0, 0, barp, barheight, event.x+2, bary, barp, barheight ); String progressString = String.valueOf( progress )+"%"; //$NON-NLS-1$ //$NON-NLS-2$ Point textExtend = event.gc.textExtent( progressString ); int progressX = event.x + 2 + ( barwidth - textExtend.x ) / 2; event.gc.setForeground( white ); event.gc.drawText( progressString, progressX+1, event.y + 2, true ); event.gc.setForeground( black ); event.gc.drawText( progressString, progressX, event.y + 1, true ); int textX = event.x + 6 + barwidth; if ( this.taskName != null ) { event.gc.setForeground( black ); event.gc.drawText( this.taskName, textX, event.y + 1, true ); } } /** * Update the tree in order to repaint this node. */ protected void update() { Tree tree = getViewer().getTree(); if ( !tree.isDisposed() ) { Display display = tree.getDisplay(); display.asyncExec( this.updater ); } } }