Mercurial > projects > dynamin
view 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 |
line wrap: on
line source
// 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 <jminer7@gmail.com> * */ 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; // not used by programs struct FileDialogFilter { string name; string[] extensions; // ignored for now bool delegate(string fileName) shouldShow; } /** * As this is an abstract class, use OpenFileDialog or SaveFileDialog instead. * * TODO: figure out a way to allow the user to type in a custom filter? * TODO: on Linux, use a GTK dialog if available, otherwise use a custom one. * * The appearance of a file dialog with Windows Classic: * * $(IMAGE ../web/example_file_dialog.png) */ abstract class FileDialog { protected: bool _multipleSelection; string _initialFileName; string _text; string _directory; string[] _files; FileDialogFilter[] _filters; int _selectedFilter; public: /** * Adds a filter that only shows files with the specified extensions. * Note that the "All Files (*.*)" filter is added automatically when the * dialog is shown if not added previously because I don't like when * programs don't let me have control over what extension to use and * don't let me be able to see all the files. * Examples: * ----- * dialog.addFilter("All Files (*.*)"); * dialog.addFilter("Cascading Style Sheets (*.css)", "css"); * dialog.addFilter("Web Pages (*.html, *.htm)", "html", "htm"); * ----- */ void addFilter(string name, string[] exts...) { FileDialogFilter filter; filter.name = name; filter.extensions = exts; _filters.length = _filters.length + 1; _filters[_filters.length-1] = filter; } /** * Adds a filter that only shows files with which the specified delegate * returns true for. * BUG: not implemented */ void addFilter(string name, string ext, bool delegate(string fileName) shouldShow) { // TODO: throw new Exception("addFilter(string, string, delegate) not implemented"); FileDialogFilter filter; filter.name = name; filter.extensions = [ext]; filter.shouldShow = shouldShow; _filters.length = _filters.length + 1; _filters[_filters.length-1] = filter; } /// Gets or sets the selected filter. An index of 0 is the first one added. int selectedFilter() { return _selectedFilter; } /// ditto void selectedFilter(int index) { _selectedFilter = index; } /** * Gets or sets whether more than one file can be selected. * The default is true for an OpenFileDialog and false for SaveFileDialog. */ bool multipleSelection() { return _multipleSelection; } /// ditto void multipleSelection(bool b) { _multipleSelection = b; } /// Gets or sets the text that is displayed in the dialog's title bar. string text() { return _text; } /// ditto void text(string str) { _text = str; } /** * Sets the text in the file name text box to the specified string. * Example: * ----- * dialog.initialFileName = "Untitled"; * ----- */ void initialFileName(string str) { // TODO: make sure str is not a path? _initialFileName = str; } /** * Sets the directory that the FileDialog shows. If this is null, * the default directory is used when the dialog is first shown. * After the dialog has been shown, this is set to the directory * the user was last looking at. */ void directory(string str) { _directory = str; } /// TODO: Should this be SelectedDirectory ? string directory() { return _directory; } /** * Gets the files selected by the user. * If the user did not type a file name extension, the correct one * will be added according to the selected filter. */ 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? // 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; } } /// class OpenFileDialog : FileDialog { this() { _multipleSelection = true; // different settings } protected int getXFileName(OPENFILENAME* ofn) { return GetOpenFileName(ofn); } } /// class SaveFileDialog : FileDialog { this() { _multipleSelection = false; // different settings } protected int getXFileName(OPENFILENAME* ofn) { return GetSaveFileName(ofn); } }