Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2012 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 "ui/base/win/hwnd_util.h"
      6 
      7 #include "base/i18n/rtl.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/win/metro.h"
     10 #include "ui/gfx/point.h"
     11 #include "ui/gfx/rect.h"
     12 #include "ui/gfx/size.h"
     13 
     14 namespace ui {
     15 
     16 namespace {
     17 
     18 // Adjust the window to fit.
     19 void AdjustWindowToFit(HWND hwnd, const RECT& bounds, bool fit_to_monitor) {
     20   if (fit_to_monitor) {
     21     // Get the monitor.
     22     HMONITOR hmon = MonitorFromRect(&bounds, MONITOR_DEFAULTTONEAREST);
     23     if (hmon) {
     24       MONITORINFO mi;
     25       mi.cbSize = sizeof(mi);
     26       GetMonitorInfo(hmon, &mi);
     27       gfx::Rect window_rect(bounds);
     28       gfx::Rect monitor_rect(mi.rcWork);
     29       gfx::Rect new_window_rect = window_rect;
     30       new_window_rect.AdjustToFit(monitor_rect);
     31       if (new_window_rect != window_rect) {
     32         // Window doesn't fit on monitor, move and possibly resize.
     33         SetWindowPos(hwnd, 0, new_window_rect.x(), new_window_rect.y(),
     34                      new_window_rect.width(), new_window_rect.height(),
     35                      SWP_NOACTIVATE | SWP_NOZORDER);
     36         return;
     37       }
     38       // Else fall through.
     39     } else {
     40       NOTREACHED() << "Unable to find default monitor";
     41       // Fall through.
     42     }
     43   }  // Else fall through.
     44 
     45   // The window is not being fit to monitor, or the window fits on the monitor
     46   // as is, or we have no monitor info; reset the bounds.
     47   ::SetWindowPos(hwnd, 0, bounds.left, bounds.top,
     48                  bounds.right - bounds.left, bounds.bottom - bounds.top,
     49                  SWP_NOACTIVATE | SWP_NOZORDER);
     50 }
     51 
     52 }  // namespace
     53 
     54 string16 GetClassName(HWND window) {
     55   // GetClassNameW will return a truncated result (properly null terminated) if
     56   // the given buffer is not large enough.  So, it is not possible to determine
     57   // that we got the entire class name if the result is exactly equal to the
     58   // size of the buffer minus one.
     59   DWORD buffer_size = MAX_PATH;
     60   while (true) {
     61     std::wstring output;
     62     DWORD size_ret =
     63         GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size);
     64     if (size_ret == 0)
     65       break;
     66     if (size_ret < (buffer_size - 1)) {
     67       output.resize(size_ret);
     68       return output;
     69     }
     70     buffer_size *= 2;
     71   }
     72   return std::wstring();  // error
     73 }
     74 
     75 #pragma warning(push)
     76 #pragma warning(disable:4312 4244)
     77 
     78 WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
     79   // The reason we don't return the SetwindowLongPtr() value is that it returns
     80   // the orignal window procedure and not the current one. I don't know if it is
     81   // a bug or an intended feature.
     82   WNDPROC oldwindow_proc =
     83       reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
     84   SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
     85   return oldwindow_proc;
     86 }
     87 
     88 void* SetWindowUserData(HWND hwnd, void* user_data) {
     89   return
     90       reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
     91           reinterpret_cast<LONG_PTR>(user_data)));
     92 }
     93 
     94 void* GetWindowUserData(HWND hwnd) {
     95   DWORD process_id = 0;
     96   DWORD thread_id = GetWindowThreadProcessId(hwnd, &process_id);
     97   // A window outside the current process needs to be ignored.
     98   if (process_id != ::GetCurrentProcessId())
     99     return NULL;
    100   return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
    101 }
    102 
    103 #pragma warning(pop)
    104 
    105 bool DoesWindowBelongToActiveWindow(HWND window) {
    106   DCHECK(window);
    107   HWND top_window = ::GetAncestor(window, GA_ROOT);
    108   if (!top_window)
    109     return false;
    110 
    111   HWND active_top_window = ::GetAncestor(::GetForegroundWindow(), GA_ROOT);
    112   return (top_window == active_top_window);
    113 }
    114 
    115 void CenterAndSizeWindow(HWND parent,
    116                          HWND window,
    117                          const gfx::Size& pref) {
    118   DCHECK(window && pref.width() > 0 && pref.height() > 0);
    119 
    120   // Calculate the ideal bounds.
    121   RECT window_bounds;
    122   RECT center_bounds = {0};
    123   if (parent) {
    124     // If there is a parent, center over the parents bounds.
    125     ::GetWindowRect(parent, &center_bounds);
    126   }
    127 
    128   if (::IsRectEmpty(&center_bounds)) {
    129     // No parent or no parent rect. Center over the monitor the window is on.
    130     HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
    131     if (monitor) {
    132       MONITORINFO mi = {0};
    133       mi.cbSize = sizeof(mi);
    134       GetMonitorInfo(monitor, &mi);
    135       center_bounds = mi.rcWork;
    136     } else {
    137       NOTREACHED() << "Unable to get default monitor";
    138     }
    139   }
    140 
    141   window_bounds.left = center_bounds.left;
    142   if (pref.width() < (center_bounds.right - center_bounds.left)) {
    143     window_bounds.left +=
    144         (center_bounds.right - center_bounds.left - pref.width()) / 2;
    145   }
    146   window_bounds.right = window_bounds.left + pref.width();
    147 
    148   window_bounds.top = center_bounds.top;
    149   if (pref.height() < (center_bounds.bottom - center_bounds.top)) {
    150     window_bounds.top +=
    151         (center_bounds.bottom - center_bounds.top - pref.height()) / 2;
    152   }
    153   window_bounds.bottom = window_bounds.top + pref.height();
    154 
    155   // If we're centering a child window, we are positioning in client
    156   // coordinates, and as such we need to offset the target rectangle by the
    157   // position of the parent window.
    158   if (::GetWindowLong(window, GWL_STYLE) & WS_CHILD) {
    159     DCHECK(parent && ::GetParent(window) == parent);
    160     POINT topleft = { window_bounds.left, window_bounds.top };
    161     ::MapWindowPoints(HWND_DESKTOP, parent, &topleft, 1);
    162     window_bounds.left = topleft.x;
    163     window_bounds.top = topleft.y;
    164     window_bounds.right = window_bounds.left + pref.width();
    165     window_bounds.bottom = window_bounds.top + pref.height();
    166   }
    167 
    168   AdjustWindowToFit(window, window_bounds, !parent);
    169 }
    170 
    171 void CheckWindowCreated(HWND hwnd) {
    172   if (!hwnd)
    173     LOG_GETLASTERROR(FATAL);
    174 }
    175 
    176 void ShowSystemMenu(HWND window) {
    177   RECT rect;
    178   GetWindowRect(window, &rect);
    179   gfx::Point point = gfx::Point(rect.left, rect.top);
    180   static const int kSystemMenuOffset = 10;
    181   point.Offset(kSystemMenuOffset, kSystemMenuOffset);
    182   ShowSystemMenuAtPoint(window, point);
    183 }
    184 
    185 void ShowSystemMenuAtPoint(HWND window, const gfx::Point& point) {
    186   // In the Metro process, we never want to show the system menu.
    187   if (base::win::IsMetroProcess())
    188     return;
    189   UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD;
    190   if (base::i18n::IsRTL())
    191     flags |= TPM_RIGHTALIGN;
    192   HMENU menu = GetSystemMenu(window, FALSE);
    193   const int command =
    194       TrackPopupMenu(menu, flags, point.x(), point.y(), 0, window, NULL);
    195   if (command)
    196     SendMessage(window, WM_SYSCOMMAND, command, 0);
    197 }
    198 
    199 extern "C" {
    200   typedef HWND (*RootWindow)();
    201 }
    202 
    203 HWND GetWindowToParentTo(bool get_real_hwnd) {
    204   HMODULE metro = base::win::GetMetroModule();
    205   if (!metro)
    206     return get_real_hwnd ? ::GetDesktopWindow() : HWND_DESKTOP;
    207   // In windows 8 metro-mode the root window is not the desktop.
    208   RootWindow root_window =
    209       reinterpret_cast<RootWindow>(::GetProcAddress(metro, "GetRootWindow"));
    210   return root_window();
    211 }
    212 
    213 }  // namespace ui
    214