001: package snow.utils.gui;
002:
003: import snow.utils.DateUtils;
004: import java.awt.*;
005: import java.awt.event.*;
006: import javax.swing.*;
007: import javax.swing.border.*;
008: import java.util.*;
009:
010: /** A progress dialog
011: should be used from another thread
012: 1) construct it
013: 2) start()
014: 3) if wasCancelled => stop process and call closeDialog()...
015: 4) always call closeDialog() !
016: [Feb2005]: newly process stop button
017: [June2007]: terminate tide option (when elapsed>1min)
018: */
019: public class ProgressModalDialog extends JDialog {
020: final int fontSize = UIManager.getFont("Label.font").getSize();
021:
022: final JPanel contentPane = new JPanel(new BorderLayout());
023: final private JPanel progressPanel = new JPanel();
024: final JProgressBar progress = new JProgressBar();
025:
026: final JLabel progressLabel = new JLabel("Please wait...");
027: final JLabel messageProgressLabel = new JLabel("Message progress: ");
028:
029: private boolean wasCanceled = false;
030: private boolean seeRunningSince = true;
031: private volatile boolean hasBeenClosed = false;
032: final JButton cancelBT = new JButton("Cancel");
033: final JLabel remainingLabel = new JLabel("");
034: final public JCheckBox terminateAppAfterCompletion = new JCheckBox(
035: "Terminate tIDE at completion", false);
036:
037: public boolean showCloseTideButtonAfter20sec = true;
038:
039: // used to switch off the indeterminate progressbar
040: boolean firstTimeCall = true;
041:
042: private Process process = null;
043:
044: private transient long startTime = System.currentTimeMillis();
045:
046: public ProgressModalDialog(JFrame owner, String title) {
047: super (owner, title, true);
048: this .initialize();
049: }
050:
051: public ProgressModalDialog(JFrame owner, String title, boolean modal) {
052: super (owner, title, modal);
053: this .initialize();
054: }
055:
056: public ProgressModalDialog(JDialog owner, String title,
057: boolean modal) {
058: super (owner, title, modal);
059: this .initialize();
060: }
061:
062: public ProgressModalDialog(JDialog owner, String title) {
063: super (owner, title, true);
064: this .initialize();
065: }
066:
067: public void setShowCancel(boolean show) {
068: this .cancelBT.setVisible(show);
069: }
070:
071: public void setCancelText(String txt, Icon ic) {
072: cancelBT.setText(txt);
073: if (ic != null) {
074: cancelBT.setIcon(ic);
075: }
076: }
077:
078: public void setProgressBounds(final int max) {
079: EventQueue.invokeLater(new Runnable() {
080: public void run() {
081: progress.setIndeterminate(false);
082: progress.setMaximum(max);
083: }
084: });
085: }
086:
087: public void setSeeRunningSince(boolean a) {
088: this .seeRunningSince = a;
089: remainingLabel.setText("");
090: }
091:
092: public void incrementProgress(final int n) {
093: EventQueue.invokeLater(new Runnable() {
094: public void run() {
095: progress.setIndeterminate(false);
096: progress.setValue(progress.getValue() + n);
097: updateEstimatedRemaining();
098: }
099: });
100: }
101:
102: JTextArea remainingtimeArea = null;
103:
104: // EDT!!
105: private void updateEstimatedRemaining() {
106: //if(this instanceof EstimatedProgressDialog) return;
107:
108: long tt = System.currentTimeMillis() - startTime;
109: if (tt < 2000)
110: return;
111:
112: double percentAchieved = this .progress.getValue() * 100.0
113: / this .progress.getMaximum();
114: StringBuilder sb = new StringBuilder();
115: if (percentAchieved > 0 && percentAchieved < 100) {
116: long rem = (long) (100.0 * tt / (percentAchieved)) - tt;
117: if (rem > 1000) {
118: sb.append("Remaining "
119: + DateUtils.formatTimeDifference(rem));
120: if (this instanceof EstimatedProgressDialog) {
121: sb.append(" (estimated)");
122: }
123: }
124: }
125:
126: if (seeRunningSince && tt > 1000 * 20) // only after 20 sec
127: {
128: if (sb.length() > 0)
129: sb.append(", ");
130: sb.append("running since "
131: + DateUtils.formatTimeDifference(tt));
132:
133: if (showCloseTideButtonAfter20sec
134: && !terminateAppAfterCompletion.isVisible()) {
135: terminateAppAfterCompletion.setVisible(true);
136: Dimension nd = new Dimension(
137: (int) getSize().getWidth(),
138: (int) (terminateAppAfterCompletion
139: .getPreferredSize().getHeight() + getSize()
140: .getHeight()));
141: setPreferredSize(nd);
142: pack();
143: }
144: }
145:
146: remainingLabel.setText(sb.toString());
147: }
148:
149: public int getValue() {
150: return progress.getValue();
151: }
152:
153: public int getMaxValue() {
154: return progress.getMaximum();
155: }
156:
157: public void setProgressValue(final int val, final String comment) {
158: if (val == 0)
159: startTime = System.currentTimeMillis();
160:
161: EventQueue.invokeLater(new Runnable() {
162: public void run() {
163: if (firstTimeCall) {
164: firstTimeCall = false;
165: progress.setIndeterminate(false);
166: progress.setStringPainted(true);
167: }
168: progress.setValue(val);
169: if (comment != null) {
170: progress.setString(comment);
171: }
172: updateEstimatedRemaining();
173: }
174: });
175: }
176:
177: /** String of the progress (can change often), sets the progress to be determined (stops no animation).
178: */
179: public void setProgressComment(final String comment) {
180: EventQueue.invokeLater(new Runnable() {
181: public void run() {
182: if (firstTimeCall) {
183: firstTimeCall = false;
184: progress.setIndeterminate(false);
185: progress.setStringPainted(true);
186: }
187: progress.setString(comment);
188: }
189: });
190: }
191:
192: public void setProgressCommentAndKeepIndeterminate(
193: final String comment) {
194: EventQueue.invokeLater(new Runnable() {
195: public void run() {
196: progress.setStringPainted(true);
197: progress.setString(comment);
198: }
199: });
200: }
201:
202: /** A label describing the "section" being progressed.
203: */
204: public void setCommentLabel(final String comment) {
205: EventQueue.invokeLater(new Runnable() {
206: public void run() {
207: progressLabel.setText(comment);
208: updateEstimatedRemaining();
209: }
210: });
211: }
212:
213: /** A label describing the "section" being progressed.
214: * sets the progress label to ""
215: * "..." automatically appended.
216: */
217: public void setProgressSection(final String comment) {
218: EventQueue.invokeLater(new Runnable() {
219: public void run() {
220: progressLabel.setText(comment + " ...");
221: progress.setString("");
222: updateEstimatedRemaining();
223: }
224: });
225: }
226:
227: /** This process will be killed if the user press cancel.
228: */
229: public void setProcessToOptionalKill(Process p) {
230: cancelBT.setVisible(true);
231: this .process = p;
232: }
233:
234: public boolean getWasCancelled() {
235: return this .wasCanceled;
236: }
237:
238: public void startDelayed(long dt) {
239: javax.swing.Timer t = new javax.swing.Timer((int) dt,
240: new ActionListener() {
241: public void actionPerformed(ActionEvent ae) {
242:
243: // only if not already terminated !
244: if (hasBeenClosed)
245: return;
246:
247: ProgressModalDialog.this .start();
248:
249: /*EventQueue.invokeLater(new Runnable()
250: {
251: public void run()
252: {
253: //contentPane.startAnimation();
254: if(hasBeenClosed) return;
255: setVisible(true);
256: }
257: });*/
258: }
259: });
260: t.start();
261: }
262:
263: public void start() {
264: startTime = System.currentTimeMillis();
265: EventQueue.invokeLater(new Runnable() {
266: public void run() {
267: //contentPane.startAnimation();
268: setVisible(true);
269: }
270: });
271: }
272:
273: public void resetStartTime() {
274: startTime = System.currentTimeMillis();
275: }
276:
277: /** Must always be called at the end !
278: */
279: public void closeDialog() {
280:
281: SwingSafeRunnable ssr = new SwingSafeRunnable(new Runnable() {
282: public void run() {
283: //contentPane.stopAnimation();
284: setVisible(false);
285: dispose();
286: }
287: }, true);
288: ssr.run();
289:
290: if (!this .getWasCancelled()
291: && this .terminateAppAfterCompletion.isSelected()) {
292: if (tide.editor.MainEditorFrame.instance != null) {
293: // in 10 secs
294: Thread ru = new Thread() {
295: public void run() {
296: try {
297: Thread.sleep(10000);
298: } catch (Exception e) {
299: }
300: tide.editor.MainEditorFrame.instance
301: .terminateTIDE();
302: }
303: };
304: ru.start();
305: }
306: }
307: }
308:
309: /** Adds to the box layout in the center
310: */
311: public void addComponentToUI(final JComponent c) {
312: EventQueue.invokeLater(new Runnable() {
313: public void run() {
314: progressPanel.add(c);
315: //pack();
316: //adapt vertical size
317: Dimension nd = new Dimension(
318: (int) getSize().getWidth(),
319: (int) (c.getPreferredSize().getHeight() + getSize()
320: .getHeight()));
321: setPreferredSize(nd);
322: //setSize( nd );
323: /*progressPanel.setPreferredSize( progressPanel.getPreferredSize() );
324: progressPanel.revalidate();
325: progressPanel.repaint();*/
326: pack();
327: }
328: });
329:
330: }
331:
332: /** should be called in the EDT !
333: */
334: public JTextArea addSomeShortDescriptionLines(String descr) {
335: JTextArea ta = new JTextArea(descr);
336: ta.setBorder(new EmptyBorder(2, 5, 2, 2));
337: ta.setOpaque(false);
338: ta.setEditable(false);
339: addComponentToUI(ta);
340: return ta;
341: }
342:
343: private void initialize() {
344: setContentPane(contentPane);
345:
346: progressPanel.setOpaque(false);
347: progressPanel.setLayout(new BoxLayout(progressPanel,
348: BoxLayout.Y_AXIS));
349: progressPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
350: add(progressPanel, BorderLayout.CENTER);
351:
352: JPanel wp = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 2));
353: wp.add(progressLabel);
354: progressPanel.add(wp);
355: //progressLabel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
356: progressPanel.add(progress);
357:
358: progressLabel.setBorder(new EmptyBorder(fontSize / 4,
359: fontSize / 2, fontSize / 4, fontSize / 2));
360: progressLabel.setFont(UIManager.getFont("SubTextFont"));
361:
362: progress.setOpaque(false);
363: progress.setIndeterminate(true);
364:
365: JPanel southPanel = new JPanel();
366: southPanel.setOpaque(false);
367: add(southPanel, BorderLayout.SOUTH);
368: southPanel
369: .setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS));
370:
371: JPanel controlPanel = new JPanel(new FlowLayout(
372: FlowLayout.LEFT, 5, 3));
373: southPanel.add(controlPanel);
374: controlPanel.setOpaque(false);
375:
376: controlPanel.add(cancelBT);
377: cancelBT.setFocusPainted(false);
378: cancelBT.setMargin(new Insets(0, 3, 0, 3));
379: controlPanel.add(remainingLabel);
380: remainingLabel.setFont(UIManager.getFont("SubTextFont"));
381:
382: JPanel controlPanel2 = new JPanel(new FlowLayout(
383: FlowLayout.LEFT, 5, 0));
384: southPanel.add(controlPanel2);
385: controlPanel2.add(terminateAppAfterCompletion);
386: terminateAppAfterCompletion.setFocusPainted(false);
387: terminateAppAfterCompletion.setVisible(false);
388:
389: cancelBT.addActionListener(new ActionListener() {
390: public void actionPerformed(ActionEvent e) {
391: cancelAction();
392: }
393: });
394:
395: ((JComponent) getContentPane()).registerKeyboardAction(
396: new ActionListener() {
397: public void actionPerformed(ActionEvent ae) {
398: cancelAction();
399: }
400: }, "Escape", KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,
401: 0, true),
402: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
403:
404: // progress.setStringPainted(true); // only after first show.
405:
406: this .addWindowListener(new WindowListener() {
407: public void windowActivated(WindowEvent e) {
408: }
409:
410: public void windowClosed(WindowEvent e) {
411: hasBeenClosed = true;
412: }
413:
414: public void windowClosing(WindowEvent e) {
415: hasBeenClosed = true;
416: }
417:
418: public void windowDeactivated(WindowEvent e) {
419: }
420:
421: public void windowDeiconified(WindowEvent e) {
422: }
423:
424: public void windowIconified(WindowEvent e) {
425: }
426:
427: public void windowOpened(WindowEvent e) {
428: }
429: });
430:
431: // fixed, but fontsize relative size :
432: this .pack();
433: Dimension dim = new Dimension(fontSize * 28, Math.max(
434: fontSize * 9, this .getHeight()));
435: this .setPreferredSize(dim);
436: this .setMinimumSize(dim);
437: this .setSize(dim);
438:
439: setPositionOnScreenDependingOnModality();
440: } // initialize
441:
442: public void setPositionOnScreenDependingOnModality() {
443: // center it on the frame if modal
444: if (this .isModal()) {
445: if (this .getOwner() != null && this .getOwner().isVisible()) {
446: this .setLocationRelativeTo(this .getOwner());
447: } else {
448: this .setLocationRelativeTo(null);
449: }
450: } else {
451: // non modal => working in background is allowed,
452: // as during a findbugs analysis...
453: // => put on the lower right side.
454: if (this .getOwner() != null && this .getOwner().isVisible()
455: && this .getOwner() instanceof JFrame) {
456: JFrame ow = (JFrame) this .getOwner();
457: this .setLocation(ow.getWidth() - this .getWidth() - 40
458: + ow.getX(), ow.getHeight() - this .getHeight()
459: - 30 + ow.getY());
460: } else {
461:
462: Dimension dimsc = Toolkit.getDefaultToolkit()
463: .getScreenSize();
464: this .setLocation((int) dimsc.getWidth()
465: - this .getWidth() - 40, (int) dimsc.getHeight()
466: - this .getHeight() - 30);
467: }
468: }
469: }
470:
471: public static ProgressModalDialog createStandaloneProgress(
472: String title) {
473: JFrame jFrame = new JFrame();
474: ProgressModalDialog pd = new ProgressModalDialog(jFrame, title,
475: true);
476: return pd;
477: }
478:
479: /** If process is not null, it is destroyed and the dialog is closed.
480: * In all cases, the flag wasCanceled is set to true.
481: * If process is null, the dialog is NOT closed. The process that progresses must then check if it should cancel.
482: */
483: void cancelAction() {
484: //System.out.println("ProgressModalDialog cancel proc="+process);
485:
486: if (process != null) {
487: wasCanceled = true;
488: try {
489: process.destroy();
490: closeDialog();
491:
492: } catch (Exception ex) {
493: ex.printStackTrace();
494: }
495: }
496: wasCanceled = true;
497: // now the process to be canceled must check...
498: }
499:
500: /* test
501: public static void main(String[] arguments)
502: {
503: JFrame fr = new JFrame();
504: fr.setDefaultCloseOperation(fr.EXIT_ON_CLOSE);
505: fr.setSize(100, 100);
506: fr.setVisible(true);
507: final ProgressModalDialog pmd = new ProgressModalDialog(fr, "Test");
508: pmd.start();
509: //pmd.addComponentToUI(new JTextArea("Hello\nTotal working time= 12m"));
510: pmd.addSomeShortDescriptionLines("Hello\nWorld!");
511:
512: java.util.Timer t = new java.util.Timer();
513: t.schedule(new TimerTask() { public void run() {
514: pmd.incrementProgress(1);
515: } },2000,100);
516: }*/
517:
518: }
|