Mercurial > projects > dynamin
comparison dynamin/gui/file_dialog.d @ 4:fc2420d39e3c
Separate out Windows backend of FileDialog.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Sat, 20 Jun 2009 10:23:31 -0500 |
parents | aa4efef0f0b1 |
children | 4029d5af7542 |
comparison
equal
deleted
inserted
replaced
3:d806edad4300 | 4:fc2420d39e3c |
---|---|
23 * | 23 * |
24 */ | 24 */ |
25 | 25 |
26 module dynamin.gui.file_dialog; | 26 module dynamin.gui.file_dialog; |
27 | 27 |
28 import dynamin.c.windows; | |
29 import dynamin.all_core; | 28 import dynamin.all_core; |
30 import dynamin.gui.window; | 29 import dynamin.gui_backend; |
31 import tango.io.Stdout; | |
32 import Utf = tango.text.convert.Utf; | |
33 | 30 |
34 // not used by programs | 31 // not used by programs |
35 struct FileDialogFilter { | 32 struct FileDialogFilter { |
36 string name; | 33 string name; |
37 string[] extensions; | 34 string[] extensions; |
48 * The appearance of a file dialog with Windows Classic: | 45 * The appearance of a file dialog with Windows Classic: |
49 * | 46 * |
50 * $(IMAGE ../web/example_file_dialog.png) | 47 * $(IMAGE ../web/example_file_dialog.png) |
51 */ | 48 */ |
52 abstract class FileDialog { | 49 abstract class FileDialog { |
50 private: | |
51 mixin FileDialogBackend; | |
53 protected: | 52 protected: |
54 bool _multipleSelection; | 53 bool _multipleSelection; |
55 string _initialFileName; | 54 string _initialFileName; |
56 string _text; | 55 string _text; |
57 string _directory; | 56 string _directory; |
58 string[] _files; | 57 string[] _files; |
59 FileDialogFilter[] _filters; | 58 FileDialogFilter[] _filters; |
60 int _selectedFilter; | 59 int _selectedFilter; |
60 | |
61 uint fileDialogType; | |
62 enum { | |
63 Open, Save | |
64 } | |
65 invariant() { | |
66 assert(fileDialogType == Open || fileDialogType == Save); | |
67 } | |
61 public: | 68 public: |
62 /** | 69 /** |
63 * Adds a filter that only shows files with the specified extensions. | 70 * Adds a filter that only shows files with the specified extensions. |
64 * Note that the "All Files (*.*)" filter is added automatically when the | 71 * 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 | 72 * dialog is shown if not added previously because I don't like when |
141 * will be added according to the selected filter. | 148 * will be added according to the selected filter. |
142 */ | 149 */ |
143 string[] files() { return _files; } | 150 string[] files() { return _files; } |
144 /// Gets the first of the files selected by the user. | 151 /// Gets the first of the files selected by the user. |
145 string file() { return _files[0]; } | 152 string file() { return _files[0]; } |
146 protected int getXFileName(OPENFILENAME* ofn); | 153 |
147 // TODO: parameters | 154 // TODO: parameters |
148 // TODO: should ShowDialog take any parameters? | 155 // TODO: should showDialog take any parameters? |
149 // what should happen if no owner is set? | 156 // what should happen if no owner is set? |
150 // Windows Forms sets the owner to the currently active window in the application | 157 // 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)? | 158 // do the same? or have no owner (really annoying, as window can get below)? |
152 DialogResult showDialog() { | 159 DialogResult showDialog() { |
153 OPENFILENAME ofn; | 160 return backend_showDialog(); |
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 } | 161 } |
248 } | 162 } |
249 | 163 |
250 /// | 164 /// |
251 class OpenFileDialog : FileDialog { | 165 class OpenFileDialog : FileDialog { |
252 this() { | 166 this() { |
253 _multipleSelection = true; | 167 _multipleSelection = true; |
254 // different settings | 168 // different settings |
255 } | 169 fileDialogType = Open; |
256 protected int getXFileName(OPENFILENAME* ofn) { | |
257 return GetOpenFileName(ofn); | |
258 } | 170 } |
259 } | 171 } |
260 | 172 |
261 /// | 173 /// |
262 class SaveFileDialog : FileDialog { | 174 class SaveFileDialog : FileDialog { |
263 this() { | 175 this() { |
264 _multipleSelection = false; | 176 _multipleSelection = false; |
265 // different settings | 177 // different settings |
266 } | 178 fileDialogType = Save; |
267 protected int getXFileName(OPENFILENAME* ofn) { | |
268 return GetSaveFileName(ofn); | |
269 } | 179 } |
270 } | 180 } |