OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/disconnect_window.h" | 5 #include "remoting/host/disconnect_window.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/string_util.h" | 11 #include "base/string_util.h" |
12 #include "base/utf_string_conversions.h" | 12 #include "base/utf_string_conversions.h" |
13 #include "base/win/scoped_gdi_object.h" | |
14 #include "base/win/scoped_hdc.h" | |
15 #include "base/win/scoped_select_object.h" | |
13 #include "remoting/host/chromoting_host.h" | 16 #include "remoting/host/chromoting_host.h" |
14 // TODO(wez): The DisconnectWindow isn't plugin-specific, so shouldn't have | 17 // TODO(wez): The DisconnectWindow isn't plugin-specific, so shouldn't have |
15 // a dependency on the plugin's resource header. | 18 // a dependency on the plugin's resource header. |
16 #include "remoting/host/plugin/host_plugin_resource.h" | 19 #include "remoting/host/plugin/host_plugin_resource.h" |
17 #include "remoting/host/ui_strings.h" | 20 #include "remoting/host/ui_strings.h" |
18 | 21 |
19 // TODO(garykac): Lots of duplicated code in this file and | 22 // TODO(garykac): Lots of duplicated code in this file and |
20 // continue_window_win.cc. These global floating windows are temporary so | 23 // continue_window_win.cc. These global floating windows are temporary so |
21 // they should be deleted soon. If we need to expand this then we should | 24 // they should be deleted soon. If we need to expand this then we should |
22 // create a class with the shared code. | 25 // create a class with the shared code. |
23 | 26 |
24 // HMODULE from DllMain/WinMain. This is needed to find our dialog resource. | 27 // HMODULE from DllMain/WinMain. This is needed to find our dialog resource. |
25 // This is defined in: | 28 // This is defined in: |
26 // Plugin: host_plugin.cc | 29 // Plugin: host_plugin.cc |
27 // SimpleHost: simple_host_process.cc | 30 // SimpleHost: simple_host_process.cc |
28 extern HMODULE g_hModule; | 31 extern HMODULE g_hModule; |
29 | 32 |
30 const int DISCONNECT_HOTKEY_ID = 1000; | 33 const int DISCONNECT_HOTKEY_ID = 1000; |
34 const int kWindowBorderRadius = 14; | |
31 | 35 |
32 namespace remoting { | 36 namespace remoting { |
33 | 37 |
34 class DisconnectWindowWin : public DisconnectWindow { | 38 class DisconnectWindowWin : public DisconnectWindow { |
35 public: | 39 public: |
36 DisconnectWindowWin(); | 40 DisconnectWindowWin(); |
37 virtual ~DisconnectWindowWin(); | 41 virtual ~DisconnectWindowWin(); |
38 | 42 |
39 virtual void Show(remoting::ChromotingHost* host, | 43 virtual void Show(remoting::ChromotingHost* host, |
40 const std::string& username) OVERRIDE; | 44 const std::string& username) OVERRIDE; |
41 virtual void Hide() OVERRIDE; | 45 virtual void Hide() OVERRIDE; |
42 | 46 |
43 private: | 47 private: |
44 static BOOL CALLBACK DialogProc(HWND hwmd, UINT msg, WPARAM wParam, | 48 static BOOL CALLBACK DialogProc(HWND hwmd, UINT msg, WPARAM wParam, |
45 LPARAM lParam); | 49 LPARAM lParam); |
46 | 50 |
47 BOOL OnDialogMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); | 51 BOOL OnDialogMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); |
48 | 52 |
49 void ShutdownHost(); | 53 void ShutdownHost(); |
50 void EndDialog(int result); | 54 void EndDialog(int result); |
51 void SetStrings(const UiStrings& strings, const std::string& username); | 55 void SetStrings(const UiStrings& strings, const std::string& username); |
52 | 56 |
53 remoting::ChromotingHost* host_; | 57 remoting::ChromotingHost* host_; |
54 HWND hwnd_; | 58 HWND hwnd_; |
55 bool has_hotkey_; | 59 bool has_hotkey_; |
60 base::win::ScopedGDIObject<HPEN> border_pen_; | |
56 | 61 |
57 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowWin); | 62 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowWin); |
58 }; | 63 }; |
59 | 64 |
60 DisconnectWindowWin::DisconnectWindowWin() | 65 DisconnectWindowWin::DisconnectWindowWin() |
61 : host_(NULL), | 66 : host_(NULL), |
62 hwnd_(NULL), | 67 hwnd_(NULL), |
63 has_hotkey_(false) { | 68 has_hotkey_(false), |
69 border_pen_(CreatePen(PS_SOLID, 5, | |
70 RGB(0.13 * 255, 0.69 * 255, 0.11 * 255))) { | |
64 } | 71 } |
65 | 72 |
66 DisconnectWindowWin::~DisconnectWindowWin() { | 73 DisconnectWindowWin::~DisconnectWindowWin() { |
67 EndDialog(0); | 74 EndDialog(0); |
68 } | 75 } |
69 | 76 |
70 BOOL CALLBACK DisconnectWindowWin::DialogProc(HWND hwnd, UINT msg, | 77 BOOL CALLBACK DisconnectWindowWin::DialogProc(HWND hwnd, UINT msg, |
71 WPARAM wParam, LPARAM lParam) { | 78 WPARAM wParam, LPARAM lParam) { |
72 DisconnectWindowWin* win = NULL; | 79 DisconnectWindowWin* win = NULL; |
73 if (msg == WM_INITDIALOG) { | 80 if (msg == WM_INITDIALOG) { |
74 win = reinterpret_cast<DisconnectWindowWin*>(lParam); | 81 win = reinterpret_cast<DisconnectWindowWin*>(lParam); |
75 CHECK(win); | 82 CHECK(win); |
76 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)win); | 83 SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)win); |
77 } else { | 84 } else { |
78 LONG_PTR lp = GetWindowLongPtr(hwnd, DWLP_USER); | 85 LONG_PTR lp = GetWindowLongPtr(hwnd, DWLP_USER); |
79 win = reinterpret_cast<DisconnectWindowWin*>(lp); | 86 win = reinterpret_cast<DisconnectWindowWin*>(lp); |
80 } | 87 } |
81 if (win == NULL) | 88 if (win == NULL) |
82 return FALSE; | 89 return FALSE; |
83 return win->OnDialogMessage(hwnd, msg, wParam, lParam); | 90 return win->OnDialogMessage(hwnd, msg, wParam, lParam); |
84 } | 91 } |
85 | 92 |
86 BOOL DisconnectWindowWin::OnDialogMessage(HWND hwnd, UINT msg, | 93 BOOL DisconnectWindowWin::OnDialogMessage(HWND hwnd, UINT msg, |
87 WPARAM wParam, LPARAM lParam) { | 94 WPARAM wParam, LPARAM lParam) { |
88 switch (msg) { | 95 switch (msg) { |
89 case WM_HOTKEY: | 96 // Ignore close messages. |
90 ShutdownHost(); | 97 case WM_CLOSE: |
91 EndDialog(0); | |
92 return TRUE; | 98 return TRUE; |
93 case WM_CLOSE: | 99 |
94 // Ignore close messages. | 100 // Handle the Disconnect button. |
95 return TRUE; | |
96 case WM_DESTROY: | |
97 // Ensure we don't try to use the HWND anymore. | |
98 hwnd_ = NULL; | |
99 return TRUE; | |
100 case WM_COMMAND: | 101 case WM_COMMAND: |
101 switch (LOWORD(wParam)) { | 102 switch (LOWORD(wParam)) { |
102 case IDC_DISCONNECT: | 103 case IDC_DISCONNECT: |
103 ShutdownHost(); | 104 ShutdownHost(); |
104 EndDialog(LOWORD(wParam)); | 105 EndDialog(LOWORD(wParam)); |
105 return TRUE; | 106 return TRUE; |
106 } | 107 } |
108 return FALSE; | |
109 | |
110 // Ensure we don't try to use the HWND anymore. | |
111 case WM_DESTROY: | |
112 hwnd_ = NULL; | |
113 return TRUE; | |
114 | |
115 // Handle the disconnect hot-key. | |
116 case WM_HOTKEY: | |
117 ShutdownHost(); | |
118 EndDialog(0); | |
119 return TRUE; | |
120 | |
121 // Let the window be draggable by its client area by responding | |
122 // that the entire window is the title bar. | |
123 case WM_NCHITTEST: | |
124 SetWindowLong(hwnd, DWL_MSGRESULT, HTCAPTION); | |
125 return TRUE; | |
126 | |
127 case WM_PAINT: | |
128 { | |
129 PAINTSTRUCT ps; | |
130 HDC hdc = BeginPaint(hwnd_, &ps); | |
131 RECT rect; | |
132 GetClientRect(hwnd_, &rect); | |
133 { | |
134 base::win::ScopedSelectObject border(hdc, border_pen_); | |
135 base::win::ScopedSelectObject brush(hdc, GetStockObject(NULL_BRUSH)); | |
136 RoundRect(hdc, rect.left, rect.top, rect.right - 1, rect.bottom - 1, | |
137 kWindowBorderRadius, kWindowBorderRadius); | |
138 } | |
139 EndPaint(hwnd_, &ps); | |
140 return TRUE; | |
141 } | |
107 } | 142 } |
108 return FALSE; | 143 return FALSE; |
109 } | 144 } |
110 | 145 |
111 void DisconnectWindowWin::Show(ChromotingHost* host, | 146 void DisconnectWindowWin::Show(ChromotingHost* host, |
112 const std::string& username) { | 147 const std::string& username) { |
148 CHECK(!hwnd_); | |
113 host_ = host; | 149 host_ = host; |
114 | 150 |
115 CHECK(!hwnd_); | 151 // Load the dialog resource so that we can modify the RTL flags if necessary. |
Wez
2011/10/21 00:46:29
nit: Rephrase so it's clear that we create a modif
Jamie
2011/10/21 01:14:50
Without an explanation of why this is important, I
| |
116 hwnd_ = CreateDialogParam(g_hModule, MAKEINTRESOURCE(IDD_DISCONNECT), NULL, | 152 // This is taken from chrome/default_plugin/install_dialog.cc |
117 (DLGPROC)DialogProc, (LPARAM)this); | 153 HRSRC dialog_resource = |
154 FindResource(g_hModule, MAKEINTRESOURCE(IDD_DISCONNECT), RT_DIALOG); | |
155 CHECK(dialog_resource); | |
156 HGLOBAL dialog_template = LoadResource(g_hModule, dialog_resource); | |
157 CHECK(dialog_template); | |
158 DLGTEMPLATE* dialog_pointer = | |
159 reinterpret_cast<DLGTEMPLATE*>(LockResource(dialog_template)); | |
160 CHECK(dialog_pointer); | |
Wez
2011/10/21 00:46:29
nit: This changes the failure mode in the case tha
Jamie
2011/10/21 01:14:50
Yes, I think we're asserting that the disconnect w
| |
161 | |
162 // The actual resource type is DLGTEMPLATEEX, but this is not defined in any | |
163 // standard headers, so we treat it as a generic pointer and manipulate the | |
164 // correct offsets explicitly. | |
165 scoped_ptr<unsigned char> rtl_dialog_template; | |
166 if (host->ui_strings().direction == UiStrings::RTL) { | |
167 unsigned long dialog_template_size = | |
168 SizeofResource(g_hModule, dialog_resource); | |
169 rtl_dialog_template.reset(new unsigned char[dialog_template_size]); | |
170 memcpy(rtl_dialog_template.get(), dialog_pointer, dialog_template_size); | |
171 DWORD* rtl_dwords = reinterpret_cast<DWORD*>(rtl_dialog_template.get()); | |
172 rtl_dwords[2] |= (WS_EX_LAYOUTRTL | WS_EX_RTLREADING); | |
173 dialog_pointer = reinterpret_cast<DLGTEMPLATE*>(rtl_dwords); | |
174 } | |
175 | |
176 hwnd_ = CreateDialogIndirectParam(g_hModule, dialog_pointer, NULL, | |
177 (DLGPROC)DialogProc, (LPARAM)this); | |
118 if (!hwnd_) { | 178 if (!hwnd_) { |
119 LOG(ERROR) << "Unable to create Disconnect dialog for remoting."; | 179 LOG(ERROR) << "Unable to create Disconnect dialog for remoting."; |
120 return; | 180 return; |
121 } | 181 } |
122 | 182 |
123 // Set up handler for Ctrl-Alt-Esc shortcut. | 183 // Set up handler for Ctrl-Alt-Esc shortcut. |
124 if (!has_hotkey_ && RegisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID, | 184 if (!has_hotkey_ && RegisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID, |
125 MOD_ALT | MOD_CONTROL, VK_ESCAPE)) { | 185 MOD_ALT | MOD_CONTROL, VK_ESCAPE)) { |
126 has_hotkey_ = true; | 186 has_hotkey_ = true; |
127 } | 187 } |
128 | 188 |
129 SetStrings(host->ui_strings(), username); | 189 SetStrings(host->ui_strings(), username); |
130 ShowWindow(hwnd_, SW_SHOW); | 190 ShowWindow(hwnd_, SW_SHOW); |
131 } | 191 } |
132 | 192 |
133 void DisconnectWindowWin::ShutdownHost() { | 193 void DisconnectWindowWin::ShutdownHost() { |
134 CHECK(host_); | 194 CHECK(host_); |
135 host_->Shutdown(NULL); | 195 host_->Shutdown(NULL); |
136 } | 196 } |
137 | 197 |
198 static int GetControlTextWidth(HWND control) { | |
199 RECT rect = {0, 0, 0, 0}; | |
200 WCHAR text[256]; | |
201 int result = GetWindowText(control, text, arraysize(text)); | |
202 if (result) { | |
203 base::win::ScopedGetDC dc(control); | |
204 base::win::ScopedSelectObject font( | |
205 dc, (HFONT)SendMessage(control, WM_GETFONT, 0, 0)); | |
206 DrawText(dc, text, -1, &rect, DT_CALCRECT|DT_SINGLELINE); | |
207 } | |
208 return rect.right; | |
209 } | |
210 | |
138 void DisconnectWindowWin::SetStrings(const UiStrings& strings, | 211 void DisconnectWindowWin::SetStrings(const UiStrings& strings, |
139 const std::string& username) { | 212 const std::string& username) { |
140 SetWindowText(hwnd_, strings.product_name.c_str()); | 213 SetWindowText(hwnd_, strings.product_name.c_str()); |
141 | 214 |
142 HWND hwndButton = GetDlgItem(hwnd_, IDC_DISCONNECT); | 215 HWND hwndButton = GetDlgItem(hwnd_, IDC_DISCONNECT); |
143 CHECK(hwndButton); | 216 CHECK(hwndButton); |
144 const WCHAR* label = | 217 const WCHAR* label = |
145 has_hotkey_ ? strings.disconnect_button_text_plus_shortcut.c_str() | 218 has_hotkey_ ? strings.disconnect_button_text_plus_shortcut.c_str() |
146 : strings.disconnect_button_text.c_str(); | 219 : strings.disconnect_button_text.c_str(); |
220 int button_old_required_width = GetControlTextWidth(hwndButton); | |
147 SetWindowText(hwndButton, label); | 221 SetWindowText(hwndButton, label); |
222 int button_new_required_width = GetControlTextWidth(hwndButton); | |
148 | 223 |
149 HWND hwndSharingWith = GetDlgItem(hwnd_, IDC_DISCONNECT_SHARINGWITH); | 224 HWND hwndSharingWith = GetDlgItem(hwnd_, IDC_DISCONNECT_SHARINGWITH); |
150 CHECK(hwndSharingWith); | 225 CHECK(hwndSharingWith); |
151 string16 text = ReplaceStringPlaceholders( | 226 string16 text = ReplaceStringPlaceholders( |
152 strings.disconnect_message, UTF8ToUTF16(username), NULL); | 227 strings.disconnect_message, UTF8ToUTF16(username), NULL); |
228 int label_old_required_width = GetControlTextWidth(hwndSharingWith); | |
153 SetWindowText(hwndSharingWith, text.c_str()); | 229 SetWindowText(hwndSharingWith, text.c_str()); |
230 int label_new_required_width = GetControlTextWidth(hwndSharingWith); | |
231 | |
232 int label_width_delta = label_new_required_width - label_old_required_width; | |
233 int button_width_delta = | |
234 button_new_required_width - button_old_required_width; | |
235 | |
236 RECT label_rect; | |
237 GetClientRect(hwndSharingWith, &label_rect); | |
238 SetWindowPos(hwndSharingWith, NULL, 0, 0, | |
239 label_rect.right + label_width_delta, label_rect.bottom, | |
240 SWP_NOMOVE|SWP_NOZORDER); | |
241 | |
242 RECT button_rect; | |
243 GetWindowRect(hwndButton, &button_rect); | |
244 int button_width = button_rect.right - button_rect.left; | |
245 int button_height = button_rect.bottom - button_rect.top; | |
246 MapWindowPoints(NULL, hwnd_, reinterpret_cast<LPPOINT>(&button_rect), 2); | |
247 SetWindowPos(hwndButton, NULL, | |
248 button_rect.left + label_width_delta, button_rect.top, | |
249 button_width + button_width_delta, button_height, SWP_NOZORDER); | |
250 | |
251 RECT window_rect; | |
252 GetWindowRect(hwnd_, &window_rect); | |
253 int width = (window_rect.right - window_rect.left) + | |
254 button_width_delta + label_width_delta; | |
255 int height = window_rect.bottom - window_rect.top; | |
256 SetWindowPos(hwnd_, NULL, 0, 0, width, height, SWP_NOMOVE|SWP_NOZORDER); | |
257 HRGN rgn = CreateRoundRectRgn(0, 0, width, height, kWindowBorderRadius, | |
258 kWindowBorderRadius); | |
259 SetWindowRgn(hwnd_, rgn, TRUE); | |
154 } | 260 } |
155 | 261 |
156 void DisconnectWindowWin::Hide() { | 262 void DisconnectWindowWin::Hide() { |
157 EndDialog(0); | 263 EndDialog(0); |
158 } | 264 } |
159 | 265 |
160 void DisconnectWindowWin::EndDialog(int result) { | 266 void DisconnectWindowWin::EndDialog(int result) { |
161 if (has_hotkey_) { | 267 if (has_hotkey_) { |
162 UnregisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID); | 268 UnregisterHotKey(hwnd_, DISCONNECT_HOTKEY_ID); |
163 has_hotkey_ = false; | 269 has_hotkey_ = false; |
164 } | 270 } |
165 | 271 |
166 if (hwnd_) { | 272 if (hwnd_) { |
167 ::EndDialog(hwnd_, result); | 273 ::EndDialog(hwnd_, result); |
168 hwnd_ = NULL; | 274 hwnd_ = NULL; |
169 } | 275 } |
170 } | 276 } |
171 | 277 |
172 DisconnectWindow* DisconnectWindow::Create() { | 278 DisconnectWindow* DisconnectWindow::Create() { |
173 return new DisconnectWindowWin; | 279 return new DisconnectWindowWin; |
174 } | 280 } |
175 | 281 |
176 } // namespace remoting | 282 } // namespace remoting |
OLD | NEW |