# HG changeset patch # User Jordan Miner # Date 1245511411 18000 # Node ID fc2420d39e3cf06aff92423832fd224f068f9f50 # Parent d806edad4300c307221056ece19e114fd0e2a4e3 Separate out Windows backend of FileDialog. diff -r d806edad4300 -r fc2420d39e3c dynamin/gui/file_dialog.d --- a/dynamin/gui/file_dialog.d Mon Jun 15 22:19:30 2009 -0500 +++ b/dynamin/gui/file_dialog.d Sat Jun 20 10:23:31 2009 -0500 @@ -25,11 +25,8 @@ module dynamin.gui.file_dialog; -import dynamin.c.windows; import dynamin.all_core; -import dynamin.gui.window; -import tango.io.Stdout; -import Utf = tango.text.convert.Utf; +import dynamin.gui_backend; // not used by programs struct FileDialogFilter { @@ -50,6 +47,8 @@ * $(IMAGE ../web/example_file_dialog.png) */ abstract class FileDialog { +private: + mixin FileDialogBackend; protected: bool _multipleSelection; string _initialFileName; @@ -58,6 +57,14 @@ string[] _files; FileDialogFilter[] _filters; int _selectedFilter; + + uint fileDialogType; + enum { + Open, Save + } + invariant() { + assert(fileDialogType == Open || fileDialogType == Save); + } public: /** * Adds a filter that only shows files with the specified extensions. @@ -143,107 +150,14 @@ string[] files() { return _files; } /// Gets the first of the files selected by the user. string file() { return _files[0]; } - protected int getXFileName(OPENFILENAME* ofn); + // TODO: parameters - // TODO: should ShowDialog take any parameters? + // TODO: should showDialog take any parameters? // what should happen if no owner is set? // Windows Forms sets the owner to the currently active window in the application // do the same? or have no owner (really annoying, as window can get below)? DialogResult showDialog() { - OPENFILENAME ofn; - ofn.lStructSize = OPENFILENAME.sizeof; - //ofn.hwndOwner = ; - - bool allFilesFilter = false; - foreach(filter; _filters) - if(filter.extensions.length == 0) - allFilesFilter = true; - if(!allFilesFilter) addFilter("All Files (*.*)"); - - string filterStr; - foreach(filter; _filters) { - if(filter.shouldShow) - continue; - string[] exts = filter.extensions.dup; - if(exts.length == 0) - exts = [cast(string)"*.*"]; - else - for(int i = 0; i < exts.length; ++i) - exts[i] = "*." ~ exts[i]; - filterStr ~= filter.name ~ "\0" ~ exts.join(";") ~ "\0"; - } - filterStr ~= "\0"; - ofn.lpstrFilter = filterStr.toWcharPtr(); - ofn.nFilterIndex = _selectedFilter + 1; - wchar[] filesBufferW = Utf.toString16(_initialFileName~"\0"); - filesBufferW.length = 4096; - scope(exit) delete filesBufferW; - ofn.lpstrFile = filesBufferW.ptr; - ofn.nMaxFile = filesBufferW.length; - ofn.lpstrInitialDir = _directory.toWcharPtr(); - ofn.lpstrTitle = _text.toWcharPtr(); - ofn.Flags = OFN_EXPLORER; - //if(canChooseLinks) - // ofn.Flags |= OFN_NODEREFERENCELINKS; - ofn.Flags |= OFN_FILEMUSTEXIST; - ofn.Flags |= OFN_HIDEREADONLY; - ofn.Flags |= OFN_OVERWRITEPROMPT; - if(_multipleSelection) - ofn.Flags |= OFN_ALLOWMULTISELECT; - - if(!getXFileName(&ofn)) { - if(CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) - MessageBoxW(null, "Too many files picked.", "Error", 0); - return DialogResult.Cancel; - } - - _selectedFilter = ofn.nFilterIndex - 1; - // must zero FFFF chars here because the - // parsing here assumes the unused part of the string is zeroed - foreach(i, c; filesBufferW) - if(c == 0xFFFF) - filesBufferW[i] = 0; - int index; // index of null char right after the last non-null char - for(index = filesBufferW.length; index > 0; --index) - if(filesBufferW[index-1] != 0) - break; - auto filesBuffer = Utf.toString(filesBufferW[0..index]); - scope(exit) delete filesBuffer; - if(filesBuffer.contains('\0')) { // multiple files - auto arr = filesBuffer.split("\0"); - _directory = arr[0]; - // make sure directory ends with a backslash - // "C:\" does but "C:\Program Files" does not - if(!_directory.endsWith("\\")) - _directory ~= "\\"; - _files = new string[arr.length-1]; - for(int i = 1; i < arr.length; ++i) { - if(arr[i].contains('\\')) // a dereferenced link--absolute - _files[i-1] = arr[i]; - else - _files[i-1] = _directory ~ arr[i]; - } - } else { //single file - assert(filesBuffer.contains('\\')); - _directory = filesBuffer[0..filesBuffer.findLast("\\")].dup; - _files = [filesBuffer.dup]; - } - - // if "All Files (*.*)" filter is not selected - if(_filters[selectedFilter].extensions.length > 0) { - // go over every chosen file and add the selected filter's - // extension if the file doesn't already have one from the selected filter - for(int i = 0; i < _files.length; ++i) { - bool validExt = false; - foreach(ext; _filters[selectedFilter].extensions) - if(_files[i].downcase().endsWith(ext.downcase())) - validExt = true; - if(!validExt) - _files[i] ~= "." ~ _filters[selectedFilter].extensions[0].downcase(); - } - } - - return DialogResult.OK; + return backend_showDialog(); } } @@ -252,9 +166,7 @@ this() { _multipleSelection = true; // different settings - } - protected int getXFileName(OPENFILENAME* ofn) { - return GetOpenFileName(ofn); + fileDialogType = Open; } } @@ -263,8 +175,6 @@ this() { _multipleSelection = false; // different settings - } - protected int getXFileName(OPENFILENAME* ofn) { - return GetSaveFileName(ofn); + fileDialogType = Save; } } diff -r d806edad4300 -r fc2420d39e3c dynamin/gui/windows_file_dialog.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dynamin/gui/windows_file_dialog.d Sat Jun 20 10:23:31 2009 -0500 @@ -0,0 +1,133 @@ +// Written in the D programming language +// www.digitalmars.com/d/ + +/* + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Dynamin library. + * + * The Initial Developer of the Original Code is Jordan Miner. + * Portions created by the Initial Developer are Copyright (C) 2006-2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jordan Miner + * + */ + +module dynamin.gui.windows_file_dialog; + +public import dynamin.c.windows; +public import Utf = tango.text.convert.Utf; + +template FileDialogBackend() { + DialogResult backend_showDialog() { + OPENFILENAME ofn; + ofn.lStructSize = OPENFILENAME.sizeof; + //ofn.hwndOwner = ; + + bool allFilesFilter = false; + foreach(filter; _filters) + if(filter.extensions.length == 0) + allFilesFilter = true; + if(!allFilesFilter) addFilter("All Files (*.*)"); + + string filterStr; + foreach(filter; _filters) { + if(filter.shouldShow) + continue; + string[] exts = filter.extensions.dup; + if(exts.length == 0) + exts = [cast(string)"*.*"]; + else + for(int i = 0; i < exts.length; ++i) + exts[i] = "*." ~ exts[i]; + filterStr ~= filter.name ~ "\0" ~ exts.join(";") ~ "\0"; + } + filterStr ~= "\0"; + ofn.lpstrFilter = filterStr.toWcharPtr(); + ofn.nFilterIndex = _selectedFilter + 1; + wchar[] filesBufferW = Utf.toString16(_initialFileName~"\0"); + // TODO: should use a static buffer of 4096, passing to toString16 + // avoid concat too + filesBufferW.length = 4096; + scope(exit) delete filesBufferW; + ofn.lpstrFile = filesBufferW.ptr; + ofn.nMaxFile = filesBufferW.length; + ofn.lpstrInitialDir = _directory.toWcharPtr(); + ofn.lpstrTitle = _text.toWcharPtr(); + ofn.Flags = OFN_EXPLORER; + //if(canChooseLinks) + // ofn.Flags |= OFN_NODEREFERENCELINKS; + ofn.Flags |= OFN_FILEMUSTEXIST; + ofn.Flags |= OFN_HIDEREADONLY; + ofn.Flags |= OFN_OVERWRITEPROMPT; + if(_multipleSelection) + ofn.Flags |= OFN_ALLOWMULTISELECT; + + auto GetFileName = fileDialogType == Open ? + &GetOpenFileName : &GetSaveFileName ; + if(!GetFileName(&ofn)) { + if(CommDlgExtendedError() == FNERR_BUFFERTOOSMALL) + MessageBoxW(null, "Too many files picked.", "Error", 0); + return DialogResult.Cancel; + } + + _selectedFilter = ofn.nFilterIndex - 1; + // must zero FFFF chars here because the + // parsing here assumes the unused part of the string is zeroed + foreach(i, c; filesBufferW) + if(c == 0xFFFF) + filesBufferW[i] = 0; + int index; // index of null char right after the last non-null char + for(index = filesBufferW.length; index > 0; --index) + if(filesBufferW[index-1] != 0) + break; + auto filesBuffer = Utf.toString(filesBufferW[0..index]); + scope(exit) delete filesBuffer; + if(filesBuffer.contains('\0')) { // multiple files + auto arr = filesBuffer.split("\0"); + _directory = arr[0]; + // make sure directory ends with a backslash + // "C:\" does but "C:\Program Files" does not + if(!_directory.endsWith("\\")) + _directory ~= "\\"; + _files = new string[arr.length-1]; + for(int i = 1; i < arr.length; ++i) { + if(arr[i].contains('\\')) // a dereferenced link--absolute + _files[i-1] = arr[i]; + else + _files[i-1] = _directory ~ arr[i]; + } + } else { //single file + assert(filesBuffer.contains('\\')); + _directory = filesBuffer[0..filesBuffer.findLast("\\")].dup; + _files = [filesBuffer.dup]; + } + + // if "All Files (*.*)" filter is not selected + if(_filters[selectedFilter].extensions.length > 0) { + // go over every chosen file and add the selected filter's + // extension if the file doesn't already have one from the selected filter + for(int i = 0; i < _files.length; ++i) { + bool validExt = false; + foreach(ext; _filters[selectedFilter].extensions) + if(_files[i].downcase().endsWith(ext.downcase())) + validExt = true; + if(!validExt) + _files[i] ~= "." ~ _filters[selectedFilter].extensions[0].downcase(); + } + } + + return DialogResult.OK; + } +} + diff -r d806edad4300 -r fc2420d39e3c dynamin/gui_backend.d --- a/dynamin/gui_backend.d Mon Jun 15 22:19:30 2009 -0500 +++ b/dynamin/gui_backend.d Sat Jun 20 10:23:31 2009 -0500 @@ -29,7 +29,7 @@ public import dynamin.gui.windows_clipboard; public import dynamin.gui.windows_cursor; //public import dynamin.gui.windows_directory_dialog; - //public import dynamin.gui.windows_file_dialog; + public import dynamin.gui.windows_file_dialog; //public import dynamin.gui.windows_screen; public import dynamin.gui.windows_window; } else {