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.form;
10 
11 public import dgui.core.dialogs.dialogresult;
12 public import dgui.menubar;
13 private import dgui.core.utils;
14 import dgui.layout.layoutcontrol;
15 import dgui.core.events.eventargs;
16 
17 alias CancelEventArgs!(Form) CancelFormEventArgs;
18 
19 enum FormBits: ulong
20 {
21 	none 		 	= 0,
22 	modalCompleted = 1,
23 }
24 
25 enum FormBorderStyle: ubyte
26 {
27 	none 				= 0,
28 	manual 				= 1, // Internal Use
29 	fixedSingle 		= 2,
30 	fixed3d 			= 4,
31 	fixedDialog		= 8,
32 	sizeable 			= 16,
33 	fixedToolWindow 	= 32,
34 	sizeableToolWindow = 64,
35 }
36 
37 enum FormStartPosition: ubyte
38 {
39 	manual 			 = 0,
40 	centerParent	 = 1,
41 	centerScreen	 = 2,
42 	defaultLocation = 4,
43 }
44 
45 class Form: LayoutControl
46 {
47 	private FormBits _fBits = FormBits.none;
48 	private FormStartPosition _startPosition = FormStartPosition.manual;
49 	private FormBorderStyle _formBorder = FormBorderStyle.sizeable;
50 	private DialogResult _dlgResult = DialogResult.cancel;
51 	private HWND _hActiveWnd;
52 	private Icon _formIcon;
53 	private MenuBar _menu;
54 
55 	public Event!(Control, EventArgs) close;
56 	public Event!(Control, CancelFormEventArgs) closing;
57 
58 	public this()
59 	{
60 		this.setStyle(WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX, true);
61 	}
62 
63 	@property public final void formBorderStyle(FormBorderStyle fbs)
64 	{
65 		if(this.created)
66 		{
67 			uint style = 0, exStyle = 0;
68 
69 			makeFormBorderStyle(this._formBorder, style, exStyle); // Vecchio Stile.
70 			this.setStyle(style, false);
71 			this.setExStyle(exStyle, false);
72 
73 			style = 0;
74 			exStyle = 0;
75 
76 			makeFormBorderStyle(fbs, style, exStyle); // Nuovo Stile.
77 			this.setStyle(style, true);
78 			this.setExStyle(exStyle, true);
79 		}
80 
81 		this._formBorder = fbs;
82 	}
83 
84 	@property public final void controlBox(bool b)
85 	{
86 		this.setStyle(WS_SYSMENU, b);
87 	}
88 
89 	@property public final void maximizeBox(bool b)
90 	{
91 		this.setStyle(WS_MAXIMIZEBOX, b);
92 	}
93 
94 	@property public final void minimizeBox(bool b)
95 	{
96 		this.setStyle(WS_MINIMIZEBOX, b);
97 	}
98 
99 	@property public final void showInTaskbar(bool b)
100 	{
101 		this.setExStyle(WS_EX_APPWINDOW, b);
102 	}
103 
104 	@property public final MenuBar menu()
105 	{
106 		return this._menu;
107 	}
108 
109 	@property public final void menu(MenuBar mb)
110 	{
111 		if(this.created)
112 		{
113 			if(this._menu)
114 			{
115 				this._menu.dispose();
116 			}
117 
118 			mb.create();
119 			SetMenu(this._handle, mb.handle);
120 		}
121 
122 		this._menu = mb;
123 	}
124 
125 	@property public final Icon icon()
126 	{
127 		return this._formIcon;
128 	}
129 
130 	@property public final void icon(Icon ico)
131 	{
132 		if(this.created)
133 		{
134 			if(this._formIcon)
135 			{
136 				this._formIcon.dispose();
137 			}
138 
139 			this.sendMessage(WM_SETICON, ICON_BIG, cast(LPARAM)ico.handle);
140 			this.sendMessage(WM_SETICON, ICON_SMALL, cast(LPARAM)ico.handle);
141 		}
142 
143 		this._formIcon = ico;
144 	}
145 
146 	@property public final void topMost(bool b)
147 	{
148 		this.setExStyle(WS_EX_TOPMOST, b);
149 	}
150 
151 	@property public final void startPosition(FormStartPosition fsp)
152 	{
153 		this._startPosition = fsp;
154 	}
155 
156 	private void doEvents()
157 	{
158 		MSG m = void;
159 
160 		while(GetMessageW(&m, null, 0, 0))
161 		{
162 			if(Form.hasBit(this._cBits, ControlBits.modalControl) && Form.hasBit(this._fBits, FormBits.modalCompleted))
163 			{
164 				break;
165 			}
166 			else if(!IsDialogMessageW(this._handle, &m))
167 			{
168 				TranslateMessage(&m);
169 				DispatchMessageW(&m);
170 			}
171 		}
172 	}
173 
174 	public override void show()
175 	{
176 		super.show();
177 
178 		this.doEvents();
179 	}
180 
181 	public final DialogResult showDialog()
182 	{
183 		Form.setBit(this._cBits, ControlBits.modalControl, true);
184 		this._hActiveWnd = GetActiveWindow();
185 		EnableWindow(this._hActiveWnd, false);
186 
187 		this.show();
188 		return this._dlgResult;
189 	}
190 
191 	private final void doFormStartPosition()
192 	{
193 		if((this._startPosition is FormStartPosition.centerParent && !this.parent) ||
194 			this._startPosition is FormStartPosition.centerScreen)
195 		{
196 			Rect wa = Screen.workArea;
197 			Rect b = this._bounds;
198 
199 			this._bounds.position = Point((wa.width - b.width) / 2,
200 										  (wa.height - b.height) / 2);
201 		}
202 		else if(this._startPosition is FormStartPosition.centerParent)
203 		{
204 			Rect pr = this.parent.bounds;
205 			Rect b = this._bounds;
206 
207 			this._bounds.position = Point(pr.left + (pr.width - b.width) / 2,
208 										  pr.top + (pr.height - b.height) / 2);
209 		}
210 		else if(this._startPosition is FormStartPosition.defaultLocation)
211 		{
212 			this._bounds.position = Point(CW_USEDEFAULT, CW_USEDEFAULT);
213 		}
214 	}
215 
216 	private static void makeFormBorderStyle(FormBorderStyle fbs, ref uint style, ref uint exStyle)
217 	{
218 		switch(fbs)
219 		{
220 			case FormBorderStyle.fixed3d:
221 				style &= ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME);
222 				exStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_STATICEDGE);
223 
224 				style |= WS_CAPTION;
225 				exStyle |= WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
226 				break;
227 
228 			case FormBorderStyle.fixedDialog:
229 				style &= ~(WS_BORDER | WS_THICKFRAME);
230 				exStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
231 
232 				style |= WS_CAPTION | WS_DLGFRAME;
233 				exStyle |= WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE;
234 				break;
235 
236 			case FormBorderStyle.fixedSingle:
237 				style &= ~(WS_THICKFRAME | WS_DLGFRAME);
238 				exStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE | WS_EX_STATICEDGE);
239 
240 				style |= WS_CAPTION | WS_BORDER;
241 				exStyle |= WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
242 				break;
243 
244 			case FormBorderStyle.fixedToolWindow:
245 				style &= ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME);
246 				exStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
247 
248 				style |= WS_CAPTION;
249 				exStyle |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
250 				break;
251 
252 			case FormBorderStyle.sizeable:
253 				style &= ~(WS_BORDER | WS_DLGFRAME);
254 				exStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE);
255 
256 				style |= WS_CAPTION | WS_THICKFRAME;
257 				exStyle |= WS_EX_WINDOWEDGE;
258 				break;
259 
260 			case FormBorderStyle.sizeableToolWindow:
261 				style &= ~(WS_BORDER | WS_DLGFRAME);
262 				exStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE);
263 
264 				style |= WS_THICKFRAME | WS_CAPTION;
265 				exStyle |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
266 				break;
267 
268 			case FormBorderStyle.none:
269 				style &= ~(WS_BORDER | WS_THICKFRAME | WS_CAPTION | WS_DLGFRAME);
270 				exStyle &= ~(WS_EX_TOOLWINDOW | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE);
271 				break;
272 
273 			default:
274 				assert(0, "Unknown Form Border Style");
275 				//break;
276 		}
277 	}
278 
279 	protected override void onDGuiMessage(ref Message m)
280 	{
281 		switch(m.msg)
282 		{
283 			case DGUI_SETDIALOGRESULT:
284 			{
285 				this._dlgResult = cast(DialogResult)m.wParam;
286 
287 				Form.setBit(this._fBits, FormBits.modalCompleted, true);
288 				ShowWindow(this._handle, SW_HIDE); // Hide this window (it waits to be destroyed)
289 				EnableWindow(this._hActiveWnd, true);
290 				SetActiveWindow(this._hActiveWnd); // Restore the previous active window
291 			}
292 			break;
293 
294 			default:
295 				break;
296 		}
297 
298 		super.onDGuiMessage(m);
299 	}
300 
301 	protected override void createControlParams(ref CreateControlParams ccp)
302 	{
303 		uint style = 0, exStyle = 0;
304 		makeFormBorderStyle(this._formBorder, style, exStyle);
305 
306 		this.setStyle(style, true);
307 		this.setExStyle(exStyle, true);
308 		ccp.className = WC_FORM;
309 		ccp.defaultCursor = SystemCursors.arrow;
310 
311 		this.doFormStartPosition();
312 		super.createControlParams(ccp);
313 	}
314 
315 	protected override void onHandleCreated(EventArgs e)
316 	{
317 		if(this._menu)
318 		{
319 			this._menu.create();
320 			SetMenu(this._handle, this._menu.handle);
321 			DrawMenuBar(this._handle);
322 		}
323 
324 		if(this._formIcon)
325 		{
326 			Message m = Message(this._handle, WM_SETICON, ICON_BIG, cast(LPARAM)this._formIcon.handle);
327 			this.originalWndProc(m);
328 
329 			m.msg = ICON_SMALL;
330 			this.originalWndProc(m);
331 		}
332 
333 		super.onHandleCreated(e);
334 	}
335 
336 	protected override void wndProc(ref Message m)
337 	{
338 		switch(m.msg)
339 		{
340 			case WM_CLOSE:
341 			{
342 				scope CancelFormEventArgs e = new CancelFormEventArgs(this);
343 				this.onClosing(e);
344 
345 				if(!e.cancel)
346 				{
347 					this.onClose(EventArgs.empty);
348 
349 					if(Form.hasBit(this._cBits, ControlBits.modalControl))
350 					{
351 						EnableWindow(this._hActiveWnd, true);
352 						SetActiveWindow(this._hActiveWnd);
353 					}
354 
355 					super.wndProc(m);
356 				}
357 
358 				m.result = 0;
359 			}
360 			break;
361 
362 			case WM_CONTEXTMENU:
363 			{
364 				// Display default shortcut menu in case of click on window's caption.
365 
366 				Rect r = void;
367 				GetClientRect(handle, &r.rect);
368 
369 				auto pt = Point(LOWORD(m.lParam), HIWORD(m.lParam));
370 				convertPoint(pt, null, this);
371 				if(pt.inRect(r))
372 					goto default;
373 
374 				originalWndProc(m);
375 			}
376 			break;
377 
378 			default:
379 				super.wndProc(m);
380 				break;
381 		}
382 	}
383 
384 	protected void onClosing(CancelFormEventArgs e)
385 	{
386 		this.closing(this, e);
387 	}
388 
389 	protected void onClose(EventArgs e)
390 	{
391 		this.close(this, e);
392 	}
393 }