KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > action > StatusLine


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jface.action;
13
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.jface.dialogs.ProgressIndicator;
16 import org.eclipse.jface.resource.ImageDescriptor;
17 import org.eclipse.jface.resource.JFaceColors;
18 import org.eclipse.jface.resource.JFaceResources;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.custom.CLabel;
21 import org.eclipse.swt.events.DisposeEvent;
22 import org.eclipse.swt.events.DisposeListener;
23 import org.eclipse.swt.events.SelectionAdapter;
24 import org.eclipse.swt.events.SelectionEvent;
25 import org.eclipse.swt.graphics.Cursor;
26 import org.eclipse.swt.graphics.Font;
27 import org.eclipse.swt.graphics.Image;
28 import org.eclipse.swt.graphics.Point;
29 import org.eclipse.swt.graphics.Rectangle;
30 import org.eclipse.swt.layout.GridData;
31 import org.eclipse.swt.layout.GridLayout;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Display;
35 import org.eclipse.swt.widgets.Layout;
36 import org.eclipse.swt.widgets.ToolBar;
37 import org.eclipse.swt.widgets.ToolItem;
38
39 /**
40  * A StatusLine control is a SWT Composite with a horizontal layout which hosts
41  * a number of status indication controls.
42  * Typically it is situated below the content area of the window.
43  * <p>
44  * By default a StatusLine has two predefined status controls: a MessageLine and a
45  * ProgressIndicator and it provides API for easy access.
46  * </p>
47  * <p>
48  * This is an internal class, not intended to be used outside the JFace framework.
49  * </p>
50  */

