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 }