1 /**
2  * Dialog module.
3  *
4  * License:
5  *     MIT. See LICENSE for full details.
6  */
7 module tkd.window.dialog.messagedialog;
8 
9 /**
10  * Imports.
11  */
12 import std.conv;
13 import std.regex;
14 import tkd.window.dialog.dialog;
15 import tkd.window.window;
16 
17 /**
18  * Pops up a dialog box with a user defined message and buttons.
19  *
20  * Example:
21  * ---
22  * auto dialog = new MessageDialog("Save file?")
23  * 	.setIcon(MessageDialogIcon.question)
24  * 	.setMessage("Do you want to save this file?")
25  * 	.setType(MessageDialogType.okcancel)
26  * 	.show();
27  *
28  * string buttonClicked = dialog.getResult();
29  * ---
30  *
31  * Result:
32  *     The symbolic name of the button pressed.
33  *
34  * See_Also:
35  *     $(LINK2 ./dialog.html, tkd.dialog.dialog) $(BR)
36  */
37 class MessageDialog : Dialog
38 {
39 	/**
40 	 * The symbolic name of the default button for this message window ('ok', 
41 	 * 'cancel', and so on).
42 	 */
43 	private string _defaultButton;
44 
45 	/**
46 	 * Specifies an auxiliary message to the main message.
47 	 */
48 	private string _detailMessage;
49 
50 	/**
51 	 * Specifies an icon to display.
52 	 */
53 	private string _icon = MessageDialogIcon.info;
54 
55 	/**
56 	 * Specifies the message to display in this message box.
57 	 */
58 	private string _message;
59 
60 	/**
61 	 * Arranges for a predefined set of buttons to be displayed.
62 	 */
63 	private string _type = MessageDialogType.ok;
64 
65 	/**
66 	 * Construct the dialog.
67 	 *
68 	 * Params:
69 	 *     parent = The parent window of the dialog.
70 	 *     title = The title of the dialog.
71 	 */
72 	this(Window parent, string title = "Information")
73 	{
74 		super(parent, title);
75 	}
76 
77 	/**
78 	 * Construct the dialog.
79 	 *
80 	 * Params:
81 	 *     title = The title of the dialog.
82 	 */
83 	this(string title = "Information")
84 	{
85 		this(null, title);
86 	}
87 
88 	/**
89 	 * Set the default button.
90 	 *
91 	 * Params:
92 	 *     button = The default button to use.
93 	 *
94 	 * Returns:
95 	 *     This dialog to aid method chaining.
96 	 *
97 	 * See_Also:
98 	 *     $(LINK2 ./messagedialog.html#MessageDialogButton, tkd.dialog.messagedialog.MessageDialogButton) $(BR)
99 	 */
100 	public auto setDefaultButton(this T)(string button)
101 	{
102 		this._defaultButton = button;
103 
104 		return cast(T) this;
105 	}
106 
107 	/**
108 	 * Set the detail message. The message detail will be presented beneath the 
109 	 * main message and, where supported by the OS, in a less emphasized font 
110 	 * than the main message.
111 	 *
112 	 * Params:
113 	 *     message = The detail message.
114 	 *
115 	 * Returns:
116 	 *     This dialog to aid method chaining.
117 	 */
118 	public auto setDetailMessage(this T)(string message)
119 	{
120 		this._detailMessage = message;
121 
122 		return cast(T) this;
123 	}
124 
125 	/**
126 	 * Set the preset dialog icon. It must be one of the following: error, 
127 	 * info, question or warning.
128 	 *
129 	 * Params:
130 	 *     icon = The preset dialog icon.
131 	 *
132 	 * Returns:
133 	 *     This dialog to aid method chaining.
134 	 *
135 	 * See_Also:
136 	 *     $(LINK2 ./messagedialog.html#MessageDialogIcon, tkd.dialog.messagedialog.MessageDialogIcon) $(BR)
137 	 */
138 	public auto setIcon(this T)(string icon)
139 	{
140 		this._icon = icon;
141 
142 		return cast(T) this;
143 	}
144 
145 	/**
146 	 * Set the main message to display in the message dialog.
147 	 *
148 	 * Params:
149 	 *     message = The message.
150 	 *
151 	 * Returns:
152 	 *     This dialog to aid method chaining.
153 	 */
154 	public auto setMessage(this T)(string message)
155 	{
156 		this._message = message;
157 
158 		return cast(T) this;
159 	}
160 
161 	/**
162 	 * Set the message dialog type. Arranges for a predefined set of buttons to 
163 	 * be displayed.
164 	 *
165 	 * Params:
166 	 *     type = The type of the message dialog.
167 	 *
168 	 * Returns:
169 	 *     This dialog to aid method chaining.
170 	 *
171 	 * See_Also:
172 	 *     $(LINK2 ./messagedialog.html#MessageDialogType, tkd.dialog.messagedialog.MessageDialogType) $(BR)
173 	 */
174 	public auto setType(this T)(string type)
175 	{
176 		this._type = type;
177 
178 		return cast(T) this;
179 	}
180 
181 	/**
182 	 * Check the default button and if not set, set it.
183 	 */
184 	private void checkDefaultButton()
185 	{
186 		if (!this._defaultButton)
187 		{
188 			switch(this._type)
189 			{
190 				case MessageDialogType.abortretryignore:
191 					this._defaultButton = MessageDialogButton.abort;
192 					break;
193 
194 				case MessageDialogType.retrycancel:
195 					this._defaultButton = MessageDialogButton.retry;
196 					break;
197 
198 				case MessageDialogType.yesno:
199 				case MessageDialogType.yesnocancel:
200 					this._defaultButton = MessageDialogButton.yes;
201 					break;
202 
203 				default:
204 					this._defaultButton = MessageDialogButton.ok;
205 					break;
206 			}
207 		}
208 	}
209 
210 	/**
211 	 * Show the dialog.
212 	 *
213 	 * Returns:
214 	 *     This dialog to aid method chaining.
215 	 */
216 	public auto show(this T)()
217 	{
218 		this.checkDefaultButton();
219 
220 		// String concatenation is used to build the script here instead of 
221 		// using format specifiers to enable supporting input which includes 
222 		// Tcl/Tk reserved characters and elements that could be construed as 
223 		// format specifiers.
224 		string script;
225 
226 		if (this._parent)
227 		{
228 			script = text(
229 				`tk_messageBox -parent `, this._parent.id,
230 				` -title {`, this._title, `}`,
231 				` -default {`, this._defaultButton, `}`,
232 				` -detail {`, this._detailMessage, `}`,
233 				` -icon {`, this._icon, `}`,
234 				` -message {`, this._message, `}`,
235 				` -type {`, this._type, `}`
236 			);
237 		}
238 		else
239 		{
240 			script = text(
241 				`tk_messageBox -title {`, this._title, `}`,
242 				` -default {`, this._defaultButton, `}`,
243 				` -detail {`, this._detailMessage, `}`,
244 				` -icon {`, this._icon, `}`,
245 				` -message {`, this._message, `}`,
246 				` -type {`, this._type, `}`
247 			);
248 		}
249 
250 		this._tk.eval(script);
251 		this._results = [this._tk.getResult!(string)];
252 
253 		return cast(T) this;
254 	}
255 }
256 
257 /**
258  * Symbolic names for message dialog buttons.
259  */
260 enum MessageDialogButton : string
261 {
262 	abort  = "abort",  /// The 'abort' button.
263 	cancel = "cancel", /// The 'cancel' button.
264 	ignore = "ignore", /// The 'ignore' button.
265 	no     = "no",     /// The 'no' button.
266 	ok     = "ok",     /// The 'ok' button.
267 	retry  = "retry",  /// The 'retry' button.
268 	yes    = "yes",    /// The 'yes' button.
269 }
270 
271 /**
272  * Preset icons used for the message dialog.
273  */
274 enum MessageDialogIcon : string
275 {
276 	error    = "error",    /// An error icon.
277 	info     = "info",     /// An information icon
278 	question = "question", /// A question icon.
279 	warning  = "warning",  /// A warning icon.
280 }
281 
282 /**
283  * Arranges for a predefined set of buttons to be displayed. The following 
284  * values are possible for predefinedType:
285  */
286 enum MessageDialogType : string
287 {
288 	abortretryignore = "abortretryignore", /// Displays three buttons whose symbolic names are abort, retry and ignore.
289 	ok               = "ok",               /// Displays one button whose symbolic name is ok.
290 	okcancel         = "okcancel",         /// Displays two buttons whose symbolic names are ok and cancel.
291 	retrycancel      = "retrycancel",      /// Displays two buttons whose symbolic names are retry and cancel.
292 	yesno            = "yesno",            /// Displays two buttons whose symbolic names are yes and no.
293 	yesnocancel      = "yesnocancel",      /// Displays three buttons whose symbolic names are yes, no and cancel.
294 }