51 /* package */class StatusLine extends Composite implements IProgressMonitor {
52
53     /** Horizontal gaps between items. */
54     public static final int GAP = 3;
55
56     /** Progress bar creation is delayed by this ms */
57     public static final int DELAY_PROGRESS = 500;
58
59     /** visibility state of the progressbar */
60     protected boolean fProgressIsVisible = false;
61
62     /** visibility state of the cancle button */
63     protected boolean fCancelButtonIsVisible = false;
64
65     /** enablement state of the cancle button */
66     protected boolean fCancelEnabled = false;
67
68     /** name of the task */
69     protected String JavaDoc fTaskName;
70
71     /** is the task is cancled */
72     protected boolean fIsCanceled;
73
74     /** the start time of the task */
75     protected long fStartTime;
76
77     private Cursor fStopButtonCursor;
78
79     /** the message text */
80     protected String JavaDoc fMessageText;
81
82     /** the message image */
83     protected Image fMessageImage;
84
85     /** the error text */
86     protected String JavaDoc fErrorText;
87
88     /** the error image */
89     protected Image fErrorImage;
90
91     /** the message label */
92     protected CLabel fMessageLabel;
93
94     /** the composite parent of the progress bar */
95     protected Composite fProgressBarComposite;
96
97     /** the progress bar */
98     protected ProgressIndicator fProgressBar;
99
100     /** the toolbar */
101     protected ToolBar fToolBar;
102
103     /** the cancle button */
104     protected ToolItem fCancelButton;
105
106     /** stop image descriptor */
107     protected static ImageDescriptor fgStopImage = ImageDescriptor
108             .createFromFile(StatusLine.class, "images/stop.gif");//$NON-NLS-1$
109
static {
110         JFaceResources.getImageRegistry().put(
111                 "org.eclipse.jface.parts.StatusLine.stopImage", fgStopImage);//$NON-NLS-1$
112
}
113
114     /**
115      * Layout the contribution item controls on the status line.
116      */

117     public class StatusLineLayout extends Layout {
118         private final StatusLineLayoutData DEFAULT_DATA = new StatusLineLayoutData();
119
120         public Point computeSize(Composite composite, int wHint, int hHint,
121                 boolean changed) {
122
123             if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) {
124                 return new Point(wHint, hHint);
125             }
126
127             Control[] children = composite.getChildren();
128             int totalWidth = 0;
129             int maxHeight = 0;
130             int totalCnt = 0;
131             for (int i = 0; i < children.length; i++) {
132                 boolean useWidth = true;
133                 Control w = children[i];
134                 if (w == fProgressBarComposite && !fProgressIsVisible) {
135                     useWidth = false;
136                 } else if (w == fToolBar && !fCancelButtonIsVisible) {
137                     useWidth = false;
138                 }
139                 StatusLineLayoutData data = (StatusLineLayoutData) w
140                         .getLayoutData();
141                 if (data == null) {
142                     data = DEFAULT_DATA;
143                 }
144                 Point e = w.computeSize(data.widthHint, data.heightHint,
145                         changed);
146                 if (useWidth) {
147                     totalWidth += e.x;
148                     totalCnt++;
149                 }
150                 maxHeight = Math.max(maxHeight, e.y);
151             }
152             if (totalCnt > 0) {
153                 totalWidth += (totalCnt - 1) * GAP;
154             }
155             if (totalWidth <= 0) {
156                 totalWidth = maxHeight * 4;
157             }
158             return new Point(totalWidth, maxHeight);
159         }
160
161         public void layout(Composite composite, boolean flushCache) {
162
163             if (composite == null) {
164                 return;
165             }
166
167             // StatusLineManager skips over the standard status line widgets
168
// in its update method. There is thus a dependency
169
// between the layout of the standard widgets and the update method.
170

171             // Make sure cancel button and progress bar are before contributions.
172
fMessageLabel.moveAbove(null);
173             fToolBar.moveBelow(fMessageLabel);
174             fProgressBarComposite.moveBelow(fToolBar);
175
176             Rectangle rect = composite.getClientArea();
177             Control[] children = composite.getChildren();
178             int count = children.length;
179
180             int ws[] = new int[count];
181
182             int h = rect.height;
183             int totalWidth = -GAP;
184             for (int i = 0; i < count; i++) {
185                 Control w = children[i];
186                 if (w == fProgressBarComposite && !fProgressIsVisible) {
187                     continue;
188                 }
189                 if (w == fToolBar && !fCancelButtonIsVisible) {
190                     continue;
191                 }
192                 StatusLineLayoutData data = (StatusLineLayoutData) w
193                         .getLayoutData();
194                 if (data == null) {
195                     data = DEFAULT_DATA;
196                 }
197                 int width = w.computeSize(data.widthHint, h, flushCache).x;
198                 ws[i] = width;
199                 totalWidth += width + GAP;
200             }
201
202             int diff = rect.width - totalWidth;
203             ws[0] += diff; // make the first StatusLabel wider
204

205             // Check against minimum recommended width
206
final int msgMinWidth = rect.width / 3;
207             if (ws[0] < msgMinWidth) {
208                 diff = ws[0] - msgMinWidth;
209                 ws[0] = msgMinWidth;
210             } else {
211                 diff = 0;
212             }
213
214             // Take space away from the contributions first.
215
for (int i = count - 1; i >= 0 && diff < 0; --i) {
216                 int min = Math.min(ws[i], -diff);
217                 ws[i] -= min;
218                 diff += min + GAP;
219             }
220
221             int x = rect.x;
222             int y = rect.y;
223             for (int i = 0; i < count; i++) {
224                 Control w = children[i];
225                 /*
226                  * Workaround for Linux Motif:
227                  * Even if the progress bar and cancel button are
228                  * not set to be visible ad of width 0, they still
229                  * draw over the first pixel of the editor
230                  * contributions.
231                  *
232                  * The fix here is to draw the progress bar and
233                  * cancel button off screen if they are not visible.
234                  */

235                 if (w == fProgressBarComposite && !fProgressIsVisible
236                         || w == fToolBar && !fCancelButtonIsVisible) {
237                     w.setBounds(x + rect.width, y, ws[i], h);
238                     continue;
239                 }
240                 w.setBounds(x, y, ws[i], h);
241                 if (ws[i] > 0) {
242                     x += ws[i] + GAP;
243                 }
244             }
245         }
246     }
247
248     /**
249      * Create a new StatusLine as a child of the given parent.
250      *
251      * @param parent the parent for this Composite
252      * @param style the style used to create this widget
253      */

