[2/2] winemenubuilder: group file open associations by application

Damjan Jovanovic damjan.jov at gmail.com
Sun Feb 27 10:41:59 CST 2011


Changelog:
* winemenubuilder: group file open associations by application

Damjan Jovanovic
-------------- next part --------------
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index 9aae73f..de28032 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -163,12 +163,24 @@ struct xdg_mime_type
     struct list entry;
 };
 
-struct rb_string_entry
-{
-    char *string;
+struct rb_application_entry
+{
+    WCHAR *commandW;
+    WCHAR *extensionW;
+    char *extensionA;
+    char *mimeTypesA;
+    WCHAR *progIdsW;
+    char *friendlyAppNameA;
+    char *openWithIconA;
     struct wine_rb_entry entry;
 };
 
+struct app_context
+{
+    BOOL hasChanged;
+    const char *applicationsDir;
+};
+
 DEFINE_GUID(CLSID_WICIcnsEncoder, 0x312fb6f1,0xb767,0x409d,0x8a,0x6d,0x0f,0xc1,0x54,0xd4,0xf0,0x5c);
 
 static char *xdg_config_dir;
@@ -236,11 +248,40 @@ static char* heap_printf(const char *format, ...)
     return ret;
 }
 
-static int winemenubuilder_rb_string_compare(const void *key, const struct wine_rb_entry *entry)
+static WCHAR* heap_printfW(const WCHAR *format, ...)
 {
-    const struct rb_string_entry *t = WINE_RB_ENTRY_VALUE(entry, const struct rb_string_entry, entry);
+    va_list args;
+    int size = 4096;
+    WCHAR *buffer, *ret;
+    int n;
 
-    return strcmp((char*)key, t->string);
+    va_start(args, format);
+    while (1)
+    {
+        buffer = HeapAlloc(GetProcessHeap(), 0, size*sizeof(WCHAR));
+        if (buffer == NULL)
+            break;
+        n = vsnprintfW(buffer, size, format, args);
+        if (n == -1)
+            size *= 2;
+        else if (n >= size)
+            size = n + 1;
+        else
+            break;
+        HeapFree(GetProcessHeap(), 0, buffer);
+    }
+    va_end(args);
+    if (!buffer) return NULL;
+    ret = HeapReAlloc(GetProcessHeap(), 0, buffer, (strlenW(buffer) + 1)*sizeof(WCHAR) );
+    if (!ret) ret = buffer;
+    return ret;
+}
+
+static int winemenubuilder_rb_application_compare(const void *key, const struct wine_rb_entry *entry)
+{
+    const struct rb_application_entry *t = WINE_RB_ENTRY_VALUE(entry, const struct rb_application_entry, entry);
+
+    return strcmpW((WCHAR*)key, t->commandW);
 }
 
 static void *winemenubuilder_rb_alloc(size_t size)
@@ -260,8 +301,14 @@ static void winemenubuilder_rb_free(void *ptr)
 
 static void winemenubuilder_rb_destroy(struct wine_rb_entry *entry, void *context)
 {
-    struct rb_string_entry *t = WINE_RB_ENTRY_VALUE(entry, struct rb_string_entry, entry);
-    HeapFree(GetProcessHeap(), 0, t->string);
+    struct rb_application_entry *t = WINE_RB_ENTRY_VALUE(entry, struct rb_application_entry, entry);
+    HeapFree(GetProcessHeap(), 0, t->commandW);
+    HeapFree(GetProcessHeap(), 0, t->extensionW);
+    HeapFree(GetProcessHeap(), 0, t->extensionA);
+    HeapFree(GetProcessHeap(), 0, t->mimeTypesA);
+    HeapFree(GetProcessHeap(), 0, t->progIdsW);
+    HeapFree(GetProcessHeap(), 0, t->friendlyAppNameA);
+    HeapFree(GetProcessHeap(), 0, t->openWithIconA);
     HeapFree(GetProcessHeap(), 0, t);
 }
 
