native file chooser under mac
authorAlexey Pegov <alexey.pegov@jetbrains.com>
Wed, 7 Apr 2010 14:48:32 +0000 (18:48 +0400)
committerAlexey Pegov <alexey.pegov@jetbrains.com>
Wed, 7 Apr 2010 14:48:32 +0000 (18:48 +0400)
13 files changed:
lib/jna.jar
platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDescriptor.java
platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserFactoryImpl.java
platform/platform-impl/src/com/intellij/ui/GrowlNotifications.java
platform/platform-impl/src/com/intellij/ui/growl/Foundation.java [deleted file]
platform/platform-impl/src/com/intellij/ui/growl/FoundationLibrary.java [deleted file]
platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialog.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/mac/foundation/Foundation.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationLibrary.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationTypeMapper.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/ui/mac/foundation/ID.java [moved from platform/platform-impl/src/com/intellij/ui/growl/ID.java with 57% similarity]
platform/platform-impl/src/com/intellij/ui/mac/foundation/Selector.java [moved from platform/platform-impl/src/com/intellij/ui/growl/Selector.java with 96% similarity]
platform/platform-impl/src/com/intellij/ui/mac/growl/Growl.java [moved from platform/platform-impl/src/com/intellij/ui/growl/Growl.java with 66% similarity]

index be4031408a11404df1f51cd4ae2d9b81607614a8..a0e8fe576c5f0596df1cc5a29c3214cf8e06b568 100644 (file)
Binary files a/lib/jna.jar and b/lib/jna.jar differ
index 652730917b29b1728e2907d0a72005bab94a792e..926524b538ec5f5000863e19c9e7e951216ace9a 100644 (file)
@@ -273,4 +273,9 @@ public class FileChooserDescriptor implements Cloneable{
   public <T> void putUserData(DataKey<T> key, T data) {
     myUserData.put(key.getName(), data);
   }
+
+  @Override
+  public String toString() {
+    return "FileChooserDescriptor [" + myTitle + "]";
+  }
 }
index 2f8624a96d8299640c7e20a27919df1b5b171550..ed4f1a1a1452445e4b560a76b150a8308298c5e9 100644 (file)
@@ -20,10 +20,11 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.application.PathMacros;
 import com.intellij.openapi.fileChooser.*;
 import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl;
-import com.intellij.openapi.fileChooser.ex.FileTextFieldImpl;
 import com.intellij.openapi.fileChooser.ex.FileSaverDialogImpl;
+import com.intellij.openapi.fileChooser.ex.FileTextFieldImpl;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.SystemInfo;
+import com.intellij.ui.mac.MacFileChooserDialog;
 
 import javax.swing.*;
 import java.awt.*;
@@ -33,10 +34,18 @@ import java.util.Set;
 
 public class FileChooserFactoryImpl extends FileChooserFactory {
   public FileChooserDialog createFileChooser(FileChooserDescriptor descriptor, Project project) {
+    if (SystemInfo.isMac && System.getProperty("idea.use.native.mac.filechooser", Boolean.FALSE.toString()).equals(Boolean.TRUE.toString())) {
+      return new MacFileChooserDialog(descriptor);
+    }
+
     return new FileChooserDialogImpl(descriptor, project);
   }
 
   public FileChooserDialog createFileChooser(FileChooserDescriptor descriptor, Component parent) {
+    if (SystemInfo.isMac && System.getProperty("idea.use.native.mac.filechooser", Boolean.FALSE.toString()).equals(Boolean.TRUE.toString())) {
+      return new MacFileChooserDialog(descriptor);
+    }
+
     return new FileChooserDialogImpl(descriptor, parent);
   }
 
@@ -69,4 +78,4 @@ public class FileChooserFactoryImpl extends FileChooserFactory {
   public FileSaverDialog createSaveFileDialog(FileSaverDescriptor descriptor, Project project) {
     return new FileSaverDialogImpl(descriptor, project);
   }
-}
\ No newline at end of file
+}
index 48386cfa8408e4fda5b431f3d37780bc4bd7ffb3..69ee728ab93168eabc556726e003b8bbdbb4ef55 100644 (file)
@@ -17,7 +17,7 @@ package com.intellij.ui;
 
 import com.intellij.openapi.application.ApplicationNamesInfo;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.ui.growl.Growl;