254     public StatusLine(Composite parent, int style) {
255         super(parent, style);
256
257         addDisposeListener(new DisposeListener() {
258             public void widgetDisposed(DisposeEvent e) {
259                 handleDispose();
260             }
261         });
262
263         // StatusLineManager skips over the standard status line widgets
264
// in its update method. There is thus a dependency
265
// between this code defining the creation and layout of the standard
266
// widgets and the update method.
267

268         setLayout(new StatusLineLayout());
269
270         fMessageLabel = new CLabel(this, SWT.NONE);//SWT.SHADOW_IN);
271
// Color[] colors = new Color[2];
272
// colors[0] = parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
273
// colors[1] = fMessageLabel.getBackground();
274
// int[] gradient = new int[] {JFaceColors.STATUS_PERCENT};
275
// fMessageLabel.setBackground(colors, gradient);
276

277         fProgressIsVisible = false;
278         fCancelEnabled = false;
279
280         fToolBar = new ToolBar(this, SWT.FLAT);
281         fCancelButton = new ToolItem(fToolBar, SWT.PUSH);
282         fCancelButton.setImage(fgStopImage.createImage());
283         fCancelButton.setToolTipText(JFaceResources
284                 .getString("Cancel_Current_Operation")); //$NON-NLS-1$
285
fCancelButton.addSelectionListener(new SelectionAdapter() {
286             public void widgetSelected(SelectionEvent e) {
287                 setCanceled(true);
288             }
289         });
290         fCancelButton.addDisposeListener(new DisposeListener() {
291             public void widgetDisposed(DisposeEvent e) {
292                 Image i = fCancelButton.getImage();
293                 if ((i != null) && (!i.isDisposed())) {
294                     i.dispose();
295                 }
296             }
297         });
298
299         // We create a composite to create the progress bar in
300
// so that it can be centered. See bug #32331
301
fProgressBarComposite = new Composite(this, SWT.NONE);
302         GridLayout layout = new GridLayout();
303         layout.horizontalSpacing = 0;
304         layout.verticalSpacing = 0;
305         layout.marginHeight = 0;
306         layout.marginWidth = 0;
307         fProgressBarComposite.setLayout(layout);
308         fProgressBar = new ProgressIndicator(fProgressBarComposite);
309         fProgressBar.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
310                 | GridData.GRAB_VERTICAL));
311
312         fStopButtonCursor = new Cursor(getDisplay(), SWT.CURSOR_ARROW);
313     }
314
315     /**
316      * Notifies that the main task is beginning.
317      *
318      * @param name the name (or description) of the main task
319      * @param totalWork the total number of work units into which
320      * the main task is been subdivided. If the value is 0 or UNKNOWN the
321      * implemenation is free to indicate progress in a way which doesn't
322      * require the total number of work units in advance. In general users
323      * should use the UNKNOWN value if they don't know the total amount of
324      * work units.
325      */

326     public void beginTask(String JavaDoc name, int totalWork) {
327         final long timestamp = System.currentTimeMillis();
328         fStartTime = timestamp;
329         final boolean animated = (totalWork == UNKNOWN || totalWork == 0);
330         // make sure the progress bar is made visible while
331
// the task is running. Fixes bug 32198 for the non-animated case.
332
Runnable JavaDoc timer = new Runnable JavaDoc() {
333             public void run() {
334                 StatusLine.this.startTask(timestamp, animated);
335             }
336         };
337         if (fProgressBar == null) {
338             return;
339         }
340
341         fProgressBar.getDisplay().timerExec(DELAY_PROGRESS, timer);
342         if (!animated) {
343             fProgressBar.beginTask(totalWork);
344         }
345         if (name == null) {
346             fTaskName = "";//$NON-NLS-1$
347
} else {
348             fTaskName = name;
349         }
350         setMessage(fTaskName);
351     }
352
353     /**
354      * Notifies that the work is done; that is, either the main task is completed or the
355      * user cancelled it.
356      * Done() can be called more than once; an implementation should be prepared to handle
357      * this case.
358      */

359     public void done() {
360
361         fStartTime = 0;
362
363         if (fProgressBar != null) {
364             fProgressBar.sendRemainingWork();
365             fProgressBar.done();
366         }
367         setMessage(null);
368
369         hideProgress();
370     }
371
372     /**
373      * Returns the status line's progress monitor
374      * @return {@link IProgressMonitor} the progress monitor
375      */

