1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 module dgui.application;
10 
11 pragma(lib, "gdi32.lib");
12 pragma(lib, "comdlg32.lib");
13 
14 import std.path;
15 private import dgui.core.winapi;
16 private import dgui.core.utils;
17 private import dgui.richtextbox;
18 private import dgui.form;
19 private import dgui.button;
20 private import dgui.label;
21 private import std.utf: toUTFz;
22 private import std.file;
23 private import std.conv;
24 public import dgui.resources;
25 
26 private enum
27 {
28 	info = "Exception Information:",
29 	xpManifestFile = "dgui.xml.manifest",
30 	errMsg = "An application exception has occured.\r\n1) Click \"Ignore\" to continue (The program can be unstable).\r\n2) Click \"Quit\" to exit.\r\n",
31 	xpManifest = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>` "\r\n"
32 					`<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">` "\r\n"
33 					  `<assemblyIdentity` "\r\n"
34 						`version="1.0.0.0"` "\r\n"
35 						`processorArchitecture="X86"` "\r\n"
36 						`name="client"` "\r\n"
37 						`type="win32"` "\r\n"
38 						`/>` "\r\n"
39 						`<description></description>` "\r\n"
40 						"\r\n"
41 						`<!-- Enable Windows XP and higher themes with common controls -->` "\r\n"
42 						`<dependency>` "\r\n"
43 							`<dependentAssembly>` "\r\n"
44 							  `<assemblyIdentity` "\r\n"
45 								`type="win32"` "\r\n"
46 								`name="Microsoft.Windows.Common-Controls"` "\r\n"
47 								`version="6.0.0.0"` "\r\n"
48 								`processorArchitecture="X86"` "\r\n"
49 								`publicKeyToken="6595b64144ccf1df"` "\r\n"
50 								`language="*"` "\r\n"
51 						 	    `/>` "\r\n"
52 							`</dependentAssembly>` "\r\n"
53 						 `</dependency>` "\r\n"
54 						  "\r\n"
55 						  `<!-- Disable Windows Vista UAC compatibility heuristics -->` "\r\n"
56 						  `<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">` "\r\n"
57 							`<security>` "\r\n"
58 								`<requestedPrivileges>` "\r\n"
59 									`<requestedExecutionLevel level="asInvoker"/>` "\r\n"
60 								`</requestedPrivileges>` "\r\n"
61 							`</security>` "\r\n"
62 						  `</trustInfo> ` "\r\n"
63 						  "\r\n"
64 						  `<!-- Enable Windows Vista-style font scaling on Vista -->` "\r\n"
65 						  `<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">` "\r\n"
66 						  `<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">` "\r\n"
67 						  `<dpiAware>true</dpiAware>` "\r\n"
68 						  `</asmv3:windowsSettings>` "\r\n"
69 						  `</asmv3:application>` "\r\n"
70 					`</assembly>` "\r\n",
71 }
72 private alias extern(Windows) BOOL function(HANDLE hActCtx, ULONG_PTR* lpCookie) ActivateActCtxProc;
73 private alias extern(Windows) HANDLE function(ACTCTXW* pActCtx) CreateActCtxWProc;
74 private alias extern(Windows) bool function(INITCOMMONCONTROLSEX*) InitCommonControlsExProc;
75 
76 /**
77    The _Application class manage the whole program, it can be used for load embedded resources,
78    close the program, get the current path and so on.
79    Internally in initialize manifest (if available), DLLs, and it handle exceptions showing a window with exception information.
80   */
81 class Application
82 {
83 	private static class ExceptionForm: Form
84 	{
85 		public this(Throwable e)
86 		{
87 			this.text = "An Exception was thrown...";
88 			this.size = Size(400, 220);
89 			this.controlBox = false;
90 			this.startPosition = FormStartPosition.centerParent;
91 			this.formBorderStyle = FormBorderStyle.fixedDialog;
92 
93 			this._lblHead = new Label();
94 			this._lblHead.alignment = TextAlignment.middle | TextAlignment.left;
95 			this._lblHead.foreColor = Color(0xB4, 0x00, 0x00);
96 			this._lblHead.dock = DockStyle.top;
97 			this._lblHead.height = 50;
98 			this._lblHead.text = errMsg;
99 			this._lblHead.parent = this;
100 
101 			this._lblInfo = new Label();
102 			this._lblInfo.alignment = TextAlignment.middle | TextAlignment.left;
103 			this._lblInfo.dock = DockStyle.top;
104 			this._lblInfo.height = 20;
105 			this._lblInfo.text = info;
106 			this._lblInfo.parent = this;
107 
108 			this._rtfText = new RichTextBox();
109 			this._rtfText.borderStyle = BorderStyle.fixed3d;
110 			this._rtfText.dock = DockStyle.top;
111 			this._rtfText.height = 90;
112 			this._rtfText.backColor = SystemColors.colorButtonFace;
113 			this._rtfText.scrollBars = true;
114 			this._rtfText.readOnly = true;
115 			this._rtfText.text = e.msg;
116 			this._rtfText.parent = this;
117 
118 			this._btnQuit = new Button();
119 			this._btnQuit.bounds = Rect(310, 164, 80, 23);
120 			this._btnQuit.dialogResult = DialogResult.abort;
121 			this._btnQuit.text = "Quit";
122 			this._btnQuit.parent = this;
123 
124 			this._btnIgnore = new Button();
125 			this._btnIgnore.bounds = Rect(225, 164, 80, 23);
126 			this._btnIgnore.dialogResult = DialogResult.ignore;
127 			this._btnIgnore.text = "Ignore";
128 			this._btnIgnore.parent = this;
129 		}
130 
131 		private RichTextBox _rtfText;
132 		private Label _lblHead;
133 		private Label _lblInfo;
134 		private Button _btnIgnore;
135 		private Button _btnQuit;
136 	}
137 
138 	/// Static constructor (it enable the manifest, if available)
139 	public static this()
140 	{
141 		Application.enableManifest(); //Enable Manifest (if available)
142 	}
143 
144 	/**
145 	      This method calls GetModuleHandle() API
146 
147 		Returns:
148 			HINSTANCE of the program
149 	  */
150 	@property public static HINSTANCE instance()
151 	{
152 		return getHInstance();
153 	}
154 
155 	/**
156 		Returns:
157 			String value of the executable path ($(B including) the executable name)
158 	   */
159 	@property public static string executablePath()
160 	{
161 		return getExecutablePath();
162 	}
163 
164 	/**
165 	   This method calls GetTempPath() API
166 
167 		Returns:
168 			String value of the system's TEMP directory
169 	   */
170 	@property public static string tempPath()
171 	{
172 		return dgui.core.utils.getTempPath();
173 	}
174 
175 	/**
176 	   Returns:
177 		String value of the executable path ($(B without) the executable name)
178 	   */
179 	@property public static string startupPath()
180 	{
181 		return getStartupPath();
182 	}
183 
184 	/**
185 	   This property allows to load embedded _resources.
186 
187 		Returns:
188 			The Instance of reource object
189 
190 		See_Also:
191 			Resources Class
192 	 */
193 	@property public static Resources resources()
194 	{
195 		return Resources.instance;
196 	}
197 
198 	/**
199 	   Internal method that enable XP Manifest (if available)
200 	 */
201 	private static void enableManifest()
202 	{
203 		HMODULE hKernel32 = getModuleHandle("kernel32.dll");
204 
205 		if(hKernel32)
206 		{
207 			CreateActCtxWProc createActCtx = cast(CreateActCtxWProc)GetProcAddress(hKernel32, "CreateActCtxW");
208 
209 			if(createActCtx) // Don't break Win2k compatibility
210 			{
211 				string temp = dgui.core.utils.getTempPath();
212 				ActivateActCtxProc activateActCtx = cast(ActivateActCtxProc)GetProcAddress(hKernel32, "ActivateActCtx");
213 				temp = std.path.buildPath(temp, xpManifestFile);
214 				std.file.write(temp, xpManifest);
215 
216 				ACTCTXW actx;
217 
218 				actx.cbSize = ACTCTXW.sizeof;
219 				actx.dwFlags = 0;
220 				actx.lpSource = toUTFz!(wchar*)(temp);
221 
222 				HANDLE hActx = createActCtx(&actx);
223 
224 				if(hActx != INVALID_HANDLE_VALUE)
225 				{
226 					ULONG_PTR cookie;
227 					activateActCtx(hActx, &cookie);
228 				}
229 
230 				if(std.file.exists(temp))
231 				{
232 					std.file.remove(temp);
233 				}
234 			}
235 		}
236 
237 		initCommonControls();
238 	}
239 
240 	/**
241 	  Internal method that loads ComCtl32 DLL
242 	  */
243 	private static void initCommonControls()
244 	{
245 		INITCOMMONCONTROLSEX icc = void;
246 
247 		icc.dwSize = INITCOMMONCONTROLSEX.sizeof;
248 		icc.dwICC = 0xFFFFFFFF;
249 
250 		HMODULE hComCtl32 = loadLibrary("comctl32.dll");
251 
252 		if(hComCtl32)
253 		{
254 			InitCommonControlsExProc iccex = cast(InitCommonControlsExProc)GetProcAddress(hComCtl32, "InitCommonControlsEx");
255 
256 			if(iccex)
257 			{
258 				iccex(&icc);
259 			}
260 		}
261 	}
262 
263 	/**
264 	  Start the program and handles handles Exception
265 	  Params:
266 		mainForm = The Application's main form
267 
268 	  Returns:
269 		Zero
270 	  */
271 
272 	private static int doRun(Form mainForm)
273 	{
274 		//try
275 		//{
276 			mainForm.show();
277 		/*}
278 		catch(Throwable e)
279 		{
280 			switch(Application.showExceptionForm(e))
281 			{
282 				case DialogResult.abort:
283 					TerminateProcess(GetCurrentProcess(), -1);
284 					break;
285 
286 				case DialogResult.ignore:
287 					Application.doRun(mainForm);
288 					break;
289 
290 				default:
291 					break;
292 			}
293 		}*/
294 
295 		return 0;
296 	}
297 
298 	/**
299 	  Start the program and adds onClose() event at the MainForm
300 	  Params:
301 		mainForm = The Application's main form
302 
303 	  Returns:
304 		Zero
305 	  */
306 	public static int run(Form mainForm)
307 	{
308 		mainForm.close.attach(&onMainFormClose);
309 		return Application.doRun(mainForm);
310 	}
311 
312 	/**
313 	  Close the program.
314 	  Params:
315 		exitCode = Exit code of the program (usually is 0)
316 	  */
317 	public static void exit(int exitCode = 0)
318 	{
319 		ExitProcess(exitCode);
320 	}
321 
322 	/**
323 	  When an exception was thrown, the _Application class call this method
324 	  showing the exception information, the user has the choice to continue the
325 	  application or terminate it.
326 
327 	  Returns:
328 		A DialogResult enum that contains the button clicked by the user (ignore or abort)
329 	  */
330 	package static DialogResult showExceptionForm(Throwable e)
331 	{
332 		ExceptionForm ef = new ExceptionForm(e);
333 		return ef.showDialog();
334 	}
335 
336 	/**
337 	  Close _Application event attached (internally) at the main form
338 	  */
339 	private static void onMainFormClose(Control sender, EventArgs e)
340 	{
341 		Application.exit();
342 	}
343 }