view dwt/program/Program.d @ 45:d8635bb48c7c

Merge with SWT 3.5
author Jacob Carlborg <doob@me.com>
date Mon, 01 Dec 2008 17:07:00 +0100
parents a9ab4c738ed8
children cfa563df4fdd
line wrap: on
line source

/*******************************************************************************
 * Copyright (c) 2000, 2008 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
 *******************************************************************************/
module dwt.program.Program;

import dwt.dwthelper.utils;


import java.util.Vector;

import dwt.DWT;
import dwt.graphics.ImageData;
import dwt.graphics.PaletteData;
import dwt.internal.C;
import dwt.internal.cocoa.NSArray;
import dwt.internal.cocoa.NSAutoreleasePool;
import dwt.internal.cocoa.NSBitmapImageRep;
import dwt.internal.cocoa.NSBundle;
import dwt.internal.cocoa.NSDictionary;
import dwt.internal.cocoa.NSDirectoryEnumerator;
import dwt.internal.cocoa.NSEnumerator;
import dwt.internal.cocoa.NSFileManager;
import dwt.internal.cocoa.NSImage;
import dwt.internal.cocoa.NSImageRep;
import dwt.internal.cocoa.NSMutableSet;
import dwt.internal.cocoa.NSSize;
import dwt.internal.cocoa.NSString;
import dwt.internal.cocoa.NSURL;
import dwt.internal.cocoa.NSWorkspace;
import dwt.internal.cocoa.OS;
import dwt.internal.cocoa.id;

/**
 * Instances of this class represent programs and
 * their associated file extensions in the operating
 * system.
 *
 * @see <a href="http://www.eclipse.org/swt/snippets/#program">Program snippets</a>
 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
 */
public final class Program {
    String name, fullPath, identifier;

    static final String PREFIX_FILE = "file://"; //$NON-NLS-1$
    static final String PREFIX_HTTP = "http://"; //$NON-NLS-1$
    static final String PREFIX_HTTPS = "https://"; //$NON-NLS-1$

/**
 * Prevents uninitialized instances from being created outside the package.
 */
Program () {
}

/**
 * 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 (String 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;
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        int /*long*/ appName = OS.malloc(C.PTR_SIZEOF);
        int /*long*/ type = OS.malloc(C.PTR_SIZEOF);
        NSString temp = new NSString(OS.NSTemporaryDirectory());
        NSString fileName = NSString.stringWith("swt" + System.currentTimeMillis() + extension);
        NSString fullPath = temp.stringByAppendingPathComponent(fileName);
        NSFileManager fileManager = NSFileManager.defaultManager();
        fileManager.createFileAtPath(fullPath, null, null);
        workspace.getInfoForFile(fullPath, appName, type);
        fileManager.removeItemAtPath(fullPath, 0);
        int /*long*/ [] buffer = new int /*long*/[1];
        OS.memmove(buffer, appName, C.PTR_SIZEOF);
        OS.free(appName);
        OS.free(type);
        if (buffer [0] !is 0) {
            NSString appPath = new NSString(buffer[0]);
            NSBundle bundle = NSBundle.bundleWithPath(appPath);
            if (bundle !is null) return getProgram(bundle);
        }
        return null;
    } finally {
        pool.release();
    }
}

