001: /*******************************************************************************
002: * Copyright (c) 2003, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.progress;
011:
012: import java.net.MalformedURLException;
013: import java.net.URL;
014:
015: import org.eclipse.core.runtime.IStatus;
016: import org.eclipse.core.runtime.QualifiedName;
017: import org.eclipse.core.runtime.jobs.Job;
018: import org.eclipse.jface.viewers.Viewer;
019: import org.eclipse.jface.viewers.ViewerComparator;
020: import org.eclipse.jface.window.IShellProvider;
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.graphics.GC;
023: import org.eclipse.swt.graphics.Point;
024: import org.eclipse.swt.graphics.Rectangle;
025: import org.eclipse.swt.widgets.Control;
026: import org.eclipse.swt.widgets.Shell;
027: import org.eclipse.ui.IWorkbench;
028: import org.eclipse.ui.IWorkbenchPage;
029: import org.eclipse.ui.IWorkbenchWindow;
030: import org.eclipse.ui.PartInitException;
031: import org.eclipse.ui.PlatformUI;
032: import org.eclipse.ui.internal.RectangleAnimation;
033: import org.eclipse.ui.internal.WorkbenchPlugin;
034: import org.eclipse.ui.internal.WorkbenchWindow;
035: import org.eclipse.ui.internal.misc.StatusUtil;
036: import org.eclipse.ui.internal.util.BundleUtility;
037: import org.eclipse.ui.progress.IProgressConstants;
038: import org.eclipse.ui.views.IViewDescriptor;
039:
040: /**
041: * The ProgressUtil is a class that contains static utility methods used for the
042: * progress API.
043: */
044:
045: public class ProgressManagerUtil {
046: /**
047: * A constant used by the progress support to determine if an operation is
048: * too short to show progress.
049: */
050: public static long SHORT_OPERATION_TIME = 250;
051:
052: static final QualifiedName KEEP_PROPERTY = IProgressConstants.KEEP_PROPERTY;
053:
054: static final QualifiedName KEEPONE_PROPERTY = IProgressConstants.KEEPONE_PROPERTY;
055:
056: static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
057:
058: static final QualifiedName INFRASTRUCTURE_PROPERTY = new QualifiedName(
059: WorkbenchPlugin.PI_WORKBENCH, "INFRASTRUCTURE_PROPERTY");//$NON-NLS-1$
060:
061: private static String ellipsis = ProgressMessages.ProgressFloatingWindow_EllipsisValue;
062:
063: /**
064: * Return a status for the exception.
065: *
066: * @param exception
067: * @return IStatus
068: */
069: static IStatus exceptionStatus(Throwable exception) {
070: return StatusUtil.newStatus(IStatus.ERROR, exception
071: .getMessage() == null ? "" : exception.getMessage(), //$NON-NLS-1$
072: exception);
073: }
074:
075: /**
076: * Log the exception for debugging.
077: *
078: * @param exception
079: */
080: static void logException(Throwable exception) {
081: BundleUtility.log(PlatformUI.PLUGIN_ID, exception);
082: }
083:
084: // /**
085: // * Sets the label provider for the viewer.
086: // *
087: // * @param viewer
088: // */
089: // static void initLabelProvider(ProgressTreeViewer viewer) {
090: // viewer.setLabelProvider(new ProgressLabelProvider());
091: // }
092: /**
093: * Return a viewer comparator for looking at the jobs.
094: *
095: * @return ViewerComparator
096: */
097: static ViewerComparator getProgressViewerComparator() {
098: return new ViewerComparator() {
099: /*
100: * (non-Javadoc)
101: *
102: * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
103: * java.lang.Object, java.lang.Object)
104: */
105: public int compare(Viewer testViewer, Object e1, Object e2) {
106: return ((Comparable) e1).compareTo(e2);
107: }
108: };
109: }
110:
111: /**
112: * Open the progress view in the supplied window.
113: *
114: * @param window
115: */
116: static void openProgressView(WorkbenchWindow window) {
117: IWorkbenchPage page = window.getActivePage();
118: if (page == null) {
119: return;
120: }
121: try {
122: IViewDescriptor reference = WorkbenchPlugin.getDefault()
123: .getViewRegistry().find(
124: IProgressConstants.PROGRESS_VIEW_ID);
125:
126: if (reference == null) {
127: return;
128: }
129: page.showView(IProgressConstants.PROGRESS_VIEW_ID);
130: } catch (PartInitException exception) {
131: logException(exception);
132: }
133: }
134:
135: /**
136: * Shorten the given text <code>t</code> so that its length doesn't exceed
137: * the given width. The default implementation replaces characters in the
138: * center of the original string with an ellipsis ("..."). Override if you
139: * need a different strategy.
140: *
141: * @param textValue
142: * @param control
143: * @return String
144: */
145: static String shortenText(String textValue, Control control) {
146: if (textValue == null) {
147: return null;
148: }
149: GC gc = new GC(control);
150: int maxWidth = control.getBounds().width - 5;
151: if (gc.textExtent(textValue).x < maxWidth) {
152: gc.dispose();
153: return textValue;
154: }
155: int length = textValue.length();
156: int ellipsisWidth = gc.textExtent(ellipsis).x;
157: // Find the second space seperator and start from there
158: int secondWord = findSecondWhitespace(textValue, gc, maxWidth);
159: int pivot = ((length - secondWord) / 2) + secondWord;
160: int start = pivot;
161: int end = pivot + 1;
162: while (start >= secondWord && end < length) {
163: String s1 = textValue.substring(0, start);
164: String s2 = textValue.substring(end, length);
165: int l1 = gc.textExtent(s1).x;
166: int l2 = gc.textExtent(s2).x;
167: if (l1 + ellipsisWidth + l2 < maxWidth) {
168: gc.dispose();
169: return s1 + ellipsis + s2;
170: }
171: start--;
172: end++;
173: }
174: gc.dispose();
175: return textValue;
176: }
177:
178: /**
179: * Find the second index of a whitespace. Return the first index if there
180: * isn't one or 0 if there is no space at all.
181: *
182: * @param textValue
183: * @param gc
184: * The GC to test max length
185: * @param maxWidth
186: * The maximim extent
187: * @return int
188: */
189: private static int findSecondWhitespace(String textValue, GC gc,
190: int maxWidth) {
191: int firstCharacter = 0;
192: char[] chars = textValue.toCharArray();
193: // Find the first whitespace
194: for (int i = 0; i < chars.length; i++) {
195: if (Character.isWhitespace(chars[i])) {
196: firstCharacter = i;
197: break;
198: }
199: }
200: // If we didn't find it once don't continue
201: if (firstCharacter == 0) {
202: return 0;
203: }
204: // Initialize to firstCharacter in case there is no more whitespace
205: int secondCharacter = firstCharacter;
206: // Find the second whitespace
207: for (int i = firstCharacter; i < chars.length; i++) {
208: if (Character.isWhitespace(chars[i])) {
209: secondCharacter = i;
210: break;
211: }
212: }
213: // Check that we haven't gone over max width. Throw
214: // out an index that is too high
215: if (gc.textExtent(textValue.substring(0, secondCharacter)).x > maxWidth) {
216: if (gc.textExtent(textValue.substring(0, firstCharacter)).x > maxWidth) {
217: return 0;
218: }
219: return firstCharacter;
220: }
221: return secondCharacter;
222: }
223:
224: /**
225: * If there are any modal shells open reschedule openJob to wait until they
226: * are closed. Return true if it rescheduled, false if there is nothing
227: * blocking it.
228: *
229: * @param openJob
230: * @return boolean. true if the job was rescheduled due to modal dialogs.
231: */
232: public static boolean rescheduleIfModalShellOpen(Job openJob) {
233: Shell modal = getModalShellExcluding(null);
234: if (modal == null) {
235: return false;
236: }
237:
238: // try again in a few seconds
239: openJob.schedule(PlatformUI.getWorkbench().getProgressService()
240: .getLongOperationTime());
241: return true;
242: }
243:
244: /**
245: * Return whether or not it is safe to open this dialog. If so then return
246: * <code>true</code>. If not then set it to open itself when it has had
247: * ProgressManager#longOperationTime worth of ticks.
248: *
249: * @param dialog
250: * ProgressMonitorJobsDialog that will be opening
251: * @param excludedShell
252: * The shell
253: * @return boolean. <code>true</code> if it can open. Otherwise return
254: * false and set the dialog to tick.
255: */
256: public static boolean safeToOpen(ProgressMonitorJobsDialog dialog,
257: Shell excludedShell) {
258: Shell modal = getModalShellExcluding(excludedShell);
259: if (modal == null) {
260: return true;
261: }
262:
263: dialog.watchTicks();
264: return false;
265: }
266:
267: /**
268: * Return the modal shell that is currently open. If there isn't one then
269: * return null.
270: *
271: * @param shell
272: * A shell to exclude from the search. May be <code>null</code>.
273: *
274: * @return Shell or <code>null</code>.
275: */
276: public static Shell getModalShellExcluding(Shell shell) {
277: IWorkbench workbench = PlatformUI.getWorkbench();
278: Shell[] shells = workbench.getDisplay().getShells();
279: int modal = SWT.APPLICATION_MODAL | SWT.SYSTEM_MODAL
280: | SWT.PRIMARY_MODAL;
281: for (int i = 0; i < shells.length; i++) {
282: if (shells[i].equals(shell)) {
283: break;
284: }
285: // Do not worry about shells that will not block the user.
286: if (shells[i].isVisible()) {
287: int style = shells[i].getStyle();
288: if ((style & modal) != 0) {
289: return shells[i];
290: }
291: }
292: }
293: return null;
294: }
295:
296: /**
297: * Utility method to get the best parenting possible for a dialog. If there
298: * is a modal shell create it so as to avoid two modal dialogs. If not then
299: * return the shell of the active workbench window. If neither can be found
300: * return null.
301: *
302: * @return Shell or <code>null</code>
303: */
304: public static Shell getDefaultParent() {
305: Shell modal = getModalShellExcluding(null);
306: if (modal != null) {
307: return modal;
308: }
309:
310: return getNonModalShell();
311: }
312:
313: /**
314: * Get the active non modal shell. If there isn't one return null.
315: *
316: * @return Shell
317: */
318: public static Shell getNonModalShell() {
319: IWorkbenchWindow window = PlatformUI.getWorkbench()
320: .getActiveWorkbenchWindow();
321: if (window == null) {
322: IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
323: .getWorkbenchWindows();
324: if (windows.length > 0)
325: return windows[0].getShell();
326: } else
327: return window.getShell();
328:
329: return null;
330: }
331:
332: /**
333: * Animate the closing of a window given the start position down to the
334: * progress region.
335: *
336: * @param startPosition
337: * Rectangle. The position to start drawing from.
338: */
339: public static void animateDown(Rectangle startPosition) {
340: IWorkbenchWindow currentWindow = PlatformUI.getWorkbench()
341: .getActiveWorkbenchWindow();
342: if (currentWindow == null) {
343: return;
344: }
345: WorkbenchWindow internalWindow = (WorkbenchWindow) currentWindow;
346:
347: ProgressRegion progressRegion = internalWindow
348: .getProgressRegion();
349: if (progressRegion == null) {
350: return;
351: }
352: Rectangle endPosition = progressRegion.getControl().getBounds();
353:
354: Point windowLocation = internalWindow.getShell().getLocation();
355: endPosition.x += windowLocation.x;
356: endPosition.y += windowLocation.y;
357: RectangleAnimation animation = new RectangleAnimation(
358: internalWindow.getShell(), startPosition, endPosition);
359: animation.schedule();
360: }
361:
362: /**
363: * Animate the opening of a window given the start position down to the
364: * progress region.
365: *
366: * @param endPosition
367: * Rectangle. The position to end drawing at.
368: */
369: public static void animateUp(Rectangle endPosition) {
370: IWorkbenchWindow currentWindow = PlatformUI.getWorkbench()
371: .getActiveWorkbenchWindow();
372: if (currentWindow == null) {
373: return;
374: }
375: WorkbenchWindow internalWindow = (WorkbenchWindow) currentWindow;
376: Point windowLocation = internalWindow.getShell().getLocation();
377:
378: ProgressRegion progressRegion = internalWindow
379: .getProgressRegion();
380: if (progressRegion == null) {
381: return;
382: }
383: Rectangle startPosition = progressRegion.getControl()
384: .getBounds();
385: startPosition.x += windowLocation.x;
386: startPosition.y += windowLocation.y;
387:
388: RectangleAnimation animation = new RectangleAnimation(
389: internalWindow.getShell(), startPosition, endPosition);
390: animation.schedule();
391: }
392:
393: /**
394: * Get the shell provider to use in the progress support dialogs. This
395: * provider will try to always parent off of an existing modal shell. If
396: * there isn't one it will use the current workbench window.
397: *
398: * @return IShellProvider
399: */
400: static IShellProvider getShellProvider() {
401: return new IShellProvider() {
402:
403: /*
404: * (non-Javadoc)
405: *
406: * @see org.eclipse.jface.window.IShellProvider#getShell()
407: */
408: public Shell getShell() {
409: return getDefaultParent();
410: }
411: };
412: }
413:
414: /**
415: * Get the icons root for the progress support.
416: * @return URL
417: */
418: public static URL getIconsRoot() {
419: return BundleUtility.find(PlatformUI.PLUGIN_ID,
420: ProgressManager.PROGRESS_FOLDER);
421: }
422:
423: /**
424: * Return the location of the progress spinner.
425: * @return URL or <code>null</code> if it cannot be found
426: */
427: public static URL getProgressSpinnerLocation() {
428: try {
429: return new URL(getIconsRoot(), "progress_spinner.gif");//$NON-NLS-1$
430: } catch (MalformedURLException e) {
431: return null;
432: }
433: }
434: }
|