| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/host/disconnect_window.h" |
| |
| #include <windows.h> |
| |
| #include "base/compiler_specific.h" |
| #include "base/logging.h" |
| #include "base/string_util.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/win/scoped_gdi_object.h" |
| #include "base/win/scoped_hdc.h" |
| #include "base/win/scoped_select_object.h" |
| #include "remoting/host/chromoting_host.h" |
| // TODO(wez): The DisconnectWindow isn't plugin-specific, so shouldn't have |
| // a dependency on the plugin's resource header. |
| #include "remoting/host/plugin/host_plugin_resource.h" |
| #include "remoting/host/ui_strings.h" |
| |
| // TODO(garykac): Lots of duplicated code in this file and |
| // continue_window_win.cc. If we need to expand this then we should |
| // create a class with the shared code. |
| |
| // HMODULE from DllMain/WinMain. This is needed to find our dialog resource. |
| // This is defined in: |
| // Plugin: host_plugin.cc |
| // SimpleHost: simple_host_process.cc |
| extern HMODULE g_hModule; |
| |
| const int DISCONNECT_HOTKEY_ID = 1000; |
| const int kWindowBorderRadius = 14; |
| |
| namespace remoting { |
| |
| class DisconnectWindowWin : public DisconnectWindow { |
| public: |
| DisconnectWindowWin(); |
| virtual ~DisconnectWindowWin(); |
| |
| virtual void Show(remoting::ChromotingHost* host, |
| const std::string& username) OVERRIDE; |
| virtual void Hide() OVERRIDE; |
| |
| private: |
| static BOOL CALLBACK DialogProc(HWND hwmd, UINT msg, WPARAM wParam, |
| LPARAM lParam); |
| |
| BOOL OnDialogMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); |
| |
| void ShutdownHost(); |
| void EndDialog(int result); |
| void SetStrings(const UiStrings& strings, const std::string& username); |
| |
| remoting::ChromotingHost* host_; |
| HWND hwnd_; |
| bool has_hotkey_; |
| base::win::ScopedGDIObject<HPEN> border_pen_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DisconnectWindowWin); |
| }; |
| |
| DisconnectWindowWin::DisconnectWindowWin() |
| : host_(NULL), |
| hwnd_(NULL), |
| has_hotkey_(false), |
| border_pen_(CreatePen(PS_SOLID, 5, |
| RGB(0.13 * 255, 0.69 * 255, 0.11 * 255))) { |
| } |
| |
| DisconnectWindowWin::~DisconnectWindowWin() { |
| EndDialog(0); |
| } |
| |
| BOOL CALLBACK DisconnectWindowWin::DialogProc(HWND hwnd, UINT msg, |
| WPARAM wParam, LPARAM lParam) { |
| DisconnectWindowWin* win = NULL; |
| if (msg == WM_INITDIALOG) { |
| win = reinterpret_cast<DisconnectWindowWin*>(lParam); |
| CHECK(win); |
| SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)win); |
| } else { |
| LONG_PTR lp = GetWindowLongPtr(hwnd, DWLP_USER); |
| win = reinterpret_cast<DisconnectWindowWin*>(lp); |
| } |
| if (win == NULL) |
| return FALSE; |
| return win->OnDialogMessage(hwnd, msg, wParam, lParam); |
| } |
| |
| BOOL DisconnectWindowWin::OnDialogMessage(HWND hwnd, UINT msg, |
| WPARAM wParam, LPARAM lParam) { |
| switch (msg) { |
| // Ignore close messages. |
| case WM_CLOSE: |
| return TRUE; |
| |
| // Handle the Disconnect button. |
| case WM_COMMAND: |
| switch (LOWORD(wParam)) { |
| case IDC_DISCONNECT: |
| ShutdownHost(); |
| EndDialog(LOWORD(wParam)); |
| return TRUE; |
| } |
| return FALSE; |
| |
| // Ensure we don't try to use the HWND anymore. |
| case WM_DESTROY: |
| hwnd_ = NULL; |
| return TRUE; |
| |
| // Handle the disconnect hot-key. |
| case WM_HOTKEY: |
| ShutdownHost(); |
| EndDialog(0); |
| return TRUE; |
| |
| // Let the window be draggable by its client area by responding |
| // that the entire window is the title bar. |
| case WM_NCHITTEST: |
| SetWindowLong(hwnd, DWL_MSGRESULT, HTCAPTION); |
| return TRUE; |
| |
| case WM_PAINT: |
| { |
| PAINTSTRUCT ps; |
| HDC hdc = BeginPaint(hwnd_, &ps); |
| RECT rect; |
| GetClientRect(hwnd_, &rect); |
| { |
| base::win::ScopedSelectObject border(hdc, border_pen_); |
| base::win::ScopedSelectObject brush(hdc, GetStockObject(NULL_BRUSH)); |
| RoundRect(hdc, rect.left, rect.top, rect.right - 1, rect.bottom - 1, |
| kWindowBorderRadius, kWindowBorderRadius); |
| } |
| EndPaint(hwnd_, &ps); |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| void DisconnectWindowWin::Show(ChromotingHost* host, |
| const std::string& username) { |
| CHECK(!hwnd_); |
| host_ = host; |
| |
| // Load the dialog resource so that we can modify the RTL flags if necessary. |
| // This is taken from chrome/default_plugin/install_dialog.cc |
| HRSRC dialog_resource = |
| FindResource(g_hModule, MAKEINTRESOURCE(IDD_DISCONNECT), RT_DIALOG); |
| CHECK(dialog_resource); |
| HGLOBAL dialog_template = LoadResource(g_hModule, dialog_resource); |
| CHECK(dialog_template); |
| DLGTEMPLATE* dialog_pointer = |
| reinterpret_cast<DLGTEMPLATE*>(LockResource(dialog_template)); |
| CHECK(dialog_pointer); |
| |
| // The actual resource type is DLGTEMPLATEEX, but this is not defined in any |
| // standard headers, so we treat it as a generic pointer and manipulate the |
| // correct offsets explicitly. |
| scoped_ptr<unsigned char> rtl_dialog_template; |
| if (host->ui_strings().direction == UiStrings::RTL) { |
| unsigned long dialog_template_size = |
| SizeofResource(g_hModule, dialog_resource); |
| rtl_dialog_template.reset(new unsigned char[dialog_template_size]); |
| memcpy(rtl_dialog_template.get(), dialog_pointer, dialog_template_size); |
| DWORD* rtl_dwords = reinterpret_cast<DWORD*>(rtl_dialog_template.get()); |
| rtl_dwords[2] |= (WS_EX_LAYOUTRTL | WS_EX_RTLREADING); |
| dialog_pointer = reinterpret_cast<DLGTEMPLATE*>(rtl_dwords); |
| } |
| |
| hwnd_ = CreateDialogIndirectParam(g_hModule, dialog_pointer, NULL, |
| (DLGPROC)DialogProc, (LPARAM)this); |
| CHECK(hwnd_); |
| |
| // Set up handler for Ctrl-Alt-Esc shortcut. |
| if (!has_hotkey_ && RegisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID, |
| MOD_ALT | MOD_CONTROL, VK_ESCAPE)) { |
| has_hotkey_ = true; |
| } |
| |
| SetStrings(host->ui_strings(), username); |
| |
| // Try to center the window above the task-bar. If that fails, use the |
| // primary monitor. If that fails (very unlikely), use the default position. |
| HWND taskbar = FindWindow(L"Shell_TrayWnd", NULL); |
| HMONITOR monitor = MonitorFromWindow(taskbar, MONITOR_DEFAULTTOPRIMARY); |
| MONITORINFO monitor_info = {sizeof(monitor_info)}; |
| RECT window_rect; |
| if (GetMonitorInfo(monitor, &monitor_info) && |
| GetWindowRect(hwnd_, &window_rect)) { |
| int window_width = window_rect.right - window_rect.left; |
| int window_height = window_rect.bottom - window_rect.top; |
| int top = monitor_info.rcWork.bottom - window_height; |
| int left = (monitor_info.rcWork.right + monitor_info.rcWork.left - |
| window_width) / 2; |
| SetWindowPos(hwnd_, NULL, left, top, 0, 0, SWP_NOSIZE | SWP_NOZORDER); |
| } |
| ShowWindow(hwnd_, SW_SHOW); |
| } |
| |
| void DisconnectWindowWin::ShutdownHost() { |
| CHECK(host_); |
| host_->Shutdown(base::Closure()); |
| } |
| |
| static int GetControlTextWidth(HWND control) { |
| RECT rect = {0, 0, 0, 0}; |
| WCHAR text[256]; |
| int result = GetWindowText(control, text, arraysize(text)); |
| if (result) { |
| base::win::ScopedGetDC dc(control); |
| base::win::ScopedSelectObject font( |
| dc, (HFONT)SendMessage(control, WM_GETFONT, 0, 0)); |
| DrawText(dc, text, -1, &rect, DT_CALCRECT|DT_SINGLELINE); |
| } |
| return rect.right; |
| } |
| |
| void DisconnectWindowWin::SetStrings(const UiStrings& strings, |
| const std::string& username) { |
| SetWindowText(hwnd_, strings.product_name.c_str()); |
| |
| HWND hwndButton = GetDlgItem(hwnd_, IDC_DISCONNECT); |
| CHECK(hwndButton); |
| const WCHAR* label = |
| has_hotkey_ ? strings.disconnect_button_text_plus_shortcut.c_str() |
| : strings.disconnect_button_text.c_str(); |
| int button_old_required_width = GetControlTextWidth(hwndButton); |
| SetWindowText(hwndButton, label); |
| int button_new_required_width = GetControlTextWidth(hwndButton); |
| |
| HWND hwndSharingWith = GetDlgItem(hwnd_, IDC_DISCONNECT_SHARINGWITH); |
| CHECK(hwndSharingWith); |
| string16 text = ReplaceStringPlaceholders( |
| strings.disconnect_message, UTF8ToUTF16(username), NULL); |
| int label_old_required_width = GetControlTextWidth(hwndSharingWith); |
| SetWindowText(hwndSharingWith, text.c_str()); |
| int label_new_required_width = GetControlTextWidth(hwndSharingWith); |
| |
| int label_width_delta = label_new_required_width - label_old_required_width; |
| int button_width_delta = |
| button_new_required_width - button_old_required_width; |
| |
| // Reposition the controls such that the label lies to the left of the |
| // disconnect button (assuming LTR layout). The dialog template determines |
| // the controls' spacing; update their size to fit the localized content. |
| RECT label_rect; |
| GetClientRect(hwndSharingWith, &label_rect); |
| SetWindowPos(hwndSharingWith, NULL, 0, 0, |
| label_rect.right + label_width_delta, label_rect.bottom, |
| SWP_NOMOVE|SWP_NOZORDER); |
| |
| RECT button_rect; |
| GetWindowRect(hwndButton, &button_rect); |
| int button_width = button_rect.right - button_rect.left; |
| int button_height = button_rect.bottom - button_rect.top; |
| MapWindowPoints(NULL, hwnd_, reinterpret_cast<LPPOINT>(&button_rect), 2); |
| SetWindowPos(hwndButton, NULL, |
| button_rect.left + label_width_delta, button_rect.top, |
| button_width + button_width_delta, button_height, SWP_NOZORDER); |
| |
| RECT window_rect; |
| GetWindowRect(hwnd_, &window_rect); |
| int width = (window_rect.right - window_rect.left) + |
| button_width_delta + label_width_delta; |
| int height = window_rect.bottom - window_rect.top; |
| SetWindowPos(hwnd_, NULL, 0, 0, width, height, SWP_NOMOVE|SWP_NOZORDER); |
| HRGN rgn = CreateRoundRectRgn(0, 0, width, height, kWindowBorderRadius, |
| kWindowBorderRadius); |
| SetWindowRgn(hwnd_, rgn, TRUE); |
| } |
| |
| void DisconnectWindowWin::Hide() { |
| EndDialog(0); |
| } |
| |
| void DisconnectWindowWin::EndDialog(int result) { |
| if (has_hotkey_) { |
| UnregisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID); |
| has_hotkey_ = false; |
| } |
| |
| if (hwnd_) { |
| ::EndDialog(hwnd_, result); |
| hwnd_ = NULL; |
| } |
| } |
| |
| scoped_ptr<DisconnectWindow> DisconnectWindow::Create() { |
| return scoped_ptr<DisconnectWindow>(new DisconnectWindowWin()); |
| } |
| |
| } // namespace remoting |