[glib/osx-appinfo: 1/3] Implement GContentType on OSX



commit 8d10702a10ddcad2e3d10e2374fea826b5d68f5f
Author: TingPing <tingping tingping se>
Date:   Fri Aug 15 06:11:38 2014 -0400

    Implement GContentType on OSX

 gio/Makefile.am            |   24 +++-
 gio/gcontenttype.c         |    5 +-
 gio/glocalfileinfo.c       |    2 +-
 gio/gnextstepcontenttype.c |  423 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 449 insertions(+), 5 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index f6aab57..e9389ed 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -3,8 +3,10 @@ include $(top_srcdir)/glib.mk
 SUBDIRS = gdbus-2.0/codegen
 
 if OS_UNIX
+if !OS_COCOA
 SUBDIRS += xdgmime
 endif
+endif
 
 if OS_WIN32_AND_DLL_COMPILATION
 if MS_LIB_AVAILABLE
@@ -221,6 +223,7 @@ local_sources = \
 platform_libadd =
 platform_deps =
 appinfo_sources =
+contenttype_sources =
 
 if HAVE_INOTIFY
 SUBDIRS += inotify
@@ -247,9 +250,13 @@ SUBDIRS += fam
 endif
 
 if OS_UNIX
-unix_appinfo_sources = gdesktopappinfo.c
+if !OS_COCOA
 platform_libadd += xdgmime/libxdgmime.la
 platform_deps += xdgmime/libxdgmime.la
+endif
+
+appinfo_sources += gdesktopappinfo.c
+
 unix_sources = \
        gfiledescriptorbased.c  \
        gunixconnection.c       \
@@ -266,7 +273,6 @@ unix_sources = \
        gunixvolumemonitor.h    \
        gunixinputstream.c      \
        gunixoutputstream.c     \
-       gcontenttype.c          \
        gcontenttypeprivate.h   \
        gfdonotificationbackend.c \
        ggtknotificationbackend.c \
@@ -321,7 +327,6 @@ win32_actual_sources = \
        $(gdbus_daemon_sources) \
        gwin32registrykey.c \
        gwin32registrykey.h \
-       gcontenttype-win32.c \
        gwin32mount.c \
        gwin32mount.h \
        gwin32volumemonitor.c \
@@ -344,6 +349,8 @@ win32_more_sources_for_vcproj = \
 
 if OS_WIN32
 win32_appinfo_sources = gwin32appinfo.c gwin32appinfo.h
+appinfo_sources += gwin32appinfo.c gwin32appinfo.h
+contenttype_sources += contenttype-win32.c
 platform_libadd += -lshlwapi -lws2_32 -ldnsapi -liphlpapi
 win32_sources = $(win32_actual_sources)
 appinfo_sources += $(win32_appinfo_sources)
@@ -387,6 +394,16 @@ portal_sources = \
        $(xdp_dbus_built_sources)       \
        $(NULL)
 
+if OS_COCOA
+contenttype_sources += gnextstepcontenttype.c
+endif
+
+if OS_UNIX
+if !OS_COCOA
+contenttype_sources += gcontenttype.c
+endif
+endif
+
 gio_base_sources =             \
        gappinfo.c              \
        gappinfoprivate.h       \
@@ -542,6 +559,7 @@ gio_base_sources =          \
 libgio_2_0_la_SOURCES =                \
        $(gio_base_sources)     \
        $(appinfo_sources)      \
+       $(contenttype_sources) \
        $(unix_sources)         \
        $(win32_sources)        \
        $(settings_sources)     \
diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c
index 76a5b13..ccdf57c 100644
--- a/gio/gcontenttype.c
+++ b/gio/gcontenttype.c
@@ -46,6 +46,8 @@
  * On Win32 it is an extension string like ".doc", ".txt" or a perceived
  * string like "audio". Such strings can be looked up in the registry at
  * HKEY_CLASSES_ROOT.
