2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.openapi
.progress
.util
;
18 import com
.intellij
.ide
.IdeEventQueue
;
19 import com
.intellij
.openapi
.Disposable
;
20 import com
.intellij
.openapi
.application
.ApplicationManager
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.openapi
.progress
.ProgressManager
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.ui
.DialogWrapper
;
25 import com
.intellij
.openapi
.ui
.DialogWrapperPeer
;
26 import com
.intellij
.openapi
.ui
.impl
.FocusTrackbackProvider
;
27 import com
.intellij
.openapi
.ui
.impl
.GlassPaneDialogWrapperPeer
;
28 import com
.intellij
.openapi
.util
.Comparing
;
29 import com
.intellij
.openapi
.util
.Condition
;
30 import com
.intellij
.openapi
.util
.Disposer
;
31 import com
.intellij
.openapi
.util
.EmptyRunnable
;
32 import com
.intellij
.openapi
.wm
.WindowManager
;
33 import com
.intellij
.openapi
.wm
.IdeFocusManager
;
34 import com
.intellij
.openapi
.wm
.ex
.WindowManagerEx
;
35 import com
.intellij
.ui
.FocusTrackback
;
36 import com
.intellij
.ui
.PopupBorder
;
37 import com
.intellij
.ui
.TitlePanel
;
38 import com
.intellij
.ui
.awt
.RelativePoint
;
39 import com
.intellij
.util
.Alarm
;
40 import com
.intellij
.util
.ui
.UIUtil
;
41 import org
.jetbrains
.annotations
.Nullable
;
44 import javax
.swing
.border
.Border
;
46 import java
.awt
.event
.*;
49 @SuppressWarnings({"NonStaticInitializer"})
50 public class ProgressWindow
extends BlockingProgressIndicator
implements Disposable
{
51 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.progress.util.ProgressWindow");
53 private static final int UPDATE_INTERVAL
= 50; //msec. 20 frames per second.
55 private MyDialog myDialog
;
56 private final Alarm myUpdateAlarm
= new Alarm(Alarm
.ThreadToUse
.SWING_THREAD
);
57 private final Alarm myInstallFunAlarm
= new Alarm(Alarm
.ThreadToUse
.SWING_THREAD
);
58 private final Alarm myShowWindowAlarm
= new Alarm(Alarm
.ThreadToUse
.SWING_THREAD
);
60 private final Project myProject
;
61 private final boolean myShouldShowCancel
;
62 private String myCancelText
;
64 private String myTitle
= null;
66 private boolean myStoppedAlready
= false;
67 protected final FocusTrackback myFocusTrackback
;
68 private boolean myStarted
= false;
69 private boolean myBackgrounded
= false;
70 private boolean myWasShown
;
71 private String myProcessId
= "<unknown>";
72 @Nullable private volatile Runnable myBackgroundHandler
;
74 public ProgressWindow(boolean shouldShowCancel
, Project project
) {
75 this(shouldShowCancel
, false, project
);
78 public ProgressWindow(boolean shouldShowCancel
, boolean shouldShowBackground
, @Nullable Project project
) {
79 this(shouldShowCancel
, shouldShowBackground
, project
, null);
82 public ProgressWindow(boolean shouldShowCancel
, boolean shouldShowBackground
, @Nullable Project project
, String cancelText
) {
83 this(shouldShowCancel
, shouldShowBackground
, project
, null, cancelText
);
86 public ProgressWindow(boolean shouldShowCancel
, boolean shouldShowBackground
, @Nullable Project project
, JComponent parentComponent
, String cancelText
) {
88 myShouldShowCancel
= shouldShowCancel
;
89 myCancelText
= cancelText
;
90 setModalityProgress(shouldShowBackground ?
null : this);
91 myFocusTrackback
= new FocusTrackback(this, WindowManager
.getInstance().suggestParentWindow(project
), false);
93 Component parent
= parentComponent
;
94 if (parent
== null && project
== null) {
95 parent
= JOptionPane
.getRootFrame();
99 myDialog
= new MyDialog(shouldShowBackground
, parent
, myCancelText
);
102 myDialog
= new MyDialog(shouldShowBackground
, myProject
, myCancelText
);
105 Disposer
.register(this, myDialog
);
107 myFocusTrackback
.registerFocusComponent(myDialog
.getPanel());
110 public synchronized void start() {
111 LOG
.assertTrue(!isRunning());
112 LOG
.assertTrue(!myStoppedAlready
);
115 if (!ApplicationManager
.getApplication().isUnitTestMode()) {
122 private synchronized boolean isStarted() {
126 protected void prepareShowDialog() {
127 SwingUtilities
.invokeLater(new Runnable() {
129 myShowWindowAlarm
.addRequest(new Runnable() {
132 SwingUtilities
.invokeLater(new Runnable() {
134 if (myDialog
!= null) {
135 final DialogWrapper popup
= myDialog
.myPopup
;
137 myFocusTrackback
.registerFocusComponent(new FocusTrackback
.ComponentQuery() {
138 public Component
getComponent() {
139 return popup
.getPreferredFocusedComponent();
142 if (popup
.isShowing()) {
152 Disposer
.dispose(ProgressWindow
.this);
155 }, 300, getModalityState());
160 public void startBlocking() {
161 ApplicationManager
.getApplication().assertIsDispatchThread();
162 LOG
.assertTrue(!isRunning());
163 LOG
.assertTrue(!myStoppedAlready
);
167 IdeEventQueue
.getInstance().pumpEventsForHierarchy(myDialog
.myPanel
, new Condition
<AWTEvent
>() {
168 public boolean value(final AWTEvent object
) {
169 if (myShouldShowCancel
&&
170 object
instanceof KeyEvent
&&
171 object
.getID() == KeyEvent
.KEY_PRESSED
&&
172 ((KeyEvent
)object
).getKeyCode() == KeyEvent
.VK_ESCAPE
&&
173 ((KeyEvent
)object
).getModifiers() == 0) {
174 SwingUtilities
.invokeLater(new Runnable() {
180 return isStarted() && !isRunning();
187 public String
getProcessId() {
191 public void setProcessId(final String processId
) {
192 myProcessId
= processId
;
195 protected void showDialog() {
196 if (!isRunning() || isCanceled()) {
200 if (!ApplicationManager
.getApplication().isHeadlessEnvironment()) {
201 Runnable installer
= new Runnable() {
203 if (isRunning() && !isCanceled() && getFraction() < 0.15 && myDialog
!=null) {
204 final JComponent cmp
= ProgressManager
.getInstance().getProvidedFunComponent(myProject
, getProcessId());
206 setFunComponent(cmp
);
211 myInstallFunAlarm
.addRequest(installer
, 3000, getModalityState());
216 if (myDialog
!= null) {
217 myDialog
.myRepaintRunnable
.run();
221 public void setIndeterminate(boolean indeterminate
) {
222 super.setIndeterminate(indeterminate
);
226 public synchronized void stop() {
227 LOG
.assertTrue(!myStoppedAlready
);
228 myInstallFunAlarm
.cancelAllRequests();
232 if (myDialog
!= null) {
234 if (myDialog
.wasShown()) {
235 myFocusTrackback
.restoreFocus();
237 myFocusTrackback
.consume();
241 myStoppedAlready
= true;
243 Disposer
.dispose(this);
245 SwingUtilities
.invokeLater(EmptyRunnable
.INSTANCE
); // Just to give blocking dispatching a chance to go out.
248 public void cancel() {
250 if (myDialog
!= null) {
255 public void background() {
256 final Runnable backgroundHandler
= myBackgroundHandler
;
257 if (backgroundHandler
!= null) {
258 backgroundHandler
.run();
262 if (myDialog
!= null) {
263 myBackgrounded
= true;
264 myDialog
.background();
266 if (myDialog
.wasShown()) {
267 myFocusTrackback
.restoreFocus();
270 myFocusTrackback
.consume();
277 public boolean isBackgrounded() {
278 return myBackgrounded
;
281 public void setText(String text
) {
282 if (!Comparing
.equal(text
, getText())) {
288 public void setFraction(double fraction
) {
289 if (fraction
!= getFraction()) {
290 super.setFraction(fraction
);
295 public void setText2(String text
) {
296 if (!Comparing
.equal(text
, getText2())) {
297 super.setText2(text
);
302 private void update() {
303 if (myDialog
!= null) {
308 public void setTitle(String title
) {
309 if (!Comparing
.equal(title
, myTitle
)) {
315 public String
getTitle() {
319 protected static int getPercentage(double fraction
) {
320 return (int)(fraction
* 99 + 0.5);
323 protected class MyDialog
implements Disposable
{
324 private long myLastTimeDrawn
= -1;
325 private volatile boolean myShouldShowBackground
;
327 private final Runnable myRepaintRunnable
= new Runnable() {
329 String text
= getText();
330 double fraction
= getFraction();
331 String text2
= getText2();
333 myTextLabel
.setText(text
!= null && text
.length() > 0 ? text
: " ");
334 if (!isIndeterminate() && fraction
> 0) {
335 myPercentLabel
.setText(getPercentage(fraction
) + "%");
338 myPercentLabel
.setText(" ");
341 if (myProgressBar
.isShowing()) {
342 final int perc
= (int)(fraction
* 100);
343 myProgressBar
.setIndeterminate(perc
== 0 || isIndeterminate());
344 myProgressBar
.setValue(perc
);
347 myText2Label
.setText(getTitle2Text(text2
, myText2Label
.getWidth()));
349 myTitlePanel
.setText(myTitle
!= null && myTitle
.length() > 0 ? myTitle
: " ");
351 myLastTimeDrawn
= System
.currentTimeMillis();
352 myRepaintedFlag
= true;
356 private String
getTitle2Text(String fullText
, int labelWidth
) {
357 if (fullText
== null || fullText
.length() == 0) return " ";
358 while (myText2Label
.getFontMetrics(myText2Label
.getFont()).stringWidth(fullText
) > labelWidth
) {
359 int sep
= fullText
.indexOf(File
.separatorChar
, 4);
360 if (sep
< 0) return fullText
;
361 fullText
= "..." + fullText
.substring(sep
);
367 private final Runnable myUpdateRequest
= new Runnable() {
373 private JPanel myPanel
;
375 private JLabel myTextLabel
;
376 private JLabel myPercentLabel
;
377 private JLabel myText2Label
;
379 private JButton myCancelButton
;
380 private JButton myBackgroundButton
;
382 private JProgressBar myProgressBar
;
383 private boolean myRepaintedFlag
= true;
384 private JPanel myFunPanel
;
385 private TitlePanel myTitlePanel
;
386 private DialogWrapper myPopup
;
387 private final Window myParentWindow
;
388 private Point myLastClicked
;
390 public MyDialog(boolean shouldShowBackground
, Project project
, String cancelText
) {
391 Window parentWindow
= WindowManager
.getInstance().suggestParentWindow(project
);
392 if (parentWindow
== null) {
393 parentWindow
= WindowManagerEx
.getInstanceEx().getMostRecentFocusedWindow();
395 myParentWindow
=parentWindow
;
397 initDialog(shouldShowBackground
, cancelText
);
400 public MyDialog(boolean shouldShowBackground
, Component parent
, String cancelText
) {
401 myParentWindow
= parent
instanceof Window
403 : (Window
)SwingUtilities
.getAncestorOfClass(Window
.class, parent
);
404 initDialog(shouldShowBackground
, cancelText
);
407 private void initDialog(boolean shouldShowBackground
, String cancelText
) {
408 myFunPanel
.setLayout(new BorderLayout());
409 myCancelButton
.addActionListener(new ActionListener() {
410 public void actionPerformed(ActionEvent e
) {
415 myCancelButton
.registerKeyboardAction(new ActionListener() {
416 public void actionPerformed(ActionEvent e
) {
417 if (myCancelButton
.isEnabled()) {
421 }, KeyStroke
.getKeyStroke(KeyEvent
.VK_ESCAPE
, 0), JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
);
423 myShouldShowBackground
= shouldShowBackground
;
424 if (cancelText
!= null) {
425 setCancelButtonText(cancelText
);
427 myProgressBar
.setMaximum(100);
430 myTitlePanel
.setActive(true);
431 myTitlePanel
.addMouseListener(new MouseAdapter() {
432 public void mousePressed(MouseEvent e
) {
433 final Point titleOffset
= RelativePoint
.getNorthWestOf(myTitlePanel
).getScreenPoint();
434 myLastClicked
= new RelativePoint(e
).getScreenPoint();
435 myLastClicked
.x
-= titleOffset
.x
;
436 myLastClicked
.y
-= titleOffset
.y
;
440 myTitlePanel
.addMouseMotionListener(new MouseMotionAdapter() {
441 public void mouseDragged(MouseEvent e
) {
442 if (myLastClicked
== null) {
445 final Point draggedTo
= new RelativePoint(e
).getScreenPoint();
446 draggedTo
.x
-= myLastClicked
.x
;
447 draggedTo
.y
-= myLastClicked
.y
;
449 if (myPopup
!= null) {
450 myPopup
.setLocation(draggedTo
);
457 public void dispose() {
458 UIUtil
.disposeProgress(myProgressBar
);
459 UIUtil
.dispose(myTitlePanel
);
462 public JPanel
getPanel() {
466 public void setShouldShowBackground(final boolean shouldShowBackground
) {
467 myShouldShowBackground
= shouldShowBackground
;
468 SwingUtilities
.invokeLater(new Runnable() {
470 myBackgroundButton
.setVisible(shouldShowBackground
);
471 myPanel
.revalidate();
476 public void changeCancelButtonText(String text
){
477 myCancelButton
.setText(text
);
480 public void doCancelAction() {
481 if (myShouldShowCancel
) {
482 ProgressWindow
.this.cancel();
486 public void cancel() {
487 if (myShouldShowCancel
) {
488 myCancelButton
.setEnabled(false);
492 private void createCenterPanel() {
493 // Cancel button (if any)
495 if (myCancelText
!= null) {
496 myCancelButton
.setText(myCancelText
);
498 myCancelButton
.setVisible(myShouldShowCancel
);
500 myBackgroundButton
.setVisible(myShouldShowBackground
);
501 myBackgroundButton
.addActionListener(
502 new ActionListener() {
503 public void actionPerformed(ActionEvent e
) {
504 if (myShouldShowBackground
) {
505 ProgressWindow
.this.background();
511 // Panel with progress indicator and percents
513 int width
= myPercentLabel
.getFontMetrics(myPercentLabel
.getFont()).stringWidth("1000%");
514 myPercentLabel
.setPreferredSize(new Dimension(width
, myPercentLabel
.getPreferredSize().height
));
515 myPercentLabel
.setHorizontalAlignment(SwingConstants
.RIGHT
);
518 private synchronized void update() {
519 if (myRepaintedFlag
) {
520 if (System
.currentTimeMillis() > myLastTimeDrawn
+ UPDATE_INTERVAL
) {
521 myRepaintedFlag
= false;
522 SwingUtilities
.invokeLater(myRepaintRunnable
);
525 if (myUpdateAlarm
.getActiveRequestCount() == 0) {
526 myUpdateAlarm
.addRequest(myUpdateRequest
, 500, getModalityState());
532 public synchronized void background() {
533 if (myShouldShowBackground
) {
534 myBackgroundButton
.setEnabled(false);
541 SwingUtilities
.invokeLater(new Runnable() {
543 if (myPopup
!= null) {
544 myPopup
.close(DialogWrapper
.CANCEL_EXIT_CODE
);
552 if (ApplicationManager
.getApplication().isHeadlessEnvironment()) return;
553 if (myParentWindow
== null) return;
554 if (myPopup
!= null) {
555 myPopup
.close(DialogWrapper
.CANCEL_EXIT_CODE
);
558 myPopup
= myParentWindow
.isShowing() ?
new MyDialogWrapper(myParentWindow
, myShouldShowCancel
) : new MyDialogWrapper(myProject
, myShouldShowCancel
);
559 myPopup
.setUndecorated(true);
561 SwingUtilities
.invokeLater(new Runnable() {
563 if (myPopup
!= null) {
564 if (myPopup
.getPeer() instanceof FocusTrackbackProvider
) {
565 final FocusTrackback focusTrackback
= ((FocusTrackbackProvider
)myPopup
.getPeer()).getFocusTrackback();
566 if (focusTrackback
!= null) {
567 focusTrackback
.consume();
571 getFocusManager().requestFocus(myCancelButton
, true);
579 public boolean wasShown() {
583 private class MyDialogWrapper
extends DialogWrapper
{
584 private boolean myIsCancellable
;
586 public MyDialogWrapper(Project project
, final boolean cancellable
) {
587 super(project
, false);
589 myIsCancellable
= cancellable
;
592 public MyDialogWrapper(Component parent
, final boolean cancellable
) {
593 super(parent
, false);
595 myIsCancellable
= cancellable
;
599 public void doCancelAction() {
600 if (myIsCancellable
) {
601 super.doCancelAction();
606 protected DialogWrapperPeer
createPeer(final Component parent
, final boolean canBeParent
) {
607 if (System
.getProperty("vintage.progress") == null) {
609 return new GlassPaneDialogWrapperPeer(this, parent
, canBeParent
);
611 catch (GlassPaneDialogWrapperPeer
.GlasspanePeerUnavailableException e
) {
612 return super.createPeer(parent
, canBeParent
);
615 return super.createPeer(parent
, canBeParent
);
620 protected DialogWrapperPeer
createPeer(final boolean canBeParent
, final boolean toolkitModalIfPossible
) {
621 if (System
.getProperty("vintage.progress") == null) {
623 return new GlassPaneDialogWrapperPeer(this, canBeParent
);
625 catch (GlassPaneDialogWrapperPeer
.GlasspanePeerUnavailableException e
) {
626 return super.createPeer(canBeParent
, toolkitModalIfPossible
);
629 return super.createPeer(canBeParent
, toolkitModalIfPossible
);
634 protected DialogWrapperPeer
createPeer(final Project project
, final boolean canBeParent
) {
635 if (System
.getProperty("vintage.progress") == null) {
637 return new GlassPaneDialogWrapperPeer(this, project
, canBeParent
);
639 catch (GlassPaneDialogWrapperPeer
.GlasspanePeerUnavailableException e
) {
640 return super.createPeer(project
, canBeParent
);
643 return super.createPeer(project
, canBeParent
);
647 protected void init() {
649 setUndecorated(true);
650 myPanel
.setBorder(PopupBorder
.Factory
.create(true));
653 protected boolean isProgressDialog() {
657 protected JComponent
createCenterPanel() {
662 protected JComponent
createSouthPanel() {
667 protected Border
createContentPaneBorder() {
673 public void setBackgroundHandler(@Nullable Runnable backgroundHandler
) {
674 myBackgroundHandler
= backgroundHandler
;
675 myDialog
.setShouldShowBackground(backgroundHandler
!= null);
678 public void setCancelButtonText(String text
){
679 if (myDialog
!= null) {
680 myDialog
.changeCancelButtonText(text
);
687 private void setFunComponent(JComponent c
) {
688 myDialog
.myFunPanel
.removeAll();
690 myDialog
.myFunPanel
.add(new JSeparator(), BorderLayout
.NORTH
);
691 myDialog
.myFunPanel
.add(c
, BorderLayout
.CENTER
);
694 if (myDialog
.myPopup
!= null && !(myDialog
.myPopup
.getPeer() instanceof GlassPaneDialogWrapperPeer
)) { // TODO[spL]: remove
695 final Window wnd
= SwingUtilities
.windowForComponent(myDialog
.myPanel
);
696 if (wnd
!= null) { // Can be null if just hidden
700 myDialog
.myPopup
.validate();
704 private IdeFocusManager
getFocusManager() {
705 return IdeFocusManager
.getInstance(myProject
);
708 public void dispose() {