/**
 * 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 String [] getExtensions () {
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        NSMutableSet supportedDocumentTypes = (NSMutableSet)NSMutableSet.set();
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        NSString CFBundleDocumentTypes = NSString.stringWith("CFBundleDocumentTypes");
        NSString CFBundleTypeExtensions = NSString.stringWith("CFBundleTypeExtensions");
        NSArray array = new NSArray(OS.NSSearchPathForDirectoriesInDomains(OS.NSAllApplicationsDirectory, OS.NSAllDomainsMask, true));
        int count = (int)/*64*/array.count();
        for (int i = 0; i < count; i++) {
            NSString path = new NSString(array.objectAtIndex(i));
            NSFileManager fileManager = NSFileManager.defaultManager();
            NSDirectoryEnumerator enumerator = fileManager.enumeratorAtPath(path);
            if (enumerator !is null) {
                id id;
                while ((id = enumerator.nextObject()) !is null) {
                    enumerator.skipDescendents();
                    NSString filePath = new NSString(id.id);
                    NSString fullPath = path.stringByAppendingPathComponent(filePath);
                    if (workspace.isFilePackageAtPath(fullPath)) {
                        NSBundle bundle = NSBundle.bundleWithPath(fullPath);
                        id = bundle.infoDictionary().objectForKey(CFBundleDocumentTypes);
                        if (id !is null) {
                            NSDictionary documentTypes = new NSDictionary(id.id);
                            NSEnumerator documentTypesEnumerator = documentTypes.objectEnumerator();
                            while ((id = documentTypesEnumerator.nextObject()) !is null) {
                                NSDictionary documentType = new NSDictionary(id.id);
                                id = documentType.objectForKey(CFBundleTypeExtensions);
                                if (id !is null) {
                                    supportedDocumentTypes.addObjectsFromArray(new NSArray(id.id));
                                }
                            }
                        }
                    }
                }
            }
        }
        int i = 0;
        String[] exts = new String[(int)/*64*/supportedDocumentTypes.count()];
        NSEnumerator enumerator = supportedDocumentTypes.objectEnumerator();
        id id;
        while ((id = enumerator.nextObject()) !is null) {
            String ext = new NSString(id.id).getString();
            if (!ext.equals("*")) exts[i++] = "." + ext;
        }
        if (i !is exts.length) {
            String[] temp = new String[i];
            System.arraycopy(exts, 0, temp, 0, i);
            exts = temp;
        }
        return exts;
    } finally {
        pool.release();
    }
}

static Program getProgram(NSBundle bundle) {
    NSString CFBundleName = NSString.stringWith("CFBundleName");
    NSString CFBundleDisplayName = NSString.stringWith("CFBundleDisplayName");
    NSString fullPath = bundle.bundlePath();
    NSString identifier = bundle.bundleIdentifier();
    id bundleName = bundle.objectForInfoDictionaryKey(CFBundleDisplayName);
    if (bundleName is null) {
        bundleName = bundle.objectForInfoDictionaryKey(CFBundleName);
    }
    if (bundleName is null) {
        bundleName = fullPath.lastPathComponent().stringByDeletingPathExtension();
    }
    NSString name = new NSString(bundleName.id);
    Program program = new Program();
    program.name = name.getString();
    program.fullPath = fullPath.getString();
    program.identifier = identifier !is null ? identifier.getString() : "";
    return program;
}

/**
 * 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 () {
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        Vector vector = new Vector();
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        NSArray array = new NSArray(OS.NSSearchPathForDirectoriesInDomains(OS.NSAllApplicationsDirectory, OS.NSAllDomainsMask, true));
        int count = (int)/*64*/array.count();
        for (int i = 0; i < count; i++) {
            NSString path = new NSString(array.objectAtIndex(i));
            NSFileManager fileManager = NSFileManager.defaultManager();
            NSDirectoryEnumerator enumerator = fileManager.enumeratorAtPath(path);
            if (enumerator !is null) {
                id id;
                while ((id = enumerator.nextObject()) !is null) {
                    enumerator.skipDescendents();
                    NSString fullPath = path.stringByAppendingPathComponent(new NSString(id.id));
                    if (workspace.isFilePackageAtPath(fullPath)) {
                        NSBundle bundle = NSBundle.bundleWithPath(fullPath);
                        vector.addElement(getProgram(bundle));
                    }
                }
            }
        }
        Program[] programs = new Program[vector.size()];
        vector.copyInto(programs);
        return programs;
    } finally {
        pool.release();
    }
}