376     public IProgressMonitor getProgressMonitor() {
377         return this;
378     }
379
380     /**
381      * @private
382      */

383     protected void handleDispose() {
384         if (fStopButtonCursor != null) {
385             fStopButtonCursor.dispose();
386             fStopButtonCursor = null;
387         }
388         if (fProgressBar != null) {
389             fProgressBar.dispose();
390             fProgressBar = null;
391         }
392     }
393
394     /**
395      * Hides the Cancel button and ProgressIndicator.
396      * @private
397      */

398     protected void hideProgress() {
399
400         if (fProgressIsVisible && !isDisposed()) {
401             fProgressIsVisible = false;
402             fCancelEnabled = false;
403             fCancelButtonIsVisible = false;
404             if (fToolBar != null && !fToolBar.isDisposed()) {
405                 fToolBar.setVisible(false);
406             }
407             if (fProgressBarComposite != null
408                     && !fProgressBarComposite.isDisposed()) {
409                 fProgressBarComposite.setVisible(false);
410             }
411             layout();
412         }
413     }
414
415     /**
416      * @see IProgressMonitor#internalWorked(double)
417      */

418     public void internalWorked(double work) {
419         if (!fProgressIsVisible) {
420             if (System.currentTimeMillis() - fStartTime > DELAY_PROGRESS) {
421                 showProgress();
422             }
423         }
424
425         if (fProgressBar != null) {
426             fProgressBar.worked(work);
427         }
428     }
429
430     /**
431      * Returns true if the user does some UI action to cancel this operation.
432      * (like hitting the Cancel button on the progress dialog).
433      * The long running operation typically polls isCanceled().
434      */

435     public boolean isCanceled() {
436         return fIsCanceled;
437     }
438
439     /**
440      * Returns <code>true</true> if the ProgressIndication provides UI for canceling
441      * a long running operation.
442      * @return <code>true</true> if the ProgressIndication provides UI for canceling
443      */

444     public boolean isCancelEnabled() {
445         return fCancelEnabled;
446     }
447
448     /**
449      * Sets the cancel status. This method is usually called with the
450      * argument false if a client wants to abort a cancel action.
451      */

452     public void setCanceled(boolean b) {
453         fIsCanceled = b;
454         if (fCancelButton != null) {
455             fCancelButton.setEnabled(!b);
456         }
457     }
458
459     /**
460      * Controls whether the ProgressIndication provides UI for canceling
461      * a long running operation.
462      * If the ProgressIndication is currently visible calling this method may have
463      * a direct effect on the layout because it will make a cancel button visible.
464      *
465      * @param enabled <code>true</true> if cancel should be enabled
466      */

467     public void setCancelEnabled(boolean enabled) {
468         fCancelEnabled = enabled;
469         if (fProgressIsVisible && !fCancelButtonIsVisible && enabled) {
470             showButton();
471             layout();
472         }
473         if (fCancelButton != null && !fCancelButton.isDisposed()) {
474             fCancelButton.setEnabled(enabled);
475         }
476     }
477
478     /**
479      * Sets the error message text to be displayed on the status line.
480      * The image on the status line is cleared.
481      *
482      * @param message the error message, or <code>null</code> for no error message
483      */

484     public void setErrorMessage(String JavaDoc message) {
485         setErrorMessage(null, message);
486     }
487
488     /**
489      * Sets an image and error message text to be displayed on the status line.
490      *
491      * @param image the image to use, or <code>null</code> for no image
492      * @param message the error message, or <code>null</code> for no error message
493      */

494     public void setErrorMessage(Image image, String JavaDoc message) {
495         fErrorText = trim(message);
496         fErrorImage = image;
497         updateMessageLabel();
498     }
499
500     /**
501      * Applies the given font to this status line.
502      */

503     public void setFont(Font font) {
504         super.setFont(font);
505         Control[] children = getChildren();
506         for (int i = 0; i < children.length; i++) {
507             children[i].setFont(font);
508         }
509     }
510
511     /**
512      * Sets the message text to be displayed on the status line.
513      * The image on the status line is cleared.
514      *
515      * @param message the error message, or <code>null</code> for no error message
516      */

