changeset 191:5db57b8ff1a9

Added Program
author Frank Benoit <benoit@tionex.de>
date Tue, 04 Mar 2008 00:42:45 +0100
parents 934fb859da8e
children 32e2151a0508
files dwt/dwthelper/utils.d dwt/program/Program.d
diffstat 2 files changed, 949 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/dwt/dwthelper/utils.d	Mon Mar 03 21:23:28 2008 +0100
+++ b/dwt/dwthelper/utils.d	Tue Mar 04 00:42:45 2008 +0100
@@ -42,6 +42,7 @@
 }
 
 alias ValueWrapperT!(bool)    ValueWrapperBool;
+alias ValueWrapperT!(int)     ValueWrapperInt;
 alias ArrayWrapperT!(byte)    ArrayWrapperByte;
 alias ArrayWrapperT!(int)     ArrayWrapperInt;
 alias ArrayWrapperT!(Object)  ArrayWrapperObject;
@@ -303,6 +304,10 @@
     return tango.text.Unicode.toLower( src );
 }
 
+public hash_t toHash( char[] src ){
+    return typeid(char[]).getHash(&src);
+}
+
 static char[] toHex(uint value, bool prefix = true, int radix = 8){
     return tango.text.convert.Integer.toString(
             value,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwt/program/Program.d	Tue Mar 04 00:42:45 2008 +0100
@@ -0,0 +1,944 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwt.program.Program;
+
+import dwt.DWT;
+import dwt.graphics.ImageData;
+import dwt.internal.Compatibility;
+import dwt.internal.Converter;
+import dwt.internal.Library;
+import dwt.internal.gtk.OS;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.dwthelper.utils;
+
+import tango.sys.SharedLib;
+import tango.stdc.stringz;
+import tango.core.Exception;
+import tango.core.Array;
+import tango.text.convert.Format;
+
+version( build ){
+    pragma(link, "gnomeui-2" );
+}
+
+private extern(C) {
+    alias int GnomeIconLookupResultFlags;
+    alias int GnomeIconLookupFlags;
+    GnomeIconTheme *gnome_icon_theme_new  ();
+    char *gnome_icon_lookup      (
+                GtkIconTheme *icon_theme,
+                void  *thumbnail_factory,
+                char  *file_uri,
+                char  *custom_icon,
+                void  *file_info,
+                char  *mime_type,
+                GnomeIconLookupFlags        flags,
+                GnomeIconLookupResultFlags *result);
+    int gnome_vfs_init();
+    char* gnome_icon_theme_lookup_icon(GnomeIconTheme *theme,char *icon_name,int size, GnomeIconData **icon_data, int *base_size) ;
+
+    alias void GnomeIconTheme;
+    alias void GnomeIconData;
+
+    struct GnomeVFSMimeApplication{
+        /*< public > */
+        char *id;
+        char *name;
+
+        /*< private > */
+        char *command;
+        int    can_open_multiple_files;
+        int    expects_uris;
+        GList *supported_uri_schemes;
+        int    requires_terminal;
+
+        /* Padded to avoid future breaks in ABI compatibility */
+        void * reserved1;
+
+        void * priv;
+    }
+}
+
+struct GNOME {
+    private static extern(C){
+        enum {
+            GNOME_ICON_LOOKUP_FLAGS_NONE = 0,
+            GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT = 1<<0,
+            GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES = 1<<1,
+            GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES = 1<<2
+        }
+        enum {
+            GNOME_VFS_MAKE_URI_DIR_NONE = 0,
+            GNOME_VFS_MAKE_URI_DIR_HOMEDIR = 1 << 0,
+            GNOME_VFS_MAKE_URI_DIR_CURRENT = 1 << 1
+        }
+        alias int GnomeVFSMakeURIDirs;
+        enum {
+            GNOME_VFS_OK,
+            GNOME_VFS_ERROR_NOT_FOUND,
+            GNOME_VFS_ERROR_GENERIC,
+            GNOME_VFS_ERROR_INTERNAL,
+            GNOME_VFS_ERROR_BAD_PARAMETERS,
+            GNOME_VFS_ERROR_NOT_SUPPORTED,
+            GNOME_VFS_ERROR_IO,
+            GNOME_VFS_ERROR_CORRUPTED_DATA,
+            GNOME_VFS_ERROR_WRONG_FORMAT,
+            GNOME_VFS_ERROR_BAD_FILE,
+            GNOME_VFS_ERROR_TOO_BIG,
+            GNOME_VFS_ERROR_NO_SPACE,
+            GNOME_VFS_ERROR_READ_ONLY,
+            GNOME_VFS_ERROR_INVALID_URI,
+            GNOME_VFS_ERROR_NOT_OPEN,
+            GNOME_VFS_ERROR_INVALID_OPEN_MODE,
+            GNOME_VFS_ERROR_ACCESS_DENIED,
+            GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES,
+            GNOME_VFS_ERROR_EOF,
+            GNOME_VFS_ERROR_NOT_A_DIRECTORY,
+            GNOME_VFS_ERROR_IN_PROGRESS,
+            GNOME_VFS_ERROR_INTERRUPTED,
+            GNOME_VFS_ERROR_FILE_EXISTS,
+            GNOME_VFS_ERROR_LOOP,
+            GNOME_VFS_ERROR_NOT_PERMITTED,
+            GNOME_VFS_ERROR_IS_DIRECTORY,
+            GNOME_VFS_ERROR_NO_MEMORY,
+            GNOME_VFS_ERROR_HOST_NOT_FOUND,
+            GNOME_VFS_ERROR_INVALID_HOST_NAME,
+            GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS,
+            GNOME_VFS_ERROR_LOGIN_FAILED,
+            GNOME_VFS_ERROR_CANCELLED,
+            GNOME_VFS_ERROR_DIRECTORY_BUSY,
+            GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY,
+            GNOME_VFS_ERROR_TOO_MANY_LINKS,
+            GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM,
+            GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM,
+            GNOME_VFS_ERROR_NAME_TOO_LONG,
+            GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE,
+            GNOME_VFS_ERROR_SERVICE_OBSOLETE,
+            GNOME_VFS_ERROR_PROTOCOL_ERROR,
+            GNOME_VFS_ERROR_NO_MASTER_BROWSER,
+            GNOME_VFS_ERROR_NO_DEFAULT,
+            GNOME_VFS_ERROR_NO_HANDLER,
+            GNOME_VFS_ERROR_PARSE,
+            GNOME_VFS_ERROR_LAUNCH,
+            GNOME_VFS_ERROR_TIMEOUT,
+            GNOME_VFS_ERROR_NAMESERVER,
+            GNOME_VFS_ERROR_LOCKED,
+            GNOME_VFS_ERROR_DEPRECATED_FUNCTION,
+            GNOME_VFS_ERROR_INVALID_FILENAME,
+            GNOME_VFS_ERROR_NOT_A_SYMBOLIC_LINK,
+            GNOME_VFS_NUM_ERRORS
+        }
+        alias int GnomeVFSResult;
+
+        enum {
+            GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS,
+            GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS,
+            GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS_FOR_NON_FILES
+        }
+
+        alias GtkIconTheme GnomeIconTheme;
+        alias .gnome_icon_theme_lookup_icon gnome_icon_theme_lookup_icon;
+        alias .gnome_vfs_init gnome_vfs_init;
+        alias .gnome_icon_lookup gnome_icon_lookup;
+        alias .gnome_icon_theme_new gnome_icon_theme_new;
+
+
+        GnomeVFSMimeApplication * function ( char *mime_type ) gnome_vfs_mime_get_default_application;
+        char* function(char*, int ) gnome_vfs_make_uri_from_input_with_dirs;
+        GnomeVFSResult function( GnomeVFSMimeApplication*, GList*) gnome_vfs_mime_application_launch;
+        void function(GnomeVFSMimeApplication*) gnome_vfs_mime_application_free;
+        GList* function(char*) gnome_vfs_mime_get_extensions_list;
+        void function(GList*) gnome_vfs_mime_extensions_list_free;
+        void function(GList*) gnome_vfs_mime_registered_mime_type_list_free;
+        GnomeVFSResult function(char*) gnome_vfs_url_show;
+        char* function(char*) gnome_vfs_make_uri_from_input;
+        GList* function() gnome_vfs_get_registered_mime_types;
+        char* function(char*) gnome_vfs_mime_type_from_name;
+    }
+}
+
+/**
+ * Instances of this class represent programs and
+ * their associated file extensions in the operating
+ * system.
+ */
+public final class Program {
+    char[] name;
+    char[] command;
+    char[] iconPath;
+    Display display;
+
+    /* Gnome specific
+     * true if command expects a URI
+     * false if expects a path
+     */
+    bool gnomeExpectUri;
+
+    static int /*long*/ cdeShell;
+
+    static const char[][] CDE_ICON_EXT = [ ".m.pm"[],   ".l.pm",   ".s.pm",   ".t.pm" ];
+    static const char[][] CDE_MASK_EXT = [ ".m_m.bm"[], ".l_m.bm", ".s_m.bm", ".t_m.bm" ];
+    static const char[] DESKTOP_DATA = "Program_DESKTOP";
+    static const char[] ICON_THEME_DATA = "Program_GNOME_ICON_THEME";
+    static const int DESKTOP_UNKNOWN = 0;
+    static const int DESKTOP_GNOME = 1;
+    static const int DESKTOP_GNOME_24 = 2;
+    static const int DESKTOP_CDE = 3;
+    static const int PREFERRED_ICON_SIZE = 16;
+
+/**
+ * Prevents uninitialized instances from being created outside the package.
+ */
+this() {
+}
+
+/* Determine the desktop for the given display. */
+static int getDesktop(Display display) {
+    if (display is null) return DESKTOP_UNKNOWN;
+    ValueWrapperInt desktopValue = cast(ValueWrapperInt)display.getData(DESKTOP_DATA);
+    if (desktopValue !is null) return desktopValue.value;
+    int desktop = DESKTOP_UNKNOWN;
+
+    /* Get the list of properties on the root window. */
+    void* xDisplay = OS.GDK_DISPLAY();
+    uint rootWindow = OS.XDefaultRootWindow(xDisplay);
+    int numProp;
+    uint* propList = OS.XListProperties(xDisplay, rootWindow, &numProp);
+    uint[] property = new uint[numProp];
+    if (propList !is null) {
+        property[ 0 .. numProp ] = propList[ 0 .. numProp ];
+        OS.XFree(propList);
+    }
+
+    /*
+     * Feature in Linux Desktop. There is currently no official way to
+     * determine whether the Gnome window manager or gnome-vfs is
+     * available. Earlier versions including Red Hat 9 and Suse 9 provide
+     * a documented Gnome specific property on the root window
+     * WIN_SUPPORTING_WM_CHECK. This property is no longer supported in newer
+     * versions such as Fedora Core 2.
+     * The workaround is to simply check that the window manager is a
+     * compliant one (property _NET_SUPPORTING_WM_CHECK) and to attempt to load
+     * our native library that depends on gnome-vfs.
+     */
+    if (desktop is DESKTOP_UNKNOWN) {
+        char[] gnomeName = "_NET_SUPPORTING_WM_CHECK";
+        int /*long*/ gnome = OS.XInternAtom(xDisplay, gnomeName.ptr, true);
+        if (gnome !is OS.None && gnome_init()) {
+            desktop = DESKTOP_GNOME;
+            int icon_theme = cast(int)GNOME.gnome_icon_theme_new();
+            display.setData(ICON_THEME_DATA, new ValueWrapperInt(icon_theme));
+            display.addListener(DWT.Dispose, new class(display) Listener {
+                Display display;
+                this( Display display ){ this.display = display; }
+                public void handleEvent(Event event) {
+                    ValueWrapperInt gnomeIconTheme = cast(ValueWrapperInt)display.getData(ICON_THEME_DATA);
+                    if (gnomeIconTheme is null) return;
+                    display.setData(ICON_THEME_DATA, null);
+                    /*
+                     * Note.  gnome_icon_theme_new uses g_object_new to allocate the
+                     * data it returns. Use g_object_unref to free the pointer it returns.
+                     */
+                    if (gnomeIconTheme.value !is 0) OS.g_object_unref( cast(void*)gnomeIconTheme.value);
+                }
+            });
+            /* Check for libgnomevfs-2 version 2.4 */
+            char[] buffer = "libgnomevfs-2.so.0";
+            auto libgnomevfs = SharedLib.load(buffer );
+            if (libgnomevfs !is null) {
+                buffer = "gnome_vfs_url_show";
+                void* gnome_vfs_url_show = libgnomevfs.getSymbol( buffer.ptr );
+                if (gnome_vfs_url_show !is null) {
+                    desktop = DESKTOP_GNOME_24;
+                }
+                *cast(void**)&GNOME.gnome_vfs_mime_get_default_application = libgnomevfs.getSymbol( "gnome_vfs_mime_get_default_application" );
+                *cast(void**)&GNOME.gnome_vfs_make_uri_from_input_with_dirs = libgnomevfs.getSymbol( "gnome_vfs_make_uri_from_input_with_dirs" );
+                *cast(void**)&GNOME.gnome_vfs_mime_application_launch = libgnomevfs.getSymbol( "gnome_vfs_mime_application_launch" );
+                *cast(void**)&GNOME.gnome_vfs_mime_application_free = libgnomevfs.getSymbol( "gnome_vfs_mime_application_free" );
+                *cast(void**)&GNOME.gnome_vfs_url_show = libgnomevfs.getSymbol( "gnome_vfs_url_show" );
+                *cast(void**)&GNOME.gnome_vfs_make_uri_from_input = libgnomevfs.getSymbol( "gnome_vfs_make_uri_from_input" );
+                *cast(void**)&GNOME.gnome_vfs_get_registered_mime_types = libgnomevfs.getSymbol( "gnome_vfs_get_registered_mime_types" );
+                *cast(void**)&GNOME.gnome_vfs_mime_get_extensions_list = libgnomevfs.getSymbol( "gnome_vfs_mime_get_extensions_list" );
+                *cast(void**)&GNOME.gnome_vfs_mime_extensions_list_free = libgnomevfs.getSymbol( "gnome_vfs_mime_extensions_list_free" );
+                *cast(void**)&GNOME.gnome_vfs_mime_registered_mime_type_list_free = libgnomevfs.getSymbol( "gnome_vfs_mime_registered_mime_type_list_free" );
+                *cast(void**)&GNOME.gnome_vfs_mime_type_from_name = libgnomevfs.getSymbol( "gnome_vfs_mime_type_from_name" );
+            }
+        }
+    }
+
+// PORTING CDE not supported
+/+
+    /*
+    * On CDE, the atom below may exist without DTWM running. If the atom
+    * below is defined, the CDE database exists and the available
+    * applications can be queried.
+    */
+    if (desktop is DESKTOP_UNKNOWN) {
+        char[] cdeName = "_DT_SM_PREFERENCES";
+        int /*long*/ cde = OS.XInternAtom(xDisplay, cdeName.ptr, true);
+        for (int index = 0; desktop is DESKTOP_UNKNOWN && index < property.length; index++) {
+            if (property[index] is OS.None) continue; /* do not match atoms that do not exist */
+            if (property[index] is cde && cde_init(display)) desktop = DESKTOP_CDE;
+        }
+    }
++/
+
+    display.setData(DESKTOP_DATA, new ValueWrapperInt(desktop));
+    return desktop;
+}
+
+// PORTING CDE not supported
+/+
+bool cde_execute(char[] fileName) {
+    /* Use the character encoding for the default locale */
+    char* action = toStringz(command);
+    char* ptr = cast(char*)OS.g_malloc(fileName.length+1);
+    ptr[ 0 .. fileName.length ] = fileName;
+    ptr[ fileName.length ] = 0;
+    DtActionArg args = new DtActionArg();
+    args.argClass = CDE.DtACTION_FILE;
+    args.name = ptr;
+    long actionID = CDE.DtActionInvoke(cdeShell, action, args, 1, null, null, null, 1, 0, 0);
+    OS.g_free(ptr);
+    return actionID !is 0;
+}
+
+static char[] cde_getAction(char[] dataType) {
+    char[] action  = null;
+    char[] actions = cde_getAttribute(dataType, CDE.DtDTS_DA_ACTION_LIST);
+    if (actions !is null) {
+        int index = actions.indexOf("Open");
+        if (index !is -1) {
+            action = actions.substring(index, index + 4);
+        } else {
+            index = actions.indexOf(",");
+            action = index !is -1 ? actions.substring(0, index) : actions;
+        }
+    }
+    return action;
+}
+
+static char[] cde_getAttribute(char[] dataType, char[] attrName) {
+    /* Use the character encoding for the default locale */
+    byte[] dataTypeBuf = Converter.wcsToMbcs(null, dataType, true);
+    byte[] attrNameBuf = Converter.wcsToMbcs(null, attrName, true);
+    byte[] optNameBuf = null;
+    int /*long*/ attrValue = CDE.DtDtsDataTypeToAttributeValue(dataTypeBuf, attrNameBuf, optNameBuf);
+    if (attrValue is 0) return null;
+    int length = OS.strlen(attrValue);
+    byte[] attrValueBuf = new byte[length];
+    OS.memmove(attrValueBuf, attrValue, length);
+    CDE.DtDtsFreeAttributeValue(attrValue);
+    /* Use the character encoding for the default locale */
+    return new char[](Converter.mbcsToWcs(null, attrValueBuf));
+}
+
+static char[][][ char[] ] cde_getDataTypeInfo() {
+    char[][][ char[] ] dataTypeInfo;
+    int index;
+    int /*long*/ dataTypeList = CDE.DtDtsDataTypeNames();
+    if (dataTypeList !is 0) {
+        /* For each data type name in the list */
+        index = 0;
+        int /*long*/ [] dataType = new int /*long*/ [1];
+        OS.memmove(dataType, dataTypeList + (index++ * 4), 4);
+        while (dataType[0] !is 0) {
+            int length = OS.strlen(dataType[0]);
+            byte[] dataTypeBuf = new byte[length];
+            OS.memmove(dataTypeBuf, dataType[0], length);
+            /* Use the character encoding for the default locale */
+            char[] dataTypeName = new char[](Converter.mbcsToWcs(null, dataTypeBuf));
+
+            /* The data type is valid if it is not an action, and it has an extension and an action. */
+            char[] extension = cde_getExtension(dataTypeName);
+            if (!CDE.DtDtsDataTypeIsAction(dataTypeBuf) &&
+                extension !is null && cde_getAction(dataTypeName) !is null) {
+                char[][] exts;
+                exts ~= extension;
+                dataTypeInfo[ dataTypeName ] = exts;
+            }
+            OS.memmove(dataType, dataTypeList + (index++ * 4), 4);
+        }
+        CDE.DtDtsFreeDataTypeNames(dataTypeList);
+    }
+
+    return dataTypeInfo;
+}
+
+static char[] cde_getExtension(char[] dataType) {
+    char[] fileExt = cde_getAttribute(dataType, CDE.DtDTS_DA_NAME_TEMPLATE);
+    if (fileExt is null || fileExt.indexOf("%s.") is -1) return null;
+    int dot = fileExt.indexOf(".");
+    return fileExt.substring(dot);
+}
+
+/**
+ * CDE - Get Image Data
+ *
+ * This method returns the image data of the icon associated with
+ * the data type. Since CDE supports multiple sizes of icons, several
+ * attempts are made to locate an icon of the desired size and format.
+ * CDE supports the sizes: tiny, small, medium and large. The best
+ * search order is medium, large, small and then tiny. Althoug CDE supports
+ * colour and monochrome bitmaps, only colour icons are tried. (The order is
+ * defined by the  cdeIconExt and cdeMaskExt arrays above.)
+ */
+ImageData cde_getImageData() {
+    // TODO
+    return null;
+}
+
+static char[] cde_getMimeType(char[] extension) {
+    char[] mimeType = null;
+    char[][][ char[] ] mimeInfo = cde_getDataTypeInfo();
+    if (mimeInfo is null) return null;
+    char[][] keys = mimeInfo.keys();
+    int keyIdx = 0;
+    while (mimeType is null && keyIdx < keys.length ) {
+        char[] type = keys[ keyIdx ];
+        char[][] mimeExts = mimeInfo[type];
+        for (int index = 0; index < mimeExts.length; index++){
+            if (extension.equals(mimeExts[index])) {
+                mimeType = type;
+                break;
+            }
+        }
+        keyIdx++;
+    }
+    return mimeType;
+}
+
+static Program cde_getProgram(Display display, char[] mimeType) {
+    Program program = new Program();
+    program.display = display;
+    program.name = mimeType;
+    program.command = cde_getAction(mimeType);
+    program.iconPath = cde_getAttribute(program.name, CDE.DtDTS_DA_ICON);
+    return program;
+}
+
+static bool cde_init(Display display) {
+    try {
+        Library.loadLibrary("swt-cde");
+    } catch (Throwable e) {
+        return false;
+    }
+
+    /* Use the character encoding for the default locale */
+    CDE.XtToolkitInitialize();
+    int /*long*/ xtContext = CDE.XtCreateApplicationContext ();
+    int /*long*/ xDisplay = OS.GDK_DISPLAY();
+    byte[] appName = Converter.wcsToMbcs(null, "CDE", true);
+    byte[] appClass = Converter.wcsToMbcs(null, "CDE", true);
+    int /*long*/ [] argc = [0];
+    CDE.XtDisplayInitialize(xtContext, xDisplay, appName, appClass, 0, 0, argc, 0);
+    int /*long*/ widgetClass = CDE.topLevelShellWidgetClass ();
+    cdeShell = CDE.XtAppCreateShell (appName, appClass, widgetClass, xDisplay, null, 0);
+    CDE.XtSetMappedWhenManaged (cdeShell, false);
+    CDE.XtResizeWidget (cdeShell, 10, 10, 0);
+    CDE.XtRealizeWidget (cdeShell);
+    bool initOK = CDE.DtAppInitialize(xtContext, xDisplay, cdeShell, appName, appName);
+    if (initOK) CDE.DtDbLoad();
+    return initOK;
+}
++/
+
+static char[][] parseCommand(char[] cmd) {
+    char[][] args;
+    int sIndex = 0;
+    int eIndex;
+    while (sIndex < cmd.length) {
+        /* Trim initial white space of argument. */
+        while (sIndex < cmd.length && Compatibility.isWhitespace(cmd.charAt(sIndex))) {
+            sIndex++;
+        }
+        if (sIndex < cmd.length) {
+            /* If the command is a quoted string */
+            if (cmd.charAt(sIndex) is '"' || cmd.charAt(sIndex) is '\'') {
+                /* Find the terminating quote (or end of line).
+                 * This code currently does not handle escaped characters (e.g., " a\"b").
+                 */
+                eIndex = sIndex + 1;
+                while (eIndex < cmd.length && cmd.charAt(eIndex) !is cmd.charAt(sIndex)) eIndex++;
+                if (eIndex >= cmd.length) {
+                    /* The terminating quote was not found
+                     * Add the argument as is with only one initial quote.
+                     */
+                    args ~= cmd.substring(sIndex, eIndex);
+                } else {
+                    /* Add the argument, trimming off the quotes. */
+                    args ~= cmd.substring(sIndex + 1, eIndex);
+                }
+                sIndex = eIndex + 1;
+            }
+            else {
+                /* Use white space for the delimiters. */
+                eIndex = sIndex;
+                while (eIndex < cmd.length && !Compatibility.isWhitespace( firstCodePoint( cmd[ eIndex .. $ ]))) eIndex++;
+                args ~= cmd.substring(sIndex, eIndex);
+                sIndex = eIndex + 1;
+            }
+        }
+    }
+
+    char[][] strings = new char[][args.length];
+    for (int index =0; index < args.length; index++) {
+        strings[index] = args[index];
+    }
+    return strings;
+}
+
+/**
+ * GNOME 2.4 - Execute the program for the given file.
+ */
+bool gnome_24_execute(char[] fileName) {
+    char* mimeTypeBuffer = toStringz(name);
+    auto ptr = GNOME.gnome_vfs_mime_get_default_application(mimeTypeBuffer);
+    char* fileNameBuffer = toStringz(fileName);
+    char* uri = GNOME.gnome_vfs_make_uri_from_input_with_dirs(fileNameBuffer, GNOME.GNOME_VFS_MAKE_URI_DIR_CURRENT);
+    GList* list = OS.g_list_append( null, uri);
+    int result = GNOME.gnome_vfs_mime_application_launch(ptr, list);
+    GNOME.gnome_vfs_mime_application_free(ptr);
+    OS.g_free(uri);
+    OS.g_list_free(list);
+    return result is GNOME.GNOME_VFS_OK;
+}
+
+/**
+ * GNOME 2.4 - Launch the default program for the given file.
+ */
+static bool gnome_24_launch(char[] fileName) {
+    char* fileNameBuffer = toStringz(fileName);
+    char* uri = GNOME.gnome_vfs_make_uri_from_input_with_dirs(fileNameBuffer, GNOME.GNOME_VFS_MAKE_URI_DIR_CURRENT);
+    int result = GNOME.gnome_vfs_url_show(uri);
+    OS.g_free(uri);
+    return (result is GNOME.GNOME_VFS_OK);
+}
+
+/**
+ * GNOME 2.2 - Execute the program for the given file.
+ */
+bool gnome_execute(char[] fileName) {
+    if (gnomeExpectUri) {
+        /* Convert the given path into a URL */
+        char* fileNameBuffer = toStringz(fileName);
+        char* uri = GNOME.gnome_vfs_make_uri_from_input(fileNameBuffer);
+        if (uri !is null) {
+            fileName = fromStringz( uri ).dup;
+            OS.g_free(uri);
+        }
+    }
+
+    /* Parse the command into its individual arguments. */
+    char[][] args = parseCommand(command);
+    int fileArg = -1;
+    int index;
+    for (index = 0; index < args.length; index++) {
+        int j = args[index].indexOf("%f");
+        if (j !is -1) {
+            char[] value = args[index];
+            fileArg = index;
+            args[index] = value.substring(0, j) ~ fileName ~ value.substring(j + 2);
+        }
+    }
+
+    /* If a file name was given but the command did not have "%f" */
+    if ((fileName.length > 0) && (fileArg < 0)) {
+        char[][] newArgs = new char[][args.length + 1];
+        for (index = 0; index < args.length; index++) newArgs[index] = args[index];
+        newArgs[args.length] = fileName;
+        args = newArgs;
+    }
+
+    /* Execute the command. */
+    try {
+        Compatibility.exec(args);
+    } catch (IOException e) {
+        return false;
+    }
+    return true;
+}
+
+/**
+ * GNOME - Get Image Data
+ *
+ */
+ImageData gnome_getImageData() {
+    if (iconPath is null) return null;
+    try {
+        return new ImageData(iconPath);
+    } catch (Exception e) {}
+    return null;
+}
+
+/**
+ * GNOME - Get mime types
+ *
+ * Obtain the registered mime type information and
+ * return it in a map. The key of each entry
+ * in the map is the mime type name. The value is
+ * a vector of the associated file extensions.
+ */
+static char[][][ char[] ] gnome_getMimeInfo() {
+    char[][][ char[] ] mimeInfo;
+    GList* mimeList = GNOME.gnome_vfs_get_registered_mime_types();
+    GList* mimeElement = mimeList;
+    while (mimeElement !is null) {
+        char* mimePtr = cast(char*) OS.g_list_data(mimeElement);
+        char[] mimeTypeBuffer = fromStringz(mimePtr).dup;
+        char[] mimeType = mimeTypeBuffer;//new char[](Converter.mbcsToWcs(null, mimeTypeBuffer));
+        GList* extensionList = GNOME.gnome_vfs_mime_get_extensions_list(mimePtr);
+        if (extensionList !is null) {
+            char[][] extensions;
+            GList* extensionElement = extensionList;
+            while (extensionElement !is null) {
+                char* extensionPtr = cast(char*) OS.g_list_data(extensionElement);
+                char[] extensionBuffer = fromStringz(extensionPtr).dup;
+                char[] extension = extensionBuffer;
+                extension = '.' ~ extension;
+                extensions ~= extension;
+                extensionElement = OS.g_list_next(extensionElement);
+            }
+            GNOME.gnome_vfs_mime_extensions_list_free(extensionList);
+            if (extensions.length > 0) mimeInfo[ mimeType ] = extensions;
+        }
+        mimeElement = OS.g_list_next(mimeElement);
+    }
+    if (mimeList !is null) GNOME.gnome_vfs_mime_registered_mime_type_list_free(mimeList);
+    return mimeInfo;
+}
+
+static char[] gnome_getMimeType(char[] extension) {
+    char[] mimeType = null;
+    char[] fileName = "swt" ~ extension;
+    char* extensionBuffer = toStringz(fileName);
+    char* typeName = GNOME.gnome_vfs_mime_type_from_name(extensionBuffer);
+    if (typeName !is null) {
+        mimeType = fromStringz(typeName).dup;
+    }
+    return mimeType;
+}
+
+static Program gnome_getProgram(Display display, char[] mimeType) {
+    Program program = null;
+    char* mimeTypeBuffer = toStringz(mimeType);
+    GnomeVFSMimeApplication* ptr = GNOME.gnome_vfs_mime_get_default_application(mimeTypeBuffer);
+    if (ptr !is null) {
+        program = new Program();
+        program.display = display;
+        program.name = mimeType;
+        GnomeVFSMimeApplication* application = ptr;
+        char[] buffer = fromStringz(application.command).dup;
+        program.command = buffer;
+        program.gnomeExpectUri = application.expects_uris is GNOME.GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS;
+
+        buffer = fromStringz( application.id) ~ \0;
+        ValueWrapperInt gnomeIconTheme = cast(ValueWrapperInt)display.getData(ICON_THEME_DATA);
+        char* icon_name = GNOME.gnome_icon_lookup( cast(GtkIconTheme*) gnomeIconTheme.value, null, null, buffer.ptr, null, mimeTypeBuffer,
+                GNOME.GNOME_ICON_LOOKUP_FLAGS_NONE, null);
+        char* path = null;
+        if (icon_name !is null) path = GNOME.gnome_icon_theme_lookup_icon(cast(GtkIconTheme*)gnomeIconTheme.value, icon_name, PREFERRED_ICON_SIZE, null, null);
+        if (path !is null) {
+            program.iconPath = fromStringz( path).dup;
+            OS.g_free(path);
+        }
+        if (icon_name !is null) OS.g_free(icon_name);
+        GNOME.gnome_vfs_mime_application_free(ptr);
+    }
+    return program;
+}
+
+static bool gnome_init() {
+    return cast(bool) GNOME.gnome_vfs_init();
+}
+
+/**
+ * Finds the program that is associated with an extension.
+ * The extension may or may not begin with a '.'.  Note that
+ * a <code>Display</code> must already exist to guarantee that
+ * this method returns an appropriate result.
+ *
+ * @param extension the program extension
+ * @return the program or <code>null</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *      <li>ERROR_NULL_ARGUMENT when extension is null</li>
+ *  </ul>
+ */
+public static Program findProgram(char[] extension) {
+    return findProgram(Display.getCurrent(), extension);
+}
+
+/*
+ *  API: When support for multiple displays is added, this method will
+ *       become public and the original method above can be deprecated.
+ */
+static Program findProgram(Display display, char[] extension) {
+    if (extension is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    if (extension.length is 0) return null;
+    if (extension.charAt(0) !is '.') extension = "." ~ extension;
+    int desktop = getDesktop(display);
+    char[] mimeType = null;
+    switch (desktop) {
+        case DESKTOP_GNOME_24:
+        case DESKTOP_GNOME: mimeType = gnome_getMimeType(extension); break;
+        //case DESKTOP_CDE: mimeType = cde_getMimeType(extension); break;
+        default:
+    }
+    if (mimeType is null) return null;
+    Program program = null;
+    switch (desktop) {
+        case DESKTOP_GNOME_24:
+        case DESKTOP_GNOME: program = gnome_getProgram(display, mimeType); break;
+        //case DESKTOP_CDE: program = cde_getProgram(display, mimeType); break;
+        default:
+    }
+    return program;
+}
+
+/**
+ * Answer all program extensions in the operating system.  Note
+ * that a <code>Display</code> must already exist to guarantee
+ * that this method returns an appropriate result.
+ *
+ * @return an array of extensions
+ */
+public static char[][] getExtensions() {
+    return getExtensions(Display.getCurrent());
+}
+
+/*
+ *  API: When support for multiple displays is added, this method will
+ *       become public and the original method above can be deprecated.
+ */
+static char[][] getExtensions(Display display) {
+    int desktop = getDesktop(display);
+    char[][][ char[] ] mimeInfo = null;
+    switch (desktop) {
+        case DESKTOP_GNOME_24: break;
+        case DESKTOP_GNOME: mimeInfo = gnome_getMimeInfo(); break;
+        //case DESKTOP_CDE: mimeInfo = cde_getDataTypeInfo(); break;
+        default:
+    }
+    if (mimeInfo is null) return new char[][0];
+
+    /* Create a unique set of the file extensions. */
+    char[][] extensions;
+    char[][] keys = mimeInfo.keys;
+    int keyIdx = 0;
+    while ( keyIdx < keys.length ) {
+        char[] mimeType = keys[ keyIdx ];
+        char[][] mimeExts = mimeInfo[mimeType];
+        for (int index = 0; index < mimeExts.length; index++){
+            if (!extensions.contains(mimeExts[index])) {
+                extensions ~= mimeExts[index];
+            }
+        }
+        keyIdx++;
+    }
+
+    /* Return the list of extensions. */
+    char[][] extStrings = new char[][]( extensions.length );
+    for (int index = 0; index < extensions.length; index++) {
+        extStrings[index] = extensions[index];
+    }
+    return extStrings;
+}
+
+/**
+ * Answers all available programs in the operating system.  Note
+ * that a <code>Display</code> must already exist to guarantee
+ * that this method returns an appropriate result.
+ *
+ * @return an array of programs
+ */
+public static Program[] getPrograms() {
+    return getPrograms(Display.getCurrent());
+}
+
+/*
+ *  API: When support for multiple displays is added, this method will
+ *       become public and the original method above can be deprecated.
+ */
+static Program[] getPrograms(Display display) {
+    int desktop = getDesktop(display);
+    char[][][ char[] ] mimeInfo = null;
+    switch (desktop) {
+        case DESKTOP_GNOME_24: break;
+        case DESKTOP_GNOME: mimeInfo = gnome_getMimeInfo(); break;
+        //case DESKTOP_CDE: mimeInfo = cde_getDataTypeInfo(); break;
+        default:
+    }
+    if (mimeInfo is null) return new Program[0];
+    Program[] programs;
+    char[][] keys = mimeInfo.keys;
+    int keyIdx = 0;
+    while ( keyIdx < keys.length ) {
+        char[] mimeType = keys[ keyIdx ];
+        Program program = null;
+        switch (desktop) {
+            case DESKTOP_GNOME: program = gnome_getProgram(display, mimeType); break;
+            //case DESKTOP_CDE: program = cde_getProgram(display, mimeType); break;
+            default:
+        }
+        if (program !is null) programs ~= program;
+        keyIdx++;
+    }
+    Program[] programList = new Program[programs.length];
+    for (int index = 0; index < programList.length; index++) {
+        programList[index] = programs[index];
+    }
+    return programList;
+}
+
+/**
+ * Launches the executable associated with the file in
+ * the operating system.  If the file is an executable,
+ * then the executable is launched.  Note that a <code>Display</code>
+ * must already exist to guarantee that this method returns
+ * an appropriate result.
+ *
+ * @param fileName the file or program name
+ * @return <code>true</code> if the file is launched, otherwise <code>false</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT when fileName is null</li>
+ * </ul>
+ */
+public static bool launch(char[] fileName) {
+    return launch(Display.getCurrent(), fileName);
+}
+
+/*
+ *  API: When support for multiple displays is added, this method will
+ *       become public and the original method above can be deprecated.
+ */
+static bool launch(Display display, char[] fileName) {
+    if (fileName is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
+    switch(getDesktop(display)) {
+        case DESKTOP_GNOME_24:
+            if (gnome_24_launch (fileName)) return true;
+        default:
+            int index = fileName.lastIndexOf('.');
+            if (index !is -1) {
+                char[] extension = fileName.substring (index);
+                Program program = Program.findProgram (display, extension);
+                if (program !is null && program.execute (fileName)) return true;
+            }
+            break;
+    }
+    try {
+        Compatibility.exec (fileName);
+        return true;
+    } catch (IOException e) {
+        return false;
+    }
+}
+
+/**
+ * Compares the argument to the receiver, and returns true
+ * if they represent the <em>same</em> object using a class
+ * specific comparison.
+ *
+ * @param other the object to compare with this object
+ * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
+ *
+ * @see #hashCode()
+ */
+public override int opEquals(Object other) {
+    if (this is other) return true;
+    if (!(cast(Program)other)) return false;
+    Program program = cast(Program)other;
+    return display is program.display && name.equals(program.name) && command.equals(program.command);
+}
+
+/**
+ * Executes the program with the file as the single argument
+ * in the operating system.  It is the responsibility of the
+ * programmer to ensure that the file contains valid data for
+ * this program.
+ *
+ * @param fileName the file or program name
+ * @return <code>true</code> if the file is launched, otherwise <code>false</code>
+ *
+ * @exception IllegalArgumentException <ul>
+ *    <li>ERROR_NULL_ARGUMENT when fileName is null</li>
+ * </ul>
+ */
+public bool execute(char[] fileName) {
+    if (fileName is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
+    int desktop = getDesktop(display);
+    switch (desktop) {
+        case DESKTOP_GNOME_24: return gnome_24_execute(fileName);
+        case DESKTOP_GNOME: return gnome_execute(fileName);
+        //case DESKTOP_CDE: return cde_execute(fileName);
+        default:
+    }
+    return false;
+}
+
+/**
+ * Returns the receiver's image data.  This is the icon
+ * that is associated with the receiver in the operating
+ * system.
+ *
+ * @return the image data for the program, may be null
+ */
+public ImageData getImageData() {
+    switch (getDesktop(display)) {
+        case DESKTOP_GNOME_24:
+        case DESKTOP_GNOME: return gnome_getImageData();
+        //case DESKTOP_CDE: return cde_getImageData();
+        default:
+    }
+    return null;
+}
+
+/**
+ * Returns the receiver's name.  This is as short and
+ * descriptive a name as possible for the program.  If
+ * the program has no descriptive name, this string may
+ * be the executable name, path or empty.
+ *
+ * @return the name of the program
+ */
+public char[] getName() {
+    return name;
+}
+
+/**
+ * Returns an integer hash code for the receiver. Any two
+ * objects that return <code>true</code> when passed to
+ * <code>equals</code> must return the same value for this
+ * method.
+ *
+ * @return the receiver's hash
+ *
+ * @see #equals(Object)
+ */
+public override hash_t toHash() {
+    return .toHash(name) ^ .toHash(command) ^ display.toHash();
+}
+
+/**
+ * Returns a string containing a concise, human-readable
+ * description of the receiver.
+ *
+ * @return a string representation of the program
+ */
+public char[] toString() {
+    return Format( "Program {{{}}", name );
+}
+}