OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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" | |
6 | |
7 #include <gtk/gtk.h> | 5 #include <gtk/gtk.h> |
8 #include <math.h> | 6 #include <math.h> |
9 | 7 |
| 8 #include "base/bind.h" |
10 #include "base/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
| 10 #include "base/location.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/memory/ref_counted.h" |
| 13 #include "base/single_thread_task_runner.h" |
12 #include "base/string_util.h" | 14 #include "base/string_util.h" |
13 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
| 16 #include "remoting/host/host_window.h" |
14 #include "remoting/host/ui_strings.h" | 17 #include "remoting/host/ui_strings.h" |
15 #include "ui/base/gtk/gtk_signal.h" | 18 #include "ui/base/gtk/gtk_signal.h" |
16 | 19 |
17 namespace remoting { | 20 namespace remoting { |
18 | 21 |
19 class DisconnectWindowGtk : public DisconnectWindow { | 22 namespace { |
| 23 |
| 24 class DisconnectWindowGtk : public HostWindow::Core { |
20 public: | 25 public: |
21 explicit DisconnectWindowGtk(const UiStrings* ui_strings); | 26 DisconnectWindowGtk( |
| 27 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| 28 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| 29 const base::Closure& disconnect_callback, |
| 30 const std::string& username, |
| 31 const UiStrings& ui_strings); |
| 32 |
| 33 private: |
| 34 friend class base::RefCountedThreadSafe<Core>; |
22 virtual ~DisconnectWindowGtk(); | 35 virtual ~DisconnectWindowGtk(); |
23 | 36 |
24 virtual bool Show(const base::Closure& disconnect_callback, | 37 // HostWindow::Core overrides. |
25 const std::string& username) OVERRIDE; | 38 virtual void StartOnUiThread() OVERRIDE; |
26 virtual void Hide() OVERRIDE; | 39 virtual void StopOnUiThread() OVERRIDE; |
27 | 40 |
28 private: | |
29 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnDelete, GdkEvent*); | 41 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnDelete, GdkEvent*); |
30 CHROMEGTK_CALLBACK_0(DisconnectWindowGtk, void, OnClicked); | 42 CHROMEGTK_CALLBACK_0(DisconnectWindowGtk, void, OnClicked); |
31 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnConfigure, | 43 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnConfigure, |
32 GdkEventConfigure*); | 44 GdkEventConfigure*); |
33 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnButtonPress, | 45 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnButtonPress, |
34 GdkEventButton*); | 46 GdkEventButton*); |
35 | 47 |
36 void CreateWindow(); | 48 // Invoked in the |caller_task_runner_| thread to disconnect the client |
| 49 // session. |
| 50 base::Closure disconnect_callback_; |
37 | 51 |
38 base::Closure disconnect_callback_; | 52 // Specifies the remote user name. |
| 53 std::string username_; |
| 54 |
39 GtkWidget* disconnect_window_; | 55 GtkWidget* disconnect_window_; |
40 GtkWidget* message_; | 56 GtkWidget* message_; |
41 GtkWidget* button_; | 57 GtkWidget* button_; |
42 | 58 |
43 // Used to distinguish resize events from other types of "configure-event" | 59 // Used to distinguish resize events from other types of "configure-event" |
44 // notifications. | 60 // notifications. |
45 int current_width_; | 61 int current_width_; |
46 int current_height_; | 62 int current_height_; |
47 | 63 |
48 // Points to the localized strings. | |
49 const UiStrings* ui_strings_; | |
50 | |
51 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowGtk); | 64 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowGtk); |
52 }; | 65 }; |
53 | 66 |
54 DisconnectWindowGtk::DisconnectWindowGtk(const UiStrings* ui_strings) | 67 // Helper function for creating a rectangular path with rounded corners, as |
55 : disconnect_window_(NULL), | 68 // Cairo doesn't have this facility. |radius| is the arc-radius of each |
| 69 // corner. The bounding rectangle extends from (0, 0) to (width, height). |
| 70 void AddRoundRectPath(cairo_t* cairo_context, int width, int height, |
| 71 int radius) { |
| 72 cairo_new_sub_path(cairo_context); |
| 73 cairo_arc(cairo_context, width - radius, radius, radius, -M_PI_2, 0); |
| 74 cairo_arc(cairo_context, width - radius, height - radius, radius, 0, M_PI_2); |
| 75 cairo_arc(cairo_context, radius, height - radius, radius, M_PI_2, 2 * M_PI_2); |
| 76 cairo_arc(cairo_context, radius, radius, radius, 2 * M_PI_2, 3 * M_PI_2); |
| 77 cairo_close_path(cairo_context); |
| 78 } |
| 79 |
| 80 DisconnectWindowGtk::DisconnectWindowGtk( |
| 81 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| 82 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| 83 const base::Closure& disconnect_callback, |
| 84 const std::string& username, |
| 85 const UiStrings& ui_strings) |
| 86 : HostWindow::Core(caller_task_runner, |
| 87 ui_task_runner, |
| 88 ui_strings), |
| 89 disconnect_callback_(disconnect_callback), |
| 90 username_(username), |
| 91 disconnect_window_(NULL), |
56 current_width_(0), | 92 current_width_(0), |
57 current_height_(0), | 93 current_height_(0) { |
58 ui_strings_(ui_strings) { | |
59 } | 94 } |
60 | 95 |
61 DisconnectWindowGtk::~DisconnectWindowGtk() { | 96 DisconnectWindowGtk::~DisconnectWindowGtk() { |
62 Hide(); | |
63 } | 97 } |
64 | 98 |
65 void DisconnectWindowGtk::CreateWindow() { | 99 void DisconnectWindowGtk::StartOnUiThread() { |
66 if (disconnect_window_) | 100 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
67 return; | 101 DCHECK(!disconnect_window_); |
68 | 102 |
69 disconnect_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); | 103 disconnect_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
70 GtkWindow* window = GTK_WINDOW(disconnect_window_); | 104 GtkWindow* window = GTK_WINDOW(disconnect_window_); |
71 | 105 |
72 g_signal_connect(disconnect_window_, "delete-event", | 106 g_signal_connect(disconnect_window_, "delete-event", |
73 G_CALLBACK(OnDeleteThunk), this); | 107 G_CALLBACK(OnDeleteThunk), this); |
74 gtk_window_set_title(window, UTF16ToUTF8(ui_strings_->product_name).c_str()); | 108 gtk_window_set_title(window, UTF16ToUTF8(ui_strings().product_name).c_str()); |
75 gtk_window_set_resizable(window, FALSE); | 109 gtk_window_set_resizable(window, FALSE); |
76 | 110 |
77 // Try to keep the window always visible. | 111 // Try to keep the window always visible. |
78 gtk_window_stick(window); | 112 gtk_window_stick(window); |
79 gtk_window_set_keep_above(window, TRUE); | 113 gtk_window_set_keep_above(window, TRUE); |
80 | 114 |
81 // Remove window titlebar. | 115 // Remove window titlebar. |
82 gtk_window_set_decorated(window, FALSE); | 116 gtk_window_set_decorated(window, FALSE); |
83 | 117 |
84 // In case the titlebar is still there, try to remove some of the buttons. | 118 // In case the titlebar is still there, try to remove some of the buttons. |
(...skipping 23 matching lines...) Expand all Loading... |
108 // left and right. The left margin is made larger to accommodate the | 142 // left and right. The left margin is made larger to accommodate the |
109 // window movement gripper. | 143 // window movement gripper. |
110 GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); | 144 GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); |
111 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12); | 145 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12); |
112 gtk_container_add(GTK_CONTAINER(window), align); | 146 gtk_container_add(GTK_CONTAINER(window), align); |
113 | 147 |
114 GtkWidget* button_row = gtk_hbox_new(FALSE, 12); | 148 GtkWidget* button_row = gtk_hbox_new(FALSE, 12); |
115 gtk_container_add(GTK_CONTAINER(align), button_row); | 149 gtk_container_add(GTK_CONTAINER(align), button_row); |
116 | 150 |
117 button_ = gtk_button_new_with_label( | 151 button_ = gtk_button_new_with_label( |
118 UTF16ToUTF8(ui_strings_->disconnect_button_text).c_str()); | 152 UTF16ToUTF8(ui_strings().disconnect_button_text).c_str()); |
119 gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0); | 153 gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0); |
120 | 154 |
121 g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this); | 155 g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this); |
122 | 156 |
123 message_ = gtk_label_new(NULL); | 157 message_ = gtk_label_new(NULL); |
124 gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0); | 158 gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0); |
125 | 159 |
126 // Override any theme setting for the text color, so that the text is | 160 // Override any theme setting for the text color, so that the text is |
127 // readable against the window's background pixmap. | 161 // readable against the window's background pixmap. |
128 PangoAttrList* attributes = pango_attr_list_new(); | 162 PangoAttrList* attributes = pango_attr_list_new(); |
129 PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0); | 163 PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0); |
130 pango_attr_list_insert(attributes, text_color); | 164 pango_attr_list_insert(attributes, text_color); |
131 gtk_label_set_attributes(GTK_LABEL(message_), attributes); | 165 gtk_label_set_attributes(GTK_LABEL(message_), attributes); |
132 | 166 |
133 gtk_widget_show_all(disconnect_window_); | 167 gtk_widget_show_all(disconnect_window_); |
| 168 |
| 169 string16 text = ReplaceStringPlaceholders( |
| 170 ui_strings().disconnect_message, UTF8ToUTF16(username_), NULL); |
| 171 gtk_label_set_text(GTK_LABEL(message_), UTF16ToUTF8(text).c_str()); |
| 172 gtk_window_present(window); |
134 } | 173 } |
135 | 174 |
136 bool DisconnectWindowGtk::Show(const base::Closure& disconnect_callback, | 175 void DisconnectWindowGtk::StopOnUiThread() { |
137 const std::string& username) { | 176 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
138 DCHECK(disconnect_callback_.is_null()); | |
139 DCHECK(!disconnect_callback.is_null()); | |
140 DCHECK(!disconnect_window_); | |
141 | 177 |
142 disconnect_callback_ = disconnect_callback; | |
143 CreateWindow(); | |
144 | |
145 string16 text = ReplaceStringPlaceholders( | |
146 ui_strings_->disconnect_message, UTF8ToUTF16(username), NULL); | |
147 gtk_label_set_text(GTK_LABEL(message_), UTF16ToUTF8(text).c_str()); | |
148 gtk_window_present(GTK_WINDOW(disconnect_window_)); | |
149 return true; | |
150 } | |
151 | |
152 void DisconnectWindowGtk::Hide() { | |
153 if (disconnect_window_) { | 178 if (disconnect_window_) { |
154 gtk_widget_destroy(disconnect_window_); | 179 gtk_widget_destroy(disconnect_window_); |
155 disconnect_window_ = NULL; | 180 disconnect_window_ = NULL; |
156 } | 181 } |
157 | |
158 disconnect_callback_.Reset(); | |
159 } | 182 } |
160 | 183 |
161 void DisconnectWindowGtk::OnClicked(GtkWidget* button) { | 184 void DisconnectWindowGtk::OnClicked(GtkWidget* button) { |
162 disconnect_callback_.Run(); | 185 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
163 Hide(); | 186 |
| 187 caller_task_runner()->PostTask(FROM_HERE, disconnect_callback_); |
164 } | 188 } |
165 | 189 |
166 gboolean DisconnectWindowGtk::OnDelete(GtkWidget* window, GdkEvent* event) { | 190 gboolean DisconnectWindowGtk::OnDelete(GtkWidget* window, |
167 disconnect_callback_.Run(); | 191 GdkEvent* event) { |
168 Hide(); | 192 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
169 | 193 |
| 194 caller_task_runner()->PostTask(FROM_HERE, disconnect_callback_); |
170 return TRUE; | 195 return TRUE; |
171 } | 196 } |
172 | 197 |
173 namespace { | 198 gboolean DisconnectWindowGtk::OnConfigure(GtkWidget* widget, |
174 // Helper function for creating a rectangular path with rounded corners, as | 199 GdkEventConfigure* event) { |
175 // Cairo doesn't have this facility. |radius| is the arc-radius of each | 200 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
176 // corner. The bounding rectangle extends from (0, 0) to (width, height). | |
177 void AddRoundRectPath(cairo_t* cairo_context, int width, int height, | |
178 int radius) { | |
179 cairo_new_sub_path(cairo_context); | |
180 cairo_arc(cairo_context, width - radius, radius, radius, -M_PI_2, 0); | |
181 cairo_arc(cairo_context, width - radius, height - radius, radius, 0, M_PI_2); | |
182 cairo_arc(cairo_context, radius, height - radius, radius, M_PI_2, 2 * M_PI_2); | |
183 cairo_arc(cairo_context, radius, radius, radius, 2 * M_PI_2, 3 * M_PI_2); | |
184 cairo_close_path(cairo_context); | |
185 } | |
186 | 201 |
187 } // namespace | |
188 | |
189 gboolean DisconnectWindowGtk::OnConfigure(GtkWidget* widget, | |
190 GdkEventConfigure* event) { | |
191 // Only generate bitmaps if the size has actually changed. | 202 // Only generate bitmaps if the size has actually changed. |
192 if (event->width == current_width_ && event->height == current_height_) | 203 if (event->width == current_width_ && event->height == current_height_) |
193 return FALSE; | 204 return FALSE; |
194 | 205 |
195 current_width_ = event->width; | 206 current_width_ = event->width; |
196 current_height_ = event->height; | 207 current_height_ = event->height; |
197 | 208 |
198 // Create the depth 1 pixmap for the window shape. | 209 // Create the depth 1 pixmap for the window shape. |
199 GdkPixmap* shape_mask = gdk_pixmap_new(NULL, current_width_, current_height_, | 210 GdkPixmap* shape_mask = gdk_pixmap_new(NULL, current_width_, current_height_, |
200 1); | 211 1); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 cairo_destroy(cairo_context); | 277 cairo_destroy(cairo_context); |
267 | 278 |
268 gdk_window_set_back_pixmap(widget->window, background, FALSE); | 279 gdk_window_set_back_pixmap(widget->window, background, FALSE); |
269 g_object_unref(background); | 280 g_object_unref(background); |
270 gdk_window_invalidate_rect(widget->window, NULL, TRUE); | 281 gdk_window_invalidate_rect(widget->window, NULL, TRUE); |
271 | 282 |
272 return FALSE; | 283 return FALSE; |
273 } | 284 } |
274 | 285 |
275 gboolean DisconnectWindowGtk::OnButtonPress(GtkWidget* widget, | 286 gboolean DisconnectWindowGtk::OnButtonPress(GtkWidget* widget, |
276 GdkEventButton* event) { | 287 GdkEventButton* event) { |
| 288 DCHECK(ui_task_runner()->BelongsToCurrentThread()); |
| 289 |
277 gtk_window_begin_move_drag(GTK_WINDOW(disconnect_window_), | 290 gtk_window_begin_move_drag(GTK_WINDOW(disconnect_window_), |
278 event->button, | 291 event->button, |
279 event->x_root, | 292 event->x_root, |
280 event->y_root, | 293 event->y_root, |
281 event->time); | 294 event->time); |
282 return FALSE; | 295 return FALSE; |
283 } | 296 } |
284 | 297 |
285 scoped_ptr<DisconnectWindow> DisconnectWindow::Create( | 298 } // namespace |
286 const UiStrings* ui_strings) { | 299 |
287 return scoped_ptr<DisconnectWindow>(new DisconnectWindowGtk(ui_strings)); | 300 scoped_ptr<HostWindow> HostWindow::CreateDisconnectWindow( |
| 301 scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, |
| 302 scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, |
| 303 const base::Closure& disconnect_callback, |
| 304 const std::string& username, |
| 305 const UiStrings& ui_strings) { |
| 306 scoped_refptr<Core> core = new DisconnectWindowGtk(caller_task_runner, |
| 307 ui_task_runner, |
| 308 disconnect_callback, |
| 309 username, |
| 310 ui_strings); |
| 311 return scoped_ptr<HostWindow>(new HostWindow(core)); |
288 } | 312 } |
289 | 313 |
290 } // namespace remoting | 314 } // namespace remoting |
OLD | NEW |