1 /*
2  * Copyright 2003, 2004, 2005 Martin Fuchs
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18 
19 
20  //
21  // Explorer clone
22  //
23  // window.h
24  //
25  // Martin Fuchs, 23.07.2003
26  //
27 
28 
29 typedef set<HWND> WindowSet;
30 
31 
32  /*
33 	Classes are declared using "struct", not "class" because the default
34 	access mode is "public". This way we can list the member functions in a
35 	natural order without explicitly specifying any access mode at the begin
36 	of the definition.
37 	First are public constructors and destructor, then public member functions.
38 	After that we list protected member varibables and functions. If needed,
39 	private implemenation varibales and functions are positioned at the end.
40  */
41 
42 
43  /// information structure for creation of a MDI child window
44 struct ChildWndInfo
45 {
ChildWndInfoChildWndInfo46 	ChildWndInfo(HWND hmdiclient)
47 	 :	_hmdiclient(hmdiclient) {}
48 
49 	HWND	_hmdiclient;
50 };
51 
52 
53  /**
54 	Class Window is the base class for several C++ window wrapper classes.
55 	Window objects are allocated from the heap. They are automatically freed
56 	when the window gets destroyed.
57  */
58 struct Window : public WindowHandle
59 {
60 	Window(HWND hwnd);
61 	virtual ~Window();
62 
63 
64 	typedef map<HWND,Window*> WindowMap;
65 
66 	typedef Window* (*CREATORFUNC)(HWND);
67 	typedef Window* (*CREATORFUNC_INFO)(HWND, const void*);
68 
69 	static HWND Create(CREATORFUNC creator,
70 				DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,
71 				DWORD dwStyle, int x, int y, int w, int h,
72 				HWND hwndParent=0, HMENU hMenu=0/*, LPVOID lpParam=0*/);
73 
74 	static HWND Create(CREATORFUNC_INFO creator, const void* info,
75 				DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,
76 				DWORD dwStyle, int x, int y, int w, int h,
77 				HWND hwndParent=0, HMENU hMenu=0/*, LPVOID lpParam=0*/);
78 
79 	static Window* create_mdi_child(const ChildWndInfo& info, const MDICREATESTRUCT& mcs, CREATORFUNC_INFO creator);
80 //	static Window* create_property_sheet(struct PropertySheetDialog* ppsd, CREATORFUNC creator, const void* info);
81 
82 	static LRESULT CALLBACK WindowWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
83 	static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
84 
85 	static Window* get_window(HWND hwnd);
86 #ifndef _MSC_VER
get_windowWindow87 	template<typename CLASS> static CLASS* get_window(HWND hwnd) {return static_cast<CLASS*>(get_window(hwnd));}
88 #define	GET_WINDOW(CLASS, hwnd) Window::get_window<CLASS>(hwnd)
89 #endif
90 
91 	static void register_pretranslate(HWND hwnd);
92 	static void unregister_pretranslate(HWND hwnd);
93 	static BOOL	pretranslate_msg(LPMSG pmsg);
94 
95 	static void	register_dialog(HWND hwnd);
96 	static void	unregister_dialog(HWND hwnd);
97 	static BOOL	dispatch_dialog_msg(LPMSG pmsg);
98 
99 	static int	MessageLoop();
100 
101 
102 	LRESULT	SendParent(UINT nmsg, WPARAM wparam=0, LPARAM lparam=0);
103 	LRESULT	PostParent(UINT nmsg, WPARAM wparam=0, LPARAM lparam=0);
104 
105 	static void CancelModes();
106 
107 
108 protected:
109 	virtual LRESULT	Init(LPCREATESTRUCT pcs);							// WM_CREATE processing
110 	virtual LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
111 	virtual int		Command(int id, int code);							// WM_COMMAND processing
112 	virtual int		Notify(int id, NMHDR* pnmh);						// WM_NOTIFY processing
113 
114 	static Window* create_controller(HWND hwnd);
115 
116 
117 	static WindowMap	s_wnd_map;
118 
119 	static const void*	s_new_info;
120 	static CREATORFUNC	s_window_creator;
121 
122 
123 	 /// structure for managing critical sections as static class information in struct Window
124 	struct StaticWindowData {
125 		CritSect	_map_crit_sect;
126 		CritSect	_create_crit_sect;
127 	};
128 
129 	static StaticWindowData& GetStaticWindowData();
130 
131 
132 	 // MDI child creation
133 	static HHOOK s_hcbtHook;
134 	static LRESULT CALLBACK MDICBTHookProc(int code, WPARAM wparam, LPARAM lparam);
135 	static LRESULT CALLBACK PropSheetCBTHookProc(int code, WPARAM wparam, LPARAM lparam);
136 
137 	static WindowSet s_pretranslate_windows;
138 	static WindowSet s_dialogs;
139 };
140 
141 #ifdef UNICODE
142 #define	NFR_CURRENT	NFR_UNICODE
143 #else
144 #define	NFR_CURRENT	NFR_ANSI
145 #endif
146 
147 
148 #ifdef _MSC_VER
149 template<typename CLASS> struct GetWindowHelper
150 {
get_windowGetWindowHelper151 	static CLASS* get_window(HWND hwnd) {
152 		return static_cast<CLASS*>(Window::get_window(hwnd));
153 	}
154 };
155 #define	GET_WINDOW(CLASS, hwnd) GetWindowHelper<CLASS>::get_window(hwnd)
156 #endif
157 
158 
159  /// dynamic casting of Window pointers
160 template<typename CLASS> struct TypeCheck
161 {
dyn_castTypeCheck162 	static CLASS* dyn_cast(Window* wnd)
163 		{return dynamic_cast<CLASS*>(wnd);}
164 };
165 
166 #define	WINDOW_DYNAMIC_CAST(CLASS, hwnd) \
167 	TypeCheck<CLASS>::dyn_cast(Window::get_window(hwnd))
168 
169 
170  /**
171 	SubclassedWindow is used to wrap already existing window handles
172 	into C++ Window objects. To construct a object, use the "new" operator
173 	to put it in the heap. It is automatically freed, when the window
174 	gets destroyed.
175  */
176 struct SubclassedWindow : public Window
177 {
178 	typedef Window super;
179 
180 	SubclassedWindow(HWND);
181 
182 protected:
183 	WNDPROC	_orgWndProc;
184 
185 	static LRESULT CALLBACK SubclassedWndProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
186 
187 	virtual LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
188 	virtual int		Command(int id, int code);
189 	virtual int		Notify(int id, NMHDR* pnmh);
190 };
191 
192 
193  /// template class used in macro WINDOW_CREATOR to define the creater functions for Window objects
194 template<typename WND_CLASS> struct WindowCreator
195 {
window_creatorWindowCreator196 	static WND_CLASS* window_creator(HWND hwnd)
197 	{
198 		return new WND_CLASS(hwnd);
199 	}
200 };
201 
202 #define WINDOW_CREATOR(WND_CLASS) \
203 	((Window::CREATORFUNC) WindowCreator<WND_CLASS>::window_creator)
204 
205 
206  /// template class used in macro WINDOW_CREATOR_INFO to the define creater functions for Window objects with additional creation information
207 template<typename WND_CLASS, typename INFO_CLASS> struct WindowCreatorInfo
208 {
window_creatorWindowCreatorInfo209 	static WND_CLASS* window_creator(HWND hwnd, const void* info)
210 	{
211 		return new WND_CLASS(hwnd, *static_cast<const INFO_CLASS*>(info));
212 	}
213 };
214 
215 #define WINDOW_CREATOR_INFO(WND_CLASS, INFO_CLASS) \
216 	((Window::CREATORFUNC_INFO) WindowCreatorInfo<WND_CLASS, INFO_CLASS>::window_creator)
217 
218 
219  /**
220 	WindowClass is a neat wrapper for RegisterClassEx().
221 	Just construct a WindowClass object, override the attributes you want
222 	to change, then call Register() or simply request the ATOM value to
223 	register the window class. You don't have to worry calling Register()
224 	more than once. It checks if, the class has already been registered.
225  */
226 struct WindowClass : public WNDCLASSEX
227 {
228 	WindowClass(LPCTSTR classname, UINT style=0, WNDPROC wndproc=Window::WindowWndProc);
229 
RegisterWindowClass230 	ATOM Register()
231 	{
232 		if (!_atomClass)
233 			_atomClass = RegisterClassEx(this);
234 
235 		return _atomClass;
236 	}
237 
ATOMWindowClass238 	operator ATOM() {return Register();}
239 
240 	 // return LPCTSTR for the CreateWindowEx() parameter
LPCTSTRWindowClass241 	operator LPCTSTR() {return (LPCTSTR)(int)Register();}
242 
243 protected:
244 	ATOM	_atomClass;
245 };
246 
247  /// window class with gray background color
248 struct BtnWindowClass : public WindowClass
249 {
250 	BtnWindowClass(LPCTSTR classname, UINT style=0, WNDPROC wndproc=Window::WindowWndProc)
WindowClassBtnWindowClass251 	 :	WindowClass(classname, style, wndproc)
252 	{
253 		hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
254 	}
255 };
256 
257  /// window class with specified icon from resources
258 struct IconWindowClass : public WindowClass
259 {
260 	IconWindowClass(LPCTSTR classname, UINT nid, UINT style=0, WNDPROC wndproc=Window::WindowWndProc);
261 };
262 
263 
264  // private message constants
265 #define	PM_DISPATCH_COMMAND		(WM_APP+0x00)
266 #define	PM_TRANSLATE_MSG		(WM_APP+0x01)
267 
268 
269 #define SPLIT_WIDTH 		5
270 #define DEFAULT_SPLIT_POS	300
271 #define	COLOR_SPLITBAR		LTGRAY_BRUSH
272 
273 
274  /// menu info structure
275 struct MenuInfo
276 {
277 	HMENU	_hMenuView;
278 };
279 
280 #define	PM_FRM_GET_MENUINFO		(WM_APP+0x02)
281 
282 #define	Frame_GetMenuInfo(hwnd) ((MenuInfo*)SNDMSG(hwnd, PM_FRM_GET_MENUINFO, 0, 0))
283 
284 
285  /**
286 	PreTranslateWindow is used to register windows to be called by Window::pretranslate_msg().
287 	This way you get PM_TRANSLATE_MSG messages before the message loop dispatches messages.
288 	You can then for example use TranslateAccelerator() to implement key shortcuts.
289  */
290 struct PreTranslateWindow : public Window
291 {
292 	typedef Window super;
293 
294 	PreTranslateWindow(HWND);
295 	~PreTranslateWindow();
296 };
297 
298 
299 
300  /**
301 	Class ChildWindow represents MDI child windows.
302 	It is used with class MainFrame.
303  */
304 struct ChildWindow : public PreTranslateWindow
305 {
306 	typedef PreTranslateWindow super;
307 
308 	ChildWindow(HWND hwnd, const ChildWndInfo& info);
309 
310 	static ChildWindow* create(const ChildWndInfo& info, const RECT& rect, CREATORFUNC_INFO creator,
311 								LPCTSTR classname, LPCTSTR title=NULL, DWORD style=0);
312 
313 	bool	go_to(LPCTSTR url);
314 
315 protected:
316 	LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
317 
318 	virtual void resize_children(int cx, int cy);
319 	virtual String jump_to_int(LPCTSTR url) = 0;
320 
321 protected:
322 	MenuInfo*_menu_info;
323 
324 	WindowHandle _left_hwnd;
325 	WindowHandle _right_hwnd;
326 	int 	_focus_pane;		// 0: left	1: right
327 
328 	int 	_split_pos;
329 	int		_last_split;
330 
331 	HWND	_hwndFrame;
332 	String	_statusText;
333 	String	_url;
334 
335 	stack<String> _url_history;
336 
337 	void	set_url(LPCTSTR url);
338 };
339 
340 #define	PM_SETSTATUSTEXT		(WM_APP+0x1E)
341 
342 
343  /**
344 	The class DialogWindow implements modeless dialogs, which are managed by
345 	Window::dispatch_dialog_msg() in Window::MessageLoop().
346 	A DialogWindow object should be constructed by calling Window::Create()
347 	and specifying the class using the WINDOW_CREATOR() macro.
348  */
349 struct DialogWindow : public Window
350 {
351 	typedef Window super;
352 
DialogWindowDialogWindow353 	DialogWindow(HWND hwnd)
354 	 :	super(hwnd)
355 	{
356 		register_dialog(hwnd);
357 	}
358 
~DialogWindowDialogWindow359 	~DialogWindow()
360 	{
361 		unregister_dialog(_hwnd);
362 	}
363 };
364 
365 
366  /**
367 	The class Dialog implements modal dialogs.
368 	A Dialog object should be constructed by calling Dialog::DoModal()
369 	and specifying the class using the WINDOW_CREATOR() macro.
370  */
371 struct Dialog : public Window
372 {
373 	typedef Window super;
374 
375 	Dialog(HWND);
376 	~Dialog();
377 
378 	static int DoModal(UINT nid, CREATORFUNC creator, HWND hwndParent=0);
379 	static int DoModal(UINT nid, CREATORFUNC_INFO creator, const void* info, HWND hwndParent=0);
380 
381 protected:
382 	LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
383 	int		Command(int id, int code);
384 };
385 
386 
387 #define	PM_FRM_CALC_CLIENT		(WM_APP+0x03)
388 #define	Frame_CalcFrameClient(hwnd, prt) ((BOOL)SNDMSG(hwnd, PM_FRM_CALC_CLIENT, 0, (LPARAM)(PRECT)prt))
389 
390 #define	PM_JUMP_TO_URL			(WM_APP+0x25)
391 #define	PM_URL_CHANGED			(WM_APP+0x26)
392 
393 
394 struct PropSheetPage : public PROPSHEETPAGE
395 {
396 	PropSheetPage(UINT nid, Window::CREATORFUNC dlg_creator);
397 
398 	void	init(struct PropertySheetDialog*);
399 
400 protected:
401 	friend struct PropSheetPageDlg;
402 
403 	Window::CREATORFUNC	_dlg_creator;
404 };
405 
406 
407  /// Property Sheet dialog
408 struct PropertySheetDialog : public PROPSHEETHEADER
409 {
410 	PropertySheetDialog(HWND owner);
411 
412 	void	add(PropSheetPage& psp);
413 	int		DoModal(int start_page=0);
414 
415 	HWND	GetCurrentPage();
416 
417 protected:
418 	typedef vector<PROPSHEETPAGE> Vector;
419 	Vector	_pages;
420 	HWND	_hwnd;
421 };
422 
423 
424  /// Property Sheet Page (inner dialog)
425 struct PropSheetPageDlg : public Dialog
426 {
427 	typedef Dialog super;
428 
429 	PropSheetPageDlg(HWND);
430 
431 protected:
432 	friend struct PropertySheetDialog;
433 	friend struct PropSheetPage;
434 
435 	static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT nmsg, WPARAM wparam, LPARAM lparam);
436 
437 	int	Command(int id, int code);
438 };
439 
440 
441 /*
442  /// Property Sheet Dialog (outer dialog)
443 struct PropertySheetDlg : public SubclassedWindow
444 {
445 	typedef SubclassedWindow super;
446 
447 	PropertySheetDlg(HWND hwnd) : super(hwnd) {}
448 };
449 */
450 
451 
452  // Layouting of resizable windows
453 
454  /// Flags to specify how to move and resize controls when resizing their parent window
455 enum RESIZE_FLAGS {
456 	MOVE_LEFT	= 0x1,
457 	MOVE_RIGHT	= 0x2,
458 	MOVE_TOP	= 0x4,
459 	MOVE_BOTTOM	= 0x8,
460 
461 	MOVE_X	=  MOVE_LEFT | MOVE_RIGHT,
462 	MOVE_Y	=  MOVE_TOP | MOVE_BOTTOM,
463 	RESIZE_X=  MOVE_RIGHT,
464 	RESIZE_Y=  MOVE_BOTTOM,
465 
466 	MOVE	= MOVE_X   | MOVE_Y,
467 	RESIZE	= RESIZE_X | RESIZE_Y
468 };
469 
470  /// structure to assign RESIZE_FLAGS to dialogs control
471 struct ResizeEntry
472 {
ResizeEntryResizeEntry473 	ResizeEntry(UINT id, int flags)
474 	 : _id(id), _flags(flags) {}
475 
ResizeEntryResizeEntry476 	ResizeEntry(HWND hwnd, int flags)
477 	 : _id(GetDlgCtrlID(hwnd)), _flags(flags) {}
478 
479 	UINT	_id;
480 	int		_flags;
481 };
482 
483 
484  /// Management of controls in resizable dialogs
485 struct ResizeManager : public std::list<ResizeEntry>
486 {
487 	typedef std::list<ResizeEntry> super;
488 
489 	ResizeManager(HWND hwnd);
490 
AddResizeManager491 	void Add(UINT id, int flags)
492 		{push_back(ResizeEntry(id, flags));}
493 
AddResizeManager494 	void Add(HWND hwnd, int flags)
495 		{push_back(ResizeEntry(hwnd, flags));}
496 
497 	void HandleSize(int cx, int cy);
498 	void Resize(int dx, int dy);
499 
SetMinMaxInfoResizeManager500 	void SetMinMaxInfo(LPMINMAXINFO lpmmi)
501 	{
502 		lpmmi->ptMinTrackSize.x = _min_wnd_size.cx;
503 		lpmmi->ptMinTrackSize.y = _min_wnd_size.cy;
504 	}
505 
506 	SIZE	_min_wnd_size;
507 
508 protected:
509 	HWND	_hwnd;
510 	SIZE	_last_size;
511 };
512 
513 
514  /// Controller base template class for resizable dialogs
515 template<typename BASE> struct ResizeController : public BASE
516 {
517 	typedef BASE super;
518 
ResizeControllerResizeController519 	ResizeController(HWND hwnd)
520 	 :	super(hwnd),
521 		_resize_mgr(hwnd)
522 	{
523 	}
524 
WndProcResizeController525 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
526 	{
527 		switch(nmsg) {
528 		  case PM_FRM_CALC_CLIENT:
529 			GetClientSpace((PRECT)lparam);
530 			return TRUE;
531 
532 		  case WM_SIZE:
533 			if (wparam != SIZE_MINIMIZED)
534 				_resize_mgr.HandleSize(LOWORD(lparam), HIWORD(lparam));
535 			goto def;
536 
537 		  case WM_GETMINMAXINFO:
538 			_resize_mgr.SetMinMaxInfo((LPMINMAXINFO)lparam);
539 			goto def;
540 
541 		  default: def:
542 			return super::WndProc(nmsg, wparam, lparam);
543 		}
544 	}
545 
GetClientSpaceResizeController546 	virtual void GetClientSpace(PRECT prect)
547 	{
548 		 if (!IsIconic(this->_hwnd)) {
549 			GetClientRect(this->_hwnd, prect);
550 		 } else {
551 			WINDOWPLACEMENT wp;
552 			GetWindowPlacement(this->_hwnd, &wp);
553 			prect->left = prect->top = 0;
554 			prect->right = wp.rcNormalPosition.right-wp.rcNormalPosition.left-
555 				2*(GetSystemMetrics(SM_CXSIZEFRAME)+GetSystemMetrics(SM_CXEDGE));
556 			prect->bottom = wp.rcNormalPosition.bottom-wp.rcNormalPosition.top-
557 				2*(GetSystemMetrics(SM_CYSIZEFRAME)+GetSystemMetrics(SM_CYEDGE))-
558 				GetSystemMetrics(SM_CYCAPTION)-GetSystemMetrics(SM_CYMENUSIZE);
559 		}
560 	}
561 
562 protected:
563 	ResizeManager _resize_mgr;
564 };
565 
566 
567  /**
568 	This class constructs button controls.
569 	The button will remain existent when the C++ Button object is destroyed.
570 	There is no conjunction between C++ object and windows control life time.
571  */
572 struct Button : public WindowHandle
573 {
574 	Button(HWND parent, LPCTSTR text, int left, int top, int width, int height,
575 			int id, DWORD flags=WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, DWORD exStyle=0);
576 };
577 
578 
579  /**
580 	This class constructs static controls.
581 	The control will remain existent when the C++ object is destroyed.
582 	There is no conjunction between C++ object and windows control life time.
583  */
584 struct Static : public WindowHandle
585 {
586 	Static(HWND parent, LPCTSTR text, int left, int top, int width, int height,
587 			int id, DWORD flags=WS_VISIBLE|WS_CHILD|SS_SIMPLE, DWORD ex_flags=0);
588 };
589 
590 
591  // control color message routing for ColorStatic and HyperlinkCtrl
592 
593 #define	PM_DISPATCH_CTLCOLOR	(WM_APP+0x08)
594 
595 template<typename BASE> struct CtlColorParent : public BASE
596 {
597 	typedef BASE super;
598 
CtlColorParentCtlColorParent599 	CtlColorParent(HWND hwnd)
600 	 : super(hwnd) {}
601 
WndProcCtlColorParent602 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
603 	{
604 		switch(nmsg) {
605 		  case WM_CTLCOLOR:
606 		  case WM_CTLCOLORBTN:
607 		  case WM_CTLCOLORDLG:
608 		  case WM_CTLCOLORSCROLLBAR:
609 		  case WM_CTLCOLORSTATIC: {
610 			HWND hctl = (HWND) lparam;
611 			return SendMessage(hctl, PM_DISPATCH_CTLCOLOR, wparam, nmsg);
612 		  }
613 
614 		  default:
615 			return super::WndProc(nmsg, wparam, lparam);
616 		}
617 	}
618 };
619 
620 
621 #define	PM_DISPATCH_DRAWITEM	(WM_APP+0x09)
622 
623  /// draw message routing for ColorButton and PictureButton
624 template<typename BASE> struct OwnerDrawParent : public BASE
625 {
626 	typedef BASE super;
627 
OwnerDrawParentOwnerDrawParent628 	OwnerDrawParent(HWND hwnd)
629 	 : super(hwnd) {}
630 
WndProcOwnerDrawParent631 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
632 	{
633 		switch(nmsg) {
634 		  case WM_DRAWITEM:
635 			if (wparam) {	// should there be drawn a control?
636 				HWND hctl = GetDlgItem(this->_hwnd, wparam);
637 
638 				if (hctl)
639 					return SendMessage(hctl, PM_DISPATCH_DRAWITEM, wparam, lparam);
640 			} /*else		// or is it a menu entry?
641 				; */
642 
643 			return 0;
644 
645 		  default:
646 			return super::WndProc(nmsg, wparam, lparam);
647 		}
648 	}
649 };
650 
651 
652  /**
653 	Subclass button controls to draw them by using PM_DISPATCH_DRAWITEM
654 	The owning window should use the OwnerDrawParent template to route owner draw messages to the buttons.
655  */
656 struct OwnerdrawnButton : public SubclassedWindow
657 {
658 	typedef SubclassedWindow super;
659 
OwnerdrawnButtonOwnerdrawnButton660 	OwnerdrawnButton(HWND hwnd)
661 	 :	super(hwnd) {}
662 
663 protected:
664 	LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
665 
666 	virtual void DrawItem(LPDRAWITEMSTRUCT dis) = 0;
667 };
668 
669 extern void DrawGrayText(HDC hdc, LPRECT pRect, LPCTSTR text, int dt_flags);
670 
671 
672  /**
673 	Subclass button controls to paint colored text labels.
674 	The owning window should use the OwnerDrawParent template to route owner draw messages to the buttons.
675  */
676 /* not yet used
677 struct ColorButton : public OwnerdrawnButton
678 {
679 	typedef OwnerdrawnButton super;
680 
681 	ColorButton(HWND hwnd, COLORREF textColor)
682 	 :	super(hwnd), _textColor(textColor) {}
683 
684 protected:
685 	virtual void DrawItem(LPDRAWITEMSTRUCT dis);
686 
687 	COLORREF _textColor;
688 };
689 */
690 
691 
692 struct FlatButton : public OwnerdrawnButton
693 {
694 	typedef OwnerdrawnButton super;
695 
FlatButtonFlatButton696 	FlatButton(HWND hwnd)
697 	 : super(hwnd), _active(false) {}
698 
FlatButtonFlatButton699 	FlatButton(HWND owner, int id)
700 	 : super(GetDlgItem(owner, IDOK)), _active(false) {}
701 
702 protected:
703 	LRESULT	WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
704 	virtual void DrawItem(LPDRAWITEMSTRUCT dis);
705 
706 	COLORREF _textColor;
707 	COLORREF _activeColor;
708 	bool	_active;
709 };
710 
711 
712  /**
713 	Subclass button controls to paint pictures left to the labels.
714 	The buttons should have set the style bit BS_OWNERDRAW.
715 	The owning window should use the OwnerDrawParent template to route owner draw messages to the buttons.
716  */
717 struct PictureButton : public OwnerdrawnButton
718 {
719 	typedef OwnerdrawnButton super;
720 
721 	PictureButton(HWND hwnd, HICON hIcon, HBRUSH hbrush=GetSysColorBrush(COLOR_BTNFACE), bool flat=false)
superPictureButton722 	 :	super(hwnd), _hIcon(hIcon), _hBmp(0), _hBrush(hbrush), _flat(flat)
723 	{
724 		_cx = 16;
725 		_cy = 16;
726 	}
727 
728 	PictureButton(HWND hparent, int id, HICON hIcon, HBRUSH hbrush=GetSysColorBrush(COLOR_BTNFACE), bool flat=false)
superPictureButton729 	 :	super(GetDlgItem(hparent, id)), _hIcon(hIcon), _hBmp(0), _hBrush(hbrush), _flat(flat)
730 	{
731 		_cx = 16;
732 		_cy = 16;
733 	}
734 
735 	PictureButton(HWND hwnd, HBITMAP hBmp, HBRUSH hbrush=GetSysColorBrush(COLOR_BTNFACE), bool flat=false)
superPictureButton736 	 :	super(hwnd), _hIcon(0), _hBmp(hBmp), _hBrush(hbrush), _flat(flat)
737 	{
738 		BITMAP bmp;
739 		GetObject(hBmp, sizeof(bmp), &bmp);
740 		_cx = bmp.bmWidth;
741 		_cy = bmp.bmHeight;
742 	}
743 
744 	PictureButton(HWND hparent, int id, HBITMAP hBmp, HBRUSH hbrush=GetSysColorBrush(COLOR_BTNFACE), bool flat=false)
superPictureButton745 	 :	super(GetDlgItem(hparent, id)), _hIcon(0), _hBmp(hBmp), _hBrush(hbrush), _flat(flat)
746 	{
747 		BITMAP bmp;
748 		GetObject(hBmp, sizeof(bmp), &bmp);
749 		_cx = bmp.bmWidth;
750 		_cy = bmp.bmHeight;
751 	}
752 
753 protected:
754 	virtual void DrawItem(LPDRAWITEMSTRUCT dis);
755 
756 	HICON	_hIcon;
757 	HBITMAP	_hBmp;
758 	HBRUSH	_hBrush;
759 
760 	int		_cx;
761 	int		_cy;
762 
763 	bool	_flat;
764 };
765 
766 
767 struct ColorStatic : public SubclassedWindow
768 {
769 	typedef SubclassedWindow super;
770 
771 	ColorStatic(HWND hwnd, COLORREF textColor=RGB(255,0,0), HBRUSH hbrush_bkgnd=0, HFONT hfont=0)
superColorStatic772 	 :	super(hwnd),
773 		_textColor(textColor),
774 		_hbrush_bkgnd(hbrush_bkgnd),
775 		_hfont(hfont)
776 	{
777 	}
778 
779 	ColorStatic(HWND owner, int id, COLORREF textColor=RGB(255,0,0), HBRUSH hbrush_bkgnd=0, HFONT hfont=0)
superColorStatic780 	 :	super(GetDlgItem(owner, id)),
781 		_textColor(textColor),
782 		_hbrush_bkgnd(hbrush_bkgnd),
783 		_hfont(hfont)
784 	{
785 	}
786 
787 protected:
WndProcColorStatic788 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
789 	{
790 		if (nmsg == PM_DISPATCH_CTLCOLOR) {
791 			HDC hdc = (HDC) wparam;
792 
793 			SetTextColor(hdc, _textColor);
794 
795 			if (_hfont)
796 				SelectFont(hdc, _hfont);
797 
798 			if (_hbrush_bkgnd)
799 				return (LRESULT)_hbrush_bkgnd;
800 			else {
801 				SetBkMode(hdc, TRANSPARENT);
802 				return (LRESULT)GetStockBrush(HOLLOW_BRUSH);
803 			}
804 		} else
805 			return super::WndProc(nmsg, wparam, lparam);
806 	}
807 
808 	COLORREF	_textColor;
809 	HBRUSH		_hbrush_bkgnd;
810 	HFONT		_hfont;
811 };
812 
813 
814   /// Hyperlink Controls
815 
816 struct HyperlinkCtrl : public SubclassedWindow
817 {
818 	typedef SubclassedWindow super;
819 
820 	HyperlinkCtrl(HWND hwnd, COLORREF colorLink=RGB(0,0,255), COLORREF colorVisited=RGB(128,0,128));
821 	HyperlinkCtrl(HWND owner, int id, COLORREF colorLink=RGB(0,0,255), COLORREF colorVisited=RGB(128,0,128));
822 
823 	~HyperlinkCtrl();
824 
825 	String	_cmd;
826 
827 protected:
828 	COLORREF _textColor;
829 	COLORREF _colorVisited;
830 	HFONT	 _hfont;
831 	HCURSOR	_crsr_link;
832 
833 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam);
834 
835 	void init();
836 
LaunchLinkHyperlinkCtrl837 	bool LaunchLink()
838 	{
839 		if (!_cmd.empty()) {
840 			HINSTANCE hinst = ShellExecute(GetParent(_hwnd), _T("open"), _cmd, 0, 0, SW_SHOWNORMAL);
841 			return (INT_PTR)hinst > HINSTANCE_ERROR;
842 		}
843 
844 		return true;
845 	}
846 };
847 
848 
849  /// encapsulation of tool tip controls
850 struct ToolTip : public WindowHandle
851 {
852 	typedef WindowHandle super;
853 
854 	ToolTip(HWND owner);
855 
856 	void activate(BOOL active=TRUE)
857 	{
858 		SendMessage(_hwnd, TTM_ACTIVATE, active, 0);
859 	}
860 
861 	void add(HWND hparent, HWND htool, LPCTSTR txt=LPSTR_TEXTCALLBACK, LPARAM lparam=0)
862 	{
863 		TOOLINFO ti = {
864 			sizeof(TOOLINFO), TTF_SUBCLASS|TTF_IDISHWND|TTF_TRANSPARENT, hparent, (UINT_PTR)htool,
865 			{0,0,0,0}, 0, (LPTSTR)txt, lparam
866 		};
867 
868 #ifdef UNICODE	///@todo Why is it neccesary to try both TTM_ADDTOOLW and TTM_ADDTOOLW ?!
869 		if (!SendMessage(_hwnd, TTM_ADDTOOLW, 0, (LPARAM)&ti))
870 			SendMessage(_hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
871 #else
872 		if (!SendMessage(_hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti))
873 			SendMessage(_hwnd, TTM_ADDTOOLW, 0, (LPARAM)&ti);
874 #endif
875 	}
876 
877 	void add(HWND hparent, UINT id, const RECT& rect, LPCTSTR txt=LPSTR_TEXTCALLBACK, LPARAM lparam=0)
878 	{
879 		TOOLINFO ti = {
880 			sizeof(TOOLINFO), TTF_SUBCLASS|TTF_TRANSPARENT, hparent, id,
881 			{rect.left,rect.top,rect.right,rect.bottom}, 0, (LPTSTR)txt, lparam
882 		};
883 
884 #ifdef UNICODE
885 		if (!SendMessage(_hwnd, TTM_ADDTOOLW, 0, (LPARAM)&ti))
886 			SendMessage(_hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
887 #else
888 		if (!SendMessage(_hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti))
889 			SendMessage(_hwnd, TTM_ADDTOOLW, 0, (LPARAM)&ti);
890 #endif
891 	}
892 
removeToolTip893 	void remove(HWND hparent, HWND htool)
894 	{
895 		TOOLINFO ti = {
896 			sizeof(TOOLINFO), TTF_IDISHWND, hparent, (UINT_PTR)htool,
897 			{0,0,0,0}, 0, 0, 0
898 		};
899 
900 		SendMessage(_hwnd, TTM_DELTOOL, 0, (LPARAM)&ti);
901 	}
902 
removeToolTip903 	void remove(HWND hparent, UINT id)
904 	{
905 		TOOLINFO ti = {
906 			sizeof(TOOLINFO), 0, hparent, id,
907 			{0,0,0,0}, 0, 0, 0
908 		};
909 
910 		SendMessage(_hwnd, TTM_DELTOOL, 0, (LPARAM)&ti);
911 	}
912 };
913 
914 
ListView_GetItemData(HWND list_ctrl,int idx)915 inline int ListView_GetItemData(HWND list_ctrl, int idx)
916 {
917 	LV_ITEM item;
918 
919 	item.mask = LVIF_PARAM;
920 	item.iItem = idx;
921 
922 	if (!ListView_GetItem(list_ctrl, &item))
923 		return 0;
924 
925 	return item.lParam;
926 }
927 
ListView_FindItemPara(HWND list_ctrl,LPARAM param)928 inline int ListView_FindItemPara(HWND list_ctrl, LPARAM param)
929 {
930 	LVFINDINFO fi;
931 
932 	fi.flags = LVFI_PARAM;
933 	fi.lParam = param;
934 
935 	return ListView_FindItem(list_ctrl, (unsigned)-1, &fi);
936 }
937 
ListView_GetFocusedItem(HWND list_ctrl)938 inline int ListView_GetFocusedItem(HWND list_ctrl)
939 {
940 	int idx = ListView_GetItemCount(list_ctrl);
941 
942 	while(--idx >= 0)
943 		if (ListView_GetItemState(list_ctrl, idx, LVIS_FOCUSED))
944 			break;
945 
946 	return idx;
947 }
948 
949 
950  /// sorting of list controls
951 struct ListSort : public WindowHandle
952 {
953 	ListSort(HWND hwndListview, PFNLVCOMPARE compare_fct);
954 
955 	void	toggle_sort(int idx);
956 	void	sort();
957 
958 	int		_sort_crit;
959 	bool	_direction;
960 
961 protected:
962 	PFNLVCOMPARE _compare_fct;
963 
964 	static int CALLBACK CompareFunc(LPARAM lparam1, LPARAM lparam2, LPARAM lparamSort);
965 };
966 
967 
TreeView_GetItemData(HWND hwndTreeView,HTREEITEM hItem)968 inline LPARAM TreeView_GetItemData(HWND hwndTreeView, HTREEITEM hItem)
969 {
970 	TVITEM tvItem;
971 
972 	tvItem.mask = TVIF_PARAM;
973 	tvItem.hItem = hItem;
974 
975 	if (!TreeView_GetItem(hwndTreeView, &tvItem))
976 		return 0;
977 
978 	return tvItem.lParam;
979 }
980 
981 
982 enum {TRAYBUTTON_LEFT=0, TRAYBUTTON_RIGHT, TRAYBUTTON_MIDDLE};
983 
984 #define	PM_TRAYICON		(WM_APP+0x20)
985 
986 #define	WINMSG_TASKBARCREATED	TEXT("TaskbarCreated")
987 
988 #define	WINMSG_SHELLHOOK		TEXT("SHELLHOOK")
989 
990 
991 struct TrayIcon
992 {
TrayIconTrayIcon993 	TrayIcon(HWND hparent, UINT id)
994 	 :	_hparent(hparent), _id(id) {}
995 
~TrayIconTrayIcon996 	~TrayIcon()
997 		{Remove();}
998 
999 	void	Add(HICON hIcon, LPCTSTR tooltip=NULL)
1000 		{Set(NIM_ADD, _id, hIcon, tooltip);}
1001 
1002 	void	Modify(HICON hIcon, LPCTSTR tooltip=NULL)
1003 		{Set(NIM_MODIFY, _id, hIcon, tooltip);}
1004 
RemoveTrayIcon1005 	void	Remove()
1006 	{
1007 		NOTIFYICONDATA nid = {
1008 			sizeof(NOTIFYICONDATA),	// cbSize
1009 			_hparent,				// hWnd
1010 			_id,					// uID
1011 		};
1012 
1013 		Shell_NotifyIcon(NIM_DELETE, &nid);
1014 	}
1015 
1016 protected:
1017 	HWND	_hparent;
1018 	UINT	_id;
1019 
1020 	void	Set(DWORD dwMessage, UINT id, HICON hIcon, LPCTSTR tooltip=NULL)
1021 	{
1022 		NOTIFYICONDATA nid = {
1023 			sizeof(NOTIFYICONDATA),	// cbSize
1024 			_hparent,				// hWnd
1025 			id,						// uID
1026 			NIF_MESSAGE|NIF_ICON,	// uFlags
1027 			PM_TRAYICON,			// uCallbackMessage
1028 			hIcon					// hIcon
1029 		};
1030 
1031 		if (tooltip)
1032 			lstrcpyn(nid.szTip, tooltip, COUNTOF(nid.szTip));
1033 
1034 		if (nid.szTip[0])
1035 			nid.uFlags |= NIF_TIP;
1036 
1037 		Shell_NotifyIcon(dwMessage, &nid);
1038 	}
1039 };
1040 
1041 
1042 template<typename BASE> struct TrayIconControllerTemplate : public BASE
1043 {
1044 	typedef BASE super;
1045 
TrayIconControllerTemplateTrayIconControllerTemplate1046 	TrayIconControllerTemplate(HWND hwnd) : BASE(hwnd),
1047 		WM_TASKBARCREATED(RegisterWindowMessage(WINMSG_TASKBARCREATED))
1048 	{
1049 	}
1050 
WndProcTrayIconControllerTemplate1051 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1052 	{
1053 		if (nmsg == PM_TRAYICON) {
1054 			switch(lparam) {
1055 			  case WM_MOUSEMOVE:
1056 				TrayMouseOver(wparam);
1057 				break;
1058 
1059 			  case WM_LBUTTONDOWN:
1060 				TrayClick(wparam, TRAYBUTTON_LEFT);
1061 				break;
1062 
1063 			  case WM_LBUTTONDBLCLK:
1064 				TrayDblClick(wparam, TRAYBUTTON_LEFT);
1065 				break;
1066 
1067 			  case WM_RBUTTONDOWN:
1068 				TrayClick(wparam, TRAYBUTTON_RIGHT);
1069 				break;
1070 
1071 			  case WM_RBUTTONDBLCLK:
1072 				TrayDblClick(wparam, TRAYBUTTON_RIGHT);
1073 				break;
1074 
1075 			  case WM_MBUTTONDOWN:
1076 				TrayClick(wparam, TRAYBUTTON_MIDDLE);
1077 				break;
1078 
1079 			  case WM_MBUTTONDBLCLK:
1080 				TrayDblClick(wparam, TRAYBUTTON_MIDDLE);
1081 				break;
1082 			}
1083 
1084 			return 0;
1085 		} else if (nmsg == WM_TASKBARCREATED) {
1086 			AddTrayIcons();
1087 			return 0;
1088 		} else
1089 			return super::WndProc(nmsg, wparam, lparam);
1090 	}
1091 
1092 	virtual void AddTrayIcons() = 0;
TrayMouseOverTrayIconControllerTemplate1093 	virtual void TrayMouseOver(UINT id) {}
TrayClickTrayIconControllerTemplate1094 	virtual void TrayClick(UINT id, int btn) {}
TrayDblClickTrayIconControllerTemplate1095 	virtual void TrayDblClick(UINT id, int btn) {}
1096 
1097 protected:
1098 	const UINT WM_TASKBARCREATED;
1099 };
1100 
1101 
1102 struct EditController : public SubclassedWindow
1103 {
1104 	typedef SubclassedWindow super;
1105 
EditControllerEditController1106 	EditController(HWND hwnd)
1107 	 :	super(hwnd)
1108 	{
1109 	}
1110 
1111 protected:
WndProcEditController1112 	LRESULT WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1113 	{
1114 		if (nmsg==WM_KEYDOWN && wparam==VK_RETURN) {
1115 			SendParent(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(_hwnd),1), (LPARAM)_hwnd);
1116 			return 0;
1117 		} else
1118 			return super::WndProc(nmsg, wparam, lparam);
1119 	}
1120 };
1121