Mercurial > projects > dynamin
comparison dynamin/gui/file_dialog.d @ 0:aa4efef0f0b1
Initial commit of code.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 15 Jun 2009 22:10:48 -0500 |
parents | |
children | fc2420d39e3c |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:aa4efef0f0b1 |
---|---|
1 // Written in the D programming language | |
2 // www.digitalmars.com/d/ | |
3 | |
4 /* | |
5 * The contents of this file are subject to the Mozilla Public License Version | |
6 * 1.1 (the "License"); you may not use this file except in compliance with | |
7 * the License. You may obtain a copy of the License at | |
8 * http://www.mozilla.org/MPL/ | |
9 * | |
10 * Software distributed under the License is distributed on an "AS IS" basis, | |
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 * for the specific language governing rights and limitations under the | |
13 * License. | |
14 * | |
15 * The Original Code is the Dynamin library. | |
16 * | |
17 * The Initial Developer of the Original Code is Jordan Miner. | |
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009 | |
19 * the Initial Developer. All Rights Reserved. | |
20 * | |
21 * Contributor(s): | |
22 * Jordan Miner <jminer7@gmail.com> | |
23 * | |
24 */ | |
25 | |
26 module dynamin.gui.file_dialog; | |
27 | |
28 import dynamin.c.windows; | |
29 import dynamin.all_core; | |
30 import dynamin.gui.window; | |
31 import tango.io.Stdout; | |
32 import Utf = tango.text.convert.Utf; | |
33 | |
34 // not used by programs | |
35 struct FileDialogFilter { | |
36 string name; | |
37 string[] extensions; | |
38 // ignored for now | |
39 bool delegate(string fileName) shouldShow; | |
40 } | |
41 | |
42 /** | |
43 * As this is an abstract class, use OpenFileDialog or SaveFileDialog instead. | |
44 * | |
45 * TODO: figure out a way to allow the user to type in a custom filter? | |
46 * TODO: on Linux, use a GTK dialog if available, otherwise use a custom one. | |
47 * | |
48 * The appearance of a file dialog with Windows Classic: | |
49 * | |
50 * $(IMAGE ../web/example_file_dialog.png) | |
51 */ | |
52 abstract class FileDialog { | |
53 protected: | |
54 bool _multipleSelection; | |
55 string _initialFileName; | |
56 string _text; | |
57 string _directory; | |
58 string[] _files; | |
59 FileDialogFilter[] _filters; | |
60 int _selectedFilter; | |
61 public: | |
62 /** | |
63 * Adds a filter that only shows files with the specified extensions. | |
64 * Note that the "All Files (*.*)" filter is added automatically when the | |
65 * dialog is shown if not added previously because I don't like when | |
66 * programs don't let me have control over what extension to use and | |
67 * don't let me be able to see all the files. | |
68 * Examples: | |
69 * ----- | |
70 * dialog.addFilter("All Files (*.*)"); | |
71 * dialog.addFilter("Cascading Style Sheets (*.css)", "css"); | |
72 * dialog.addFilter("Web Pages (*.html, *.htm)", "html", "htm"); | |
73 * ----- | |
74 */ | |
75 void addFilter(string name, string[] exts...) { | |
76 FileDialogFilter filter; | |
77 filter.name = name; | |
78 filter.extensions = exts; | |
79 | |
80 _filters.length = _filters.length + 1; | |
81 _filters[_filters.length-1] = filter; | |
82 } | |
83 /** | |
84 * Adds a filter that only shows files with which the specified delegate | |
85 * returns true for. | |
86 * BUG: not implemented | |
87 */ | |
88 void addFilter(string name, string ext, bool delegate(string fileName) shouldShow) { | |
89 // TODO: | |
90 throw new Exception("addFilter(string, string, delegate) not implemented"); | |
91 FileDialogFilter filter; | |
92 filter.name = name; | |
93 filter.extensions = [ext]; | |
94 filter.shouldShow = shouldShow; | |
95 | |
96 _filters.length = _filters.length + 1; | |
97 _filters[_filters.length-1] = filter; | |
98 } | |
99 /// Gets or sets the selected filter. An index of 0 is the first one added. | |
100 int selectedFilter() { return _selectedFilter; } | |
101 /// ditto | |
102 void selectedFilter(int index) { _selectedFilter = index; } | |
103 /** | |
104 * Gets or sets whether more than one file can be selected. | |
105 * The default is true for an OpenFileDialog and false for SaveFileDialog. | |
106 */ | |
107 bool multipleSelection() { return _multipleSelection; } | |
108 /// ditto | |
109 void multipleSelection(bool b) { _multipleSelection = b; } | |
110 /// Gets or sets the text that is displayed in the dialog's title bar. | |
111 string text() { return _text; } | |
112 /// ditto | |
113 void text(string str) { _text = str; } | |
114 /** | |
115 * Sets the text in the file name text box to the specified string. | |
116 * Example: | |
117 * ----- | |
118 * dialog.initialFileName = "Untitled"; | |
119 * ----- | |
120 */ | |
121 void initialFileName(string str) { | |
122 // TODO: make sure str is not a path? | |
123 _initialFileName = str; | |
124 } | |
125 /** | |
126 * Sets the directory that the FileDialog shows. If this is null, | |
127 * the default directory is used when the dialog is first shown. | |
128 * After the dialog has been shown, this is set to the directory | |
129 * the user was last looking at. | |
130 */ | |
131 void directory(string str) { | |
132 _directory = str; | |
133 } | |
134 /// TODO: Should this be SelectedDirectory ? | |
135 string directory() { | |
136 return _directory; | |
137 } | |
138 /** | |
139 * Gets the files selected by the user. | |
140 * If the user did not type a file name extension, the correct one | |
141 * will be added according to the selected filter. | |
142 */ | |
143 string[] files() { return _files; } | |
144 /// Gets the first of the files selected by the user. | |
145 string file() { return _files[0]; } | |
146 protected int getXFileName(OPENFILENAME* ofn); | |
147 // TODO: parameters | |
148 // TODO: should ShowDialog take any parameters? | |
149 // what should happen if no owner is set? | |
150 // Windows Forms sets the owner to the currently active window in the application | |
151 // do the same? or have no owner (really annoying, as window can get below)? | |
152 DialogResult showDialog() { | |
153 OPENFILENAME ofn; | |
154 ofn.lStructSize = OPENFILENAME.sizeof; | |
155 //ofn.hwndOwner = ; | |
156 | |
157 bool allFilesFilter = false; | |
158 foreach(filter; _filters) | |
159 if(filter.extensions.length == 0) | |
160 allFilesFilter = true; | |
161 if(!allFilesFilter) addFilter("All Files (*.*)"); | |
162 | |
163 string filterStr; | |
164 foreach(filter; _filters) { | |
165 if(filter.shouldShow) | |
166 continue; | |
167 string[] exts = filter.extensions.dup; | |
168 if(exts.length == 0) | |
169 exts = [cast(string)"*.*"]; | |
170 else | |
171 for(int i = 0; i < exts.length; ++i) | |
172 exts[i] = "*." ~ exts[i]; | |
173 filterStr ~= filter.name ~ "\0" ~ exts.join(";") ~ "\0"; | |
174 } | |
175 filterStr ~= "\0"; | |
176 ofn.lpstrFilter = filterStr.toWcharPtr(); | |
177 ofn.nFilterIndex = _selectedFilter + 1; | |
178 wchar[] filesBufferW = Utf.toString16(_initialFileName~"\0"); | |
179 filesBufferW.length = 4096; | |
180 scope(exit) delete filesBufferW; | |
181 ofn.lpstrFile = filesBufferW.ptr; | |
182 ofn.nMaxFile = filesBufferW.length; | |
183 ofn.lpstrInitialDir = _directory.toWcharPtr(); | |
184 ofn.lpstrTitle = _text.toWcharPtr(); | |
185 ofn.Flags = OFN_EXPLORER; | |
186 //if(canChooseLinks) | |
187 // ofn.Flags |= OFN_NODEREFERENCELINKS; | |
188 ofn.Flags |= OFN_FILEMUSTEXIST; | |
189 ofn.Flags |= OFN_HIDEREADONLY; | |
190 ofn.Flags |= OFN_OVERWRITEPROMPT; | |
191 if(_multipleSelection) | |
192 ofn.Flags |= OFN_ALLOWMULTISELECT; | |
193 | |
194 if(!getXFileName(&ofn)) { | |
195 if(CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) | |
196 MessageBoxW(null, "Too many files picked.", "Error", 0); | |
197 return DialogResult.Cancel; | |
198 } | |
199 | |
200 _selectedFilter = ofn.nFilterIndex - 1; | |
201 // must zero FFFF chars here because the | |
202 // parsing here assumes the unused part of the string is zeroed | |
203 foreach(i, c; filesBufferW) | |
204 if(c == 0xFFFF) | |
205 filesBufferW[i] = 0; | |
206 int index; // index of null char right after the last non-null char | |
207 for(index = filesBufferW.length; index > 0; --index) | |
208 if(filesBufferW[index-1] != 0) | |
209 break; | |
210 auto filesBuffer = Utf.toString(filesBufferW[0..index]); | |
211 scope(exit) delete filesBuffer; | |
212 if(filesBuffer.contains('\0')) { // multiple files | |
213 auto arr = filesBuffer.split("\0"); | |
214 _directory = arr[0]; | |
215 // make sure directory ends with a backslash | |
216 // "C:\" does but "C:\Program Files" does not | |
217 if(!_directory.endsWith("\\")) | |
218 _directory ~= "\\"; | |
219 _files = new string[arr.length-1]; | |
220 for(int i = 1; i < arr.length; ++i) { | |
221 if(arr[i].contains('\\')) // a dereferenced link--absolute | |
222 _files[i-1] = arr[i]; | |
223 else | |
224 _files[i-1] = _directory ~ arr[i]; | |
225 } | |
226 } else { //single file | |
227 assert(filesBuffer.contains('\\')); | |
228 _directory = filesBuffer[0..filesBuffer.findLast("\\")].dup; | |
229 _files = [filesBuffer.dup]; | |
230 } | |
231 | |
232 // if "All Files (*.*)" filter is not selected | |
233 if(_filters[selectedFilter].extensions.length > 0) { | |
234 // go over every chosen file and add the selected filter's | |
235 // extension if the file doesn't already have one from the selected filter | |
236 for(int i = 0; i < _files.length; ++i) { | |
237 bool validExt = false; | |
238 foreach(ext; _filters[selectedFilter].extensions) | |
239 if(_files[i].downcase().endsWith(ext.downcase())) | |
240 validExt = true; | |
241 if(!validExt) | |
242 _files[i] ~= "." ~ _filters[selectedFilter].extensions[0].downcase(); | |
243 } | |
244 } | |
245 | |
246 return DialogResult.OK; | |
247 } | |
248 } | |
249 | |
250 /// | |
251 class OpenFileDialog : FileDialog { | |
252 this() { | |
253 _multipleSelection = true; | |
254 // different settings | |
255 } | |
256 protected int getXFileName(OPENFILENAME* ofn) { | |
257 return GetOpenFileName(ofn); | |
258 } | |
259 } | |
260 | |
261 /// | |
262 class SaveFileDialog : FileDialog { | |
263 this() { | |
264 _multipleSelection = false; | |
265 // different settings | |
266 } | |
267 protected int getXFileName(OPENFILENAME* ofn) { | |
268 return GetSaveFileName(ofn); | |
269 } | |
270 } |