/**
 * Launches the operating system executable associated with the file or
 * URL (http:// or https://).  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 or URL (http:// or https://)
 * @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 (String fileName) {
    if (fileName is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        NSString unescapedStr = NSString.stringWith("%"); //$NON-NLS-1$
        if (fileName.indexOf(':') is -1) {
            fileName = PREFIX_FILE + fileName;
        } else {
            String lowercaseName = fileName.toLowerCase ();
            if (lowercaseName.startsWith (PREFIX_HTTP) || lowercaseName.startsWith (PREFIX_HTTPS)) {
                unescapedStr = NSString.stringWith("%#"); //$NON-NLS-1$
            }
        }
        NSString fullPath = NSString.stringWith(fileName);
        int /*long*/ ptr = OS.CFURLCreateStringByAddingPercentEscapes(0, fullPath.id, unescapedStr.id, 0, OS.kCFStringEncodingUTF8);
        NSString escapedString = new NSString(ptr);
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        bool result = workspace.openURL(NSURL.URLWithString(escapedString));
        OS.CFRelease(ptr);
        return result;
    } finally {
        pool.release();
    }
}

/**
 * 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 (String fileName) {
    if (fileName is null) DWT.error(DWT.ERROR_NULL_ARGUMENT);
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        NSString fullPath = NSString.stringWith(fileName);
        if (fileName.indexOf(':') is -1) {
            return workspace.openFile(fullPath, NSString.stringWith(name));
        }
        NSString unescapedStr = NSString.stringWith("%"); //$NON-NLS-1$
        String lowercaseName = fileName.toLowerCase ();
        if (lowercaseName.startsWith (PREFIX_HTTP) || lowercaseName.startsWith (PREFIX_HTTPS)) {
            unescapedStr = NSString.stringWith("%#"); //$NON-NLS-1$
        }
        int /*long*/ ptr = OS.CFURLCreateStringByAddingPercentEscapes(0, fullPath.id, unescapedStr.id, 0, OS.kCFStringEncodingUTF8);
        NSString escapedString = new NSString(ptr);
        NSArray urls = NSArray.arrayWithObject(NSURL.URLWithString(escapedString));
        OS.CFRelease(ptr);
        return workspace.openURLs(urls, NSString.stringWith(identifier), 0, null, 0);
    } finally {
        pool.release();
    }
}

/**
 * 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 () {
    NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
    try {
        NSWorkspace workspace = NSWorkspace.sharedWorkspace();
        NSString fullPath;
        if (this.fullPath !is null) {
            fullPath = NSString.stringWith(this.fullPath);
        } else {
            fullPath = workspace.fullPathForApplication(NSString.stringWith(name));
        }
        if (fullPath !is null) {
            NSImage nsImage = workspace.iconForFile(fullPath);
            if (nsImage !is null) {
                NSSize size = new NSSize();
                size.width = size.height = 16;
                nsImage.setSize(size);
                NSBitmapImageRep imageRep = null;
                NSImageRep rep = nsImage.bestRepresentationForDevice(null);
                if (rep.isKindOfClass(OS.class_NSBitmapImageRep)) { 
                    imageRep = new NSBitmapImageRep(rep.id);
                }
                if (imageRep !is null) {
                    int width = (int)/*64*/imageRep.pixelsWide();
                    int height = (int)/*64*/imageRep.pixelsHigh();
                    int bpr = (int)/*64*/imageRep.bytesPerRow();
                    int bpp = (int)/*64*/imageRep.bitsPerPixel();
                    int dataSize = height * bpr;
                    byte[] srcData = new byte[dataSize];
                    OS.memmove(srcData, imageRep.bitmapData(), dataSize);
                    //TODO check color info
                    PaletteData palette = new PaletteData(0xFF000000, 0xFF0000, 0xFF00);
                    ImageData data = new ImageData(width, height, bpp, palette, 4, srcData);
                    data.bytesPerLine = bpr;
                    data.alphaData = new byte[width * height];
                    for (int i = 3, o = 0; i < srcData.length; i+= 4, o++) {
                        data.alphaData[o] = srcData[i];
                    }
                    return data;
                }
            }
        }
        return null;
    } finally {
        pool.release();
    }
}

/**
 * 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 String getName () {
    return name;
}

/**
 * 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 bool equals(Object other) {
    if (this is other) return true;
    if (other instanceof Program) {
        final Program program = (Program) other;
        return name.equals(program.name);
    }
    return false;
}

/**
 * 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 int hashCode() {
    return name.hashCode();
}

/**
 * Returns a string containing a concise, human-readable
 * description of the receiver.
 *
 * @return a string representation of the program
 */
public String toString () {
    return "Program {" + name + "}";
}

}