Translated using Weblate (Chinese (Simplified))
[cygwin-setup.git] / window.cc
blob5f435cc84bd16b998185a9d95a8285f3ff43b5dc
1 /*
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
10 * http://www.gnu.org/
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,
18 // PropSheet).
20 #include "win32.h"
21 #include "window.h"
22 #include "RECTWrapper.h"
23 #include "msg.h"
24 #include "resource.h"
26 ATOM Window::WindowClassAtom = 0;
27 HINSTANCE Window::AppInstance = NULL;
29 Window::Window ()
31 WindowHandle = NULL;
32 Parent = NULL;
33 TooltipHandle = NULL;
34 BusyCount = 0;
35 BusyCursor = NULL;
39 Window::~Window ()
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
46 if (TooltipHandle)
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);
55 LRESULT CALLBACK
56 Window::FirstWindowProcReflector (HWND hwnd, UINT uMsg, WPARAM wParam,
57 LPARAM lParam)
59 Window *wnd = NULL;
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;
79 else
81 // Should never get here.
82 Log (LOG_PLAIN) << "Warning: Unexpected windows message " << uMsg
83 << "received early in window initialization."
84 << endLog;
87 return wnd->WindowProc (uMsg, wParam, lParam);
90 LRESULT CALLBACK
91 Window::WindowProcReflector (HWND hwnd, UINT uMsg, WPARAM wParam,
92 LPARAM lParam)
94 Window *This;
96 // Get our this pointer
97 This = reinterpret_cast<Window *>(GetWindowLongPtr (hwnd, GWLP_USERDATA));
99 return This->WindowProc (uMsg, wParam, lParam);
102 bool
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
109 return false;
112 // Save our parent, we'll probably need it eventually.
113 Parent = parent;
115 // Create the window instance
116 WindowHandle = CreateWindowEx (
117 // Extended Style
119 "MainWindowClass", //MAKEINTATOM(WindowClassAtom), // window class atom (name)
120 "Hello", // no title-bar string yet
121 // Style bits
122 Style,
123 // Default positions and size
124 CW_USEDEFAULT,
125 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
126 // Parent Window
127 parent ==
128 NULL ? (HWND) NULL : parent->GetHWND (),
129 // use class menu
130 (HMENU) NULL,
131 // The application instance
132 GetInstance (),
133 // The this ptr, which we'll use to set up the WindowProc reflection.
134 (LPVOID) this);
136 if (WindowHandle == NULL)
138 // Failed
139 return false;
142 return true;
145 bool
146 Window::registerWindowClass ()
148 if (WindowClassAtom == 0)
150 // We're not registered yet
151 WNDCLASSEX
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;
160 // No class bytes
161 wc.cbClsExtra = 0;
162 // One pointer to REFLECTION_INFO in the extra window instance bytes
163 wc.cbWndExtra = 4;
164 // The app instance
165 wc.hInstance = GetInstance ();
166 // Use a bunch of system defaults for the GUI elements
167 wc.hIcon = NULL;
168 wc.hIconSm = NULL;
169 wc.hCursor = NULL;
170 wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND + 1);
171 // No menu
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)
181 // Failed
182 return false;
186 // We're registered, or already were before the call,
187 // return success in either case.
188 return true;
191 void
192 Window::Show (int State)
194 ::ShowWindow (WindowHandle, State);
197 RECT
198 Window::GetWindowRect() const
200 RECT retval;
201 ::GetWindowRect(WindowHandle, &retval);
202 return retval;
205 RECT
206 Window::GetClientRect() const
208 RECT retval;
209 ::GetClientRect(WindowHandle, &retval);
210 return retval;
213 bool
214 Window::MoveWindow(long x, long y, long w, long h, bool Repaint)
216 return ::MoveWindow (WindowHandle, x, y, w, h, Repaint);
219 bool
220 Window::MoveWindow(const RECTWrapper &r, bool Repaint)
222 return ::MoveWindow (WindowHandle, r.left, r.top, r.width(), r.height(), Repaint);
225 void
226 Window::CenterWindow ()
228 RECT WindowRect, ParentRect;
229 int WindowWidth, WindowHeight;
230 POINT p;
232 // Get the window rectangle
233 WindowRect = GetWindowRect ();
235 if (GetParent () == NULL)
237 // Center on desktop window
238 ::GetWindowRect (GetDesktopWindow (), &ParentRect);
240 else
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);
258 else
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);
271 LRESULT
272 Window::WindowProc (UINT uMsg, WPARAM wParam, LPARAM lParam)
274 return DefWindowProc (WindowHandle, uMsg, wParam, lParam);
277 bool
278 Window::MessageLoop ()
281 msg;
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);
293 return true;
296 void
297 Window::PostMessageNow (UINT uMsg, WPARAM wParam, LPARAM lParam)
299 ::PostMessage (GetHWND (), uMsg, wParam, lParam);
302 UINT
303 Window::IsButtonChecked (int nIDButton) const
305 return ::IsDlgButtonChecked (GetHWND (), nIDButton);
308 bool
309 Window::SetDlgItemFont (int id, const TCHAR * fontname, int Pointsize,
310 int Weight, bool Italic, bool Underline,
311 bool Strikeout)
313 HWND ctrl;
315 ctrl = GetDlgItem (id);
316 if (ctrl == NULL)
318 // Couldn't get that ID
319 return false;
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.
327 HFONT hfnt;
328 hfnt =
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);
334 if (hfnt == NULL)
336 // Font creation failed
337 return false;
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);
346 return true;
349 void
350 Window::SetWindowText (const std::wstring& s)
352 ::SetWindowTextW (WindowHandle, s.c_str ());
355 RECT
356 Window::ScreenToClient(const RECT &r) const
358 POINT tl;
359 POINT br;
361 tl.y = r.top;
362 tl.x = r.left;
363 ::ScreenToClient(GetHWND(), &tl);
364 br.y = r.bottom;
365 br.x = r.right;
366 ::ScreenToClient(GetHWND(), &br);
368 RECT ret;
370 ret.top = tl.y;
371 ret.left = tl.x;
372 ret.bottom = br.y;
373 ret.right = br.x;
375 return ret;
378 void
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 ()
393 << endLog;
394 return;
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));
407 void
408 Window::SetTooltipState (bool b)
409 // enable/disable tooltips
411 SendMessage (TooltipHandle, (UINT)TTM_ACTIVATE, (WPARAM)(BOOL)b, 0);
414 void
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)
419 if (!TooltipHandle)
420 ActivateTooltips ();
422 TOOLINFO ti;
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
430 ti.hwnd = win;
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);
438 void
439 Window::AddTooltip (int id, const char *text)
440 // adds a tooltip to a control identified by its ID
442 HWND target, parent;
444 if ((target = GetDlgItem (id)) != NULL &&
445 (parent = ::GetParent (target)) != NULL)
446 AddTooltip (target, parent, text);
449 void
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;
461 BOOL
462 Window::TooltipNotificationHandler (LPARAM lParam)
463 // this is the handler for TTN_GETDISPINFO notifications
465 NMTTDISPINFOW *dispinfo = (NMTTDISPINFOW *)lParam;
466 int ctrlID;
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
480 WCHAR buf[2048];
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;
489 return TRUE;
492 return FALSE;
495 void
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
503 // out of busy mode.
504 if (BusyCount++ == 0)
506 if (BusyCursor == NULL)
507 BusyCursor = LoadCursor (NULL, IDC_WAIT);
508 OldCursor = SetCursor (BusyCursor);
512 void
513 Window::ClearBusy (void)
515 if (BusyCount && (--BusyCount == 0))
517 SetCursor (OldCursor);