1 // Copyright (c) 2017-2018 Matthew Brennan Jones <matthew.brennan.jones@gmail.com>
2 // Boost Software License - Version 1.0
3 // A simple message box for the D programming language
4 // https://github.com/workhorsy/d-message-box
5 
6 
7 /++
8 A simple message box for the D programming language
9 
10 It should work without requiring any 3rd party GUI toolkits. But will work with what
11 it can find on your OS at runtime.
12 
13 It tries to use the following:
14 
15 * DlangUI (win32 on Windows or SDL2 on Linux)
16 
17 * SDL_ShowSimpleMessageBox (Derelict SDL2)
18 
19 * MessageBoxW (Windows)
20 
21 * Zenity (Gtk/Gnome)
22 
23 * Kdialog (KDE)
24 
25 * gxmessage (X11)
26 
27 Home page:
28 $(LINK https://github.com/workhorsy/d-message-box)
29 
30 Version: 0.3.0
31 
32 License:
33 Boost Software License - Version 1.0
34 
35 Examples:
36 ----
37 import std.stdio : stdout, stderr;
38 import message_box : MessageBox, IconType, RUN_MAIN;
39 
40 mixin RUN_MAIN;
41 
42 extern (C) int UIAppMain(string[] args) {
43 	// Create the message box
44 	auto dialog = new MessageBox("Party Time", "The roof is on fire!", IconType.Warning);
45 
46 	// Set the error handler
47 	dialog.onError((Throwable err) {
48 		stderr.writefln("Failed to show message box: %s", err);
49 	});
50 
51 	// Show the message box
52 	dialog.show();
53 
54 	return 0;
55 }
56 ----
57 +/
58 
59 module message_box;
60 
61 bool is_sdl2_loadable = false;
62 bool use_log = false;
63 
64 static this() {
65 	import std.stdio : stdout;
66 
67 	// Figure out if the SDL2 libraries can be loaded
68 	version (Have_derelict_sdl2) {
69 		import derelict.util.exception : SharedLibLoadException;
70 		import derelict.sdl2.sdl : DerelictSDL2, SharedLibVersion;
71 		try {
72 			DerelictSDL2.load(SharedLibVersion(2, 0, 2));
73 			is_sdl2_loadable = true;
74 			stdout.writefln("SDL was found ...");
75 		} catch (SharedLibLoadException) {
76 			stdout.writefln("SDL was NOT found ...");
77 		}
78 	}
79 }
80 
81 /++
82 This should be called once at the start of a program. It generates the proper
83 main function for your environment (win32/posix/dmain) and boot straps the
84 main loop for the GUI. This will call your UIAppMain function when ready.
85 +/
86 mixin template RUN_MAIN() {
87 	version (unittest) { } else {
88 		// On Windows use the normal dlangui main
89 		version (Windows) {
90 			import dlangui;
91 			mixin APP_ENTRY_POINT;
92 		// On Linux use a custom main that checks if SDL is installed
93 		} else {
94 			int main(string[] args) {
95 				import message_box : is_sdl2_loadable;
96 				// If SDL2 can be loaded, start the SDL2 main
97 				if (is_sdl2_loadable) {
98 					import dlangui.platforms.sdl.sdlapp : sdlmain;
99 					return sdlmain(args);
100 				// If not, use the normal main provided by the user
101 				} else {
102 					return UIAppMain(args);
103 				}
104 			}
105 		}
106 	}
107 }
108 
109 
110 /++
111 If true will print output of external program to console.
112 Params:
113  is_logging = If true will print to output
114 +/
115 public void setUseLog(bool is_logging) {
116 	use_log = is_logging;
117 }
118 
119 /++
120 Returns if external program logging is on or off.
121 +/
122 public bool getUseLog() {
123 	return use_log;
124 }
125 
126 /++
127 The type of icon to show in the message box. Some message boxes will not show
128 the icon.
129 
130 ----
131 enum IconType {
132 	None,
133 	Information,
134 	Error,
135 	Warning,
136 }
137 ----
138 +/
139 
140 enum IconType {
141 	None,
142 	Information,
143 	Error,
144 	Warning,
145 }
146 
147 abstract class MessageBoxBase {
148 	this(string title, string message, IconType icon_type) {
149 		_title = title;
150 		_message = message;
151 		_icon_type = icon_type;
152 	}
153 
154 	void onError(void delegate(Throwable err) cb) {
155 		_on_error_cb = cb;
156 	}
157 
158 	void fireOnError(Throwable err) {
159 		auto old_cb = _on_error_cb;
160 		_on_error_cb = null;
161 
162 		if (old_cb) old_cb(err);
163 	}
164 
165 	void show();
166 
167 	string _title;
168 	string _message;
169 	IconType _icon_type;
170 	void delegate(Throwable err) _on_error_cb;
171 }
172 
173 /++
174 The MessageBox class
175 +/
176 class MessageBox {
177 	import message_box_dlangui : MessageBoxDlangUI;
178 	import message_box_sdl : MessageBoxSDL;
179 	import message_box_win32 : MessageBoxWin32;
180 	import message_box_zenity : MessageBoxZenity;
181 	import message_box_kdialog : MessageBoxKdialog;
182 	import message_box_gxmessage : MessageBoxGxmessage;
183 
184 	/++
185 	Sets up the message box with the desired title, message, and icon. Does not
186 	show it until the show method is called.
187 	Params:
188 	 title = The string to show in the message box title
189 	 message = The string to show in the message box body
190 	 icon = The type of icon to show in the message box
191 	Throws:
192 	 If it fails to find any programs or libraries to make a message box with.
193 	+/
194 	this(string title, string message, IconType icon_type) {
195 		if (MessageBoxDlangUI.isSupported()) {
196 			_dialog = new MessageBoxDlangUI(title, message, icon_type);
197 		} else if (MessageBoxSDL.isSupported()) {
198 			_dialog = new MessageBoxSDL(title, message, icon_type);
199 		} else if (MessageBoxWin32.isSupported()) {
200 			_dialog = new MessageBoxWin32(title, message, icon_type);
201 		} else if (MessageBoxZenity.isSupported()) {
202 			_dialog = new MessageBoxZenity(title, message, icon_type);
203 		} else if (MessageBoxKdialog.isSupported()) {
204 			_dialog = new MessageBoxKdialog(title, message, icon_type);
205 		} else if (MessageBoxGxmessage.isSupported()) {
206 			_dialog = new MessageBoxGxmessage(title, message, icon_type);
207 		} else {
208 			throw new Exception("Failed to find a way to make a message box.");
209 		}
210 	}
211 
212 	/++
213 	This method is called if there is an error when showing the message box.
214 	Params:
215 	 cb = The call back to fire when there is an error.
216 	+/
217 	void onError(void delegate(Throwable err) cb) {
218 		_dialog._on_error_cb = cb;
219 	}
220 
221 	/++
222 	Shows the message box. Will block until it is closed.
223 	+/
224 	void show() {
225 		_dialog.show();
226 	}
227 
228 	MessageBoxBase _dialog;
229 }