517     public void setMessage(String JavaDoc message) {
518         setMessage(null, message);
519     }
520
521     /**
522      * Sets an image and a message text to be displayed on the status line.
523      *
524      * @param image the image to use, or <code>null</code> for no image
525      * @param message the message, or <code>null</code> for no message
526      */

527     public void setMessage(Image image, String JavaDoc message) {
528         fMessageText = trim(message);
529         fMessageImage = image;
530         updateMessageLabel();
531     }
532
533     /**
534      * @see IProgressMonitor#setTaskName(java.lang.String)
535      */

536     public void setTaskName(String JavaDoc name) {
537         fTaskName = name;
538     }
539
540     /**
541      * Makes the Cancel button visible.
542      * @private
543      */

544     protected void showButton() {
545         if (fToolBar != null && !fToolBar.isDisposed()) {
546             fToolBar.setVisible(true);
547             fToolBar.setEnabled(true);
548             fToolBar.setCursor(fStopButtonCursor);
549             fCancelButtonIsVisible = true;
550         }
551     }
552
553     /**
554      * Shows the Cancel button and ProgressIndicator.
555      * @private
556      */

557     protected void showProgress() {
558         if (!fProgressIsVisible && !isDisposed()) {
559             fProgressIsVisible = true;
560             if (fCancelEnabled) {
561                 showButton();
562             }
563             if (fProgressBarComposite != null
564                     && !fProgressBarComposite.isDisposed()) {
565                 fProgressBarComposite.setVisible(true);
566             }
567             layout();
568         }
569     }
570
571     /**
572      * @private
573      */

574     void startTask(final long timestamp, final boolean animated) {
575         if (!fProgressIsVisible && fStartTime == timestamp) {
576             showProgress();
577             if (animated) {
578                 if (fProgressBar != null && !fProgressBar.isDisposed()) {
579                     fProgressBar.beginAnimatedTask();
580                 }
581             }
582         }
583     }
584
585     /**
586      * Notifies that a subtask of the main task is beginning.
587      * Subtasks are optional; the main task might not have subtasks.
588      * @param name the name (or description) of the subtask
589      * @see IProgressMonitor#subTask(String)
590      */

591     public void subTask(String JavaDoc name) {
592         String JavaDoc text;
593         if (fTaskName.length() == 0) {
594             text = name;
595         } else {
596             text = JFaceResources.format(
597                     "Set_SubTask", new Object JavaDoc[] { fTaskName, name });//$NON-NLS-1$
598
}
599         setMessage(text);
600     }
601
602     /**
603      * Trims the message to be displayable in the status line.
604      * This just pulls out the first line of the message.
605      * Allows null.
606      */

607     String JavaDoc trim(String JavaDoc message) {
608         if (message == null) {
609             return null;
610         }
611         int cr = message.indexOf('\r');
612         int lf = message.indexOf('\n');
613         if (cr == -1 && lf == -1) {
614             return message;
615         }
616         int len;
617         if (cr == -1) {
618             len = lf;
619         } else if (lf == -1) {
620             len = cr;
621         } else {
622             len = Math.min(cr, lf);
623         }
624         return message.substring(0, len);
625     }
626
627     /**
628      * Updates the message label widget.
629      */

630     protected void updateMessageLabel() {
631         if (fMessageLabel != null && !fMessageLabel.isDisposed()) {
632             Display display = fMessageLabel.getDisplay();
633             if ((fErrorText != null && fErrorText.length() > 0)
634                     || fErrorImage != null) {
635                 fMessageLabel.setForeground(JFaceColors.getErrorText(display));
636                 fMessageLabel.setText(fErrorText);
637                 fMessageLabel.setImage(fErrorImage);
638             } else {
639                 fMessageLabel.setForeground(display
640                         .getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
641                 fMessageLabel.setText(fMessageText == null ? "" : fMessageText); //$NON-NLS-1$
642
fMessageLabel.setImage(fMessageImage);
643             }
644         }
645     }
646
647     /**
648      * @see IProgressMonitor#worked(int)
649      */

650     public void worked(int work) {
651         internalWorked(work);
652     }
653 }
654
Popular Tags