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.widgets.DirectoryDialog;
14 
15 import java.lang.all;
16 
17 
18 
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.SWTException;
21 import org.eclipse.swt.internal.gtk.OS;
22 import org.eclipse.swt.widgets.Dialog;
23 import org.eclipse.swt.widgets.Shell;
24 import org.eclipse.swt.widgets.Display;
25 
26 version(Tango){
27     static import tango.io.model.IFile;
28 } else { // Phobos
29     static import std.path;
30 }
31 
32 /**
33  * Instances of this class allow the user to navigate
34  * the file system and select a directory.
35  * <dl>
36  * <dt><b>Styles:</b></dt>
37  * <dd>(none)</dd>
38  * <dt><b>Events:</b></dt>
39  * <dd>(none)</dd>
40  * </dl>
41  * <p>
42  * IMPORTANT: This class is intended to be subclassed <em>only</em>
43  * within the SWT implementation.
44  * </p>
45  *
46  * @see <a href="http://www.eclipse.org/swt/snippets/#directorydialog">DirectoryDialog snippets</a>
47  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample, Dialog tab</a>
48  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
49  */
50 public class DirectoryDialog : Dialog {
51     String message = "", filterPath = "";
52     version(Tango){
53         static const String SEPARATOR = tango.io.model.IFile.FileConst.PathSeparatorString;
54     } else { // Phobos
55         static String SEPARATOR = std.path.dirSeparator;
56     }
57 
58 /**
59  * Constructs a new instance of this class given only its parent.
60  *
61  * @param parent a shell which will be the parent of the new instance
62  *
63  * @exception IllegalArgumentException <ul>
64  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
65  * </ul>
66  * @exception SWTException <ul>
67  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
68  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
69  * </ul>
70  */
71 public this (Shell parent) {
72     this (parent, SWT.APPLICATION_MODAL);
73 }
74 /**
75  * Constructs a new instance of this class given its parent
76  * and a style value describing its behavior and appearance.
77  * <p>
78  * The style value is either one of the style constants defined in
79  * class <code>SWT</code> which is applicable to instances of this
80  * class, or must be built by <em>bitwise OR</em>'ing together
81  * (that is, using the <code>int</code> "|" operator) two or more
82  * of those <code>SWT</code> style constants. The class description
83  * lists the style constants that are applicable to the class.
84  * Style bits are also inherited from superclasses.
85  * </p>
86  *
87  * @param parent a shell which will be the parent of the new instance
88  * @param style the style of dialog to construct
89  *
90  * @exception IllegalArgumentException <ul>
91  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
92  * </ul>
93  * @exception SWTException <ul>
94  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
95  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
96  * </ul>
97  */
98 public this (Shell parent, int style) {
99     super (parent, checkStyle (parent, style));
100     checkSubclass ();
101 }
102 /**
103  * Returns the path which the dialog will use to filter
104  * the directories it shows.
105  *
106  * @return the filter path
107  *
108  * @see #setFilterPath
109  */
110 public String getFilterPath () {
111     return filterPath;
112 }
113 /**
114  * Returns the dialog's message, which is a description of
115  * the purpose for which it was opened. This message will be
116  * visible on the dialog while it is open.
117  *
118  * @return the message
119  */
120 public String getMessage () {
121     return message;
122 }
123 /**
124  * Makes the dialog visible and brings it to the front
125  * of the display.
126  *
127  * @return a string describing the absolute path of the selected directory,
128  *         or null if the dialog was cancelled or an error occurred
129  *
130  * @exception SWTException <ul>
131  *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
132  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
133  * </ul>
134  */
135 public String open () {
136     bool useChooserDialog = OS.GTK_VERSION >= OS.buildVERSION (2, 4, 10);
137     if (useChooserDialog) {
138         return openChooserDialog ();
139     } else {
140         return openClassicDialog ();
141     }
142 }
143 String openChooserDialog () {
144     char* titleBytes = toStringz(title);
145     auto shellHandle = parent.topHandle ();
146     auto handle = OS.gtk_file_chooser_dialog_new2 (
147         titleBytes,
148         shellHandle,
149         OS.GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
150         OS.GTK_STOCK_CANCEL (), OS.GTK_RESPONSE_CANCEL,
151         OS.GTK_STOCK_OK (), OS.GTK_RESPONSE_OK );
152     auto pixbufs = OS.gtk_window_get_icon_list (shellHandle);
153     if (pixbufs !is null) {
154         OS.gtk_window_set_icon_list (handle, pixbufs);
155         OS.g_list_free (pixbufs);
156     }
157     if (filterPath !is null && filterPath.length > 0) {
158         String p;
159         /* filename must be a full path */
160         if ( filterPath[ 0 .. SEPARATOR.length ] != SEPARATOR ) {
161             p ~= SEPARATOR;
162             p ~= filterPath;
163         }
164         else{
165             p = filterPath;
166         }
167         char* buffer = toStringz(p);
168         /*
169         * Bug in GTK. GtkFileChooser may crash on GTK versions 2.4.10 to 2.6
170         * when setting a file name that is not a true canonical path.
171         * The fix is to use the canonical path.
172         */
173         char* ptr = OS.realpath (buffer, null);
174         if (ptr !is null) {
175             OS.gtk_file_chooser_set_current_folder (handle, ptr);
176             OS.g_free (ptr);
177         }
178     }
179     if (message.length > 0) {
180         char* buffer = toStringz(message);
181         auto box = OS.gtk_hbox_new (false, 0);
182         if (box is null) error (SWT.ERROR_NO_HANDLES);
183         auto label = OS.gtk_label_new (buffer);
184         if (label is null) error (SWT.ERROR_NO_HANDLES);
185         OS.gtk_container_add (box, label);
186         OS.gtk_widget_show (label);
187         OS.gtk_label_set_line_wrap (label, true);
188         OS.gtk_label_set_justify (label, OS.GTK_JUSTIFY_CENTER);
189         OS.gtk_file_chooser_set_extra_widget (handle, box);
190     }
191     String answer = null;
192     Display display = parent !is null ? parent.getDisplay (): Display.getCurrent ();
193     display.addIdleProc ();
194     Dialog oldModal = null;
195     if (OS.gtk_window_get_modal (handle)) {
196         oldModal = display.getModalDialog ();
197         display.setModalDialog (this);
198     }
199     int signalId = 0;
200     ptrdiff_t hookId = 0;
201     CallbackData emissionData;
202     emissionData.display = display;
203     emissionData.data = handle;
204     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
205         signalId = OS.g_signal_lookup (OS.map.ptr, OS.GTK_TYPE_WIDGET());
206         hookId = OS.g_signal_add_emission_hook (signalId, 0, &Display.emissionFunc, &emissionData, null);
207     }
208     int response = OS.gtk_dialog_run (handle);
209     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
210         OS.g_signal_remove_emission_hook (signalId, hookId);
211     }
212     if (OS.gtk_window_get_modal (handle)) {
213         display.setModalDialog (oldModal);
214     }
215     if (response is OS.GTK_RESPONSE_OK) {
216         auto path = OS.gtk_file_chooser_get_filename (handle);
217         if (path !is null) {
218             size_t items_written;
219             auto utf8Ptr = OS.g_filename_to_utf8 (path, -1, null, &items_written, null);
220             OS.g_free (path);
221             if (utf8Ptr !is null) {
222                 answer = utf8Ptr[ 0 .. items_written ]._idup();
223                 filterPath = answer;
224                 OS.g_free (utf8Ptr);
225             }
226         }
227     }
228     display.removeIdleProc ();
229     OS.gtk_widget_destroy (handle);
230     return answer;
231 }
232 String openClassicDialog () {
233     char* titleBytes = toStringz(title);
234     auto handle = OS.gtk_file_selection_new (titleBytes);
235     if (parent !is null) {
236         auto shellHandle = parent.topHandle ();
237         OS.gtk_window_set_transient_for (handle, shellHandle);
238         auto pixbufs = OS.gtk_window_get_icon_list (shellHandle);
239         if (pixbufs !is null) {
240             OS.gtk_window_set_icon_list (handle, pixbufs);
241             OS.g_list_free (pixbufs);
242         }
243     }
244     String answer = null;
245     if (filterPath !is null) {
246         String path = filterPath;
247         if (path.length > 0 && path[ $-1 .. $ ] != SEPARATOR ) {
248             path ~= SEPARATOR;
249         }
250         char* fileNamePtr = OS.g_filename_from_utf8 (toStringz(path), -1, null, null, null);
251         OS.gtk_file_selection_set_filename (handle, fileNamePtr);
252         OS.g_free (fileNamePtr);
253     }
254     GtkFileSelection* selection = cast(GtkFileSelection*)handle;
255     OS.gtk_file_selection_hide_fileop_buttons (handle);
256     auto fileListParent = OS.gtk_widget_get_parent (selection.file_list);
257     OS.gtk_widget_hide (selection.file_list);
258     OS.gtk_widget_hide (fileListParent);
259     if (message.length > 0) {
260         auto labelHandle = OS.gtk_label_new (toStringz(message));
261         OS.gtk_label_set_line_wrap (labelHandle, true);
262         OS.gtk_misc_set_alignment (labelHandle, 0.0f, 0.0f);
263         OS.gtk_container_add (selection.main_vbox, labelHandle);
264         OS.gtk_box_set_child_packing (
265             selection.main_vbox, labelHandle, false, false, 0, OS.GTK_PACK_START);
266         OS.gtk_widget_show (labelHandle);
267     }
268     Display display = parent !is null ? parent.getDisplay (): Display.getCurrent ();
269     display.addIdleProc ();
270     Dialog oldModal = null;
271     if (OS.gtk_window_get_modal (handle)) {
272         oldModal = display.getModalDialog ();
273         display.setModalDialog (this);
274     }
275     int signalId = 0;
276     ptrdiff_t hookId = 0;
277     CallbackData emissionData;
278     emissionData.display = display;
279     emissionData.data = handle;
280     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
281         signalId = OS.g_signal_lookup (OS.map.ptr, OS.GTK_TYPE_WIDGET());
282         hookId = OS.g_signal_add_emission_hook (signalId, 0, &Display.emissionFunc, &emissionData, null);
283     }
284     int response = OS.gtk_dialog_run (handle);
285     if ((style & SWT.RIGHT_TO_LEFT) !is 0) {
286         OS.g_signal_remove_emission_hook (signalId, hookId);
287     }
288     if (OS.gtk_window_get_modal (handle)) {
289         display.setModalDialog (oldModal);
290     }
291     if (response is OS.GTK_RESPONSE_OK) {
292         char* fileNamePtr = OS.gtk_file_selection_get_filename (handle);
293         size_t items_written;
294         char* utf8Ptr = OS.g_filename_to_utf8 (fileNamePtr, -1, null, &items_written, null);
295         if (utf8Ptr !is null) {
296             String osAnswer = utf8Ptr[ 0 .. items_written ]._idup();
297             if (osAnswer.length !is 0) {
298                 /* remove trailing separator, unless root directory */
299                 if ( osAnswer != SEPARATOR && osAnswer[ $-1 .. $ ] == SEPARATOR ) {
300                     osAnswer = osAnswer[ 0 .. $ - 1 ];
301                 }
302                 answer = filterPath = osAnswer._idup();
303             }
304             OS.g_free (utf8Ptr);
305         }
306     }
307     display.removeIdleProc ();
308     OS.gtk_widget_destroy (handle);
309     return answer;
310 }
311 /**
312  * Sets the path that the dialog will use to filter
313  * the directories it shows to the argument, which may
314  * be null. If the string is null, then the operating
315  * system's default filter path will be used.
316  * <p>
317  * Note that the path string is platform dependent.
318  * For convenience, either '/' or '\' can be used
319  * as a path separator.
320  * </p>
321  *
322  * @param string the filter path
323  */
324 public void setFilterPath (String string) {
325     filterPath = string._idup();
326 }
327 /**
328  * Sets the dialog's message, which is a description of
329  * the purpose for which it was opened. This message will be
330  * visible on the dialog while it is open.
331  *
332  * @param string the message
333  *
334  */
335 public void setMessage (String string) {
336     // SWT extension: allow null for zero length string
337     //if (string is null) error (SWT.ERROR_NULL_ARGUMENT);
338     message = string._idup();
339 }
340 }