growl support
authorMike Aizatsky <no_reply@jetbrains.com>
Thu, 8 Nov 2007 20:41:58 +0000 (23:41 +0300)
committerMike Aizatsky <no_reply@jetbrains.com>
Thu, 8 Nov 2007 20:41:58 +0000 (23:41 +0300)
14 files changed:
UsageView/src/com/intellij/usages/impl/UsageViewManagerImpl.java
compiler/impl/com/intellij/compiler/progress/CompilerTask.java
license/growl.license [new file with mode: 0644]
platform-api/src/com/intellij/openapi/progress/Task.java
platform-impl/platform-impl.iml
platform-impl/src/com/intellij/openapi/progress/impl/ProgressManagerImpl.java
platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java
platform-impl/src/com/intellij/ui/GrowlNotifications.java [new file with mode: 0644]
platform-impl/src/com/intellij/ui/Notifications.java [new file with mode: 0644]
plugins/ant/src/com/intellij/lang/ant/config/impl/AntConfigurationImpl.java
source/com/intellij/analysis/BaseClassesAnalysisAction.java
source/com/intellij/find/findUsages/FindUsagesManager.java
source/com/intellij/openapi/vcs/changes/ui/CommitHelper.java
source/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java

index c2f12941e4f8612549b9891d349a07a6596ef4f6..ce98b8952d4ff5a995bf516cb95a8096814e635d 100644 (file)
@@ -39,6 +39,7 @@ import com.intellij.usages.*;
 import com.intellij.util.Processor;
 import com.intellij.util.ui.RangeBlinker;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.event.ActionEvent;
@@ -109,6 +110,11 @@ public class UsageViewManagerImpl extends UsageViewManager {
       public void run(final ProgressIndicator indicator) {
         new SearchForUsagesRunnable(usageView, presentation, searchFor, searcherFactory, processPresentation, listener).run();
       }
+
+      @Nullable
+      public NotificationInfo getNotificationInfo() {
+        return new NotificationInfo("Find Usages",  "Find Usages Finished", usageView.get() != null ? (usageView.get().getUsagesCount() + " Usage(s) Found") : "No Usages Found");
+      }
     };
     ProgressManager.getInstance().run(task);
     return usageView.get();
index b40279c7197cf873d36230ed0a468ff59e26dcfb..14f87b19abd8c0eae41f27eeaeb9c27e7cecdb6c 100644 (file)
@@ -41,6 +41,7 @@ import com.intellij.ui.content.*;
 import com.intellij.util.Alarm;
 import com.intellij.util.ui.MessageCategory;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
 import java.awt.*;
@@ -88,6 +89,11 @@ public class CompilerTask extends Task.Backgroundable {
     return myIndicator;
   }
 
+  @Nullable
+  public NotificationInfo getNotificationInfo() {
+    return new NotificationInfo("Compiler", "Compilation Finished", myErrorCount + " Errors, " + myWarningCount + " Warnings", true);
+  }
+
   public void run(final ProgressIndicator indicator) {
     myIndicator = indicator;
 
diff --git a/license/growl.license b/license/growl.license
new file mode 100644 (file)
index 0000000..24da257
--- /dev/null
@@ -0,0 +1,28 @@
+Copyright (c) The Growl Project, 2004 
+All rights reserved.
+
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of Growl nor the names of its contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
index 0ff2c563bbb91964a42f3f6b4280a680f70a5b01..8fab4bb8fde60b07871b7b2229cde339811706e6 100644 (file)
@@ -82,6 +82,10 @@ public abstract class Task implements TaskInfo {
     return this;
   }
 
+  @Nullable
+  public NotificationInfo getNotificationInfo() {
+    return null;
+  }
 
   public boolean isHeadless() {
     return ApplicationManager.getApplication().isUnitTestMode();
@@ -182,4 +186,41 @@ public abstract class Task implements TaskInfo {
       return true;
     }
   }
+
+  public static class NotificationInfo {
+    private final String myNotificationName;
+    private final String myNotificationTitle;
+    private final String myNotificationText;
+    private final boolean myShowWhenFocused;
+
+    public NotificationInfo(final String notificationName, final String notificationTitle, final String notificationText) {
+      this(notificationName,  notificationTitle, notificationText, false);
+    }
+
+    public NotificationInfo(final String notificationName,
+                            final String notificationTitle,
+                            final String notificationText,
+                            final boolean showWhenFocused) {
+      myNotificationName = notificationName;
+      myNotificationTitle = notificationTitle;
+      myNotificationText = notificationText;
+      myShowWhenFocused = showWhenFocused;
+    }
+
+    public String getNotificationName() {
+      return myNotificationName;
+    }
+
+    public String getNotificationTitle() {
+      return myNotificationTitle;
+    }
+
+    public String getNotificationText() {
+      return myNotificationText;
+    }
+
+    public boolean isShowWhenFocused() {
+      return myShowWhenFocused;
+    }
+  }
 }
