view dynamin/gui/file_dialog.d @ 106:acdbb30fee7e

Port to D2. Most of the effort was dealing with immutable and const.
author Jordan Miner <jminer7@gmail.com>
date Mon, 17 Dec 2012 23:41:50 -0600
parents 73060bc3f004
children
line wrap: on
line source


/*
 * Copyright Jordan Miner
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

module dynamin.gui.file_dialog;

import dynamin.all_core;
import dynamin.gui_backend;

// not used by programs
struct FileDialogFilter {
	string name;
	string[] extensions;
	// ignored for now
	bool delegate(cstring 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 {
private:
	mixin FileDialogBackend;
protected:
	bool _multipleSelection;
	string _initialFileName;
	string _text;
	string _folder;
	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.
	 * 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(cstring 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 folder that the FileDialog shows. If this is null,
	 * the default folder is used when the dialog is first shown.
	 * After the dialog has been shown, this is set to the folder
	 * the user was last looking at.
	 */
	void folder(string str) {
		_folder = str;
	}
	/// TODO: Should this be selectedFolder ?
	string folder() {
		return _folder;
	}

	/**
	 * 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]; }

	// TODO: parameters
	// TODO: should showDialog take any parameters?
	//       what should happen if no owner is set?
	//       WinForms sets the owner to the currently active window in the app
	//       do the same? or have no owner (annoying, as window can get below)?
	DialogResult showDialog() {
		return backend_showDialog();
	}
	private void ensureAllFilesFilter() {
		foreach(filter; _filters)
			if(filter.extensions.length == 0)
				return;
		addFilter("All Files (*.*)");
	}
	private void maybeAddExt(ref string file) {
		auto selFilter = _filters[selectedFilter];

		// return if the "All Files (*.*)" filter is selected
		if(selFilter.extensions.length == 0)
			return;

		// return if the file already has an extension from the selected filter
		foreach(ext; selFilter.extensions)
			if(file.downcase().endsWith(ext.downcase()))
				return;

		file ~= "." ~ selFilter.extensions[0].downcase();
	}
}

///
class OpenFileDialog : FileDialog {
	this() {
		_multipleSelection = true;
		// different settings
		fileDialogType = Open;
	}
}

///
class SaveFileDialog : FileDialog {
	this() {
		_multipleSelection = false;
		// different settings
		fileDialogType = Save;
	}
}