1 // Copyright (c) 2017 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.sdl2.sdl : DerelictSDL2, SharedLibVersion, SharedLibLoadException;
70 		try {
71 			DerelictSDL2.load(SharedLibVersion(2, 0, 2));
72 			is_sdl2_loadable = true;
73 			stdout.writefln("SDL was found ...");
74 		} catch (SharedLibLoadException) {
75 			stdout.writefln("SDL was NOT found ...");
76 		}
77 	}
78 }
79 
80 /++
81 This should be called once at the start of a program. It generates the proper
82 main function for your environment (win32/posix/dmain) and boot straps the
83 main loop for the GUI. This will call your UIAppMain function when ready.
84 +/
85 mixin template RUN_MAIN() {
86 	version (unittest) { } else {
87 		// On Windows use the normal dlangui main
88 		version (Windows) {
89 			import dlangui;
90 			mixin APP_ENTRY_POINT;
91 		// On Linux use a custom main that checks if SDL is installed
92 		} else {
93 			int main(string[] args) {
94 				import message_box : is_sdl2_loadable;
95 				// If SDL2 can be loaded, start the SDL2 main
96 				if (is_sdl2_loadable) {
97 					import dlangui.platforms.sdl.sdlapp : sdlmain;
98 					return sdlmain(args);
99 				// If not, use the normal main provided by the user
100 				} else {
101 					return UIAppMain(args);
102 				}
103 			}
104 		}
105 	}
106 }
107 
108 
109 /++
110 If true will print output of external program to console.
111 Params:
112  is_logging = If true will print to output
113 +/
114 public void setUseLog(bool is_logging) {
115 	use_log = is_logging;
116 }
117 
118 /++
119 Returns if external program logging is on or off.
120 +/
121 public bool getUseLog() {
122 	return use_log;
123 }
124 
125 /++
126 The type of icon to show in the message box. Some message boxes will not show
127 the icon.
128 
129 ----
130 enum IconType {
131 	None,
132 	Information,
133 	Error,
134 	Warning,
135 }
136 ----
137 +/
138 
139 enum IconType {
140 	None,
141 	Information,
142 	Error,
143 	Warning,
144 }
145 
146 abstract class MessageBoxBase {
147 	this(string title, string message, IconType icon_type) {
148 		_title = title;
149 		_message = message;
150 		_icon_type = icon_type;
151 	}
152 
153 	void onError(void delegate(Throwable err) cb) {
154 		_on_error_cb = cb;
155 	}
156 
157 	void fireOnError(Throwable err) {
158 		auto old_cb = _on_error_cb;
159 		_on_error_cb = null;
160 
161 		if (old_cb) old_cb(err);
162 	}
163 
164 	void show();
165 
166 	string _title;
167 	string _message;
168 	IconType _icon_type;
169 	void delegate(Throwable err) _on_error_cb;
170 }
171 
172 /++
173 The MessageBox class
174 +/
175 class MessageBox {
176 	import message_box_dlangui : MessageBoxDlangUI;
177 	import message_box_sdl : MessageBoxSDL;
178 	import message_box_win32 : MessageBoxWin32;
179 	import message_box_zenity : MessageBoxZenity;
180 	import message_box_kdialog : MessageBoxKdialog;
181 	import message_box_gxmessage : MessageBoxGxmessage;
182 
183 	/++
184 	Sets up the message box with the desired title, message, and icon. Does not
185 	show it until the show method is called.
186 	Params:
187 	 title = The string to show in the message box title
188 	 message = The string to show in the message box body
189 	 icon = The type of icon to show in the message box
190 	Throws:
191 	 If it fails to find any programs or libraries to make a message box with.
192 	+/
193 	this(string title, string message, IconType icon_type) {
194 		if (MessageBoxDlangUI.isSupported()) {
195 			_dialog = new MessageBoxDlangUI(title, message, icon_type);
196 		} else if (MessageBoxSDL.isSupported()) {
197 			_dialog = new MessageBoxSDL(title, message, icon_type);
198 		} else if (MessageBoxWin32.isSupported()) {
199 			_dialog = new MessageBoxWin32(title, message, icon_type);
200 		} else if (MessageBoxZenity.isSupported()) {
201 			_dialog = new MessageBoxZenity(title, message, icon_type);
202 		} else if (MessageBoxKdialog.isSupported()) {
203 			_dialog = new MessageBoxKdialog(title, message, icon_type);
204 		} else if (MessageBoxGxmessage.isSupported()) {
205 			_dialog = new MessageBoxGxmessage(title, message, icon_type);
206 		} else {
207 			throw new Exception("Failed to find a way to make a message box.");
208 		}
209 	}
210 
211 	/++
212 	This method is called if there is an error when showing the message box.
213 	Params:
214 	 cb = The call back to fire when there is an error.
215 	+/
216 	void onError(void delegate(Throwable err) cb) {
217 		_dialog._on_error_cb = cb;
218 	}
219 
220 	/++
221 	Shows the message box. Will block until it is closed.
222 	+/
223 	void show() {
224 		_dialog.show();
225 	}
226 
227 	MessageBoxBase _dialog;
228 }