1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.program.Program; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.graphics.ImageData; 17 import org.eclipse.swt.internal.Compatibility; 18 import org.eclipse.swt.internal.Converter; 19 import org.eclipse.swt.internal.Library; 20 import org.eclipse.swt.internal.gtk.OS; 21 import org.eclipse.swt.widgets.Display; 22 import org.eclipse.swt.widgets.Event; 23 import org.eclipse.swt.widgets.Listener; 24 import java.lang.all; 25 import java.nonstandard.SharedLib; 26 27 version(Tango){ 28 static import tango.core.Array; 29 static import tango.io.device.File; 30 static import tango.io.stream.Lines; 31 } else { // Phobos 32 static import std..string; 33 static import std.file; 34 static import std.algorithm; 35 } 36 37 private extern(C) { 38 alias int GnomeIconLookupResultFlags; 39 alias int GnomeIconLookupFlags; 40 GnomeIconTheme *gnome_icon_theme_new (); 41 char *gnome_icon_lookup ( 42 GtkIconTheme *icon_theme, 43 void *thumbnail_factory, 44 char *file_uri, 45 char *custom_icon, 46 void *file_info, 47 char *mime_type, 48 GnomeIconLookupFlags flags, 49 GnomeIconLookupResultFlags *result); 50 int gnome_vfs_init(); 51 char* gnome_icon_theme_lookup_icon(GnomeIconTheme *theme,char *icon_name,int size, GnomeIconData **icon_data, int *base_size) ; 52 53 alias void GnomeIconTheme; 54 alias void GnomeIconData; 55 56 struct GnomeVFSMimeApplication{ 57 /*< public > */ 58 char *id; 59 char *name; 60 61 /*< private > */ 62 char *command; 63 int can_open_multiple_files; 64 int expects_uris; 65 GList *supported_uri_schemes; 66 int requires_terminal; 67 68 /* Padded to avoid future breaks in ABI compatibility */ 69 void * reserved1; 70 71 void * priv; 72 } 73 } 74 75 struct GNOME { 76 private static extern(C){ 77 enum { 78 GNOME_ICON_LOOKUP_FLAGS_NONE = 0, 79 GNOME_ICON_LOOKUP_FLAGS_EMBEDDING_TEXT = 1<<0, 80 GNOME_ICON_LOOKUP_FLAGS_SHOW_SMALL_IMAGES_AS_THEMSELVES = 1<<1, 81 GNOME_ICON_LOOKUP_FLAGS_ALLOW_SVG_AS_THEMSELVES = 1<<2 82 } 83 enum { 84 GNOME_VFS_MAKE_URI_DIR_NONE = 0, 85 GNOME_VFS_MAKE_URI_DIR_HOMEDIR = 1 << 0, 86 GNOME_VFS_MAKE_URI_DIR_CURRENT = 1 << 1 87 } 88 alias int GnomeVFSMakeURIDirs; 89 enum { 90 GNOME_VFS_OK, 91 GNOME_VFS_ERROR_NOT_FOUND, 92 GNOME_VFS_ERROR_GENERIC, 93 GNOME_VFS_ERROR_INTERNAL, 94 GNOME_VFS_ERROR_BAD_PARAMETERS, 95 GNOME_VFS_ERROR_NOT_SUPPORTED, 96 GNOME_VFS_ERROR_IO, 97 GNOME_VFS_ERROR_CORRUPTED_DATA, 98 GNOME_VFS_ERROR_WRONG_FORMAT, 99 GNOME_VFS_ERROR_BAD_FILE, 100 GNOME_VFS_ERROR_TOO_BIG, 101 GNOME_VFS_ERROR_NO_SPACE, 102 GNOME_VFS_ERROR_READ_ONLY, 103 GNOME_VFS_ERROR_INVALID_URI, 104 GNOME_VFS_ERROR_NOT_OPEN, 105 GNOME_VFS_ERROR_INVALID_OPEN_MODE, 106 GNOME_VFS_ERROR_ACCESS_DENIED, 107 GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES, 108 GNOME_VFS_ERROR_EOF, 109 GNOME_VFS_ERROR_NOT_A_DIRECTORY, 110 GNOME_VFS_ERROR_IN_PROGRESS, 111 GNOME_VFS_ERROR_INTERRUPTED, 112 GNOME_VFS_ERROR_FILE_EXISTS, 113 GNOME_VFS_ERROR_LOOP, 114 GNOME_VFS_ERROR_NOT_PERMITTED, 115 GNOME_VFS_ERROR_IS_DIRECTORY, 116 GNOME_VFS_ERROR_NO_MEMORY, 117 GNOME_VFS_ERROR_HOST_NOT_FOUND, 118 GNOME_VFS_ERROR_INVALID_HOST_NAME, 119 GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS, 120 GNOME_VFS_ERROR_LOGIN_FAILED, 121 GNOME_VFS_ERROR_CANCELLED, 122 GNOME_VFS_ERROR_DIRECTORY_BUSY, 123 GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY, 124 GNOME_VFS_ERROR_TOO_MANY_LINKS, 125 GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM, 126 GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM, 127 GNOME_VFS_ERROR_NAME_TOO_LONG, 128 GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE, 129 GNOME_VFS_ERROR_SERVICE_OBSOLETE, 130 GNOME_VFS_ERROR_PROTOCOL_ERROR, 131 GNOME_VFS_ERROR_NO_MASTER_BROWSER, 132 GNOME_VFS_ERROR_NO_DEFAULT, 133 GNOME_VFS_ERROR_NO_HANDLER, 134 GNOME_VFS_ERROR_PARSE, 135 GNOME_VFS_ERROR_LAUNCH, 136 GNOME_VFS_ERROR_TIMEOUT, 137 GNOME_VFS_ERROR_NAMESERVER, 138 GNOME_VFS_ERROR_LOCKED, 139 GNOME_VFS_ERROR_DEPRECATED_FUNCTION, 140 GNOME_VFS_ERROR_INVALID_FILENAME, 141 GNOME_VFS_ERROR_NOT_A_SYMBOLIC_LINK, 142 GNOME_VFS_NUM_ERRORS 143 } 144 alias int GnomeVFSResult; 145 146 enum { 147 GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS, 148 GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_PATHS, 149 GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS_FOR_NON_FILES 150 } 151 152 alias GtkIconTheme GnomeIconTheme; 153 alias .gnome_icon_theme_lookup_icon gnome_icon_theme_lookup_icon; 154 alias .gnome_vfs_init gnome_vfs_init; 155 alias .gnome_icon_lookup gnome_icon_lookup; 156 alias .gnome_icon_theme_new gnome_icon_theme_new; 157 158 159 GnomeVFSMimeApplication * function ( char *mime_type ) gnome_vfs_mime_get_default_application; 160 char* function(char*, int ) gnome_vfs_make_uri_from_input_with_dirs; 161 GnomeVFSResult function( GnomeVFSMimeApplication*, GList*) gnome_vfs_mime_application_launch; 162 void function(GnomeVFSMimeApplication*) gnome_vfs_mime_application_free; 163 GList* function(char*) gnome_vfs_mime_get_extensions_list; 164 void function(GList*) gnome_vfs_mime_extensions_list_free; 165 void function(GList*) gnome_vfs_mime_registered_mime_type_list_free; 166 GnomeVFSResult function(char*) gnome_vfs_url_show; 167 char* function(char*) gnome_vfs_make_uri_from_input; 168 GList* function() gnome_vfs_get_registered_mime_types; 169 char* function(char*) gnome_vfs_mime_type_from_name; 170 } 171 static Symbol[] symbols; 172 static this () { 173 symbols = [ 174 Symbol("gnome_vfs_mime_get_default_application", cast(void**)&gnome_vfs_mime_get_default_application ), 175 Symbol("gnome_vfs_make_uri_from_input_with_dirs", cast(void**)&gnome_vfs_make_uri_from_input_with_dirs ), 176 Symbol("gnome_vfs_mime_application_launch", cast(void**)&gnome_vfs_mime_application_launch ), 177 Symbol("gnome_vfs_mime_application_free", cast(void**)&gnome_vfs_mime_application_free ), 178 Symbol("gnome_vfs_url_show", cast(void**)&gnome_vfs_url_show ), 179 Symbol("gnome_vfs_make_uri_from_input", cast(void**)&gnome_vfs_make_uri_from_input ), 180 Symbol("gnome_vfs_get_registered_mime_types", cast(void**)&gnome_vfs_get_registered_mime_types ), 181 Symbol("gnome_vfs_mime_get_extensions_list", cast(void**)&gnome_vfs_mime_get_extensions_list ), 182 Symbol("gnome_vfs_mime_extensions_list_free", cast(void**)&gnome_vfs_mime_extensions_list_free ), 183 Symbol("gnome_vfs_mime_registered_mime_type_list_free", cast(void**)&gnome_vfs_mime_registered_mime_type_list_free ), 184 Symbol("gnome_vfs_mime_type_from_name", cast(void**)&gnome_vfs_mime_type_from_name ) 185 ]; 186 } 187 } 188 189 /** 190 * Instances of this class represent programs and 191 * their associated file extensions in the operating 192 * system. 193 * 194 * @see <a href="http://www.eclipse.org/swt/snippets/#program">Program snippets</a> 195 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 196 */ 197 public final class Program { 198 String name; 199 String command; 200 String iconPath; 201 Display display; 202 203 /* Gnome specific 204 * true if command expects a URI 205 * false if expects a path 206 */ 207 bool gnomeExpectUri; 208 209 static ptrdiff_t cdeShell; 210 211 static const String[] CDE_ICON_EXT = [ ".m.pm"[], ".l.pm", ".s.pm", ".t.pm" ]; 212 static const String[] CDE_MASK_EXT = [ ".m_m.bm"[], ".l_m.bm", ".s_m.bm", ".t_m.bm" ]; 213 static const String DESKTOP_DATA = "Program_DESKTOP"; 214 static const String ICON_THEME_DATA = "Program_GNOME_ICON_THEME"; 215 static const String PREFIX_HTTP = "http://"; //$NON-NLS-1$ 216 static const String PREFIX_HTTPS = "https://"; //$NON-NLS-1$ 217 static const int DESKTOP_UNKNOWN = 0; 218 static const int DESKTOP_GNOME = 1; 219 static const int DESKTOP_GNOME_24 = 2; 220 static const int DESKTOP_CDE = 3; 221 static const int PREFERRED_ICON_SIZE = 16; 222 223 /** 224 * Prevents uninitialized instances from being created outside the package. 225 */ 226 this() { 227 } 228 229 /* Determine the desktop for the given display. */ 230 static int getDesktop(Display display) { 231 if (display is null) return DESKTOP_UNKNOWN; 232 Integer desktopValue = cast(Integer)display.getData(DESKTOP_DATA); 233 if (desktopValue !is null) return desktopValue.intValue; 234 int desktop = DESKTOP_UNKNOWN; 235 236 /* Get the list of properties on the root window. */ 237 void* xDisplay = OS.GDK_DISPLAY(); 238 size_t rootWindow = OS.XDefaultRootWindow(xDisplay); 239 int numProp; 240 size_t* propList = OS.XListProperties(xDisplay, rootWindow, &numProp); 241 size_t[] property = new size_t[numProp]; 242 if (propList !is null) { 243 property[ 0 .. numProp ] = propList[ 0 .. numProp ]; 244 OS.XFree(propList); 245 } 246 247 /* 248 * Feature in Linux Desktop. There is currently no official way to 249 * determine whether the Gnome window manager or gnome-vfs is 250 * available. Earlier versions including Red Hat 9 and Suse 9 provide 251 * a documented Gnome specific property on the root window 252 * WIN_SUPPORTING_WM_CHECK. This property is no longer supported in newer 253 * versions such as Fedora Core 2. 254 * The workaround is to simply check that the window manager is a 255 * compliant one (property _NET_SUPPORTING_WM_CHECK) and to attempt to load 256 * our native library that depends on gnome-vfs. 257 */ 258 if (desktop is DESKTOP_UNKNOWN) { 259 String gnomeName = "_NET_SUPPORTING_WM_CHECK"; 260 ptrdiff_t gnome = OS.XInternAtom(xDisplay, gnomeName.ptr, true); 261 if (gnome !is OS.None && (OS.GTK_VERSION >= OS.buildVERSION (2, 2, 0)) && gnome_init()) { 262 desktop = DESKTOP_GNOME; 263 int icon_theme = cast(int)GNOME.gnome_icon_theme_new(); 264 display.setData(ICON_THEME_DATA, new Integer(icon_theme)); 265 display.addListener(SWT.Dispose, new class(display) Listener { 266 Display display; 267 this( Display display ){ this.display = display; } 268 public void handleEvent(Event event) { 269 Integer gnomeIconTheme = cast(Integer)display.getData(ICON_THEME_DATA); 270 if (gnomeIconTheme is null) return; 271 display.setData(ICON_THEME_DATA, null); 272 /* 273 * Note. gnome_icon_theme_new uses g_object_new to allocate the 274 * data it returns. Use g_object_unref to free the pointer it returns. 275 */ 276 if (gnomeIconTheme.intValue !is 0) OS.g_object_unref( cast(void*)gnomeIconTheme.intValue); 277 } 278 }); 279 /* Check for libgnomevfs-2 version 2.4 */ 280 String buffer = "libgnomevfs-2.so.0"; 281 void delegate (void*) dg = (void*){ desktop = DESKTOP_GNOME_24; }; 282 SharedLib.tryUseSymbol( "gnome_vfs_url_show", buffer, dg); 283 if( desktop is DESKTOP_GNOME_24 ){ 284 SharedLib.loadLibSymbols( GNOME.symbols, buffer ); 285 } 286 } 287 } 288 289 // PORTING CDE not supported 290 /+ 291 /* 292 * On CDE, the atom below may exist without DTWM running. If the atom 293 * below is defined, the CDE database exists and the available 294 * applications can be queried. 295 */ 296 if (desktop is DESKTOP_UNKNOWN) { 297 String cdeName = "_DT_SM_PREFERENCES"; 298 ptrdiff_t cde = OS.XInternAtom(xDisplay, cdeName.ptr, true); 299 for (int index = 0; desktop is DESKTOP_UNKNOWN && index < property.length; index++) { 300 if (property[index] is OS.None) continue; /* do not match atoms that do not exist */ 301 if (property[index] is cde && cde_init(display)) desktop = DESKTOP_CDE; 302 } 303 } 304 +/ 305 306 display.setData(DESKTOP_DATA, new Integer(desktop)); 307 return desktop; 308 } 309 310 // PORTING CDE not supported 311 /+ 312 bool cde_execute(String fileName) { 313 /* Use the character encoding for the default locale */ 314 char* action = toStringz(command); 315 char* ptr = cast(char*)OS.g_malloc(fileName.length+1); 316 ptr[ 0 .. fileName.length ] = fileName; 317 ptr[ fileName.length ] = 0; 318 DtActionArg args = new DtActionArg(); 319 args.argClass = CDE.DtACTION_FILE; 320 args.name = ptr; 321 long actionID = CDE.DtActionInvoke(cdeShell, action, args, 1, null, null, null, 1, 0, 0); 322 OS.g_free(ptr); 323 return actionID !is 0; 324 } 325 326 static String cde_getAction(String dataType) { 327 String action = null; 328 String actions = cde_getAttribute(dataType, CDE.DtDTS_DA_ACTION_LIST); 329 if (actions !is null) { 330 int index = actions.indexOf("Open"); 331 if (index !is -1) { 332 action = actions.substring(index, index + 4); 333 } else { 334 index = actions.indexOf(","); 335 action = index !is -1 ? actions.substring(0, index) : actions; 336 } 337 } 338 return action; 339 } 340 341 static String cde_getAttribute(String dataType, String attrName) { 342 /* Use the character encoding for the default locale */ 343 byte[] dataTypeBuf = Converter.wcsToMbcs(null, dataType, true); 344 byte[] attrNameBuf = Converter.wcsToMbcs(null, attrName, true); 345 byte[] optNameBuf = null; 346 ptrdiff_t attrValue = CDE.DtDtsDataTypeToAttributeValue(dataTypeBuf, attrNameBuf, optNameBuf); 347 if (attrValue is 0) return null; 348 int length = OS.strlen(attrValue); 349 byte[] attrValueBuf = new byte[length]; 350 OS.memmove(attrValueBuf, attrValue, length); 351 CDE.DtDtsFreeAttributeValue(attrValue); 352 /* Use the character encoding for the default locale */ 353 return new String(Converter.mbcsToWcs(null, attrValueBuf)); 354 } 355 356 static String[][ String ] cde_getDataTypeInfo() { 357 String[][ String ] dataTypeInfo; 358 int index; 359 ptrdiff_t dataTypeList = CDE.DtDtsDataTypeNames(); 360 if (dataTypeList !is 0) { 361 /* For each data type name in the list */ 362 index = 0; 363 ptrdiff_t [] dataType = new ptrdiff_t [1]; 364 OS.memmove(dataType, dataTypeList + (index++ * 4), 4); 365 while (dataType[0] !is 0) { 366 int length = OS.strlen(dataType[0]); 367 byte[] dataTypeBuf = new byte[length]; 368 OS.memmove(dataTypeBuf, dataType[0], length); 369 /* Use the character encoding for the default locale */ 370 String dataTypeName = new String(Converter.mbcsToWcs(null, dataTypeBuf)); 371 372 /* The data type is valid if it is not an action, and it has an extension and an action. */ 373 String extension = cde_getExtension(dataTypeName); 374 if (!CDE.DtDtsDataTypeIsAction(dataTypeBuf) && 375 extension !is null && cde_getAction(dataTypeName) !is null) { 376 String[] exts; 377 exts ~= extension; 378 dataTypeInfo[ dataTypeName ] = exts; 379 } 380 OS.memmove(dataType, dataTypeList + (index++ * 4), 4); 381 } 382 CDE.DtDtsFreeDataTypeNames(dataTypeList); 383 } 384 385 return dataTypeInfo; 386 } 387 388 static String cde_getExtension(String dataType) { 389 String fileExt = cde_getAttribute(dataType, CDE.DtDTS_DA_NAME_TEMPLATE); 390 if (fileExt is null || fileExt.indexOf("%s.") is -1) return null; 391 int dot = fileExt.indexOf("."); 392 return fileExt.substring(dot); 393 } 394 395 /** 396 * CDE - Get Image Data 397 * 398 * This method returns the image data of the icon associated with 399 * the data type. Since CDE supports multiple sizes of icons, several 400 * attempts are made to locate an icon of the desired size and format. 401 * CDE supports the sizes: tiny, small, medium and large. The best 402 * search order is medium, large, small and then tiny. Althoug CDE supports 403 * colour and monochrome bitmaps, only colour icons are tried. (The order is 404 * defined by the cdeIconExt and cdeMaskExt arrays above.) 405 */ 406 ImageData cde_getImageData() { 407 // TODO 408 return null; 409 } 410 411 static String cde_getMimeType(String extension) { 412 String mimeType = null; 413 String[][ String ] mimeInfo = cde_getDataTypeInfo(); 414 if (mimeInfo is null) return null; 415 String[] keys = mimeInfo.keys(); 416 int keyIdx = 0; 417 while (mimeType is null && keyIdx < keys.length ) { 418 String type = keys[ keyIdx ]; 419 String[] mimeExts = mimeInfo[type]; 420 for (int index = 0; index < mimeExts.length; index++){ 421 if (extension.equals(mimeExts[index])) { 422 mimeType = type; 423 break; 424 } 425 } 426 keyIdx++; 427 } 428 return mimeType; 429 } 430 431 static Program cde_getProgram(Display display, String mimeType) { 432 Program program = new Program(); 433 program.display = display; 434 program.name = mimeType; 435 program.command = cde_getAction(mimeType); 436 program.iconPath = cde_getAttribute(program.name, CDE.DtDTS_DA_ICON); 437 return program; 438 } 439 440 static bool cde_init(Display display) { 441 try { 442 Library.loadLibrary("swt-cde"); 443 } catch (Throwable e) { 444 return false; 445 } 446 447 /* Use the character encoding for the default locale */ 448 CDE.XtToolkitInitialize(); 449 ptrdiff_t xtContext = CDE.XtCreateApplicationContext (); 450 ptrdiff_t xDisplay = OS.GDK_DISPLAY(); 451 byte[] appName = Converter.wcsToMbcs(null, "CDE", true); 452 byte[] appClass = Converter.wcsToMbcs(null, "CDE", true); 453 ptrdiff_t [] argc = [0]; 454 CDE.XtDisplayInitialize(xtContext, xDisplay, appName, appClass, 0, 0, argc, 0); 455 ptrdiff_t widgetClass = CDE.topLevelShellWidgetClass (); 456 cdeShell = CDE.XtAppCreateShell (appName, appClass, widgetClass, xDisplay, null, 0); 457 CDE.XtSetMappedWhenManaged (cdeShell, false); 458 CDE.XtResizeWidget (cdeShell, 10, 10, 0); 459 CDE.XtRealizeWidget (cdeShell); 460 bool initOK = CDE.DtAppInitialize(xtContext, xDisplay, cdeShell, appName, appName); 461 if (initOK) CDE.DtDbLoad(); 462 return initOK; 463 } 464 +/ 465 466 static String[] parseCommand(String cmd) { 467 String[] args; 468 int sIndex = 0; 469 int eIndex; 470 while (sIndex < cmd.length) { 471 /* Trim initial white space of argument. */ 472 while (sIndex < cmd.length && Compatibility.isWhitespace(cmd.charAt(sIndex))) { 473 sIndex++; 474 } 475 if (sIndex < cmd.length) { 476 /* If the command is a quoted string */ 477 if (cmd.charAt(sIndex) is '"' || cmd.charAt(sIndex) is '\'') { 478 /* Find the terminating quote (or end of line). 479 * This code currently does not handle escaped characters (e.g., " a\"b"). 480 */ 481 eIndex = sIndex + 1; 482 while (eIndex < cmd.length && cmd.charAt(eIndex) !is cmd.charAt(sIndex)) eIndex++; 483 if (eIndex >= cmd.length) { 484 /* The terminating quote was not found 485 * Add the argument as is with only one initial quote. 486 */ 487 args ~= cmd.substring(sIndex, eIndex); 488 } else { 489 /* Add the argument, trimming off the quotes. */ 490 args ~= cmd.substring(sIndex + 1, eIndex); 491 } 492 sIndex = eIndex + 1; 493 } 494 else { 495 /* Use white space for the delimiters. */ 496 eIndex = sIndex; 497 while (eIndex < cmd.length && !Compatibility.isWhitespace(cmd.charAt(eIndex))) eIndex++; 498 args ~= cmd.substring(sIndex, eIndex); 499 sIndex = eIndex + 1; 500 } 501 } 502 } 503 504 String[] strings = new String[args.length]; 505 for (int index =0; index < args.length; index++) { 506 strings[index] = args[index]; 507 } 508 return strings; 509 } 510 511 /** 512 * GNOME 2.4 - Execute the program for the given file. 513 */ 514 bool gnome_24_execute(String fileName) { 515 char* mimeTypeBuffer = toStringz(name); 516 auto ptr = GNOME.gnome_vfs_mime_get_default_application(mimeTypeBuffer); 517 char* fileNameBuffer = toStringz(fileName); 518 char* uri = GNOME.gnome_vfs_make_uri_from_input_with_dirs(fileNameBuffer, GNOME.GNOME_VFS_MAKE_URI_DIR_CURRENT); 519 GList* list = OS.g_list_append( null, uri); 520 int result = GNOME.gnome_vfs_mime_application_launch(ptr, list); 521 GNOME.gnome_vfs_mime_application_free(ptr); 522 OS.g_free(uri); 523 OS.g_list_free(list); 524 return result is GNOME.GNOME_VFS_OK; 525 } 526 527 /** 528 * GNOME 2.4 - Launch the default program for the given file. 529 */ 530 static bool gnome_24_launch(String fileName) { 531 char* fileNameBuffer = toStringz(fileName); 532 char* uri = GNOME.gnome_vfs_make_uri_from_input_with_dirs(fileNameBuffer, GNOME.GNOME_VFS_MAKE_URI_DIR_CURRENT); 533 int result = GNOME.gnome_vfs_url_show(uri); 534 OS.g_free(uri); 535 return (result is GNOME.GNOME_VFS_OK); 536 } 537 538 /** 539 * GNOME 2.2 - Execute the program for the given file. 540 */ 541 bool gnome_execute(String fileName) { 542 if (gnomeExpectUri) { 543 /* Convert the given path into a URL */ 544 char* fileNameBuffer = toStringz(fileName); 545 char* uri = GNOME.gnome_vfs_make_uri_from_input(fileNameBuffer); 546 if (uri !is null) { 547 fileName = fromStringz( uri )._idup(); 548 OS.g_free(uri); 549 } 550 } 551 552 /* Parse the command into its individual arguments. */ 553 String[] args = parseCommand(command); 554 int fileArg = -1; 555 int index; 556 for (index = 0; index < args.length; index++) { 557 int j = args[index].indexOf("%f"); 558 if (j !is -1) { 559 String value = args[index]; 560 fileArg = index; 561 args[index] = value.substring(0, j) ~ fileName ~ value.substring(j + 2); 562 } 563 } 564 565 /* If a file name was given but the command did not have "%f" */ 566 if ((fileName.length > 0) && (fileArg < 0)) { 567 String[] newArgs = new String[args.length + 1]; 568 for (index = 0; index < args.length; index++) newArgs[index] = args[index]; 569 newArgs[args.length] = fileName; 570 args = newArgs; 571 } 572 573 /* Execute the command. */ 574 try { 575 Compatibility.exec(args); 576 } catch (IOException e) { 577 return false; 578 } 579 return true; 580 } 581 582 /** 583 * GNOME - Get Image Data 584 * 585 */ 586 ImageData gnome_getImageData() { 587 if (iconPath is null) return null; 588 try { 589 return new ImageData(iconPath); 590 } catch (Exception e) {} 591 return null; 592 } 593 594 /++ 595 + SWT Extension 596 + This is a temporary workaround until SWT will get the real implementation. 597 +/ 598 static String[][ String ] gnome24_getMimeInfo() { 599 version(Tango){ 600 scope file = new tango.io.device.File.File("/usr/share/mime/globs"); 601 scope it = new tango.io.stream.Lines.Lines!(char)( file ); 602 } else { // Phobos 603 scope it = std..string.splitLines( cast(String)std.file.read("/usr/share/mime/globs")); 604 } 605 // process file one line at a time 606 String[][ String ] mimeInfo; 607 foreach (line; it ){ 608 int colon = line.indexOf(':'); 609 if( colon is line.length ){ 610 continue; 611 } 612 if( line.length < colon+3 || line[colon+1 .. colon+3 ] != "*." ){ 613 continue; 614 } 615 String mimeType = line[0..colon]._idup(); 616 String ext = line[colon+3 .. $]._idup(); 617 if( auto exts = mimeType in mimeInfo ){ 618 mimeInfo[ mimeType ] = *exts ~ ext; 619 } 620 else{ 621 mimeInfo[ mimeType ] = [ ext ]; 622 } 623 } 624 return mimeInfo; 625 } 626 /** 627 * GNOME - Get mime types 628 * 629 * Obtain the registered mime type information and 630 * return it in a map. The key of each entry 631 * in the map is the mime type name. The value is 632 * a vector of the associated file extensions. 633 */ 634 static String[][ String ] gnome_getMimeInfo() { 635 String[][ String ] mimeInfo; 636 GList* mimeList = GNOME.gnome_vfs_get_registered_mime_types(); 637 GList* mimeElement = mimeList; 638 while (mimeElement !is null) { 639 auto mimePtr = cast(char*) OS.g_list_data(mimeElement); 640 String mimeTypeBuffer = fromStringz(mimePtr)._idup(); 641 String mimeType = mimeTypeBuffer;//new String(Converter.mbcsToWcs(null, mimeTypeBuffer)); 642 GList* extensionList = GNOME.gnome_vfs_mime_get_extensions_list(mimePtr); 643 if (extensionList !is null) { 644 String[] extensions; 645 GList* extensionElement = extensionList; 646 while (extensionElement !is null) { 647 char* extensionPtr = cast(char*) OS.g_list_data(extensionElement); 648 String extensionBuffer = fromStringz(extensionPtr)._idup(); 649 String extension = extensionBuffer; 650 extension = '.' ~ extension; 651 extensions ~= extension; 652 extensionElement = OS.g_list_next(extensionElement); 653 } 654 GNOME.gnome_vfs_mime_extensions_list_free(extensionList); 655 if (extensions.length > 0) mimeInfo[ mimeType ] = extensions; 656 } 657 mimeElement = OS.g_list_next(mimeElement); 658 } 659 if (mimeList !is null) GNOME.gnome_vfs_mime_registered_mime_type_list_free(mimeList); 660 return mimeInfo; 661 } 662 663 static String gnome_getMimeType(String extension) { 664 String mimeType = null; 665 String fileName = "swt" ~ extension; 666 char* extensionBuffer = toStringz(fileName); 667 char* typeName = GNOME.gnome_vfs_mime_type_from_name(extensionBuffer); 668 if (typeName !is null) { 669 mimeType = fromStringz(typeName)._idup(); 670 } 671 return mimeType; 672 } 673 674 static Program gnome_getProgram(Display display, String mimeType) { 675 Program program = null; 676 char* mimeTypeBuffer = toStringz(mimeType); 677 GnomeVFSMimeApplication* ptr = GNOME.gnome_vfs_mime_get_default_application(mimeTypeBuffer); 678 if (ptr !is null) { 679 program = new Program(); 680 program.display = display; 681 program.name = mimeType; 682 GnomeVFSMimeApplication* application = ptr; 683 String buffer = fromStringz(application.command)._idup(); 684 program.command = buffer; 685 program.gnomeExpectUri = application.expects_uris is GNOME.GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS; 686 687 buffer = (fromStringz( application.id) ~ '\0')._idup(); 688 Integer gnomeIconTheme = cast(Integer)display.getData(ICON_THEME_DATA); 689 char* icon_name = GNOME.gnome_icon_lookup( cast(GtkIconTheme*) gnomeIconTheme.intValue, null, null, cast(char*)buffer.ptr, null, mimeTypeBuffer, 690 GNOME.GNOME_ICON_LOOKUP_FLAGS_NONE, null); 691 char* path = null; 692 if (icon_name !is null) path = GNOME.gnome_icon_theme_lookup_icon(cast(GtkIconTheme*)gnomeIconTheme.intValue, icon_name, PREFERRED_ICON_SIZE, null, null); 693 if (path !is null) { 694 program.iconPath = fromStringz( path)._idup(); 695 OS.g_free(path); 696 } 697 if (icon_name !is null) OS.g_free(icon_name); 698 GNOME.gnome_vfs_mime_application_free(ptr); 699 } 700 return program; 701 } 702 703 static bool gnome_init() { 704 return cast(bool) GNOME.gnome_vfs_init(); 705 } 706 707 /** 708 * Finds the program that is associated with an extension. 709 * The extension may or may not begin with a '.'. Note that 710 * a <code>Display</code> must already exist to guarantee that 711 * this method returns an appropriate result. 712 * 713 * @param extension the program extension 714 * @return the program or <code>null</code> 715 * 716 */ 717 public static Program findProgram(String extension) { 718 return findProgram(Display.getCurrent(), extension); 719 } 720 721 /* 722 * API: When support for multiple displays is added, this method will 723 * become public and the original method above can be deprecated. 724 */ 725 static Program findProgram(Display display, String extension) { 726 // SWT extension: allow null for zero length string 727 //if (extension is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 728 if (extension.length is 0) return null; 729 if (extension.charAt(0) !is '.') extension = "." ~ extension; 730 int desktop = getDesktop(display); 731 String mimeType = null; 732 switch (desktop) { 733 case DESKTOP_GNOME_24: 734 case DESKTOP_GNOME: mimeType = gnome_getMimeType(extension); break; 735 //case DESKTOP_CDE: mimeType = cde_getMimeType(extension); break; 736 default: 737 } 738 if (mimeType is null) return null; 739 Program program = null; 740 switch (desktop) { 741 case DESKTOP_GNOME_24: 742 case DESKTOP_GNOME: program = gnome_getProgram(display, mimeType); break; 743 //case DESKTOP_CDE: program = cde_getProgram(display, mimeType); break; 744 default: 745 } 746 return program; 747 } 748 749 /** 750 * Answer all program extensions in the operating system. Note 751 * that a <code>Display</code> must already exist to guarantee 752 * that this method returns an appropriate result. 753 * 754 * @return an array of extensions 755 */ 756 public static String[] getExtensions() { 757 return getExtensions(Display.getCurrent()); 758 } 759 760 /* 761 * API: When support for multiple displays is added, this method will 762 * become public and the original method above can be deprecated. 763 */ 764 static String[] getExtensions(Display display) { 765 int desktop = getDesktop(display); 766 String[][ String ] mimeInfo = null; 767 switch (desktop) { 768 case DESKTOP_GNOME_24: mimeInfo = gnome24_getMimeInfo(); break; 769 case DESKTOP_GNOME: mimeInfo = gnome_getMimeInfo(); break; 770 //case DESKTOP_CDE: mimeInfo = cde_getDataTypeInfo(); break; 771 default: 772 } 773 if (mimeInfo is null) return null; 774 775 /* Create a unique set of the file extensions. */ 776 String[] extensions; 777 String[] keys = mimeInfo.keys; 778 int keyIdx = 0; 779 while ( keyIdx < keys.length ) { 780 String mimeType = keys[ keyIdx ]; 781 String[] mimeExts = mimeInfo[mimeType]; 782 for (int index = 0; index < mimeExts.length; index++){ 783 version(Tango){ 784 bool contains = cast(bool)tango.core.Array.contains(extensions, mimeExts[index]); 785 } else { // Phobos 786 bool contains = std.algorithm.canFind(extensions, mimeExts[index]); 787 } 788 if (!contains) { 789 extensions ~= mimeExts[index]; 790 } 791 } 792 keyIdx++; 793 } 794 795 /* Return the list of extensions. */ 796 String[] extStrings = new String[]( extensions.length ); 797 for (int index = 0; index < extensions.length; index++) { 798 extStrings[index] = extensions[index]; 799 } 800 return extStrings; 801 } 802 803 /** 804 * Answers all available programs in the operating system. Note 805 * that a <code>Display</code> must already exist to guarantee 806 * that this method returns an appropriate result. 807 * 808 * @return an array of programs 809 */ 810 public static Program[] getPrograms() { 811 return getPrograms(Display.getCurrent()); 812 } 813 814 /* 815 * API: When support for multiple displays is added, this method will 816 * become public and the original method above can be deprecated. 817 */ 818 static Program[] getPrograms(Display display) { 819 int desktop = getDesktop(display); 820 String[][ String ] mimeInfo = null; 821 switch (desktop) { 822 case DESKTOP_GNOME_24: break; 823 case DESKTOP_GNOME: mimeInfo = gnome_getMimeInfo(); break; 824 //case DESKTOP_CDE: mimeInfo = cde_getDataTypeInfo(); break; 825 default: 826 } 827 if (mimeInfo is null) return new Program[0]; 828 Program[] programs; 829 String[] keys = mimeInfo.keys; 830 int keyIdx = 0; 831 while ( keyIdx < keys.length ) { 832 String mimeType = keys[ keyIdx ]; 833 Program program = null; 834 switch (desktop) { 835 case DESKTOP_GNOME: program = gnome_getProgram(display, mimeType); break; 836 //case DESKTOP_CDE: program = cde_getProgram(display, mimeType); break; 837 default: 838 } 839 if (program !is null) programs ~= program; 840 keyIdx++; 841 } 842 Program[] programList = new Program[programs.length]; 843 for (int index = 0; index < programList.length; index++) { 844 programList[index] = programs[index]; 845 } 846 return programList; 847 } 848 849 /** 850 * Launches the operating system executable associated with the file or 851 * URL (http:// or https://). If the file is an executable then the 852 * executable is launched. Note that a <code>Display</code> must already 853 * exist to guarantee that this method returns an appropriate result. 854 * 855 * @param fileName the file or program name or URL (http:// or https://) 856 * @return <code>true</code> if the file is launched, otherwise <code>false</code> 857 * 858 * @exception IllegalArgumentException <ul> 859 * <li>ERROR_NULL_ARGUMENT when fileName is null</li> 860 * </ul> 861 */ 862 public static bool launch(String fileName) { 863 return launch(Display.getCurrent(), fileName); 864 } 865 866 /* 867 * API: When support for multiple displays is added, this method will 868 * become public and the original method above can be deprecated. 869 */ 870 static bool launch (Display display, String fileName) { 871 if (fileName is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 872 switch (getDesktop (display)) { 873 case DESKTOP_GNOME_24: 874 if (gnome_24_launch (fileName)) return true; 875 goto default; 876 default: 877 int index = fileName.lastIndexOf ('.'); 878 if (index !is -1) { 879 String extension = fileName.substring (index); 880 Program program = Program.findProgram (display, extension); 881 if (program !is null && program.execute (fileName)) return true; 882 } 883 String lowercaseName = fileName.toLowerCase (); 884 if (lowercaseName.startsWith (PREFIX_HTTP) || lowercaseName.startsWith (PREFIX_HTTPS)) { 885 Program program = Program.findProgram (display, ".html"); //$NON-NLS-1$ 886 if (program is null) { 887 program = Program.findProgram (display, ".htm"); //$NON-NLS-1$ 888 } 889 if (program !is null && program.execute (fileName)) return true; 890 } 891 break; 892 } 893 try { 894 Compatibility.exec (fileName); 895 return true; 896 } catch (IOException e) { 897 return false; 898 } 899 } 900 901 /** 902 * Compares the argument to the receiver, and returns true 903 * if they represent the <em>same</em> object using a class 904 * specific comparison. 905 * 906 * @param other the object to compare with this object 907 * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise 908 * 909 * @see #hashCode() 910 */ 911 public override equals_t opEquals(Object other) { 912 if (this is other) return true; 913 if (!(cast(Program)other)) return false; 914 Program program = cast(Program)other; 915 return display is program.display && name.equals(program.name) && command.equals(program.command); 916 } 917 918 /** 919 * Executes the program with the file as the single argument 920 * in the operating system. It is the responsibility of the 921 * programmer to ensure that the file contains valid data for 922 * this program. 923 * 924 * @param fileName the file or program name 925 * @return <code>true</code> if the file is launched, otherwise <code>false</code> 926 * 927 * @exception IllegalArgumentException <ul> 928 * <li>ERROR_NULL_ARGUMENT when fileName is null</li> 929 * </ul> 930 */ 931 public bool execute(String fileName) { 932 if (fileName is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 933 int desktop = getDesktop(display); 934 switch (desktop) { 935 case DESKTOP_GNOME_24: return gnome_24_execute(fileName); 936 case DESKTOP_GNOME: return gnome_execute(fileName); 937 //case DESKTOP_CDE: return cde_execute(fileName); 938 default: 939 } 940 return false; 941 } 942 943 /** 944 * Returns the receiver's image data. This is the icon 945 * that is associated with the receiver in the operating 946 * system. 947 * 948 * @return the image data for the program, may be null 949 */ 950 public ImageData getImageData() { 951 switch (getDesktop(display)) { 952 case DESKTOP_GNOME_24: 953 case DESKTOP_GNOME: return gnome_getImageData(); 954 //case DESKTOP_CDE: return cde_getImageData(); 955 default: 956 } 957 return null; 958 } 959 960 /** 961 * Returns the receiver's name. This is as short and 962 * descriptive a name as possible for the program. If 963 * the program has no descriptive name, this string may 964 * be the executable name, path or empty. 965 * 966 * @return the name of the program 967 */ 968 public String getName() { 969 return name; 970 } 971 972 /** 973 * Returns an integer hash code for the receiver. Any two 974 * objects that return <code>true</code> when passed to 975 * <code>equals</code> must return the same value for this 976 * method. 977 * 978 * @return the receiver's hash 979 * 980 * @see #equals(Object) 981 */ 982 public override hash_t toHash() { 983 return .toHash(name) ^ .toHash(command) ^ display.toHash(); 984 } 985 986 /** 987 * Returns a string containing a concise, human-readable 988 * description of the receiver. 989 * 990 * @return a string representation of the program 991 */ 992 override 993 public String toString() { 994 return Format( "Program {{{}}", name ); 995 } 996 }