@@ -270,7 +317,7 @@ static const struct wine_rb_functions winemenubuilder_rb_functions =
     winemenubuilder_rb_alloc,
     winemenubuilder_rb_realloc,
     winemenubuilder_rb_free,
-    winemenubuilder_rb_string_compare,
+    winemenubuilder_rb_application_compare,
 };
 
 static void write_xml_text(FILE *file, const char *text)
@@ -2226,14 +2273,14 @@ static const char* get_special_mime_type(LPCWSTR extension)
 
 static BOOL write_freedesktop_association_entry(const char *desktopPath, const char *dot_extension,
                                                 const char *friendlyAppName, const char *mimeType,
-                                                const char *progId, const char *openWithIcon)
+                                                const char *openWithIcon)
 {
     BOOL ret = FALSE;
     FILE *desktop;
 
-    WINE_TRACE("writing association for file type %s, friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
+    WINE_TRACE("writing association for file type %s, friendlyAppName=%s, MIME type %s, icon=%s to file %s\n",
                wine_dbgstr_a(dot_extension), wine_dbgstr_a(friendlyAppName), wine_dbgstr_a(mimeType),
-               wine_dbgstr_a(progId), wine_dbgstr_a(openWithIcon), wine_dbgstr_a(desktopPath));
+               wine_dbgstr_a(openWithIcon), wine_dbgstr_a(desktopPath));
 
     desktop = fopen(desktopPath, "w");
     if (desktop)
@@ -2242,7 +2289,7 @@ static BOOL write_freedesktop_association_entry(const char *desktopPath, const c
         fprintf(desktop, "Type=Application\n");
         fprintf(desktop, "Name=%s\n", friendlyAppName);
         fprintf(desktop, "MimeType=%s;\n", mimeType);
-        fprintf(desktop, "Exec=wine start /ProgIDOpen %s %%f\n", progId);
+        fprintf(desktop, "Exec=wine start /ProgIDOpen2 %s %%f\n", dot_extension);
         fprintf(desktop, "NoDisplay=true\n");
         fprintf(desktop, "StartupNotify=true\n");
         if (openWithIcon)
@@ -2255,16 +2302,40 @@ static BOOL write_freedesktop_association_entry(const char *desktopPath, const c
     return ret;
 }
 
+static void update_application(struct wine_rb_entry *entry, void *context)
+{
+    struct rb_application_entry *app;
+    struct app_context *appContext = (struct app_context*)context;
+    app = WINE_RB_ENTRY_VALUE(entry, struct rb_application_entry, entry);
+    if (has_association_changed(app->extensionW, app->mimeTypesA, app->progIdsW, app->friendlyAppNameA, app->openWithIconA))
+    {
+        char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", appContext->applicationsDir, &app->extensionA[1]);
+        if (desktopPath)
+        {
+            if (write_freedesktop_association_entry(desktopPath, app->extensionA, app->friendlyAppNameA, app->mimeTypesA,
+                                                    app->openWithIconA))
+            {
+                appContext->hasChanged = TRUE;
+                update_association(app->extensionW, app->mimeTypesA, app->progIdsW, app->friendlyAppNameA, desktopPath,
+                                   app->openWithIconA);
+            }
+            HeapFree(GetProcessHeap(), 0, desktopPath);
+        }
+    }
+}
+
 static BOOL generate_associations(const char *xdg_data_home, const char *packages_dir, const char *applications_dir)
 {
     static const WCHAR openW[] = {'o','p','e','n',0};
-    struct wine_rb_tree mimeProgidTree;
+    struct wine_rb_tree applicationTree;
     struct list *nativeMimeTypes = NULL;
     LSTATUS ret = 0;
     int i;
-    BOOL hasChanged = FALSE;
+    struct app_context appContext;
 
-    if (wine_rb_init(&mimeProgidTree, &winemenubuilder_rb_functions))
+    appContext.applicationsDir = applications_dir;
+    appContext.hasChanged = FALSE;
+    if (wine_rb_init(&applicationTree, &winemenubuilder_rb_functions))
     {
         WINE_ERR("wine_rb_init failed\n");
         return FALSE;
@@ -2309,8 +2380,7 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
             WCHAR *friendlyAppNameW = NULL;
             char *friendlyAppNameA = NULL;
             WCHAR *progIdW = NULL;
-            char *progIdA = NULL;
-            char *mimeProgId = NULL;
+            struct wine_rb_entry *entry;
 
             extensionA = wchars_to_utf8_chars(strlwrW(extensionW));
             if (extensionA == NULL)
@@ -2371,7 +2441,7 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
                     }
 
                     write_freedesktop_mime_type_entry(packages_dir, extensionA, mimeTypeA, friendlyDocNameA);
-                    hasChanged = TRUE;
+                    appContext.hasChanged = TRUE;
                 }
                 else
                 {
@@ -2385,78 +2455,94 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
                 /* no command => no application is associated */
                 goto end;
 
-            executableW = assoc_query(ASSOCSTR_EXECUTABLE, extensionW, openW);
-            if (executableW)
-                openWithIconA = extract_icon(executableW, 0, NULL, FALSE);
+            progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL);
+            if (progIdW == NULL)
+                goto end; /* no progID => not a file type association */
 
-            friendlyAppNameW = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, NULL);
-            if (friendlyAppNameW)
+            entry = wine_rb_get(&applicationTree, commandW);
+            if (entry == NULL)
             {
-                friendlyAppNameA = wchars_to_utf8_chars(friendlyAppNameW);
-                if (friendlyAppNameA == NULL)
+                struct rb_application_entry *applicationEntry;
+
+                executableW = assoc_query(ASSOCSTR_EXECUTABLE, extensionW, openW);
+                if (executableW)
+                    openWithIconA = extract_icon(executableW, 0, NULL, FALSE);
+
+                friendlyAppNameW = assoc_query(ASSOCSTR_FRIENDLYAPPNAME, extensionW, NULL);
+                if (friendlyAppNameW)
                 {
-                    WINE_ERR("out of memory\n");
-                    goto end;
+                    friendlyAppNameA = wchars_to_utf8_chars(friendlyAppNameW);
+                    if (friendlyAppNameA == NULL)
+                    {
+                        WINE_ERR("out of memory\n");
+                        goto end;
+                    }
                 }
-            }
-            else
-            {
-                friendlyAppNameA = heap_printf("A Wine application");
-                if (friendlyAppNameA == NULL)
+                else
                 {
-                    WINE_ERR("out of memory\n");
-                    goto end;
+                    friendlyAppNameA = heap_printf("A Wine application");
+                    if (friendlyAppNameA == NULL)
+                    {
+                        WINE_ERR("out of memory\n");
+                        goto end;
+                    }
                 }
-            }
 
-            progIdW = reg_get_valW(HKEY_CLASSES_ROOT, extensionW, NULL);
-            if (progIdW)
-            {
-                progIdA = escape(progIdW);
-                if (progIdA == NULL)
+                applicationEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct rb_application_entry));
+                if (applicationEntry == NULL)
                 {
                     WINE_ERR("out of memory\n");
                     goto end;
                 }
