Mercurial > projects > dwt-linux
changeset 80:f9349a0d8101
FileDialog
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 15 Jan 2008 18:26:22 +0100 |
parents | eb0144eddf0f |
children | c263acbfae34 |
files | dwt/internal/gtk/OS.d dwt/widgets/FileDialog.d |
diffstat | 2 files changed, 522 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/dwt/internal/gtk/OS.d Tue Jan 15 17:11:43 2008 +0100 +++ b/dwt/internal/gtk/OS.d Tue Jan 15 18:26:22 2008 +0100 @@ -22,6 +22,7 @@ import dwt.internal.Platform; import tango.core.Traits; import tango.stdc.locale; +import tango.stdc.posix.stdlib : realpath; import dwt.internal.c.gtk, dwt.internal.c.gdk, @@ -1135,7 +1136,7 @@ return .gtk_micro_version; } mixin ForwardGtkOsCFunc!(localeconv_decimal_point); -// mixin ForwardGtkOsCFunc!(realpath); + mixin ForwardGtkOsCFunc!(realpath); // mixin ForwardGtkOsCFunc!(X_EVENT_TYPE); // mixin ForwardGtkOsCFunc!(X_EVENT_WINDOW);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwt/widgets/FileDialog.d Tue Jan 15 18:26:22 2008 +0100 @@ -0,0 +1,520 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ +module dwt.widgets.FileDialog; + + + +import dwt.DWT; +import dwt.DWTException; +import dwt.internal.gtk.OS; +import dwt.widgets.Dialog; +import dwt.widgets.Shell; +import dwt.widgets.Display; + +static import tango.io.FileConst; +static import tango.text.Util; +static import tango.text.Text; + +/** + * Instances of this class allow the user to navigate + * the file system and select or enter a file name. + * <dl> + * <dt><b>Styles:</b></dt> + * <dd>SAVE, OPEN, MULTI</dd> + * <dt><b>Events:</b></dt> + * <dd>(none)</dd> + * </dl> + * <p> + * Note: Only one of the styles SAVE and OPEN may be specified. + * </p><p> + * IMPORTANT: This class is intended to be subclassed <em>only</em> + * within the DWT implementation. + * </p> + */ +public class FileDialog : Dialog { + char[] [] filterNames; + char[] [] filterExtensions; + char[] filterPath = ""; + char[] fileName = ""; + char[][] fileNames; + char[] fullPath = ""; + GtkWidget* handle; + static final char SEPARATOR = tango.io.FileConst.FileConst.PathSeparatorChar; + static final char EXTENSION_SEPARATOR = ';'; + +/** + * Constructs a new instance of this class given only its parent. + * + * @param parent a shell which will be the parent of the new instance + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception DWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public this (Shell parent) { + this (parent, DWT.PRIMARY_MODAL); +} +/** + * Constructs a new instance of this class given its parent + * and a style value describing its behavior and appearance. + * <p> + * The style value is either one of the style constants defined in + * class <code>DWT</code> which is applicable to instances of this + * class, or must be built by <em>bitwise OR</em>'ing together + * (that is, using the <code>int</code> "|" operator) two or more + * of those <code>DWT</code> style constants. The class description + * lists the style constants that are applicable to the class. + * Style bits are also inherited from superclasses. + * </p> + * + * @param parent a shell which will be the parent of the new instance + * @param style the style of dialog to construct + * + * @exception IllegalArgumentException <ul> + * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> + * </ul> + * @exception DWTException <ul> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> + * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> + * </ul> + */ +public this (Shell parent, int style) { + super (parent, style); + checkSubclass (); +} +char[] computeResultChooserDialog () { + /* MULTI is only valid if the native dialog's action is Open */ + fullPath = null; + if ((style & (DWT.SAVE | DWT.MULTI)) is DWT.MULTI) { + auto list = OS.gtk_file_chooser_get_filenames (handle); + int listLength = OS.g_slist_length (list); + fileNames = new char[] [listLength]; + auto current = list; + int writePos = 0; + for (int i = 0; i < listLength; i++) { + auto name = cast(char*)OS.g_slist_data (current); + auto utf8Ptr = OS.g_filename_to_utf8 (name, -1, null, null, null); + OS.g_free (name); + if (utf8Ptr !is null) { + fullPath = tango.stdc.stringz.fromUtf8z( utf8Ptr ); + int start = tango.text.Util.locatePrior( fullPath, SEPARATOR); + if( start == fullPath.length ) start = -1; + fileNames [writePos++] = fullPath[ start + 1 .. $ ]; + OS.g_free (utf8Ptr); + } + current = OS.g_slist_next (current); + } + if (writePos !is 0 && writePos !is listLength) { + char[] [] validFileNames = new char[] [writePos]; + System.arraycopy (fileNames, 0, validFileNames, 0, writePos); + fileNames = validFileNames; + } + OS.g_slist_free (list); + } else { + auto path = OS.gtk_file_chooser_get_filename (handle); + if (path !is null) { + auto utf8Ptr = OS.g_filename_to_utf8 (path, -1, null, null, null); + OS.g_free (path); + if (utf8Ptr !is null) { + fullPath = tango.stdc.stringz.fromUtf8z( utf8Ptr ); + fileNames = new char[] [1]; + int start = tango.text.Util.locatePrior( fullPath, SEPARATOR); + if( start == fullPath.length ) start = -1; + fileNames[0] = fullPath[ start + 1 .. $ ]; + OS.g_free (utf8Ptr); + } + } + } + if (fullPath !is null) { + int separatorIndex = tango.text.Util.locatePrior( fullPath, SEPARATOR); + if( separatorIndex is fullPath.length ) separatorIndex = -1; + fileName = fullPath[separatorIndex + 1 .. $ ]; + filterPath = fullPath[0 .. separatorIndex ]; + } + return fullPath; +} +char[] computeResultClassicDialog () { + GtkFileSelection* selection = cast(GtkFileSelection*)handle; + auto entry = selection.selection_entry; + auto entryText = OS.gtk_entry_get_text (entry); + char[] txt = tango.stdc.stringz.fromUtf8z( entryText ); + if (txt.length is 0) { + auto fileList = selection.file_list; + auto listSelection = OS.gtk_tree_view_get_selection (fileList); + void* model; + auto selectedList = OS.gtk_tree_selection_get_selected_rows (listSelection, &model); + if (selectedList is null) return null; + int listLength = OS.g_list_length (selectedList); + if (listLength is 0) { + OS.g_list_free (selectedList); + return null; + } + auto path = OS.g_list_nth_data (selectedList, 0); + char* ptr; + GtkTreeIter iter; + if (OS.gtk_tree_model_get_iter (&model, &iter, path)) { + OS.gtk_tree_model_get1 (&model, &iter, 0, cast(void**)&ptr); + } + for (int i = 0; i < listLength; i++) { + OS.gtk_tree_path_free (OS.g_list_nth_data (selectedList, i)); + } + OS.g_list_free (selectedList); + if (ptr is null) return null; + OS.gtk_entry_set_text (entry, ptr); + OS.g_free (ptr); + } + + auto fileNamePtr = OS.gtk_file_selection_get_filename (handle); + auto utf8Ptr = OS.g_filename_to_utf8 (fileNamePtr, -1, null, null, null); + char[] osAnswer = tango.stdc.stringz.fromUtf8z( utf8Ptr ).dup; + OS.g_free (utf8Ptr); + + if (osAnswer.length is 0) return null; + int separatorIndex = tango.text.Util.locatePrior( osAnswer, SEPARATOR); + if (separatorIndex is osAnswer.length ) separatorIndex = -1; + if (separatorIndex+1 is osAnswer.length ) return null; + + char[] answer = fullPath = osAnswer; + fileName = fullPath[ separatorIndex+1 .. $ ]; + filterPath = fullPath[ 0 .. separatorIndex ]; + if ((style & DWT.MULTI) is 0) { + fileNames = [ fileName ]; + } else { + auto namesPtr = OS.gtk_file_selection_get_selections (handle); + auto namesPtr1 = namesPtr; + char* namePtr = namesPtr1[0]; + int length_ = 0; + while (namePtr !is null) { + length_++; + namePtr = namesPtr1[length_]; + } + fileNames = new char[][](length_); + for (int i = 0; i < length_; i++) { + utf8Ptr = OS.g_filename_to_utf8 (namesPtr [i], -1, null, null, null); + char[] name = tango.stdc.stringz.fromUtf8z(utf8Ptr); + int start = tango.text.Util.locatePrior( name, SEPARATOR); + if( start == name.length ) start = -1; + fileNames [i] = name[ start + 1 .. $ ].dup; + OS.g_free (utf8Ptr); + } + OS.g_strfreev (namesPtr); + } + return answer; +} +/** + * Returns the path of the first file that was + * selected in the dialog relative to the filter path, or an + * empty string if no such file has been selected. + * + * @return the relative path of the file + */ +public char[] getFileName () { + return fileName; +} +/** + * Returns a (possibly empty) array with the paths of all files + * that were selected in the dialog relative to the filter path. + * + * @return the relative paths of the files + */ +public char[] [] getFileNames () { + return fileNames; +} +/** + * Returns the file extensions which the dialog will + * use to filter the files it shows. + * + * @return the file extensions filter + */ +public char[] [] getFilterExtensions () { + return filterExtensions; +} +/** + * Returns the names that describe the filter extensions + * which the dialog will use to filter the files it shows. + * + * @return the list of filter names + */ +public char[] [] getFilterNames () { + return filterNames; +} +/** + * Returns the directory path that the dialog will use, or an empty + * string if this is not set. File names in this path will appear + * in the dialog, filtered according to the filter extensions. + * + * @return the directory path string + * + * @see #setFilterExtensions + */ +public char[] getFilterPath () { + return filterPath; +} +/** + * Makes the dialog visible and brings it to the front + * of the display. + * + * @return a string describing the absolute path of the first selected file, + * or null if the dialog was cancelled or an error occurred + * + * @exception DWTException <ul> + * <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li> + * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li> + * </ul> + */ +public char[] open () { + bool useChooserDialog = OS.GTK_VERSION >= OS.buildVERSION (2, 4, 10); + if (useChooserDialog) { + return openChooserDialog (); + } else { + return openClassicDialog (); + } +} +char[] openChooserDialog () { + char* titleBytes = tango.stdc.stringz.toStringz( title ); + int action = (style & DWT.SAVE) !is 0 ? + OS.GTK_FILE_CHOOSER_ACTION_SAVE : + OS.GTK_FILE_CHOOSER_ACTION_OPEN; + auto shellHandle = parent.topHandle (); + handle = OS.gtk_file_chooser_dialog_new2 ( + titleBytes, + shellHandle, + action, + OS.GTK_STOCK_CANCEL (), OS.GTK_RESPONSE_CANCEL, + OS.GTK_STOCK_OK (), OS.GTK_RESPONSE_OK); + auto pixbufs = OS.gtk_window_get_icon_list (shellHandle); + if (pixbufs !is null) { + OS.gtk_window_set_icon_list (handle, pixbufs); + OS.g_list_free (pixbufs); + } + presetChooserDialog (); + Display display = parent !is null ? parent.getDisplay (): Display.getCurrent (); + display.addIdleProc (); + char[] answer = null; + if (OS.gtk_dialog_run (handle) is OS.GTK_RESPONSE_OK) { + answer = computeResultChooserDialog (); + } + display.removeIdleProc (); + OS.gtk_widget_destroy (handle); + return answer; +} +char[] openClassicDialog () { + char* titleBytes = tango.stdc.stringz.toStringz( title ); + handle = OS.gtk_file_selection_new (titleBytes); + if (parent !is null) { + auto shellHandle = parent.topHandle (); + OS.gtk_window_set_transient_for (handle, shellHandle); + auto pixbufs = OS.gtk_window_get_icon_list (shellHandle); + if (pixbufs !is null) { + OS.gtk_window_set_icon_list (handle, pixbufs); + OS.g_list_free (pixbufs); + } + } + presetClassicDialog (); + Display display = parent !is null ? parent.getDisplay (): Display.getCurrent (); + display.addIdleProc (); + char[] answer = null; + if (OS.gtk_dialog_run (handle) is OS.GTK_RESPONSE_OK) { + answer = computeResultClassicDialog (); + } + display.removeIdleProc (); + OS.gtk_widget_destroy (handle); + return answer; +} +void presetChooserDialog () { + /* MULTI is only valid if the native dialog's action is Open */ + if ((style & (DWT.SAVE | DWT.MULTI)) is DWT.MULTI) { + OS.gtk_file_chooser_set_select_multiple (handle, true); + } + if (filterPath is null) filterPath = ""; + if (fileName is null) fileName = ""; + if (filterPath.length > 0) { + tango.text.Text.Text!(char) stringBuffer = new tango.text.Text.Text!(char)(); + /* filename must be a full path */ + if (filterPath[0] !is SEPARATOR) { + stringBuffer.append (SEPARATOR); + } + stringBuffer.append (filterPath); + if (filterPath[filterPath.length - 1 ] !is SEPARATOR) { + stringBuffer.append (SEPARATOR); + } + if (fileName.length > 0) { + stringBuffer.append (fileName); + } else { + /* go into the specified directory */ + stringBuffer.append ('.'); + } + char* buffer = tango.stdc.stringz.toStringz( stringBuffer.toString ()); + /* + * Bug in GTK. GtkFileChooser may crash on GTK versions 2.4.10 to 2.6 + * when setting a file name that is not a true canonical path. + * The fix is to use the canonical path. + */ + auto ptr = OS.realpath (buffer, null); + if (ptr !is null) { + OS.gtk_file_chooser_set_filename (handle, ptr); + OS.g_free (ptr); + } + } else { + if (fileName.length > 0) { + if (fileName[0] is SEPARATOR) { + char* buffer = tango.stdc.stringz.toStringz(fileName); + /* + * Bug in GTK. GtkFileChooser may crash on GTK versions 2.4.10 to 2.6 + * when setting a file name that is not a true canonical path. + * The fix is to use the canonical path. + */ + auto ptr = OS.realpath (buffer, null); + if (ptr !is null) { + OS.gtk_file_chooser_set_filename (handle, ptr); + OS.g_free (ptr); + } + } + } + } + if ((style & DWT.SAVE) !is 0 && fileName.length > 0) { + char* buffer = tango.stdc.stringz.toStringz(fileName); + OS.gtk_file_chooser_set_current_name (handle, buffer); + } + + /* Set the extension filters */ + if (filterNames is null) filterNames = null; + if (filterExtensions is null) filterExtensions = null; + for (int i = 0; i < filterExtensions.length; i++) { + if (filterExtensions [i] !is null) { + auto filter = OS.gtk_file_filter_new (); + if (filterNames.length > i && filterNames [i] !is null) { + char* name = tango.stdc.stringz.toStringz(filterNames [i]); + OS.gtk_file_filter_set_name (filter, name); + } else { + char* name = tango.stdc.stringz.toStringz(filterExtensions [i]); + OS.gtk_file_filter_set_name (filter, name); + } + int start = 0; + int index = tango.text.Util.locate( filterExtensions [i], EXTENSION_SEPARATOR ); + while (index !is filterExtensions [i].length ) { + char[] current = filterExtensions [i][ start .. index ]; + char* filterString = tango.stdc.stringz.toStringz(current); + OS.gtk_file_filter_add_pattern (filter, filterString); + start = index + 1; + index = tango.text.Util.locate( filterExtensions [i], EXTENSION_SEPARATOR, start); + if( index == filterExtensions [i].length) index = -1; + } + char[] current = filterExtensions [i][ start .. $ ]; + char* filterString = tango.stdc.stringz.toStringz(current); + OS.gtk_file_filter_add_pattern (filter, filterString); + OS.gtk_file_chooser_add_filter (handle, filter); + } + } + fullPath = null; + fileNames = null; +} +void presetClassicDialog () { + OS.gtk_file_selection_set_select_multiple(handle, (style & DWT.MULTI) !is 0); + + /* Calculate the fully-specified file name and convert to bytes */ + tango.text.Text.Text!(char) stringBuffer = new tango.text.Text.Text!(char)(); + if (filterPath is null) { + filterPath = ""; + } else { + if (filterPath.length > 0) { + stringBuffer.append (filterPath); + if (filterPath[filterPath.length - 1] !is SEPARATOR) { + stringBuffer.append (SEPARATOR); + } + } + } + if (fileName is null) { + fileName = ""; + } else { + stringBuffer.append (fileName); + } + fullPath = stringBuffer.toString (); + auto fileNamePtr = OS.g_filename_from_utf8 (tango.stdc.stringz.toStringz( fullPath ), -1, null, null, null); + OS.gtk_file_selection_set_filename (handle, fileNamePtr); + OS.g_free (fileNamePtr); + + if (filterNames is null) filterNames = null; + if (filterExtensions is null) filterExtensions = null; + fullPath = null; + fileNames = null; +} +/** + * Set the initial filename which the dialog will + * select by default when opened to the argument, + * which may be null. The name will be prefixed with + * the filter path when one is supplied. + * + * @param string the file name + */ +public void setFileName (char[] string) { + fileName = string; +} +/** + * Set the file extensions which the dialog will + * use to filter the files it shows to the argument, + * which may be null. + * <p> + * The strings are platform specific. For example, on + * Windows, an extension filter string is typically of + * the form "*.extension", where "*.*" matches all files. + * </p> + * + * @param extensions the file extension filter + * + * @see #setFilterNames to specify the user-friendly + * names corresponding to the extensions + */ +public void setFilterExtensions (char[] [] extensions) { + filterExtensions = extensions; +} +/** + * Sets the the names that describe the filter extensions + * which the dialog will use to filter the files it shows + * to the argument, which may be null. + * <p> + * Each name is a user-friendly short description shown for + * its corresponding filter. The <code>names</code> array must + * be the same length as the <code>extensions</code> array. + * </p> + * + * @param names the list of filter names, or null for no filter names + * + * @see #setFilterExtensions + */ +public void setFilterNames (char[] [] names) { + filterNames = names; +} +/** + * Sets the directory path that the dialog will use + * to the argument, which may be null. File names in this + * path will appear in the dialog, filtered according + * to the filter extensions. If the string is null, + * then the operating system's default filter path + * will be used. + * <p> + * Note that the path string is platform dependent. + * For convenience, either '/' or '\' can be used + * as a path separator. + * </p> + * + * @param string the directory path + * + * @see #setFilterExtensions + */ +public void setFilterPath (char[] string) { + filterPath = string; +} +}