2 * Copyright (c) 2001, 2002, 2003 Gary R. Van Sickle.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * A copy of the GNU General Public License can be found at
12 * Written by Gary R. Van Sickle <g.r.vansickle@worldnet.att.net>
16 // This is the implementation of the Window class. It serves both as a window class
17 // in its own right and as a base class for other window-like classes (e.g. PropertyPage,
22 #include "RECTWrapper.h"
26 ATOM
Window::WindowClassAtom
= 0;
27 HINSTANCE
Window::AppInstance
= NULL
;
41 // Delete any fonts we created.
42 for (unsigned int i
= 0; i
< Fonts
.size (); i
++)
43 DeleteObject (Fonts
[i
]);
45 // shut down the tooltip control, if activated
47 DestroyWindow (TooltipHandle
);
49 // FIXME: Maybe do some reference counting and do this Unregister
50 // when there are no more of us left. Not real critical unless
51 // we're in a DLL which we're not right now.
52 //UnregisterClass(WindowClassAtom, InstanceHandle);
56 Window::FirstWindowProcReflector (HWND hwnd
, UINT uMsg
, WPARAM wParam
,
61 if(uMsg
== WM_NCCREATE
)
63 // This is the first message a window gets (so MSDN says anyway).
64 // Take this opportunity to "link" the HWND to the 'this' ptr, steering
65 // messages to the class instance's WindowProc().
66 wnd
= reinterpret_cast<Window
*>(((LPCREATESTRUCT
)lParam
)->lpCreateParams
);
68 // Set a backreference to this class instance in the HWND.
69 SetWindowLongPtr (hwnd
, GWLP_USERDATA
, reinterpret_cast<LONG_PTR
>(wnd
));
71 // Set a new WindowProc now that we have the peliminaries done.
72 // We could instead simply do the contents of Window::WindowProcReflector
73 // in the 'else' clause below, but this way we eliminate an unnecessary 'if/else' on
74 // every message. Yeah, it's probably not worth the trouble.
75 SetWindowLongPtr (hwnd
, GWLP_WNDPROC
, (LONG_PTR
) & Window::WindowProcReflector
);
76 // Finally, store the window handle in the class.
77 wnd
->WindowHandle
= hwnd
;
81 // Should never get here.
82 Log (LOG_PLAIN
) << "Warning: Unexpected windows message " << uMsg
83 << "received early in window initialization."
87 return wnd
->WindowProc (uMsg
, wParam
, lParam
);
91 Window::WindowProcReflector (HWND hwnd
, UINT uMsg
, WPARAM wParam
,
96 // Get our this pointer
97 This
= reinterpret_cast<Window
*>(GetWindowLongPtr (hwnd
, GWLP_USERDATA
));
99 return This
->WindowProc (uMsg
, wParam
, lParam
);
103 Window::Create (Window
* parent
, DWORD Style
)
105 // First register the window class, if we haven't already
106 if (registerWindowClass () == false)
108 // Registration failed
112 // Save our parent, we'll probably need it eventually.
115 // Create the window instance
116 WindowHandle
= CreateWindowEx (
119 "MainWindowClass", //MAKEINTATOM(WindowClassAtom), // window class atom (name)
120 "Hello", // no title-bar string yet
123 // Default positions and size
125 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
,
128 NULL
? (HWND
) NULL
: parent
->GetHWND (),
131 // The application instance
133 // The this ptr, which we'll use to set up the WindowProc reflection.
136 if (WindowHandle
== NULL
)
146 Window::registerWindowClass ()
148 if (WindowClassAtom
== 0)
150 // We're not registered yet
154 wc
.cbSize
= sizeof (wc
);
155 // Some sensible style defaults
156 wc
.style
= CS_DBLCLKS
| CS_HREDRAW
| CS_VREDRAW
;
157 // Our default window procedure. This replaces itself
158 // on the first call with the simpler Window::WindowProcReflector().
159 wc
.lpfnWndProc
= Window::FirstWindowProcReflector
;
162 // One pointer to REFLECTION_INFO in the extra window instance bytes
165 wc
.hInstance
= GetInstance ();
166 // Use a bunch of system defaults for the GUI elements
170 wc
.hbrBackground
= (HBRUSH
) (COLOR_BACKGROUND
+ 1);
172 wc
.lpszMenuName
= NULL
;
173 // We'll get a little crazy here with the class name
174 wc
.lpszClassName
= "MainWindowClass";
176 // All set, try to register
177 WindowClassAtom
= RegisterClassEx (&wc
);
179 if (WindowClassAtom
== 0)
186 // We're registered, or already were before the call,
187 // return success in either case.
192 Window::Show (int State
)
194 ::ShowWindow (WindowHandle
, State
);
198 Window::GetWindowRect() const
201 ::GetWindowRect(WindowHandle
, &retval
);
206 Window::GetClientRect() const
209 ::GetClientRect(WindowHandle
, &retval
);
214 Window::MoveWindow(long x
, long y
, long w
, long h
, bool Repaint
)
216 return ::MoveWindow (WindowHandle
, x
, y
, w
, h
, Repaint
);
220 Window::MoveWindow(const RECTWrapper
&r
, bool Repaint
)
222 return ::MoveWindow (WindowHandle
, r
.left
, r
.top
, r
.width(), r
.height(), Repaint
);
226 Window::CenterWindow ()
228 RECT WindowRect
, ParentRect
;
229 int WindowWidth
, WindowHeight
;
232 // Get the window rectangle
233 WindowRect
= GetWindowRect ();
235 if (GetParent () == NULL
)
237 // Center on desktop window
238 ::GetWindowRect (GetDesktopWindow (), &ParentRect
);
242 // Center on client area of parent
243 ::GetClientRect (GetParent ()->GetHWND (), &ParentRect
);
246 WindowWidth
= WindowRect
.right
- WindowRect
.left
;
247 WindowHeight
= WindowRect
.bottom
- WindowRect
.top
;
249 // Find center of area we're centering on
250 p
.x
= (ParentRect
.right
- ParentRect
.left
) / 2;
251 p
.y
= (ParentRect
.bottom
- ParentRect
.top
) / 2;
253 // Convert that to screen coords
254 if (GetParent () == NULL
)
256 ClientToScreen (GetDesktopWindow (), &p
);
260 ClientToScreen (GetParent ()->GetHWND (), &p
);
263 // Calculate new top left corner for window
264 p
.x
-= WindowWidth
/ 2;
265 p
.y
-= WindowHeight
/ 2;
267 // And finally move the window
268 MoveWindow (p
.x
, p
.y
, WindowWidth
, WindowHeight
);
272 Window::WindowProc (UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
274 return DefWindowProc (WindowHandle
, uMsg
, wParam
, lParam
);
278 Window::MessageLoop ()
283 while (GetMessage (&msg
, NULL
, 0, 0) != 0
284 && GetMessage (&msg
, (HWND
) NULL
, 0, 0) != -1)
286 if (!IsWindow (WindowHandle
) || !IsDialogMessage (WindowHandle
, &msg
))
288 TranslateMessage (&msg
);
289 DispatchMessage (&msg
);
297 Window::PostMessageNow (UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
299 ::PostMessage (GetHWND (), uMsg
, wParam
, lParam
);
303 Window::IsButtonChecked (int nIDButton
) const
305 return ::IsDlgButtonChecked (GetHWND (), nIDButton
);
309 Window::SetDlgItemFont (int id
, const TCHAR
* fontname
, int Pointsize
,
310 int Weight
, bool Italic
, bool Underline
,
315 ctrl
= GetDlgItem (id
);
318 // Couldn't get that ID
322 // We need the DC for the point size calculation.
323 HDC hdc
= GetDC (ctrl
);
325 // Create the font. We have to keep it around until the dialog item
326 // goes away - basically until we're destroyed.
329 CreateFont (-MulDiv (Pointsize
, GetDeviceCaps (hdc
, LOGPIXELSY
), 72), 0,
330 0, 0, Weight
, Italic
? TRUE
: FALSE
,
331 Underline
? TRUE
: FALSE
, Strikeout
? TRUE
: FALSE
,
332 ANSI_CHARSET
, OUT_TT_PRECIS
, CLIP_DEFAULT_PRECIS
,
333 PROOF_QUALITY
, DEFAULT_PITCH
| FF_DONTCARE
, fontname
);
336 // Font creation failed
340 // Set the new font, and redraw any text which was already in the item.
341 SendMessage (ctrl
, WM_SETFONT
, (WPARAM
) hfnt
, TRUE
);
343 // Store the handle so that we can DeleteObject() it in dtor
344 Fonts
.push_back (hfnt
);
350 Window::SetWindowText (const std::wstring
& s
)
352 ::SetWindowTextW (WindowHandle
, s
.c_str ());
356 Window::ScreenToClient(const RECT
&r
) const
363 ::ScreenToClient(GetHWND(), &tl
);
366 ::ScreenToClient(GetHWND(), &br
);
379 Window::ActivateTooltips ()
380 // initialization of the tooltip capability
382 if (TooltipHandle
!= NULL
)
383 return; // already initialized
385 // create a window for the tool tips - will be invisible most of the time
386 if ((TooltipHandle
= CreateWindowEx (0, (LPCTSTR
) TOOLTIPS_CLASS
, NULL
,
387 WS_POPUP
| TTS_NOPREFIX
| TTS_ALWAYSTIP
, CW_USEDEFAULT
,
388 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, GetHWND (),
389 (HMENU
) 0, GetInstance (), (LPVOID
) 0)) == (HWND
) NULL
)
391 Log (LOG_PLAIN
) << "Warning: call to CreateWindowEx failed when "
392 "initializing tooltips. Error = %8.8x" << GetLastError ()
397 // must be topmost so that tooltips will display on top
398 SetWindowPos (TooltipHandle
, HWND_TOPMOST
, 0, 0, 0, 0,
399 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
401 // some of our tooltips are lengthy, and will disappear before they can be
402 // read with the default windows delay, so we set a long (30s) delay here.
403 SendMessage (TooltipHandle
, TTM_SETDELAYTIME
, TTDT_AUTOPOP
,
404 (LPARAM
) MAKELONG (30000, 0));
408 Window::SetTooltipState (bool b
)
409 // enable/disable tooltips
411 SendMessage (TooltipHandle
, (UINT
)TTM_ACTIVATE
, (WPARAM
)(BOOL
)b
, 0);
415 Window::AddTooltip (HWND target
, HWND win
, const char *text
)
416 // adds a tooltip to element 'target' in window 'win'
417 // note: text is limited to 80 chars (grumble)
424 memset ((void *)&ti
, 0, sizeof(ti
));
425 ti
.cbSize
= sizeof(ti
);
427 ti
.uFlags
= TTF_IDISHWND
// add tool based on handle not ID
428 | TTF_SUBCLASS
; // tool is to subclass the window in order
429 // to automatically get mouse events
431 ti
.uId
= reinterpret_cast<UINT_PTR
>(target
);
432 ti
.lpszText
= (LPTSTR
)text
; // pointer to text or string resource
434 SendMessage (TooltipHandle
, (UINT
)TTM_ADDTOOL
, 0,
435 (LPARAM
)(LPTOOLINFO
)&ti
);
439 Window::AddTooltip (int id
, const char *text
)
440 // adds a tooltip to a control identified by its ID
444 if ((target
= GetDlgItem (id
)) != NULL
&&
445 (parent
= ::GetParent (target
)) != NULL
)
446 AddTooltip (target
, parent
, text
);
450 Window::AddTooltip (int id
, int string_resource
)
451 // adds a tooltip that's represented by a string resource
452 // this also allows for tooltips greater than 80 characters
453 // we do this by setting the lpszText to LPSTR_TEXTCALLBACK
454 // and then responding to the TTN_GETDISPINFO notification
455 // in order to do this we store a list of (control ID, string ID) pairs
457 AddTooltip (id
, (const char *)LPSTR_TEXTCALLBACK
);
458 TooltipStrings
[id
] = string_resource
;
462 Window::TooltipNotificationHandler (LPARAM lParam
)
463 // this is the handler for TTN_GETDISPINFO notifications
465 NMTTDISPINFOW
*dispinfo
= (NMTTDISPINFOW
*)lParam
;
467 std::map
<int, int>::iterator findID
;
469 if ((dispinfo
->uFlags
& TTF_IDISHWND
) &&
470 ((ctrlID
= GetDlgCtrlID ((HWND
)dispinfo
->hdr
.idFrom
)) != 0) &&
471 ((findID
= TooltipStrings
.find (ctrlID
)) != TooltipStrings
.end ())) {
473 // enable multiple lines
474 SendMessage(dispinfo
->hdr
.hwndFrom
, TTM_SETMAXTIPWIDTH
, 0, 450);
476 // this is quite ugly. Apparently even when using string resources
477 // the tooltip length still can't exceed 80 chars. So, we fetch the
478 // resource into our own buffer and use that
481 LoadStringW (GetInstance (), findID
->second
, (LPWSTR
)buf
,
482 (sizeof (buf
) / sizeof (WCHAR
)));
484 dispinfo
->lpszText
= buf
;
486 // set this flag so that the control will not ask for this again
487 dispinfo
->uFlags
|= TTF_DI_SETITEM
;
488 dispinfo
->hinst
= NULL
;
496 Window::SetBusy (void)
498 // The docs suggest that you can call SetCursor, and it won't do
499 // anything if you've chosen the same cursor as is already set.
500 // However it looked to me as if it was resetting the animation
501 // frame every time when I tried it, hence this routine to make
502 // sure we only call it once on the way into and once on the way
504 if (BusyCount
++ == 0)
506 if (BusyCursor
== NULL
)
507 BusyCursor
= LoadCursor (NULL
, IDC_WAIT
);
508 OldCursor
= SetCursor (BusyCursor
);
513 Window::ClearBusy (void)
515 if (BusyCount
&& (--BusyCount
== 0))
517 SetCursor (OldCursor
);