| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/views/custom_frame_window.h" | |
| 6 | |
| 7 #include "base/gfx/point.h" | |
| 8 #include "base/gfx/size.h" | |
| 9 #include "chrome/common/gfx/chrome_canvas.h" | |
| 10 #include "chrome/common/gfx/path.h" | |
| 11 #include "chrome/common/l10n_util.h" | |
| 12 #include "chrome/common/win_util.h" | |
| 13 #include "chrome/views/client_view.h" | |
| 14 #include "chrome/views/default_non_client_view.h" | |
| 15 #include "chrome/views/root_view.h" | |
| 16 #include "chrome/views/window_delegate.h" | |
| 17 #include "grit/generated_resources.h" | |
| 18 | |
| 19 namespace views { | |
| 20 | |
| 21 // A scoping class that prevents a window from being able to redraw in response | |
| 22 // to invalidations that may occur within it for the lifetime of the object. | |
| 23 // | |
| 24 // Why would we want such a thing? Well, it turns out Windows has some | |
| 25 // "unorthodox" behavior when it comes to painting its non-client areas. | |
| 26 // Occasionally, Windows will paint portions of the default non-client area | |
| 27 // right over the top of the custom frame. This is not simply fixed by handling | |
| 28 // WM_NCPAINT/WM_PAINT, with some investigation it turns out that this | |
| 29 // rendering is being done *inside* the default implementation of some message | |
| 30 // handlers and functions: | |
| 31 // . WM_SETTEXT | |
| 32 // . WM_SETICON | |
| 33 // . WM_NCLBUTTONDOWN | |
| 34 // . EnableMenuItem, called from our WM_INITMENU handler | |
| 35 // The solution is to handle these messages and call DefWindowProc ourselves, | |
| 36 // but prevent the window from being able to update itself for the duration of | |
| 37 // the call. We do this with this class, which automatically calls its | |
| 38 // associated CustomFrameWindow's lock and unlock functions as it is created | |
| 39 // and destroyed. See documentation in those methods for the technique used. | |
| 40 // | |
| 41 // IMPORTANT: Do not use this scoping object for large scopes or periods of | |
| 42 // time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh). | |
| 43 // | |
| 44 // I would love to hear Raymond Chen's explanation for all this. And maybe a | |
| 45 // list of other messages that this applies to ;-) | |
| 46 class CustomFrameWindow::ScopedRedrawLock { | |
| 47 public: | |
| 48 explicit ScopedRedrawLock(CustomFrameWindow* window) : window_(window) { | |
| 49 window_->LockUpdates(); | |
| 50 } | |
| 51 | |
| 52 ~ScopedRedrawLock() { | |
| 53 window_->UnlockUpdates(); | |
| 54 } | |
| 55 | |
| 56 private: | |
| 57 // The window having its style changed. | |
| 58 CustomFrameWindow* window_; | |
| 59 }; | |
| 60 | |
| 61 HCURSOR CustomFrameWindow::resize_cursors_[6]; | |
| 62 | |
| 63 /////////////////////////////////////////////////////////////////////////////// | |
| 64 // CustomFrameWindow, public: | |
| 65 | |
| 66 CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate) | |
| 67 : Window(window_delegate), | |
| 68 is_active_(false), | |
| 69 lock_updates_(false), | |
| 70 saved_window_style_(0) { | |
| 71 InitClass(); | |
| 72 non_client_view_ = new DefaultNonClientView(this); | |
| 73 } | |
| 74 | |
| 75 CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate, | |
| 76 NonClientView* non_client_view) | |
| 77 : Window(window_delegate) { | |
| 78 InitClass(); | |
| 79 non_client_view_ = non_client_view; | |
| 80 } | |
| 81 | |
| 82 CustomFrameWindow::~CustomFrameWindow() { | |
| 83 } | |
| 84 | |
| 85 /////////////////////////////////////////////////////////////////////////////// | |
| 86 // CustomFrameWindow, Window overrides: | |
| 87 | |
| 88 void CustomFrameWindow::Init(HWND parent, const gfx::Rect& bounds) { | |
| 89 // TODO(beng): (Cleanup) Right now, the only way to specify a different | |
| 90 // non-client view is to subclass this object and provide one | |
| 91 // by setting this member before calling Init. | |
| 92 if (!non_client_view_) | |
| 93 non_client_view_ = new DefaultNonClientView(this); | |
| 94 Window::Init(parent, bounds); | |
| 95 | |
| 96 ResetWindowRegion(); | |
| 97 } | |
| 98 | |
| 99 void CustomFrameWindow::UpdateWindowTitle() { | |
| 100 // Layout winds up causing the title to be re-validated during | |
| 101 // string measurement. | |
| 102 non_client_view_->Layout(); | |
| 103 // Must call the base class too so that places like the Task Bar get updated. | |
| 104 Window::UpdateWindowTitle(); | |
| 105 } | |
| 106 | |
| 107 void CustomFrameWindow::UpdateWindowIcon() { | |
| 108 // The icon will be re-validated during painting. | |
| 109 non_client_view_->SchedulePaint(); | |
| 110 // Call the base class so that places like the Task Bar get updated. | |
| 111 Window::UpdateWindowIcon(); | |
| 112 } | |
| 113 | |
| 114 void CustomFrameWindow::EnableClose(bool enable) { | |
| 115 non_client_view_->EnableClose(enable); | |
| 116 // Make sure the SysMenu changes to reflect this change as well. | |
| 117 Window::EnableClose(enable); | |
| 118 } | |
| 119 | |
| 120 void CustomFrameWindow::DisableInactiveRendering(bool disable) { | |
| 121 Window::DisableInactiveRendering(disable); | |
| 122 non_client_view_->set_paint_as_active(disable); | |
| 123 if (!disable) | |
| 124 non_client_view_->SchedulePaint(); | |
| 125 } | |
| 126 | |
| 127 void CustomFrameWindow::SizeWindowToDefault() { | |
| 128 gfx::Size pref = client_view()->GetPreferredSize(); | |
| 129 DCHECK(pref.width() > 0 && pref.height() > 0); | |
| 130 gfx::Size window_size = | |
| 131 non_client_view_->CalculateWindowSizeForClientSize(pref.width(), | |
| 132 pref.height()); | |
| 133 win_util::CenterAndSizeWindow(owning_window(), GetHWND(), | |
| 134 window_size.ToSIZE(), false); | |
| 135 } | |
| 136 | |
| 137 /////////////////////////////////////////////////////////////////////////////// | |
| 138 // CustomFrameWindow, WidgetWin overrides: | |
| 139 | |
| 140 static void EnableMenuItem(HMENU menu, UINT command, bool enabled) { | |
| 141 UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED); | |
| 142 EnableMenuItem(menu, command, flags); | |
| 143 } | |
| 144 | |
| 145 void CustomFrameWindow::OnInitMenu(HMENU menu) { | |
| 146 bool is_minimized = IsMinimized(); | |
| 147 bool is_maximized = IsMaximized(); | |
| 148 bool is_restored = !is_minimized && !is_maximized; | |
| 149 | |
| 150 ScopedRedrawLock lock(this); | |
| 151 EnableMenuItem(menu, SC_RESTORE, !is_restored); | |
| 152 EnableMenuItem(menu, SC_MOVE, is_restored); | |
| 153 EnableMenuItem(menu, SC_SIZE, window_delegate()->CanResize() && is_restored); | |
| 154 EnableMenuItem(menu, SC_MAXIMIZE, | |
| 155 window_delegate()->CanMaximize() && !is_maximized); | |
| 156 EnableMenuItem(menu, SC_MINIMIZE, | |
| 157 window_delegate()->CanMaximize() && !is_minimized); | |
| 158 } | |
| 159 | |
| 160 void CustomFrameWindow::OnMouseLeave() { | |
| 161 bool process_mouse_exited = true; | |
| 162 POINT pt; | |
| 163 if (GetCursorPos(&pt)) { | |
| 164 LRESULT ht_component = | |
| 165 ::SendMessage(GetHWND(), WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); | |
| 166 if (ht_component != HTNOWHERE) { | |
| 167 // If the mouse moved into a part of the window's non-client area, then | |
| 168 // don't send a mouse exited event since the mouse is still within the | |
| 169 // bounds of the ChromeView that's rendering the frame. Note that we do | |
| 170 // _NOT_ do this for windows with native frames, since in that case the | |
| 171 // mouse really will have left the bounds of the RootView. | |
| 172 process_mouse_exited = false; | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 if (process_mouse_exited) | |
| 177 ProcessMouseExited(); | |
| 178 } | |
| 179 | |
| 180 LRESULT CustomFrameWindow::OnNCActivate(BOOL active) { | |
| 181 is_active_ = !!active; | |
| 182 | |
| 183 // We can get WM_NCACTIVATE before we're actually visible. If we're not | |
| 184 // visible, no need to paint. | |
| 185 if (IsWindowVisible(GetHWND())) { | |
| 186 non_client_view_->SchedulePaint(); | |
| 187 // We need to force a paint now, as a user dragging a window will block | |
| 188 // painting operations while the move is in progress. | |
| 189 PaintNow(root_view_->GetScheduledPaintRect()); | |
| 190 } | |
| 191 | |
| 192 // Defering to our parent as it is important that the NCActivate message gets | |
| 193 // DefProc'ed or the task bar won't show our process as active. | |
| 194 // See bug http://crbug.com/4513. | |
| 195 return Window::OnNCActivate(active); | |
| 196 } | |
| 197 | |
| 198 LRESULT CustomFrameWindow::OnNCCalcSize(BOOL mode, LPARAM l_param) { | |
| 199 // We need to repaint all when the window bounds change. | |
| 200 return WVR_REDRAW; | |
| 201 } | |
| 202 | |
| 203 LRESULT CustomFrameWindow::OnNCHitTest(const CPoint& point) { | |
| 204 // NC points are in screen coordinates. | |
| 205 CPoint temp = point; | |
| 206 MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); | |
| 207 return non_client_view_->NonClientHitTest(gfx::Point(temp.x, temp.y)); | |
| 208 } | |
| 209 | |
| 210 struct ClipState { | |
| 211 // The window being painted. | |
| 212 HWND parent; | |
| 213 | |
| 214 // DC painting to. | |
| 215 HDC dc; | |
| 216 | |
| 217 // Origin of the window in terms of the screen. | |
| 218 int x; | |
| 219 int y; | |
| 220 }; | |
| 221 | |
| 222 // See comments in OnNCPaint for details of this function. | |
| 223 static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { | |
| 224 ClipState* clip_state = reinterpret_cast<ClipState*>(param); | |
| 225 if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { | |
| 226 RECT bounds; | |
| 227 GetWindowRect(window, &bounds); | |
| 228 ExcludeClipRect(clip_state->dc, | |
| 229 bounds.left - clip_state->x, | |
| 230 bounds.top - clip_state->y, | |
| 231 bounds.right - clip_state->x, | |
| 232 bounds.bottom - clip_state->y); | |
| 233 } | |
| 234 return TRUE; | |
| 235 } | |
| 236 | |
| 237 void CustomFrameWindow::OnNCPaint(HRGN rgn) { | |
| 238 // We have an NC region and need to paint it. We expand the NC region to | |
| 239 // include the dirty region of the root view. This is done to minimize | |
| 240 // paints. | |
| 241 CRect window_rect; | |
| 242 GetWindowRect(&window_rect); | |
| 243 | |
| 244 if (window_rect.Width() != root_view_->width() || | |
| 245 window_rect.Height() != root_view_->height()) { | |
| 246 // If the size of the window differs from the size of the root view it | |
| 247 // means we're being asked to paint before we've gotten a WM_SIZE. This can | |
| 248 // happen when the user is interactively resizing the window. To avoid | |
| 249 // mass flickering we don't do anything here. Once we get the WM_SIZE we'll | |
| 250 // reset the region of the window which triggers another WM_NCPAINT and | |
| 251 // all is well. | |
| 252 return; | |
| 253 } | |
| 254 | |
| 255 CRect dirty_region; | |
| 256 // A value of 1 indicates paint all. | |
| 257 if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { | |
| 258 dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); | |
| 259 } else { | |
| 260 RECT rgn_bounding_box; | |
| 261 GetRgnBox(rgn, &rgn_bounding_box); | |
| 262 if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) | |
| 263 return; // Dirty region doesn't intersect window bounds, bale. | |
| 264 | |
| 265 // rgn_bounding_box is in screen coordinates. Map it to window coordinates. | |
| 266 OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); | |
| 267 } | |
| 268 | |
| 269 // In theory GetDCEx should do what we want, but I couldn't get it to work. | |
| 270 // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell | |
| 271 // it doesn't work at all. So, instead we get the DC for the window then | |
| 272 // manually clip out the children. | |
| 273 HDC dc = GetWindowDC(GetHWND()); | |
| 274 ClipState clip_state; | |
| 275 clip_state.x = window_rect.left; | |
| 276 clip_state.y = window_rect.top; | |
| 277 clip_state.parent = GetHWND(); | |
| 278 clip_state.dc = dc; | |
| 279 EnumChildWindows(GetHWND(), &ClipDCToChild, | |
| 280 reinterpret_cast<LPARAM>(&clip_state)); | |
| 281 | |
| 282 RootView* root_view = GetRootView(); | |
| 283 CRect old_paint_region = root_view->GetScheduledPaintRectConstrainedToSize(); | |
| 284 | |
| 285 if (!old_paint_region.IsRectEmpty()) { | |
| 286 // The root view has a region that needs to be painted. Include it in the | |
| 287 // region we're going to paint. | |
| 288 | |
| 289 CRect tmp = dirty_region; | |
| 290 UnionRect(&dirty_region, &tmp, &old_paint_region); | |
| 291 } | |
| 292 | |
| 293 root_view->SchedulePaint(gfx::Rect(dirty_region), false); | |
| 294 | |
| 295 // ChromeCanvasPaints destructor does the actual painting. As such, wrap the | |
| 296 // following in a block to force paint to occur so that we can release the dc. | |
| 297 { | |
| 298 ChromeCanvasPaint canvas(dc, opaque(), dirty_region.left, dirty_region.top, | |
| 299 dirty_region.Width(), dirty_region.Height()); | |
| 300 | |
| 301 root_view->ProcessPaint(&canvas); | |
| 302 } | |
| 303 | |
| 304 ReleaseDC(GetHWND(), dc); | |
| 305 } | |
| 306 | |
| 307 void CustomFrameWindow::OnNCLButtonDown(UINT ht_component, | |
| 308 const CPoint& point) { | |
| 309 switch (ht_component) { | |
| 310 case HTCLOSE: | |
| 311 case HTMINBUTTON: | |
| 312 case HTMAXBUTTON: { | |
| 313 // When the mouse is pressed down in these specific non-client areas, we | |
| 314 // need to tell the RootView to send the mouse pressed event (which sets | |
| 315 // capture, allowing subsequent WM_LBUTTONUP (note, _not_ WM_NCLBUTTONUP) | |
| 316 // to fire so that the appropriate WM_SYSCOMMAND can be sent by the | |
| 317 // applicable button's ButtonListener. We _have_ to do this this way | |
| 318 // rather than letting Windows just send the syscommand itself (as would | |
| 319 // happen if we never did this dance) because for some insane reason | |
| 320 // DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed window | |
| 321 // control button appearance, in the Windows classic style, over our | |
| 322 // view! Ick! By handling this message we prevent Windows from doing this | |
| 323 // undesirable thing, but that means we need to roll the sys-command | |
| 324 // handling ourselves. | |
| 325 ProcessNCMousePress(point, MK_LBUTTON); | |
| 326 return; | |
| 327 } | |
| 328 default: | |
| 329 Window::OnNCLButtonDown(ht_component, point); | |
| 330 /* | |
| 331 if (!IsMsgHandled()) { | |
| 332 // Window::OnNCLButtonDown set the message as unhandled. This normally | |
| 333 // means WidgetWin::ProcessWindowMessage will pass it to | |
| 334 // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird | |
| 335 // non-client painting, so we need to call it directly here inside a | |
| 336 // scoped update lock. | |
| 337 ScopedRedrawLock lock(this); | |
| 338 DefWindowProc(GetHWND(), WM_NCLBUTTONDOWN, ht_component, | |
| 339 MAKELPARAM(point.x, point.y)); | |
| 340 SetMsgHandled(TRUE); | |
| 341 } | |
| 342 */ | |
| 343 break; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 LRESULT CustomFrameWindow::OnNCUAHDrawCaption(UINT msg, WPARAM w_param, | |
| 348 LPARAM l_param) { | |
| 349 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for | |
| 350 // an explanation about why we need to handle this message. | |
| 351 SetMsgHandled(TRUE); | |
| 352 return 0; | |
| 353 } | |
| 354 | |
| 355 LRESULT CustomFrameWindow::OnNCUAHDrawFrame(UINT msg, WPARAM w_param, | |
| 356 LPARAM l_param) { | |
| 357 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for | |
| 358 // an explanation about why we need to handle this message. | |
| 359 SetMsgHandled(TRUE); | |
| 360 return 0; | |
| 361 } | |
| 362 | |
| 363 LRESULT CustomFrameWindow::OnSetCursor(HWND window, UINT hittest_code, | |
| 364 UINT message) { | |
| 365 int index = RC_NORMAL; | |
| 366 switch (hittest_code) { | |
| 367 case HTTOP: | |
| 368 case HTBOTTOM: | |
| 369 index = RC_VERTICAL; | |
| 370 break; | |
| 371 case HTTOPLEFT: | |
| 372 case HTBOTTOMRIGHT: | |
| 373 index = RC_NWSE; | |
| 374 break; | |
| 375 case HTTOPRIGHT: | |
| 376 case HTBOTTOMLEFT: | |
| 377 index = RC_NESW; | |
| 378 break; | |
| 379 case HTLEFT: | |
| 380 case HTRIGHT: | |
| 381 index = RC_HORIZONTAL; | |
| 382 break; | |
| 383 case HTCAPTION: | |
| 384 case HTCLIENT: | |
| 385 index = RC_NORMAL; | |
| 386 break; | |
| 387 } | |
| 388 SetCursor(resize_cursors_[index]); | |
| 389 return 0; | |
| 390 } | |
| 391 | |
| 392 LRESULT CustomFrameWindow::OnSetIcon(UINT size_type, HICON new_icon) { | |
| 393 ScopedRedrawLock lock(this); | |
| 394 return DefWindowProc(GetHWND(), WM_SETICON, size_type, | |
| 395 reinterpret_cast<LPARAM>(new_icon)); | |
| 396 } | |
| 397 | |
| 398 LRESULT CustomFrameWindow::OnSetText(const wchar_t* text) { | |
| 399 ScopedRedrawLock lock(this); | |
| 400 return DefWindowProc(GetHWND(), WM_SETTEXT, NULL, | |
| 401 reinterpret_cast<LPARAM>(text)); | |
| 402 } | |
| 403 | |
| 404 void CustomFrameWindow::OnSize(UINT param, const CSize& size) { | |
| 405 Window::OnSize(param, size); | |
| 406 | |
| 407 // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've | |
| 408 // invoked OnSize we ensure the RootView has been layed out. | |
| 409 ResetWindowRegion(); | |
| 410 } | |
| 411 | |
| 412 void CustomFrameWindow::OnSysCommand(UINT notification_code, CPoint click) { | |
| 413 // Windows uses the 4 lower order bits of |notification_code| for type- | |
| 414 // specific information so we must exclude this when comparing. | |
| 415 static const int sc_mask = 0xFFF0; | |
| 416 if ((notification_code & sc_mask) == SC_MINIMIZE || | |
| 417 (notification_code & sc_mask) == SC_MAXIMIZE || | |
| 418 (notification_code & sc_mask) == SC_RESTORE) { | |
| 419 non_client_view_->ResetWindowControls(); | |
| 420 } else if ((notification_code & sc_mask) == SC_MOVE || | |
| 421 (notification_code & sc_mask) == SC_SIZE) { | |
| 422 if (lock_updates_) { | |
| 423 // We were locked, before entering a resize or move modal loop. Now that | |
| 424 // we've begun to move the window, we need to unlock updates so that the | |
| 425 // sizing/moving feedback can be continuous. | |
| 426 UnlockUpdates(); | |
| 427 } | |
| 428 } | |
| 429 Window::OnSysCommand(notification_code, click); | |
| 430 } | |
| 431 | |
| 432 /////////////////////////////////////////////////////////////////////////////// | |
| 433 // CustomFrameWindow, private: | |
| 434 | |
| 435 // static | |
| 436 void CustomFrameWindow::InitClass() { | |
| 437 static bool initialized = false; | |
| 438 if (!initialized) { | |
| 439 resize_cursors_[RC_NORMAL] = LoadCursor(NULL, IDC_ARROW); | |
| 440 resize_cursors_[RC_VERTICAL] = LoadCursor(NULL, IDC_SIZENS); | |
| 441 resize_cursors_[RC_HORIZONTAL] = LoadCursor(NULL, IDC_SIZEWE); | |
| 442 resize_cursors_[RC_NESW] = LoadCursor(NULL, IDC_SIZENESW); | |
| 443 resize_cursors_[RC_NWSE] = LoadCursor(NULL, IDC_SIZENWSE); | |
| 444 initialized = true; | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 void CustomFrameWindow::LockUpdates() { | |
| 449 lock_updates_ = true; | |
| 450 saved_window_style_ = GetWindowLong(GetHWND(), GWL_STYLE); | |
| 451 SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_ & ~WS_VISIBLE); | |
| 452 } | |
| 453 | |
| 454 void CustomFrameWindow::UnlockUpdates() { | |
| 455 SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_); | |
| 456 lock_updates_ = false; | |
| 457 } | |
| 458 | |
| 459 void CustomFrameWindow::ResetWindowRegion() { | |
| 460 // Changing the window region is going to force a paint. Only change the | |
| 461 // window region if the region really differs. | |
| 462 HRGN current_rgn = CreateRectRgn(0, 0, 0, 0); | |
| 463 int current_rgn_result = GetWindowRgn(GetHWND(), current_rgn); | |
| 464 | |
| 465 CRect window_rect; | |
| 466 GetWindowRect(&window_rect); | |
| 467 HRGN new_region; | |
| 468 if (IsMaximized()) { | |
| 469 HMONITOR monitor = MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTONEAREST); | |
| 470 MONITORINFO mi; | |
| 471 mi.cbSize = sizeof mi; | |
| 472 GetMonitorInfo(monitor, &mi); | |
| 473 CRect work_rect = mi.rcWork; | |
| 474 work_rect.OffsetRect(-window_rect.left, -window_rect.top); | |
| 475 new_region = CreateRectRgnIndirect(&work_rect); | |
| 476 } else { | |
| 477 gfx::Path window_mask; | |
| 478 non_client_view_->GetWindowMask(gfx::Size(window_rect.Width(), | |
| 479 window_rect.Height()), | |
| 480 &window_mask); | |
| 481 new_region = window_mask.CreateHRGN(); | |
| 482 } | |
| 483 | |
| 484 if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) { | |
| 485 // SetWindowRgn takes ownership of the HRGN created by CreateHRGN. | |
| 486 SetWindowRgn(new_region, TRUE); | |
| 487 } else { | |
| 488 DeleteObject(new_region); | |
| 489 } | |
| 490 | |
| 491 DeleteObject(current_rgn); | |
| 492 } | |
| 493 | |
| 494 void CustomFrameWindow::ProcessNCMousePress(const CPoint& point, int flags) { | |
| 495 CPoint temp = point; | |
| 496 MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1); | |
| 497 UINT message_flags = 0; | |
| 498 if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80) | |
| 499 message_flags |= MK_CONTROL; | |
| 500 if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80) | |
| 501 message_flags |= MK_SHIFT; | |
| 502 message_flags |= flags; | |
| 503 ProcessMousePressed(temp, message_flags, false, false); | |
| 504 } | |
| 505 | |
| 506 } // namespace views | |
| OLD | NEW |