WinapiExtension
 All Classes Files Functions Variables Typedefs Pages
Window.h
1 #ifndef WINAPI_EX_WINDOW_H
2 #define WINAPI_EX_WINDOW_H
3 
5 class WindowClass
6 {
7 public:
8  WindowClass(const WNDCLASSEX& windowClass, HINSTANCE instance, WNDPROC proc);
9 
10  bool Register();
11  template<class T>
12  tstring HexString(T value);
13 
14  WNDCLASSEX cls;
15  tstring clsName;
16 };
18 
23 {
24 public:
26  WindowParams();
31  WindowParams& Position(int x, int y);
35  WindowParams& Position(const POINT& p) { return Position(p.x, p.y); }
40  WindowParams& Size(int w, int h);
44  WindowParams& Size(const SIZE& s) { return Size(s.cx, s.cy); }
49  WindowParams& Rect(const RECT& r);
55  WindowParams& InnerSize(int w, int h);
59  WindowParams& InnerSize(const SIZE& s) { return InnerSize(s.cx, s.cy); }
64  WindowParams& Style(DWORD style);
69  WindowParams& ExStyle(DWORD exStyle);
74  WindowParams& Parent(HWND parent);
79  WindowParams& Menu(HMENU menu);
84  WindowParams& Text(const tstring& text);
94  WindowParams& Cursor(HCURSOR cursor);
98  WindowParams& Icon(HICON icon);
102  WindowParams& Background(HBRUSH bg);
107  WindowParams& BackgroundNull() { return Background(static_cast<HBRUSH>(GetStockObject(NULL_BRUSH))); }
108 
109  POINT creationPos;
114  HMENU creationMenu;
121  WNDCLASSEX cls;
122 };
123 
130 class Window : public WindowMessageHandler, public IWindow
131 {
132 public:
134  Window();
136  virtual ~Window();
137 
138  HWND _InternalGetHandle() { return window; }
139 
145  bool Create(const WindowParams& params);
147  void AttachFontRecursive(HFONT font);
149  static void SetDefaultFont(HFONT font) { DefaultFont() = font; }
151  static HFONT GetDefaultFont() { return DefaultFont(); }
152 
154  static HCURSOR GetDefaultCursor() { return DefaultCursor(); }
158  static void SetDefaultCursor(HCURSOR cursor) { DefaultCursor() = cursor; }
159 
161  static HICON GetDefaultIcon() { return DefaultIcon(); }
165  static void SetDefaultIcon(HICON icon) { DefaultIcon() = icon; }
166 
168  static HBRUSH GetDefaultBackground() { return DefaultBackground(); }
172  static void SetDefaultBackground(HBRUSH bg) { DefaultBackground() = bg; }
173 
175  void SetClientSize(int w, int h);
176 
178  static LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
180  static Window* GetObjectFromWindow(HWND hwnd);
181 
183  HINSTANCE GetInstance() const;
185  Window* GetParent() const;
187  operator HWND() const { return window; }
189  HWND GetHandle() const { return window; }
190 protected:
191  virtual LRESULT OnMessage(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { return DefWindowProc(hwnd, message, wp, lp); }
192 private:
194  LRESULT WndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
195  {
196  // New style, use functor message handlers
197  LRESULT result;
198  if(PassMessageToHandlerFunction(hwnd, message, wp, lp, result))
199  return result;
200 
201  // Old style message handling, everything in one big virtual function
202  return OnMessage(hwnd, message, wp, lp);
203  }
204 
205  void AttachFontRecursive(HFONT font, HWND win);
207  HWND window;
208 
209  static HFONT& DefaultFont() { static HFONT defaultFont = NULL; return defaultFont; }
210  static HCURSOR& DefaultCursor() { static HCURSOR defaultCursor = LoadCursor(NULL, IDC_ARROW); return defaultCursor; }
211  static HICON& DefaultIcon() { static HICON defaultIcon = LoadIcon(NULL, IDI_APPLICATION); return defaultIcon; }
212  static HBRUSH& DefaultBackground() { static HBRUSH defaultBackground = GetSysColorBrush(COLOR_BTNFACE); return defaultBackground; }
213 };
214 
215 //-----------------------------------------------------------------------------
216 
217 inline WindowClass::WindowClass(const WNDCLASSEX& windowClass, HINSTANCE instance, WNDPROC proc)
218 {
219  memcpy(&cls, &windowClass, sizeof(windowClass));
220  cls.cbSize = sizeof(cls);
221  cls.hInstance = instance;
222  cls.lpfnWndProc = proc;
223 }
224 
225 inline bool WindowClass::Register()
226 {
227  // build the classname from the members of the class
228  // TODO: cls.lpszMenuName is not supported, should it?
229  clsName = tstring(_T("WINAPIEX_") +
230  HexString(cls.style) + _T("_") +
231  HexString(cls.cbClsExtra) + _T("_") +
232  HexString(cls.cbWndExtra) + _T("_") +
233  HexString((LONG_PTR)cls.hCursor)) + _T("_") +
234  HexString((LONG_PTR)cls.hIcon) + _T("_") +
235  HexString((LONG_PTR)cls.hIconSm) + _T("_") +
236  HexString((LONG_PTR)cls.hbrBackground);
237 
238  cls.lpszClassName = clsName.c_str();
239 
240  if(RegisterClassEx(&cls))
241  return true;
242 
243  // check if the class is already registered and equal
244  WNDCLASSEX registeredClass = {0};
245  registeredClass.cbSize = sizeof(registeredClass);
246  if(GetClassInfoEx(cls.hInstance, cls.lpszClassName, &registeredClass))
247  if( registeredClass.lpfnWndProc == cls.lpfnWndProc &&
248  registeredClass.hCursor == cls.hCursor &&
249  registeredClass.hIcon == cls.hIcon &&
250  registeredClass.hbrBackground == cls.hbrBackground &&
251  registeredClass.style == cls.style)
252  {
253  return true;
254  }
255 
256  // can't register the class, someone else did it before
257  return false;
258 }
259 
260 template<class T>
261 tstring WindowClass::HexString(T value)
262 {
263  // convert number to string without sprintf
264  TCHAR buffer[256];
265 
266  buffer[0] = _T('\0');
267  int i = 1;
268 
269  if(value == 0)
270  buffer[i++] = _T('0');
271 
272  while(value != 0)
273  {
274  int rem = static_cast<int>(value % 16);
275  if(rem >= 10)
276  buffer[i] = _T('A') + rem - 10;
277  else
278  buffer[i] = _T('0') + rem;
279  i++;
280  value /= 16;
281  }
282 
283  for(int j = 0; j < i/2; ++j)
284  std::swap(buffer[j], buffer[i - 1 - j]);
285 
286  return buffer;
287 }
288 
289 //-----------------------------------------------------------------------------
290 
292 {
293  creationPos.x = CW_USEDEFAULT;
294  creationPos.y = CW_USEDEFAULT;
295  creationSize.cx = 100;
296  creationSize.cy = 100;
297  creationStyle = WS_OVERLAPPEDWINDOW;
298  creationExStyle = 0;
299  creationParent = NULL;
300  creationMenu = NULL;
301 
302  ZeroMemory(&cls, sizeof(cls));
303  cls.cbSize = sizeof(cls);
304  cls.style = CS_DBLCLKS; // class styles are mostly useless
305 }
306 
308 {
309  creationPos.x = x;
310  creationPos.y = y;
311  return *this;
312 }
313 
314 inline WindowParams& WindowParams::Size(int w, int h)
315 {
316  creationSize.cx = w;
317  creationSize.cy = h;
318  return *this;
319 }
320 
321 inline WindowParams& WindowParams::Rect(const RECT& r)
322 {
323  creationPos.x = r.left;
324  creationPos.y = r.top;
325  creationSize.cx = r.right - r.left;
326  creationSize.cy = r.bottom - r.top;
327  return *this;
328 }
329 
331 {
332  RECT adjRect;
333  adjRect.left = adjRect.top = 0;
334  adjRect.right = w;
335  adjRect.bottom = h;
336  AdjustWindowRectEx(&adjRect, creationStyle, creationMenu != NULL, creationExStyle);
337 
338  creationSize.cx = adjRect.right - adjRect.left;
339  creationSize.cy = adjRect.bottom - adjRect.top;
340  return *this;
341 }
342 
343 inline WindowParams& WindowParams::Style(DWORD style)
344 {
345  creationStyle = style;
346  return *this;
347 }
348 
349 inline WindowParams& WindowParams::ExStyle(DWORD exStyle)
350 {
351  creationExStyle = exStyle;
352  return *this;
353 }
354 
355 inline WindowParams& WindowParams::Parent(HWND parent)
356 {
357  creationParent = parent;
358  return *this;
359 }
360 
361 inline WindowParams& WindowParams::Menu(HMENU menu)
362 {
363  creationMenu = menu;
364  return *this;
365 }
366 
368 {
369  creationText = text;
370  return *this;
371 }
372 
374 {
375  const UINT bits = CS_HREDRAW|CS_VREDRAW;
376  if(set) cls.style |= bits;
377  else cls.style &= ~bits;
378  return *this;
379 }
380 
381 inline WindowParams& WindowParams::Cursor(HCURSOR cursor)
382 {
383  cls.hCursor = cursor;
384  return *this;
385 }
386 
387 inline WindowParams& WindowParams::Icon(HICON icon)
388 {
389  cls.hIcon = icon;
390  return *this;
391 }
392 
394 {
395  cls.hbrBackground = bg;
396  return *this;
397 }
398 
399 //-----------------------------------------------------------------------------
400 
402 {
403  // empty ctor, because we want to support stack-allocated instances of this class
404  window = NULL;
405 }
406 
408 {
409  if(window)
410  {
411  WINAPI_EX_TRACE(_T("DestroyWindow() called in Window::~Window, virtual functions will not work properly. Call DestroyWindow() in your derived class's dtor."), __LINE__);
412  }
413 
414  // if the handle is not NULL, DestroyWindow() will send the messages WM_DESTROY and WM_NCDESTROY
415  // but this is the base destructor, virtual functions don't work properly here
416  // this can only be solved if the user calls DestroyWindow in his derived class
417  // otherwise WM_DESTROY and WM_NCDESTROY cannot be received in the derived class
418 
419  DestroyWindow(window);
420 }
421 
422 inline bool Window::Create(const WindowParams& params)
423 {
424  // don't call Create() twice without destroying the window first
425  assert(window == NULL);
426 
427  // we need a HINSTANCE, and we don't want to annoy the user about it
428  // so we take whichever HINSTANCE we can get
429  HINSTANCE instance;
430  if(params.creationParent)
431  instance = (HINSTANCE)GetWindowLongPtr(params.creationParent, GWLP_HINSTANCE);
432  else
433  instance = GetModuleHandle(NULL);
434 
435  WindowClass cls(params.cls, instance, Window::StaticWndProc);
436  if(cls.cls.hCursor == 0)
437  cls.cls.hCursor = Window::DefaultCursor();
438  if(cls.cls.hIcon == 0)
439  cls.cls.hIcon = Window::DefaultIcon();
440  if(cls.cls.hbrBackground == 0)
441  cls.cls.hbrBackground = Window::DefaultBackground();
442 
443  bool windowClassRegistered = cls.Register();
444  assert(windowClassRegistered);
445  if(!windowClassRegistered)
446  return false;
447 
448  window = CreateWindowEx(
449  params.creationExStyle,
450  cls.cls.lpszClassName,
451  params.creationText.c_str(),
452  params.creationStyle,
453  params.creationPos.x,
454  params.creationPos.y,
455  params.creationSize.cx,
456  params.creationSize.cy,
457  params.creationParent,
458  params.creationMenu,
459  instance,
460  this); // this-pointer as USERDATA, we need it for WM_NCCREATE
461 
462  // we except CreateWindowEx() to always work
463  // but maybe the WindowClass isn't registered
464  assert(window != NULL);
465 
466  if(window && DefaultFont())
467  SendMessage(window, WM_SETFONT, (WPARAM)DefaultFont(), 0);
468 
469  return window != NULL;
470 }
471 
472 inline LRESULT CALLBACK Window::StaticWndProc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
473 {
474  if(message == WM_NCCREATE)
475  {
476  // get the pointer to the window from lpCreateParams which was set in CreateWindow
477  SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)(((CREATESTRUCT*)lp)->lpCreateParams));
478  }
479 
480  // get the pointer to the window
481  // must be after WM_NCCREATE
482  Window* windowObj = GetObjectFromWindow(hwnd);
483 
484  if(message == WM_NCDESTROY)
485  {
486  // this is the absolute last message that the window receives
487  // clean up our stuff
488  SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
489  windowObj->window = NULL;
490  }
491 
492  // if we have the pointer, go to the message handler of the window
493  if(windowObj)
494  return windowObj->WndProc(hwnd, message, wp, lp);
495  else
496  // it would be strange if this path is executed
497  // but it can happen, for some windows WM_GETMINMAXINFO arrives before WM_NCCREATE
498  return DefWindowProc(hwnd, message, wp, lp);
499 }
500 
502 {
503  // a pointer to the class instance is attached to the HWND
504  // at least that's the case when the HWND is created by this class
505  return (Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
506 }
507 
508 inline HINSTANCE Window::GetInstance() const
509 {
510  return (HINSTANCE)GetWindowLongPtr(window, GWLP_HINSTANCE);
511 }
512 
513 inline Window* Window::GetParent() const
514 {
515  // GWLP_HWNDPARENT is more reliable than GetParent()
516  return GetObjectFromWindow((HWND)GetWindowLongPtr(window, GWLP_HWNDPARENT));
517 }
518 
519 inline void Window::SetClientSize(int w, int h)
520 {
521  // calculate size
522  Rect adjRect(0, 0, w, h);
523  AdjustWindowRectEx(&adjRect, GetStyle(), GetMenu(window) != NULL, GetExStyle());
524 
525  // set size without changing the position
526  Move(WindowRect().TopLeft(), adjRect.GetSize());
527 }
528 
529 inline void Window::AttachFontRecursive(HFONT font)
530 {
531  AttachFontRecursive(font, window);
532 }
533 
534 inline void Window::AttachFontRecursive(HFONT font, HWND win)
535 {
536  // if this were done without recursion, a queue would be needed
537  // and that would have bloated the code
538  // also, how many children does a window have? it can't be many
539  SendMessage(win, WM_SETFONT, (WPARAM)font, TRUE);
540 
541  HWND child = GetWindow(win, GW_CHILD | GW_HWNDFIRST);
542  while(child)
543  {
544  AttachFontRecursive(font, child);
545  child = GetWindow(child, GW_HWNDNEXT);
546  }
547 }
548 
549 #endif