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