+ * On OSX it is a [Uniform Type Identifier](https://en.wikipedia.org/wiki/Uniform_Type_Identifier)
+ * such as "com.apple.application".
  **/
 
 #include <dirent.h>
@@ -167,7 +169,8 @@ g_content_type_is_a (const gchar *type,
  *
  * Checks if the content type is the generic "unknown" type.
  * On UNIX this is the "application/octet-stream" mimetype,
- * while on win32 it is "*".
+ * while on win32 it is "*" and on OSX it is a dynamic type
+ * or octet-stream.
  *
  * Returns: %TRUE if the type is the unknown type.
  */
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
index 595d736..113a20b 100644
--- a/gio/glocalfileinfo.c
+++ b/gio/glocalfileinfo.c
@@ -1259,7 +1259,7 @@ get_content_type (const char          *basename,
 
       content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
       
-#ifndef G_OS_WIN32
+#if !defined(G_OS_WIN32) && !defined(HAVE_COCOA)
       if (!fast && result_uncertain && path != NULL)
        {
          guchar sniff_buffer[4096];
diff --git a/gio/gnextstepcontenttype.c b/gio/gnextstepcontenttype.c
new file mode 100644
index 0000000..04c7439
--- /dev/null
+++ b/gio/gnextstepcontenttype.c
@@ -0,0 +1,423 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2014 Patrick Griffis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include "gcontenttype.h"
+#include "gicon.h"
+#include "gthemedicon.h"
+
+#include <CoreServices/CoreServices.h>
+
+
+/*< internal >
+ * create_cfstring_from_cstr:
+ * @cstr: a #gchar
+ *
+ * Converts a cstr to a utf8 cfstring
+ * It must be CFReleased()'d.
+ *
+ */
+static CFStringRef
+create_cfstring_from_cstr (const gchar *cstr)
+{
+  return CFStringCreateWithCString (NULL, cstr, kCFStringEncodingUTF8);
+}
+
+/*< internal >
+ * create_cstr_from_cfstring:
+ * @str: a #CFStringRef
+ *
+ * Converts a cfstring to a utf8 cstring.
+ * The incoming cfstring is released for you.
+ * The returned string must be g_free()'d.
+ *
+ */
+static gchar *
+create_cstr_from_cfstring (CFStringRef str)
+{
+  const gchar *cstr;
+
+  if (str == NULL)
+    return NULL;
+
+  cstr = CFStringGetCStringPtr (str, kCFStringEncodingUTF8);
+  CFRelease (str);
+
+  return g_strdup (cstr);
+}
+
+/*< internal >
+ * create_cstr_from_cfstring_with_fallback:
+ * @str: a #CFStringRef
+ * @fallback: a #gchar
+ *
+ * Tries to convert a cfstring to a utf8 cstring.
+ * If @str is NULL or conversion fails @fallback is returned.
+ * The incoming cfstring is released for you.
+ * The returned string must be g_free()'d.
+ *
+ */
+static gchar *
+create_cstr_from_cfstring_with_fallback (CFStringRef  str,
+                                         const gchar *fallback)
+{
+  gchar *cstr;
+
+  cstr = create_cstr_from_cfstring (str);
+  if (!cstr)
+    return g_strdup (fallback);
+
+  return cstr;
+}
+
+gboolean
+g_content_type_equals (const gchar *type1,
+                       const gchar *type2)
+{
+  CFStringRef str1, str2;
+  gboolean ret;
+
+  g_return_val_if_fail (type1 != NULL, FALSE);
+  g_return_val_if_fail (type2 != NULL, FALSE);
+
+  if (g_ascii_strcasecmp (type1, type2) == 0)
+    return TRUE;
+
+  str1 = create_cfstring_from_cstr (type1);
+  str2 = create_cfstring_from_cstr (type2);
+
+  ret = UTTypeEqual (str1, str2);
+
+  CFRelease (str1);
+  CFRelease (str2);
+
+  return ret;
+}
+
+gboolean
+g_content_type_is_a (const gchar *ctype,
+                     const gchar *csupertype)
+{
+  CFStringRef type, supertype;
+  gboolean ret;
+
+  g_return_val_if_fail (ctype != NULL, FALSE);
+  g_return_val_if_fail (csupertype != NULL, FALSE);
+
+  type = create_cfstring_from_cstr (ctype);
+  supertype = create_cfstring_from_cstr (csupertype);
+
+  ret = UTTypeConformsTo (type, supertype);
+
+  CFRelease (type);
+  CFRelease (supertype);
+
+  return ret;
+}
+
+gboolean
+g_content_type_is_unknown (const gchar *type)
+{
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  /* Should dynamic types be considered "unknown"? */
+  if (g_str_has_prefix (type, "dyn."))
+    return TRUE;
+  /* application/octet-stream */
+  else if (g_strcmp0 (type, "public.data") == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+gchar *
+g_content_type_get_description (const gchar *type)
+{
+  CFStringRef str;
+  CFStringRef desc_str;
+
+  g_return_val_if_fail (type != NULL, NULL);
+
+  str = create_cfstring_from_cstr (type);
+  desc_str = UTTypeCopyDescription (str);
+
+  CFRelease (str);
+  return create_cstr_from_cfstring_with_fallback (desc_str, "unknown");
+}
+
+static GIcon *
+g_content_type_get_icon_internal (const gchar *type,
+                                  gboolean     symbolic)
+{
+  GIcon *icon = NULL;
+  gchar *name;
+
+  g_return_val_if_fail (type != NULL, NULL);
+
+  /* TODO: Show mimetype icons. */
+  if (g_content_type_can_be_executable (type))
+    name = "gtk-execute";
+  else if (g_content_type_is_a (type, "public.directory"))
+    name = symbolic ? "inode-directory-symbolic" : "inode-directory";
+  else
+    name = "gtk-file";
+
+  icon = g_themed_icon_new_with_default_fallbacks (name);
+
+  return icon;
+}
+
+GIcon *
+g_content_type_get_icon (const gchar *type)
+{
+  return g_content_type_get_icon_internal (type, FALSE);
+}
+
+GIcon *
+g_content_type_get_symbolic_icon (const gchar *type)
+{
+  return g_content_type_get_icon_internal (type, TRUE);
+}
+
+gchar *
+g_content_type_get_generic_icon_name (const gchar *type)
+{
+  return NULL;
+}
+
+gboolean
+g_content_type_can_be_executable (const gchar *type)
+{
+  CFStringRef uti;
+  gboolean ret = FALSE;
+
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  uti = create_cfstring_from_cstr (type);
+
+  if (UTTypeConformsTo (uti, kUTTypeApplication))
+    ret = TRUE;
+  else if (UTTypeConformsTo (uti, CFSTR("public.executable")))
+    ret = TRUE;
+  else if (UTTypeConformsTo (uti, CFSTR("public.script")))
+    ret = TRUE;
+
+  CFRelease (uti);
+  return ret;
+}
+
+gchar *
+g_content_type_from_mime_type (const gchar *mime_type)
+{
+  CFStringRef mime_str;
+  CFStringRef uti_str;
+
+  g_return_val_if_fail (mime_type != NULL, NULL);
+
+  /* Their api does not handle globs but they are common. */
+  if (g_str_has_suffix (mime_type, "*"))
+    {
+      if (g_str_has_prefix (mime_type, "audio"))
+        return g_strdup ("public.audio");
+      if (g_str_has_prefix (mime_type, "image"))
+        return g_strdup ("public.image");
+      if (g_str_has_prefix (mime_type, "text"))
+        return g_strdup ("public.text");
+      if (g_str_has_prefix (mime_type, "video"))
+        return g_strdup ("public.movie");
+    }
+
+  /* Some exceptions are needed for gdk-pixbuf.
+   * This list is not exhaustive.
+   */
+  if (g_str_has_prefix (mime_type, "image"))
+    {
+      if (g_str_has_suffix (mime_type, "x-icns"))
+        return g_strdup ("com.apple.icns");
+      if (g_str_has_suffix (mime_type, "x-tga"))
+        return g_strdup ("com.truevision.tga-image");
+      if (g_str_has_suffix (mime_type, "x-ico"))
+        return g_strdup ("com.microsoft.ico ");
+    }
+
+  /* These are also not supported...
+   * Used in glocalfileinfo.c
+   */
+  if (g_str_has_prefix (mime_type, "inode"))
+    {
+      if (g_str_has_suffix (mime_type, "directory"))
+        return g_strdup ("public.directory");
+      if (g_str_has_suffix (mime_type, "symlink"))
+        return g_strdup ("public.symlink");
+    }
+
+  mime_str = create_cfstring_from_cstr (mime_type);
+  uti_str = UTTypeCreatePreferredIdentifierForTag (kUTTagClassMIMEType, mime_str, NULL);
+
+  CFRelease (mime_str);
+  return create_cstr_from_cfstring_with_fallback (uti_str, "public.data");
+}
+
+gchar *
+g_content_type_get_mime_type (const gchar *type)
+{
+  CFStringRef uti_str;
+  CFStringRef mime_str;
+
+  g_return_val_if_fail (type != NULL, NULL);
+
+  /* We must match the additions above
+   * so conversions back and forth work.
+   */
+  if (g_str_has_prefix (type, "public"))
+    {
+      if (g_str_has_suffix (type, ".image"))
+        return g_strdup ("image/*");
+      if (g_str_has_suffix (type, ".movie"))
+        return g_strdup ("video/*");
+      if (g_str_has_suffix (type, ".text"))
+        return g_strdup ("text/*");
+      if (g_str_has_suffix (type, ".audio"))
+        return g_strdup ("audio/*");
+      if (g_str_has_suffix (type, ".directory"))
+        return g_strdup ("inode/directory");
+      if (g_str_has_suffix (type, ".symlink"))
+        return g_strdup ("inode/symlink");
+    }
+
+  uti_str = create_cfstring_from_cstr (type);
+  mime_str = UTTypeCopyPreferredTagWithClass(uti_str, kUTTagClassMIMEType);
+
+  CFRelease (uti_str);
+  return create_cstr_from_cfstring_with_fallback (mime_str, "application/octet-stream");
+}
+
+static gboolean
+looks_like_text (const guchar *data,
+                 gsize         data_size)
+{
+  gsize i;
+  guchar c;
+
+  for (i = 0; i < data_size; i++)
+    {
+      c = data[i];
+      if (g_ascii_iscntrl (c) && !g_ascii_isspace (c) && c != '\b')
+        return FALSE;
+    }
+  return TRUE;
+}
+
+gchar *
+g_content_type_guess (const gchar  *filename,
+                      const guchar *data,
+                      gsize         data_size,
+                      gboolean     *result_uncertain)
+{
+  CFStringRef uti = NULL;
+  gchar *cextension;
+  CFStringRef extension;
+
+  g_return_val_if_fail (data_size != (gsize) -1, NULL);
+
+  if (filename && *filename)
+    {
+      gchar *basename = g_path_get_basename (filename);
+      gchar *dirname = g_path_get_dirname (filename);
+      gsize i = strlen (filename);
+
+      if (filename[i - 1] == '/')
+        {
+          if (g_strcmp0 (dirname, "/Volumes") == 0)
+            {
+              uti = CFStringCreateCopy (NULL, kUTTypeVolume);
+            }
+          else if ((cextension = strrchr (basename, '.')) != NULL)
+            {
+              cextension++;
+              extension = create_cfstring_from_cstr (cextension);
+              uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension,
+                                                           extension, NULL);
+              CFRelease (extension);
+
+              if (CFStringHasPrefix (uti, CFSTR ("dyn.")))
+                {
+                  CFRelease (uti);
+                  uti = CFStringCreateCopy (NULL, kUTTypeFolder);
+                }
+            }
+          else
+            {
+              uti = CFStringCreateCopy (NULL, kUTTypeFolder);
+            }
+        }
+      else
+        {
+          /* GTK needs this... */
+          if (g_str_has_suffix (basename, ".ui"))
+            {
+              uti = CFStringCreateCopy (NULL, kUTTypeXML);
+            }
+          else if ((cextension = strrchr (basename, '.')) != NULL)
+            {
+              cextension++;
+              extension = create_cfstring_from_cstr (cextension);
+              uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension,
+                                                           extension, NULL);
+              CFRelease (extension);
+            }
+          g_free (basename);
+          g_free (dirname);
+        }
+    }
+  else if (data)
+    {
+      if (looks_like_text (data, data_size))
+        {
+          if (g_str_has_prefix ((const gchar*)data, "#!/"))
+            uti = CFStringCreateCopy (NULL, CFSTR ("public.script"));
+          else
+            uti = CFStringCreateCopy (NULL, kUTTypePlainText);
+        }
+    }
+
+  if (!uti)
+    {
+      /* Generic data type */
+      uti = CFStringCreateCopy (NULL, CFSTR ("public.data"));
+      if (result_uncertain)
+        *result_uncertain = TRUE;
+    }
+
+  return create_cstr_from_cfstring (uti);
+}
+
+GList *
+g_content_types_get_registered (void)
+{
+  /* TODO: UTTypeCreateAllIdentifiersForTag? */
+  return NULL;
+}
+
+gchar **
+g_content_type_guess_for_tree (GFile *root)
+{
+  return NULL;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]