+import com.intellij.ui.mac.growl.Growl;
 import com.intellij.util.ArrayUtil;
 import org.jetbrains.annotations.NotNull;
 
diff --git a/platform/platform-impl/src/com/intellij/ui/growl/Foundation.java b/platform/platform-impl/src/com/intellij/ui/growl/Foundation.java
deleted file mode 100644 (file)
index dfcd132..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2000-2009 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.ui.growl;
-
-import com.intellij.openapi.diagnostic.Logger;
-import com.sun.jna.Native;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * @author spleaner
- */
-public class Foundation {
-  private static final Logger LOG = Logger.getInstance("#com.intellij.ui.growl.Foundation");
-
-  private static final FoundationLibrary myFoundationLibrary;
-
-  static {
-    // Set JNA to convert java.lang.String to char* using UTF-8, and match that with
-    // the way we tell CF to interpret our char*
-    // May be removed if we use toStringViaUTF16
-    System.setProperty("jna.encoding", "UTF8");
-
-    myFoundationLibrary = (FoundationLibrary) Native.loadLibrary("Foundation", FoundationLibrary.class);
-  }
-
-  private Foundation() {
-  }
-
-  /**
-   * Get the ID of the NSClass with className
-   */
-  public static ID getClass(String className) {
-    LOG.debug(String.format("calling objc_getClass(%s)", className));
-    return myFoundationLibrary.objc_getClass(className);
-  }
-
-  public static Selector createSelector(String s) {
-    return myFoundationLibrary.sel_registerName(s).initName(s);
-  }
-
-  public static ID invoke(final ID id, final Selector selector, Object... args) {
-    return myFoundationLibrary.objc_msgSend(id, selector, args);
-  }
-
-  /**
-   * Return a CFString as an ID, toll-free bridged to NSString.
-   *
-   * Note that the returned string must be freed with {@link #cfRelease(ID)}.
-   */
-  public static ID cfString(String s) {
-      // Use a byte[] rather than letting jna do the String -> char* marshalling itself.
-      // Turns out about 10% quicker for long strings.
-      try {
-          byte[] utf16Bytes = s.getBytes("UTF-16LE");
-          return myFoundationLibrary.CFStringCreateWithBytes(null, utf16Bytes,
-                  utf16Bytes.length, 0x14000100, (byte) 0); /* kTextEncodingUnicodeDefault + kUnicodeUTF16LEFormat */
-      } catch (UnsupportedEncodingException x) {
-          throw new RuntimeException(x);
-      }
-  }
-
-  public static void cfRetain(final ID id) {
-    myFoundationLibrary.CFRetain(id);
-  }
-
-  public static void cfRelease(final ID id) {
-    myFoundationLibrary.CFRelease(id);
-  }
-}
diff --git a/platform/platform-impl/src/com/intellij/ui/growl/FoundationLibrary.java b/platform/platform-impl/src/com/intellij/ui/growl/FoundationLibrary.java
deleted file mode 100644 (file)
index 45d08e6..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2000-2009 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.ui.growl;
-
-import com.sun.jna.Library;
-import com.sun.jna.Structure;
-
-/**
- * @author spleaner
- */
-public interface FoundationLibrary extends Library {
-  void NSLog(ID pString, Object thing);
-
-  ID CFStringCreateWithCString(ID allocator, String string, int encoding);
-  ID CFStringCreateWithBytes(ID allocator, byte[] bytes, int byteCount, int encoding, byte isExternalRepresentation);
-  String CFStringGetCStringPtr(ID string, int encoding);
-  byte CFStringGetCString(ID theString, byte[] buffer, int bufferSize, int encoding);
-  int CFStringGetLength(ID theString);
-
-  void CFRetain(ID cfTypeRef);
-  void CFRelease(ID cfTypeRef);
-  int CFGetRetainCount (ID cfTypeRef);
-
-  ID objc_getClass(String className);
-  ID class_createInstance(ID pClass, int extraBytes);
-  Selector sel_registerName(String selectorName);
-
-  ID objc_msgSend(ID receiver, Selector selector, Object... args);
-  Structure objc_msgSend_stret(ID receiver, Selector selector, Object... args);
-}
diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialog.java b/platform/platform-impl/src/com/intellij/ui/mac/MacFileChooserDialog.java
new file mode 100644 (file)
index 0000000..c240e8f
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.mac;
+
+import com.intellij.openapi.fileChooser.FileChooserDescriptor;
+import com.intellij.openapi.fileChooser.FileChooserDialog;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.ui.mac.foundation.Foundation;
+import com.intellij.ui.mac.foundation.ID;
+import com.sun.jna.Callback;
+import com.sun.jna.Pointer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * @author spleaner
+ */
+public class MacFileChooserDialog implements FileChooserDialog {
+  private static final int OK = 1;
+
+  private static FileChooserDescriptor myChooserDescriptor;
+  private static boolean myFileChooserActive = false;
+
+  private static final Callback SHOULD_SHOW_FILENAME_CALLBACK = new Callback() {
+      public boolean callback(Pointer self, String selector, Pointer panel, Pointer filename) {
+        final String fileName = Foundation.toStringViaUTF8(filename);
+        final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
+        return virtualFile != null && (virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
+      }
+    };
+
+  private static final Callback IS_VALID_FILENAME_CALLBACK = new Callback() {
+      public boolean callback(Pointer self, String selector, Pointer panel, Pointer filename) {
+        final String fileName = Foundation.toStringViaUTF8(filename);
+        final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(fileName);
+        return virtualFile != null && (!virtualFile.isDirectory() || getDescriptor().isFileSelectable(virtualFile));
+      }
+    };
+
+  static {
+    final Pointer delegateClass = Foundation.registerObjcClass(Foundation.getClass("NSObject"), "NSOpenPanelDelegate_");
+    if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:shouldShowFilename:"), SHOULD_SHOW_FILENAME_CALLBACK, "B@:*"))
+      throw new RuntimeException("Unable to add method to objective-c delegate class!");
+    if (!Foundation.addMethod(delegateClass, Foundation.createSelector("panel:isValidFilename:"), IS_VALID_FILENAME_CALLBACK, "B@:*"))
+      throw new RuntimeException("Unable to add method to objective-c delegate class!");
+    Foundation.registerObjcClassPair(delegateClass);
+  }
+
+  public MacFileChooserDialog(FileChooserDescriptor chooserDescriptor) {
+    setDescriptor(chooserDescriptor);
+  }
+
+  private static void setDescriptor(@Nullable final FileChooserDescriptor descriptor) {
+    myChooserDescriptor = descriptor;
+  }
+
+  private static FileChooserDescriptor getDescriptor() {
+    return myChooserDescriptor;
+  }
+
+  @NotNull
+  public VirtualFile[] choose(@Nullable VirtualFile toSelect, @Nullable Project project) {
+    assert !myFileChooserActive: "Current native file chooser should finish before next usage!";
+
+    myFileChooserActive = true;
+
+    final Pointer autoReleasePool = createAutoReleasePool();
+
+    try {
+      final Pointer chooser = invoke("NSOpenPanel", "openPanel");
+
+      invoke(chooser, "setPrompt:", Foundation.cfString("Choose"));
+      invoke(chooser, "autorelease");
+      invoke(chooser, "setCanChooseFiles:", myChooserDescriptor.isChooseFiles());
+      invoke(chooser, "setCanChooseDirectories:", myChooserDescriptor.isChooseFolders());
+      invoke(chooser, "setAllowsMultipleSelection:", myChooserDescriptor.isChooseMultiple());
+      if (Foundation.isClassRespondsToSelector(Foundation.getClass("NSOpenPanel"), Foundation.createSelector("_setIncludeNewFolderButton:"))) {
+        invoke(chooser, "_setIncludeNewFolderButton:", true);
+      }
+
+      final Pointer delegate = invoke(Foundation.getClass("NSOpenPanelDelegate_"), "new");
+      invoke(chooser, "setDelegate:", delegate);
+      
+      final Object directory = toSelect != null ? toSelect.isDirectory() ? Foundation.cfString(toSelect.getPath()).toLong() : null : null;
+      final Object file = toSelect != null ? !toSelect.isDirectory() ? Foundation.cfString(toSelect.getPath()).toLong() : null : null;
+      final ID result = invoke(chooser, "runModalForDirectory:file:", directory, file);
+      if (result != null && OK == result.toInt()) {
+        ID fileNamesArray = invoke(chooser, "filenames");
+        ID enumerator = invoke(fileNamesArray, "objectEnumerator");
+
+        final ArrayList<VirtualFile> fileNames = new ArrayList<VirtualFile>();
+        while (true) {
+          final ID filename = invoke(enumerator, "nextObject");
+          if (0 == filename.toInt()) break;
+
+          String s = Foundation.toStringViaUTF8(filename);
+          if (s != null) {
+            VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(s);
+            if (virtualFile != null && virtualFile.isValid()) fileNames.add(virtualFile);
+          }
+        }
+
+        return fileNames.toArray(new VirtualFile[fileNames.size()]);
+      }
+
+      invoke(delegate, "release");
+    }
+    finally {
+      invoke(autoReleasePool, "drain");
+      myFileChooserActive = false;
+    }
+
+    return new VirtualFile[0];
+  }
+
+  private static Pointer createAutoReleasePool() {
+    return invoke("NSAutoreleasePool", "new");
+  }
+
+  private static ID invoke(@NotNull final String className, @NotNull final String selector, Object... args) {
+    return invoke(Foundation.getClass(className), selector, args);
+  }
+
+  private static ID invoke(@NotNull final Pointer id, @NotNull final String selector, Object... args) {
+    return Foundation.invoke(id, Foundation.createSelector(selector), args);
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/ui/mac/foundation/Foundation.java b/platform/platform-impl/src/com/intellij/ui/mac/foundation/Foundation.java
new file mode 100644 (file)
index 0000000..c9cda85
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.mac.foundation;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.util.containers.HashMap;
+import com.sun.jna.Callback;
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+/**
+ * @author spleaner
+ */
+public class Foundation {
+  private static final Logger LOG = Logger.getInstance("#com.intellij.ui.mac.foundation.Foundation");
+
+  private static final FoundationLibrary myFoundationLibrary;
+
+  static {
+    // Set JNA to convert java.lang.String to char* using UTF-8, and match that with
+    // the way we tell CF to interpret our char*
+    // May be removed if we use toStringViaUTF16
+    System.setProperty("jna.encoding", "UTF8");
+
+    Map<String, Object> foundationOptions = new HashMap<String, Object>();
+    foundationOptions.put(Library.OPTION_TYPE_MAPPER, FoundationTypeMapper.INSTANCE);
+
+    myFoundationLibrary = (FoundationLibrary)Native.loadLibrary("Foundation", FoundationLibrary.class, foundationOptions);
+  }
+
+  private Foundation() {
+  }
+
+  /**
+   * Get the ID of the NSClass with className
+   */
+  public static Pointer getClass(String className) {
+    return myFoundationLibrary.objc_getClass(className);
+  }
+
+  public static Pointer createSelector(String s) {
+    return myFoundationLibrary.sel_registerName(s);
+  }
+
+  public static ID invoke(final Pointer id, final Pointer selector, Object... args) {
+    return myFoundationLibrary.objc_msgSend(id, selector, args);
+  }
+
+  public static Pointer registerObjcClass(Pointer superCls, String name) {
+    return myFoundationLibrary.objc_allocateClassPair(superCls, name, 0);
+  }
+
+  public static void registerObjcClassPair(Pointer cls) {
+    myFoundationLibrary.objc_registerClassPair(cls);
+  }
+
+  public static boolean isClassRespondsToSelector(Pointer cls, Pointer selectorName) {
+    return myFoundationLibrary.class_respondsToSelector(cls, selectorName);
+  }
+
+  public static Pointer createClassInstance(Pointer cls) {
+    return myFoundationLibrary.class_createInstance(cls, 0);
+  }
+
+  public static boolean addMethod(Pointer cls, Pointer selectorName, Callback impl, String types) {
+    return myFoundationLibrary.class_addMethod(cls, selectorName, impl, types);
+  }
+
+  public static Pointer getClass(Pointer clazz) {
+    return myFoundationLibrary.objc_getClass(clazz);
+  }
+
+  /**
+   * Return a CFString as an ID, toll-free bridged to NSString.
+   * <p/>
+   * Note that the returned string must be freed with {@link #cfRelease(ID)}.
+   */
+  public static ID cfString(String s) {
+    // Use a byte[] rather than letting jna do the String -> char* marshalling itself.
+    // Turns out about 10% quicker for long strings.
+    try {
+      byte[] utf16Bytes = s.getBytes("UTF-16LE");
+      return myFoundationLibrary.CFStringCreateWithBytes(null, utf16Bytes, utf16Bytes.length, 0x14000100,
+                                                         (byte)0); /* kTextEncodingUnicodeDefault + kUnicodeUTF16LEFormat */
+    }
+    catch (UnsupportedEncodingException x) {
+      throw new RuntimeException(x);
+    }
+  }
+
+  public static String toStringViaUTF8(Pointer cfString) {
+    int lengthInChars = myFoundationLibrary.CFStringGetLength(cfString);
+    int potentialLengthInBytes = 3 * lengthInChars + 1; // UTF8 fully escaped 16 bit chars, plus nul
+
+    byte[] buffer = new byte[potentialLengthInBytes];
+    byte ok = myFoundationLibrary.CFStringGetCString(cfString, buffer, buffer.length, 0x08000100);
+    if (ok == 0) throw new RuntimeException("Could not convert string");
+    return Native.toString(buffer);
+  }
+
+  public static void cfRetain(final Pointer id) {
+    myFoundationLibrary.CFRetain(id);
+  }
+
+  public static void cfRelease(final Pointer id) {
+    myFoundationLibrary.CFRelease(id);
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationLibrary.java b/platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationLibrary.java
new file mode 100644 (file)
index 0000000..67cfead
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2009 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.mac.foundation;
+
+import com.sun.jna.Callback;
+import com.sun.jna.Library;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+
+/**
+ * @author spleaner
+ */
+public interface FoundationLibrary extends Library {
+  void NSLog(Pointer pString, Object thing);
+
+  Pointer objc_allocateClassPair(Pointer supercls, String name, int extraBytes);
+  void objc_registerClassPair(Pointer cls);
+
+  ID CFStringCreateWithCString(Pointer allocator, String string, int encoding);
+  ID CFStringCreateWithBytes(Pointer allocator, byte[] bytes, int byteCount, int encoding, byte isExternalRepresentation);
+  String CFStringGetCStringPtr(Pointer string, int encoding);
+  byte CFStringGetCString(Pointer theString, byte[] buffer, int bufferSize, int encoding);
+  int CFStringGetLength(Pointer theString);
+
+  void CFRetain(Pointer cfTypeRef);
+  void CFRelease(Pointer cfTypeRef);
+  int CFGetRetainCount (Pointer cfTypeRef);
+
+  Pointer objc_getClass(String className);
+  Pointer class_createInstance(Pointer pClass, int extraBytes);
+  Pointer sel_registerName(String selectorName);
+
+  ID objc_msgSend(Pointer receiver, Pointer selector, Object... args);
+  Structure objc_msgSend_stret(Pointer receiver, Pointer selector, Object... args);
+
+  boolean class_respondsToSelector(Pointer cls, Pointer selName);
+
+  boolean class_addMethod(Pointer cls, Pointer selName, Callback imp, String types);
+  boolean class_replaceMethod(Pointer cls, Pointer selName, Callback imp, String types);
+
+  Pointer objc_getClass(Pointer clazz);
+}
diff --git a/platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationTypeMapper.java b/platform/platform-impl/src/com/intellij/ui/mac/foundation/FoundationTypeMapper.java
new file mode 100644 (file)
index 0000000..2a37309
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.ui.mac.foundation;
+
+import com.sun.jna.*;
+
+/**
+ * @author spleaner
+ */
+public class FoundationTypeMapper extends DefaultTypeMapper {
+
+  public static final FoundationTypeMapper INSTANCE = new FoundationTypeMapper();
+
+  private static final ToNativeConverter myToNativeConverter = new IDtoConverter();
+  private static final FromNativeConverter myFromNativeConverter = new IDFromConverter();
+
+  @Override
+  public FromNativeConverter getFromNativeConverter(Class javaType) {
+    if (javaType == ID.class) return myFromNativeConverter;
+    return super.getFromNativeConverter(javaType);
+  }
+
+  @Override
+  public ToNativeConverter getToNativeConverter(Class javaType) {
+    if (javaType == ID.class) return myToNativeConverter;
+    return  super.getToNativeConverter(javaType);
+  }
+
+  static class IDFromConverter implements FromNativeConverter {
+    public Object fromNative(Object o, FromNativeContext fromNativeContext) {
+      return new ID((Long) o);
+    }
+
+    public Class nativeType() {
+      return Long.TYPE;
+    }
+  }
+
+  static class IDtoConverter implements ToNativeConverter {
+    public Object toNative(Object o, ToNativeContext toNativeContext) {
+      if (o != null) {
+        return ((ID)o).getAddress();
+      } else {
+        return new Long(0);
+      }
+    }
+
+    public Class nativeType() {
+      return Long.TYPE;
+    }
+  }
+}
similarity index 57%
rename from platform/platform-impl/src/com/intellij/ui/growl/ID.java
rename to platform/platform-impl/src/com/intellij/ui/mac/foundation/ID.java
index feee43215832f12d2ee1efb643ea419e2070a521..b98cfeabbade4a3791a1a234c26ca61f9744931f 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.ui.growl;
+package com.intellij.ui.mac.foundation;
 
-import com.sun.jna.NativeLong;
+import com.sun.jna.Pointer;
 
 /**
  * @author spleaner
  */
-public class ID extends NativeLong {
-
-  static ID fromLong(long value) {
-    return new ID(value);
-  }
-
-  // for JNA
-  public ID() {
-    super();
+public class ID extends Pointer {
+  public ID(long peer) {
+    super(peer);
   }
 
-  protected ID(long value) {
-    super(value);
+  public long toLong() {
+    return peer;
   }
-
-  protected ID(ID anotherID) {
-    this(anotherID.longValue());
-  }
-
-  @Override
-  public String toString() {
-    return String.format("[ID 0x%x]", longValue()); //$NON-NLS-1$
+  
+  public int toInt() {
+    return (int) peer;
   }
 
-  public boolean isNull() {
-    return longValue() == 0;
+  public long getAddress() {
+    return peer;
   }
 }
similarity index 96%
rename from platform/platform-impl/src/com/intellij/ui/growl/Selector.java
rename to platform/platform-impl/src/com/intellij/ui/mac/foundation/Selector.java
index a51ab05b367b9e6dc403c5f1600cfb2332b704ea..d8c62a1a8ca835a8043ec3b9bff1f3f79680dcab 100644 (file)
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.ui.growl;
+package com.intellij.ui.mac.foundation;
 
 import com.sun.jna.NativeLong;
 
similarity index 66%
rename from platform/platform-impl/src/com/intellij/ui/growl/Growl.java
rename to platform/platform-impl/src/com/intellij/ui/mac/growl/Growl.java
index 3d845f82db3a18b4c5951aec09cfa6c8c109865a..2329a744cde7924c2f3470be0e3cd8293c36526c 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.intellij.ui.growl;
+package com.intellij.ui.mac.growl;
 
+import com.intellij.ui.mac.foundation.Foundation;
+import com.intellij.ui.mac.foundation.ID;
+import com.sun.jna.Pointer;
 import org.jetbrains.annotations.NotNull;
 
 /**
@@ -40,30 +43,30 @@ public class Growl {
   }
 
   public void register() {
-    final ID autoReleasePool = createAutoReleasePool();
-    final ID applicationIcon = getApplicationIcon();
+    final Pointer autoReleasePool = createAutoReleasePool();
+    final Pointer applicationIcon = getApplicationIcon();
 
-    final ID defaultNotifications = fillArray(myDefaultNotification);
-    final ID allNotifications = fillArray(myAllNotifications);
+    final Pointer defaultNotifications = fillArray(myDefaultNotification);
+    final Pointer allNotifications = fillArray(myAllNotifications);
 
-    final ID userDict = createDict(new String[]{GROWL_APP_NAME, GROWL_APP_ICON, GROWL_DEFAULT_NOTIFICATIONS, GROWL_ALL_NOTIFICATIONS},
+    final Pointer userDict = createDict(new String[]{GROWL_APP_NAME, GROWL_APP_ICON, GROWL_DEFAULT_NOTIFICATIONS, GROWL_ALL_NOTIFICATIONS},
         new Object[]{myProductName, applicationIcon, defaultNotifications, allNotifications});
 
-    final ID center = invoke("NSDistributedNotificationCenter", "defaultCenter");
-    final Object notificationName = Foundation.cfString(GROWL_APPLICATION_REGISTRATION_NOTIFICATION).toNative();
+    final Pointer center = invoke("NSDistributedNotificationCenter", "defaultCenter");
+    final Object notificationName = Foundation.cfString(GROWL_APPLICATION_REGISTRATION_NOTIFICATION);
     invoke(center, "postNotificationName:object:userInfo:deliverImmediately:", notificationName, null, userDict, true);
 
     invoke(autoReleasePool, "release");
   }
 
   public void notifyGrowlOf(final String notification, final String title, final String description) {
-    final ID autoReleasePool = createAutoReleasePool();
+    final Pointer autoReleasePool = createAutoReleasePool();
 
-    final ID dict = createDict(new String[]{
+    final Pointer dict = createDict(new String[]{
         GROWL_NOTIFICATION_NAME, GROWL_NOTIFICATION_TITLE, GROWL_NOTIFICATION_DESCRIPTION, GROWL_APP_NAME},
         new Object[]{notification, title, description, myProductName});
-    final ID center = invoke("NSDistributedNotificationCenter", "defaultCenter");
-    final Object notificationName = Foundation.cfString(GROWL_NOTIFICATION).toNative();
+    final Pointer center = invoke("NSDistributedNotificationCenter", "defaultCenter");
+    final Object notificationName = Foundation.cfString(GROWL_NOTIFICATION);
 
     invoke(center, "postNotificationName:object:userInfo:deliverImmediately:", notificationName, null, dict, true);
     invoke(autoReleasePool, "release");
@@ -77,12 +80,12 @@ public class Growl {
     myDefaultNotification = defaultNotification;
   }
 
-  private static ID createAutoReleasePool() {
+  private static Pointer createAutoReleasePool() {
     return invoke("NSAutoreleasePool", "new");
   }
 
-  private static ID fillArray(final Object[] a) {
-    final ID result = invoke("NSMutableArray", "array");
+  private static Pointer fillArray(final Object[] a) {
+    final Pointer result = invoke("NSMutableArray", "array");
     for (Object s : a) {
       invoke(result, "addObject:", convertType(s));
     }
@@ -90,9 +93,9 @@ public class Growl {
     return result;
   }
 
-  private static ID createDict(@NotNull final String[] keys, @NotNull final Object[] values) {
-    final ID nsKeys = invoke("NSArray", "arrayWithObjects:", convertTypes(keys));
-    final ID nsData = invoke("NSArray", "arrayWithObjects:", convertTypes(values));
+  private static Pointer createDict(@NotNull final String[] keys, @NotNull final Object[] values) {
+    final Pointer nsKeys = invoke("NSArray", "arrayWithObjects:", convertTypes(keys));
+    final Pointer nsData = invoke("NSArray", "arrayWithObjects:", convertTypes(values));
 
     return invoke("NSDictionary", "dictionaryWithObjects:forKeys:", nsData, nsKeys);
   }
@@ -101,7 +104,7 @@ public class Growl {
     if (o instanceof ID) {
       return o;
     } else if (o instanceof String) {
-      return Foundation.cfString((String) o).toNative();
+      return Foundation.cfString((String) o);
     } else {
       throw new IllegalArgumentException("Unsupported type! " + o.getClass());
     }
@@ -116,21 +119,21 @@ public class Growl {
     return result;
   }
 
-  private static ID getApplicationIcon() {
-    final ID sharedApp = invoke("NSApplication", "sharedApplication");
-    final ID nsImage = invoke(sharedApp, "applicationIconImage");
+  private static Pointer getApplicationIcon() {
+    final Pointer sharedApp = invoke("NSApplication", "sharedApplication");
+    final Pointer nsImage = invoke(sharedApp, "applicationIconImage");
     return invoke(nsImage, "TIFFRepresentation");
   }
 
-  private static ID invoke(@NotNull final String className, @NotNull final String selector, Object... args) {
+  private static Pointer invoke(@NotNull final String className, @NotNull final String selector, Object... args) {
     return invoke(Foundation.getClass(className), selector, args);
   }
 
-  private static ID invoke(@NotNull final ID id, @NotNull final String selector, Object... args) {
+  private static Pointer invoke(@NotNull final Pointer id, @NotNull final String selector, Object... args) {
     return invoke(id, Foundation.createSelector(selector), args);
   }
 
-  private static ID invoke(@NotNull final ID id, @NotNull final Selector selector, Object... args) {
+  private static Pointer invoke(@NotNull final Pointer id, @NotNull final Pointer selector, Object... args) {
     return Foundation.invoke(id, selector, args);
   }
 }