-            }
-            else
-                goto end; /* no progID => not a file type association */
-
-            /* Do not allow duplicate ProgIDs for a MIME type, it causes unnecessary duplication in Open dialogs */
-            mimeProgId = heap_printf("%s=>%s", mimeTypeA, progIdA);
-            if (mimeProgId)
-            {
-                struct rb_string_entry *entry;
-                if (wine_rb_get(&mimeProgidTree, mimeProgId))
-                {
-                    HeapFree(GetProcessHeap(), 0, mimeProgId);
-                    goto end;
-                }
-                entry = HeapAlloc(GetProcessHeap(), 0, sizeof(struct rb_string_entry));
-                if (!entry)
+                applicationEntry->commandW = commandW;
+                commandW = NULL;
+                applicationEntry->extensionW = extensionW;
+                extensionW = NULL;
+                applicationEntry->extensionA = extensionA;
+                extensionA = NULL;
+                applicationEntry->mimeTypesA = mimeTypeA;
+                mimeTypeA = NULL;
+                applicationEntry->progIdsW = progIdW;
+                progIdW = NULL;
+                applicationEntry->friendlyAppNameA = friendlyAppNameA;
+                friendlyAppNameA = NULL;
+                applicationEntry->openWithIconA = openWithIconA;
+                openWithIconA = NULL;
+                if (wine_rb_put(&applicationTree, applicationEntry->commandW, &applicationEntry->entry))
                 {
-                    WINE_ERR("out of memory allocating rb_string_entry\n");
-                    goto end;
-                }
-                entry->string = mimeProgId;
-                if (wine_rb_put(&mimeProgidTree, mimeProgId, &entry->entry))
-                {
-                    WINE_ERR("error updating rb tree\n");
+                    WINE_ERR("error updating applications rb tree (out of memory?)\n");
+                    winemenubuilder_rb_destroy(&applicationEntry->entry, NULL);
                     goto end;
                 }
             }
