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