index 1b4ba6dbd50ede0574aa9bec87651c7657180718..87da38a0c080b45bc20131b3fa0df90ef4b8f1b9 100644 (file)
@@ -29,6 +29,7 @@
     <orderEntry type="module" module-name="platform-api" exported="" />
     <orderEntry type="module" module-name="idea" />
     <orderEntry type="module" module-name="UsageView" />
+    <orderEntry type="library" name="Mac" level="project" />
     <orderEntryProperties />
   </component>
 </module>
index 7c5c93c8d993d6438e4619cf9f776028929c0f72..be88c164159d2af8ebaf97c3e9308bb85fb03651 100644 (file)
@@ -9,13 +9,16 @@ import com.intellij.openapi.progress.util.ProgressWindow;
 import com.intellij.openapi.progress.util.SmoothProgressAdapter;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.wm.WindowManager;
 import com.intellij.psi.PsiLock;
+import com.intellij.ui.Notifications;
 import org.jetbrains.annotations.Nls;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import javax.swing.*;
+import java.awt.*;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -169,6 +172,14 @@ public class ProgressManagerImpl extends ProgressManager {
         }
       }, task.getTitle(), task.isCancellable(), task.getProject());
     if (result) {
+      final Task.NotificationInfo notificationInfo = task.getNotificationInfo();
+      if (notificationInfo != null) {
+        final JFrame frame = WindowManager.getInstance().getFrame(task.getProject());
+        if (!frame.hasFocus()) {
+          Notifications.notify(notificationInfo.getNotificationName(), notificationInfo.getNotificationTitle(), notificationInfo.getNotificationText());
+        }
+      }
+
       task.onSuccess();
     }
     else {
@@ -239,6 +250,14 @@ public class ProgressManagerImpl extends ProgressManager {
           }, ModalityState.NON_MODAL);
         }
         else if (!canceled) {
+          final Task.NotificationInfo notificationInfo = task.getNotificationInfo();
+          if (notificationInfo != null) {
+            final Component window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
+            if (window == null || notificationInfo.isShowWhenFocused()) {
+              Notifications.notify(notificationInfo.getNotificationName(), notificationInfo.getNotificationTitle(), notificationInfo.getNotificationText());
+            }
+          }
+
           ApplicationManager.getApplication().invokeLater(new Runnable() {
             public void run() {
               task.onSuccess();
index 05fe8e0670814be44707c317403447439e20634f..0e04acce3b9b85fc3070e001f79b97807fe28db6 100644 (file)
@@ -218,6 +218,10 @@ public class ProgressWindow extends BlockingProgressIndicator {
     }
   }
 
+  public boolean isBackgrounded() {
+    return myBackgrounded;
+  }
+
   public void setText(String text) {
     if (!Comparing.equal(text, getText())) {
       super.setText(text);
diff --git a/platform-impl/src/com/intellij/ui/GrowlNotifications.java b/platform-impl/src/com/intellij/ui/GrowlNotifications.java
new file mode 100644 (file)
index 0000000..7b5d768
--- /dev/null
@@ -0,0 +1,67 @@
+package com.intellij.ui;
+
+import com.apple.cocoa.application.NSApplication;
+import com.apple.cocoa.foundation.NSArray;
+import com.growl.Growl;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.diagnostic.Logger;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * @author mike
+ */
+class GrowlNotifications {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.ui.GrowlNotifications");
+
+  private static GrowlNotifications ourNotifications;
+  private Growl myGrowl;
+  private Set<String> myNotifications = new TreeSet<String>();
+
+  public GrowlNotifications() {
+    myGrowl = new Growl(
+      ApplicationNamesInfo.getInstance().getFullProductName(),
+      NSApplication.sharedApplication().applicationIconImage().TIFFRepresentation(),
+        new NSArray(), new NSArray(), false);
+    register();
+  }
+
+  private String[] getAllNotifications() {
+    return myNotifications.toArray(new String[myNotifications.size()]);
+  }
+
+  public static synchronized GrowlNotifications getNofications() {
+    if (ourNotifications == null) {
+      ourNotifications = new GrowlNotifications();
+    }
+
+    return ourNotifications;
+  }
+
+  public void notify(@NotNull String notificationName, String title, String description) {
+    if (!myNotifications.contains(notificationName)) {
+      myNotifications.add(notificationName);
+      register();
+    }
+
+    try {
+      myGrowl.notifyGrowlOf(notificationName, title, description);
+    }
+    catch (Exception e) {
+      LOG.error(e);
+    }
+  }
+
+  private void register() {
+    myGrowl.setAllowedNotifications(getAllNotifications());
+    try {
+      myGrowl.setDefaultNotifications(getAllNotifications());
+    }
+    catch (Exception e) {
+      LOG.error(e);
+    }
+    myGrowl.register();
+  }
+}
diff --git a/platform-impl/src/com/intellij/ui/Notifications.java b/platform-impl/src/com/intellij/ui/Notifications.java
new file mode 100644 (file)
index 0000000..92dddf4
--- /dev/null
@@ -0,0 +1,18 @@
+package com.intellij.ui;
+
+import com.intellij.openapi.util.SystemInfo;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author mike
+ */
+public class Notifications {
+  private Notifications() {
+  }
+
+  public static void notify(@NotNull String notificationName, @NotNull String title, @NotNull String text) {
+    if (!SystemInfo.isMac) return;
+
+    GrowlNotifications.getNofications().notify(notificationName, title, text);
+  }
+}
index 22b4450b84a1d02f3e3576e2d266c827d809471b..46133222265922169d5cd96c8b487aa3b8e8bfb0 100644 (file)
@@ -142,6 +142,11 @@ public class AntConfigurationImpl extends AntConfigurationBase implements Persis
     final AntNoFileException[] ex = new AntNoFileException[]{null};
     final String title = AntBundle.message("register.ant.build.progress", file.getPresentableUrl());
     ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), title, false) {
+      @Nullable
+      public NotificationInfo getNotificationInfo() {
+        return new NotificationInfo("Ant", "Ant Task Finished", "");
+      }
+
       public void run(final ProgressIndicator indicator) {
         indicator.setIndeterminate(true);
         indicator.pushState();
index 69537a68a5acf8c361895fc0eaf46a21ebce2601..b3c68933f6701932c0f20167d2568a63c1f78b86 100644 (file)
@@ -11,6 +11,7 @@ import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Messages;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * @author mike
@@ -59,6 +60,11 @@ public abstract class BaseClassesAnalysisAction extends BaseAnalysisAction {
 
   private void doAnalyze(final Project project, final AnalysisScope scope) {
     ProgressManager.getInstance().run(new Task.Backgroundable(project, AnalysisScopeBundle.message("analyzing.project"), true) {
+      @Nullable
+      public NotificationInfo getNotificationInfo() {
+        return new NotificationInfo("Analysis",  "\"" + getTitle() + "\" Analysis Finished", "");
+      }
+
       public void run(final ProgressIndicator indicator) {
         analyzeClasses(project, scope, indicator);
       }
index d57193fcc7887090247e2e8568ba9abc0e84b5dc..4b90680412082a91809a4e588e75b37e4cc73a84 100644 (file)
@@ -308,9 +308,20 @@ public class FindUsagesManager implements JDOMExternalizable {
     UsageViewPresentation presentation = createPresentation(element, findUsagesOptions, myToOpenInNewTab);
     final UsageSearcher usageSearcher = createUsageSearcher(descriptor, findUsagesOptions, null);
     final boolean[] canceled = new boolean[]{false};
+    final int[] usageCount = new int[]{0};
     Task task = new Task.Modal(myProject, UsageViewManagerImpl.getProgressTitle(presentation), true) {
       public void run(final ProgressIndicator indicator) {
-        usageSearcher.generate(processor);
+        usageSearcher.generate(new Processor<Usage>() {
+          public boolean process(final Usage usage) {
+            usageCount[0]++;
+            return processor.process(usage);
+          }
+        });
+      }
+
+      @Nullable
+      public NotificationInfo getNotificationInfo() {
+        return new NotificationInfo("Find Usages",  "Find Usages Finished", usageCount[0] + " Usage(s) Found");
       }
 
       public void onCancel() {
index 8abdaa5347449073eea0d76462e5a166766959be..de53edec92b7eaf0e52cc081cfede8fd8139562d 100644 (file)
@@ -22,6 +22,7 @@ import com.intellij.openapi.vcs.checkin.CheckinEnvironment;
 import com.intellij.openapi.vcs.checkin.CheckinHandler;
 import com.intellij.openapi.vfs.VirtualFileManager;
 import com.intellij.util.ui.ConfirmationDialog;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -85,6 +86,12 @@ public class CommitHelper {
               vcsManager.stopBackgroundVcsOperation();
             }
           }
+
+          @Nullable
+          public NotificationInfo getNotificationInfo() {
+            final String text = (myChangeList.getChanges().size() - changesFailedToCommit.size()) + " Change(s) Commited, " + changesFailedToCommit.size() + " Change(s) Failed To Commit";
+            return new NotificationInfo("VCS Commit",  "VCS Commit Finished", text, true);
+          }
         };
       ProgressManager.getInstance().run(task);
       return false;
index e13f2c508a83f43e9cc4d2821d6004b191e3094d..4e669cb8e4fe69ff5785d5ffc536bcf2605229d2 100644 (file)
@@ -57,6 +57,7 @@ import com.intellij.util.messages.MessageBusConnection;
 import com.intellij.util.ui.OptionsDialog;
 import com.intellij.vcsUtil.VcsUtil;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
@@ -151,6 +152,19 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction {
             }
           }
 
+          @Nullable
+          public NotificationInfo getNotificationInfo() {
+            StringBuffer text = new StringBuffer();
+            final List<FileGroup> groups = updatedFiles.getTopLevelGroups();
+            for (FileGroup group : groups) {
+              final int s = group.getFiles().size();
+              if (text.length() > 0) text.append("\n");
+              text.append(s + " Files " + group.getUpdateName());
+            }
+
+            return new NotificationInfo("VCS Update", "VCS Update Finished", text.toString(), true);
+          }
+
           public void onSuccess() {
             if (!someSessionWasCanceled(updateSessions)) {
               for (final UpdateSession updateSession : updateSessions) {