-
-            if (has_association_changed(extensionW, mimeTypeA, progIdW, friendlyAppNameA, openWithIconA))
+            else
             {
-                char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", applications_dir, &extensionA[1]);
-                if (desktopPath)
+                static const WCHAR progid_fmtW[] = {'%','s',';','%','s',0};
+                char *mimeTypes;
+                struct rb_application_entry *applicationEntry =
+                    WINE_RB_ENTRY_VALUE(entry, struct rb_application_entry, entry);
+                mimeTypes = heap_printf("%s;%s", applicationEntry->mimeTypesA, mimeTypeA);
+                if (mimeTypes)
                 {
-                    if (write_freedesktop_association_entry(desktopPath, extensionA, friendlyAppNameA, mimeTypeA, progIdA, openWithIconA))
+                    WCHAR *progIds = heap_printfW(progid_fmtW, applicationEntry->progIdsW, progIdW);
+                    if (progIds)
                     {
-                        hasChanged = TRUE;
-                        update_association(extensionW, mimeTypeA, progIdW, friendlyAppNameA, desktopPath, openWithIconA);
+                        HeapFree(GetProcessHeap(), 0, applicationEntry->mimeTypesA);
+                        applicationEntry->mimeTypesA = mimeTypes;
+                        HeapFree(GetProcessHeap(), 0, applicationEntry->progIdsW);
+                        applicationEntry->progIdsW = progIds;
                     }
-                    HeapFree(GetProcessHeap(), 0, desktopPath);
+                    else
+                    {
+                        HeapFree(GetProcessHeap(), 0, mimeTypes);
+                        WINE_ERR("out of memory\n");
+                        goto end;
+                    }
+                }
+                else
+                {
+                    WINE_ERR("out of memory\n");
+                    goto end;
                 }
             }
 
@@ -2474,16 +2560,17 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
             HeapFree(GetProcessHeap(), 0, friendlyAppNameW);
             HeapFree(GetProcessHeap(), 0, friendlyAppNameA);
             HeapFree(GetProcessHeap(), 0, progIdW);
-            HeapFree(GetProcessHeap(), 0, progIdA);
         }
         HeapFree(GetProcessHeap(), 0, extensionW);
         if (ret != ERROR_SUCCESS)
             break;
     }
 
-    wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL);
+    wine_rb_for_each_entry(&applicationTree, update_application, &appContext);
+
+    wine_rb_destroy(&applicationTree, winemenubuilder_rb_destroy, NULL);
     free_native_mime_types(nativeMimeTypes);
-    return hasChanged;
+    return appContext.hasChanged;
 }
 
 static char *get_start_exe_path(void)


More information about the wine-patches mailing list