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
.impl
;
18 import com
.intellij
.openapi
.application
.Application
;
19 import com
.intellij
.openapi
.application
.ApplicationManager
;
20 import com
.intellij
.openapi
.application
.ModalityState
;
21 import com
.intellij
.openapi
.application
.ex
.ApplicationEx
;
22 import com
.intellij
.openapi
.components
.ServiceManager
;
23 import com
.intellij
.openapi
.extensions
.Extensions
;
24 import com
.intellij
.openapi
.progress
.*;
25 import com
.intellij
.openapi
.progress
.util
.ProgressWindow
;
26 import com
.intellij
.openapi
.progress
.util
.SmoothProgressAdapter
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.util
.Comparing
;
29 import com
.intellij
.openapi
.util
.Disposer
;
30 import com
.intellij
.openapi
.wm
.WindowManager
;
31 import com
.intellij
.openapi
.wm
.ex
.ProgressIndicatorEx
;
32 import com
.intellij
.psi
.PsiLock
;
33 import com
.intellij
.ui
.SystemNotifications
;
34 import org
.jetbrains
.annotations
.*;
38 import java
.util
.ArrayList
;
39 import java
.util
.List
;
40 import java
.util
.concurrent
.atomic
.AtomicInteger
;
42 public class ProgressManagerImpl
extends ProgressManager
{
43 @NonNls private static final String PROCESS_CANCELED_EXCEPTION
= "idea.ProcessCanceledException";
45 private static final ThreadLocal
<ProgressIndicator
> myThreadIndicator
= new ThreadLocal
<ProgressIndicator
>();
46 private final AtomicInteger myCurrentProgressCount
= new AtomicInteger(0);
47 private final AtomicInteger myCurrentUnsafeProgressCount
= new AtomicInteger(0);
48 private final AtomicInteger myCurrentModalProgressCount
= new AtomicInteger(0);
50 private static volatile int ourLockedCheckCounter
= 0;
51 private final List
<ProgressFunComponentProvider
> myFunComponentProviders
= new ArrayList
<ProgressFunComponentProvider
>();
52 @NonNls private static final String NAME
= "Progress Cancel Checker";
54 public ProgressManagerImpl(Application application
) {
55 if (!application
.isUnitTestMode() && !Comparing
.equal(System
.getProperty(PROCESS_CANCELED_EXCEPTION
), "disabled")) {
62 catch (InterruptedException ignored
) {
64 ourNeedToCheckCancel
= true;
72 protected void doCheckCanceled() throws ProcessCanceledException
{
73 final ProgressIndicator progress
= getProgressIndicator();
74 if (progress
!= null) {
76 progress
.checkCanceled();
78 catch (ProcessCanceledException e
) {
79 if (Thread
.holdsLock(PsiLock
.LOCK
)) {
80 ourLockedCheckCounter
++;
81 if (ourLockedCheckCounter
> 10) {
82 ourLockedCheckCounter
= 0;
83 ourNeedToCheckCancel
= true;
87 ourLockedCheckCounter
= 0;
88 progress
.checkCanceled();
94 public static void canceled() {
95 ourNeedToCheckCancel
= true;
98 private static class NonCancelableIndicator
extends EmptyProgressIndicator
implements NonCancelableSection
{
99 private final ProgressIndicator myOld
;
101 private NonCancelableIndicator(ProgressIndicator old
) {
106 ProgressIndicator currentIndicator
= myThreadIndicator
.get();
107 if (currentIndicator
!= this) {
108 throw new AssertionError("Trying do .done() NonCancelableSection, which is already done");
111 myThreadIndicator
.set(myOld
);
115 public void checkCanceled() {
119 public NonCancelableSection
startNonCancelableSection() {
120 NonCancelableIndicator nonCancelor
= new NonCancelableIndicator(myThreadIndicator
.get());
121 myThreadIndicator
.set(nonCancelor
);
125 public void executeNonCancelableSection(Runnable r
) {
126 NonCancelableSection nonCancelor
= startNonCancelableSection();
135 public JComponent
getProvidedFunComponent(Project project
, String processId
) {
136 for(ProgressFunComponentProvider provider
: Extensions
.getExtensions(ProgressFunComponentProvider
.EP_NAME
)) {
137 JComponent cmp
= provider
.getProgressFunComponent(project
, processId
);
138 if (cmp
!= null) return cmp
;
141 for (ProgressFunComponentProvider provider
: myFunComponentProviders
) {
142 JComponent cmp
= provider
.getProgressFunComponent(project
, processId
);
143 if (cmp
!= null) return cmp
;
148 public void setCancelButtonText(String cancelButtonText
) {
149 ProgressIndicator progressIndicator
= getProgressIndicator();
150 if (progressIndicator
!= null) {
151 if (progressIndicator
instanceof SmoothProgressAdapter
&& cancelButtonText
!= null) {
152 ProgressIndicator original
= ((SmoothProgressAdapter
)progressIndicator
).getOriginal();
153 if (original
instanceof ProgressWindow
) {
154 ((ProgressWindow
)original
).setCancelButtonText(cancelButtonText
);
161 public void registerFunComponentProvider(ProgressFunComponentProvider provider
) {
162 myFunComponentProviders
.add(provider
);
165 public void removeFunComponentProvider(ProgressFunComponentProvider provider
) {
166 myFunComponentProviders
.remove(provider
);
169 public boolean hasProgressIndicator() {
170 return myCurrentProgressCount
.get() > 0;
173 public boolean hasUnsafeProgressIndicator() {
174 return myCurrentUnsafeProgressCount
.get() > 0;
177 public boolean hasModalProgressIndicator() {
178 return myCurrentModalProgressCount
.get() > 0;
181 public void runProcess(@NotNull final Runnable process
, final ProgressIndicator progress
) {
182 executeProcessUnderProgress(new Runnable(){
184 synchronized (process
) {
188 if (progress
!= null && !progress
.isRunning()) {
194 if (progress
!= null && progress
.isRunning()) {
196 if (progress
instanceof ProgressIndicatorEx
) {
197 ((ProgressIndicatorEx
)progress
).processFinish();
205 public void executeProcessUnderProgress(@NotNull Runnable process
, ProgressIndicator progress
) throws ProcessCanceledException
{
206 ProgressIndicator oldIndicator
= myThreadIndicator
.get();
208 if (progress
!= null) myThreadIndicator
.set(progress
);
209 myCurrentProgressCount
.incrementAndGet();
211 final boolean modal
= progress
!= null && progress
.isModal();
212 if (modal
) myCurrentModalProgressCount
.incrementAndGet();
213 if (progress
== null || progress
instanceof ProgressWindow
) myCurrentUnsafeProgressCount
.incrementAndGet();
219 myThreadIndicator
.set(oldIndicator
);
221 myCurrentProgressCount
.decrementAndGet();
222 if (modal
) myCurrentModalProgressCount
.decrementAndGet();
223 if (progress
== null || progress
instanceof ProgressWindow
) myCurrentUnsafeProgressCount
.decrementAndGet();
227 public ProgressIndicator
getProgressIndicator() {
228 return myThreadIndicator
.get();
231 public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process
, String progressTitle
, boolean canBeCanceled
, Project project
) {
232 return runProcessWithProgressSynchronously(process
, progressTitle
, canBeCanceled
, project
, null);
235 public boolean runProcessWithProgressSynchronously(@NotNull final Runnable process
, String progressTitle
, boolean canBeCanceled
, Project project
, JComponent parentComponent
) {
236 Task
.Modal task
= new Task
.Modal(project
, progressTitle
, canBeCanceled
) {
237 public void run(@NotNull ProgressIndicator indicator
) {
241 return runProcessWithProgressSynchronously(task
, parentComponent
);
244 private static boolean runProcessWithProgressSynchronously(final Task task
, final JComponent parentComponent
) {
245 final long start
= System
.currentTimeMillis();
246 final boolean result
= ((ApplicationEx
)ApplicationManager
.getApplication())
247 .runProcessWithProgressSynchronously(new TaskContainer(task
) {
249 new TaskRunnable(task
, ProgressManager
.getInstance().getProgressIndicator()).run();
251 }, task
.getTitle(), task
.isCancellable(), task
.getProject(), parentComponent
, task
.getCancelText());
253 final long end
= System
.currentTimeMillis();
254 final Task
.NotificationInfo notificationInfo
= task
.getNotificationInfo();
255 if (notificationInfo
!= null && end
- start
> 5000) { // show notification only if process took more than 5 secs
256 final JFrame frame
= WindowManager
.getInstance().getFrame(task
.getProject());
257 if (!frame
.hasFocus()) {
258 systemNotify(notificationInfo
);
270 private static void systemNotify(final Task
.NotificationInfo notificationInfo
) {
271 final SystemNotifications notifications
= ServiceManager
.getService(SystemNotifications
.class);
272 if (notifications
== null) return;
273 notifications
.notify(notificationInfo
.getNotificationName(), notificationInfo
.getNotificationTitle(), notificationInfo
.getNotificationText());
276 public void runProcessWithProgressAsynchronously(@NotNull Project project
,
277 @NotNull String progressTitle
,
278 @NotNull final Runnable process
,
279 @Nullable final Runnable successRunnable
,
280 @Nullable final Runnable canceledRunnable
) {
281 runProcessWithProgressAsynchronously(project
, progressTitle
, process
, successRunnable
, canceledRunnable
, PerformInBackgroundOption
.DEAF
);
284 public void runProcessWithProgressAsynchronously(@NotNull final Project project
,
285 @Nls @NotNull final String progressTitle
,
286 @NotNull final Runnable process
,
287 @Nullable final Runnable successRunnable
,
288 @Nullable final Runnable canceledRunnable
,
289 @NotNull final PerformInBackgroundOption option
) {
291 runProcessWithProgressAsynchronously(new Task
.Backgroundable(project
, progressTitle
, true, option
) {
292 public void run(@NotNull final ProgressIndicator indicator
) {
297 public void onCancel() {
298 if (canceledRunnable
!= null) {
299 canceledRunnable
.run();
303 public void onSuccess() {
304 if (successRunnable
!= null) {
305 successRunnable
.run();
311 public static void runProcessWithProgressAsynchronously(final Task
.Backgroundable task
) {
312 final ProgressIndicator progressIndicator
;
313 if (ApplicationManager
.getApplication().isHeadlessEnvironment()) {
314 progressIndicator
= new EmptyProgressIndicator();
317 final BackgroundableProcessIndicator indicator
= new BackgroundableProcessIndicator(task
);
318 Disposer
.register(ApplicationManager
.getApplication(), indicator
);
319 progressIndicator
= indicator
;
323 final Runnable process
= new TaskRunnable(task
, progressIndicator
);
325 TaskContainer action
= new TaskContainer(task
) {
327 boolean canceled
= false;
328 final long start
= System
.currentTimeMillis();
330 ProgressManager
.getInstance().runProcess(process
, progressIndicator
);
332 catch (ProcessCanceledException e
) {
335 final long end
= System
.currentTimeMillis();
337 if (canceled
|| progressIndicator
.isCanceled()) {
338 ApplicationManager
.getApplication().invokeLater(new Runnable() {
342 }, ModalityState
.NON_MODAL
);
344 else if (!canceled
) {
345 final Task
.NotificationInfo notificationInfo
= task
.getNotificationInfo();
346 if (notificationInfo
!= null && end
- start
> 5000) { // snow notification if process took more than 5 secs
347 final Component window
= KeyboardFocusManager
.getCurrentKeyboardFocusManager().getActiveWindow();
348 if (window
== null || notificationInfo
.isShowWhenFocused()) {
349 systemNotify(notificationInfo
);
353 ApplicationManager
.getApplication().invokeLater(new Runnable() {
357 }, ModalityState
.NON_MODAL
);
362 synchronized (process
) {
363 ApplicationManager
.getApplication().executeOnPooledThread(action
);
367 catch (InterruptedException e
) {
368 throw new RuntimeException(e
);
373 public void run(@NotNull final Task task
) {
374 if (task
.isHeadless()) {
375 new TaskRunnable(task
, new EmptyProgressIndicator()).run();
379 if (task
.isModal()) {
380 runProcessWithProgressSynchronously(task
.asModal(), null);
383 final Task
.Backgroundable backgroundable
= task
.asBackgroundable();
384 if (backgroundable
.isConditionalModal() && !backgroundable
.shouldStartInBackground()) {
385 runProcessWithProgressSynchronously(task
, null);
388 runProcessWithProgressAsynchronously(backgroundable
);
393 private abstract static class TaskContainer
implements Runnable
{
394 private final Task myTask
;
396 protected TaskContainer(final Task task
) {
400 public Task
getTask() {
405 private static class TaskRunnable
extends TaskContainer
{
406 private final ProgressIndicator myIndicator
;
408 private TaskRunnable(@NotNull Task task
, @NotNull ProgressIndicator indicator
) {
410 myIndicator
= indicator
;
415 getTask().run(myIndicator
);
418 if (myIndicator
instanceof ProgressIndicatorEx
) {
419 ((ProgressIndicatorEx
)myIndicator
).finish(getTask());
426 @SuppressWarnings({"UnusedDeclaration"})
427 private static void stopCheckCanceled() {
428 Thread
[] threads
= new Thread
[500];
429 Thread
.enumerate(threads
);
430 for (Thread thread
: threads
) {
431 if (thread
== null) continue;
432 if (NAME
.equals(thread
.getName())) {
433 Thread
.State oldState
= thread
.getState();
435 System
.out
.println(thread
+" suspended ("+oldState
+ "->"+thread
.getState()+")");
441 public static void setNeedToCheckCancel(boolean needToCheckCancel
) {
442 ourNeedToCheckCancel
= needToCheckCancel
;