OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "chrome/browser/ui/screen_capture_notification_ui.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 #include <math.h> | 8 #include <math.h> |
9 | 9 |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/string_util.h" | 12 #include "base/string_util.h" |
13 #include "base/utf_string_conversions.h" | 13 #include "base/utf_string_conversions.h" |
14 #include "remoting/host/ui_strings.h" | 14 #include "grit/generated_resources.h" |
15 #include "ui/base/gtk/gtk_signal.h" | 15 #include "ui/base/gtk/gtk_signal.h" |
| 16 #include "ui/base/l10n/l10n_util.h" |
16 | 17 |
17 namespace remoting { | 18 class ScreenCaptureNotificationUIGtk : public ScreenCaptureNotificationUI { |
| 19 public: |
| 20 ScreenCaptureNotificationUIGtk(); |
| 21 virtual ~ScreenCaptureNotificationUIGtk(); |
18 | 22 |
19 class DisconnectWindowGtk : public DisconnectWindow { | 23 // ScreenCaptureNotificationUI interface |
20 public: | 24 virtual bool Show(const base::Closure& stop_callback, |
21 explicit DisconnectWindowGtk(const UiStrings* ui_strings); | 25 const string16& page_title) OVERRIDE; |
22 virtual ~DisconnectWindowGtk(); | |
23 | |
24 virtual bool Show(const base::Closure& disconnect_callback, | |
25 const std::string& username) OVERRIDE; | |
26 virtual void Hide() OVERRIDE; | |
27 | 26 |
28 private: | 27 private: |
29 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnDelete, GdkEvent*); | 28 CHROMEGTK_CALLBACK_1(ScreenCaptureNotificationUIGtk, gboolean, OnDelete, |
30 CHROMEGTK_CALLBACK_0(DisconnectWindowGtk, void, OnClicked); | 29 GdkEvent*); |
31 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnConfigure, | 30 CHROMEGTK_CALLBACK_0(ScreenCaptureNotificationUIGtk, void, OnClicked); |
| 31 CHROMEGTK_CALLBACK_1(ScreenCaptureNotificationUIGtk, gboolean, OnConfigure, |
32 GdkEventConfigure*); | 32 GdkEventConfigure*); |
33 CHROMEGTK_CALLBACK_1(DisconnectWindowGtk, gboolean, OnButtonPress, | 33 CHROMEGTK_CALLBACK_1(ScreenCaptureNotificationUIGtk, gboolean, OnButtonPress, |
34 GdkEventButton*); | 34 GdkEventButton*); |
35 | 35 |
36 void CreateWindow(); | 36 void CreateWindow(const string16& title); |
| 37 void HideWindow(); |
37 | 38 |
38 base::Closure disconnect_callback_; | 39 base::Closure stop_callback_; |
39 GtkWidget* disconnect_window_; | 40 GtkWidget* window_; |
40 GtkWidget* message_; | 41 GtkWidget* message_; |
41 GtkWidget* button_; | 42 GtkWidget* button_; |
42 | 43 |
43 // Used to distinguish resize events from other types of "configure-event" | 44 // Used to distinguish resize events from other types of "configure-event" |
44 // notifications. | 45 // notifications. |
45 int current_width_; | 46 int current_width_; |
46 int current_height_; | 47 int current_height_; |
47 | 48 |
48 // Points to the localized strings. | 49 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIGtk); |
49 const UiStrings* ui_strings_; | |
50 | |
51 DISALLOW_COPY_AND_ASSIGN(DisconnectWindowGtk); | |
52 }; | 50 }; |
53 | 51 |
54 DisconnectWindowGtk::DisconnectWindowGtk(const UiStrings* ui_strings) | 52 ScreenCaptureNotificationUIGtk::ScreenCaptureNotificationUIGtk() |
55 : disconnect_window_(NULL), | 53 : window_(NULL), |
56 current_width_(0), | 54 current_width_(0), |
57 current_height_(0), | 55 current_height_(0) { |
58 ui_strings_(ui_strings) { | |
59 } | 56 } |
60 | 57 |
61 DisconnectWindowGtk::~DisconnectWindowGtk() { | 58 ScreenCaptureNotificationUIGtk::~ScreenCaptureNotificationUIGtk() { |
62 Hide(); | 59 HideWindow(); |
63 } | 60 } |
64 | 61 |
65 void DisconnectWindowGtk::CreateWindow() { | 62 void ScreenCaptureNotificationUIGtk::CreateWindow(const string16& title) { |
66 if (disconnect_window_) | 63 if (window_) |
67 return; | 64 return; |
68 | 65 |
69 disconnect_window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); | 66 window_ = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
70 GtkWindow* window = GTK_WINDOW(disconnect_window_); | 67 GtkWindow* window = GTK_WINDOW(window_); |
71 | 68 |
72 g_signal_connect(disconnect_window_, "delete-event", | 69 g_signal_connect(window_, "delete-event", G_CALLBACK(OnDeleteThunk), this); |
73 G_CALLBACK(OnDeleteThunk), this); | 70 std::string window_title = |
74 gtk_window_set_title(window, UTF16ToUTF8(ui_strings_->product_name).c_str()); | 71 l10n_util::GetStringFUTF8(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TITLE, |
| 72 title); |
| 73 gtk_window_set_title(window, window_title.c_str()); |
75 gtk_window_set_resizable(window, FALSE); | 74 gtk_window_set_resizable(window, FALSE); |
76 | 75 |
77 // Try to keep the window always visible. | 76 // Try to keep the window always visible. |
78 gtk_window_stick(window); | 77 gtk_window_stick(window); |
79 gtk_window_set_keep_above(window, TRUE); | 78 gtk_window_set_keep_above(window, TRUE); |
80 | 79 |
81 // Remove window titlebar. | 80 // Remove window titlebar. |
82 gtk_window_set_decorated(window, FALSE); | 81 gtk_window_set_decorated(window, FALSE); |
83 | 82 |
84 // In case the titlebar is still there, try to remove some of the buttons. | 83 // In case the titlebar is still there, try to remove some of the buttons. |
85 // Utility windows have no minimize button or taskbar presence. | 84 // Utility windows have no minimize button or taskbar presence. |
86 gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY); | 85 gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY); |
87 gtk_window_set_deletable(window, FALSE); | 86 gtk_window_set_deletable(window, FALSE); |
88 | 87 |
89 // Allow custom rendering of the background pixmap. | 88 // Allow custom rendering of the background pixmap. |
90 gtk_widget_set_app_paintable(disconnect_window_, TRUE); | 89 gtk_widget_set_app_paintable(window_, TRUE); |
91 | 90 |
92 // Handle window resizing, to regenerate the background pixmap and window | 91 // Handle window resizing, to regenerate the background pixmap and window |
93 // shape bitmap. The stored width & height need to be initialized here | 92 // shape bitmap. The stored width & height need to be initialized here |
94 // in case the window is created a second time (the size of the previous | 93 // in case the window is created a second time (the size of the previous |
95 // window would be remembered, preventing the generation of bitmaps for the | 94 // window would be remembered, preventing the generation of bitmaps for the |
96 // new window). | 95 // new window). |
97 current_height_ = current_width_ = 0; | 96 current_height_ = current_width_ = 0; |
98 g_signal_connect(disconnect_window_, "configure-event", | 97 g_signal_connect(window_, "configure-event", |
99 G_CALLBACK(OnConfigureThunk), this); | 98 G_CALLBACK(OnConfigureThunk), this); |
100 | 99 |
101 // Handle mouse events to allow the user to drag the window around. | 100 // Handle mouse events to allow the user to drag the window around. |
102 gtk_widget_set_events(disconnect_window_, GDK_BUTTON_PRESS_MASK); | 101 gtk_widget_set_events(window_, GDK_BUTTON_PRESS_MASK); |
103 g_signal_connect(disconnect_window_, "button-press-event", | 102 g_signal_connect(window_, "button-press-event", |
104 G_CALLBACK(OnButtonPressThunk), this); | 103 G_CALLBACK(OnButtonPressThunk), this); |
105 | 104 |
106 // All magic numbers taken from screen shots provided by UX. | 105 // All magic numbers taken from screen shots provided by UX. |
107 // The alignment sets narrow margins at the top and bottom, compared with | 106 // The alignment sets narrow margins at the top and bottom, compared with |
108 // left and right. The left margin is made larger to accommodate the | 107 // left and right. The left margin is made larger to accommodate the |
109 // window movement gripper. | 108 // window movement gripper. |
110 GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); | 109 GtkWidget* align = gtk_alignment_new(0, 0, 1, 1); |
111 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12); | 110 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12); |
112 gtk_container_add(GTK_CONTAINER(window), align); | 111 gtk_container_add(GTK_CONTAINER(window), align); |
113 | 112 |
114 GtkWidget* button_row = gtk_hbox_new(FALSE, 12); | 113 GtkWidget* button_row = gtk_hbox_new(FALSE, 12); |
115 gtk_container_add(GTK_CONTAINER(align), button_row); | 114 gtk_container_add(GTK_CONTAINER(align), button_row); |
116 | 115 |
117 button_ = gtk_button_new_with_label( | 116 std::string button_label = |
118 UTF16ToUTF8(ui_strings_->disconnect_button_text).c_str()); | 117 l10n_util::GetStringUTF8(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP); |
| 118 button_ = gtk_button_new_with_label(button_label.c_str()); |
119 gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0); | 119 gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0); |
120 | 120 |
121 g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this); | 121 g_signal_connect(button_, "clicked", G_CALLBACK(OnClickedThunk), this); |
122 | 122 |
123 message_ = gtk_label_new(NULL); | 123 message_ = gtk_label_new(NULL); |
124 gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0); | 124 gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0); |
125 | 125 |
126 // Override any theme setting for the text color, so that the text is | 126 // Override any theme setting for the text color, so that the text is |
127 // readable against the window's background pixmap. | 127 // readable against the window's background pixmap. |
128 PangoAttrList* attributes = pango_attr_list_new(); | 128 PangoAttrList* attributes = pango_attr_list_new(); |
129 PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0); | 129 PangoAttribute* text_color = pango_attr_foreground_new(0, 0, 0); |
130 pango_attr_list_insert(attributes, text_color); | 130 pango_attr_list_insert(attributes, text_color); |
131 gtk_label_set_attributes(GTK_LABEL(message_), attributes); | 131 gtk_label_set_attributes(GTK_LABEL(message_), attributes); |
132 | 132 |
133 gtk_widget_show_all(disconnect_window_); | 133 std::string text = l10n_util::GetStringFUTF8( |
| 134 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, title); |
| 135 gtk_label_set_text(GTK_LABEL(message_), text.c_str()); |
| 136 |
| 137 gtk_widget_show_all(window_); |
| 138 gtk_window_present(GTK_WINDOW(window_)); |
134 } | 139 } |
135 | 140 |
136 bool DisconnectWindowGtk::Show(const base::Closure& disconnect_callback, | 141 bool ScreenCaptureNotificationUIGtk::Show(const base::Closure& stop_callback, |
137 const std::string& username) { | 142 const string16& title) { |
138 DCHECK(disconnect_callback_.is_null()); | 143 DCHECK(stop_callback_.is_null()); |
139 DCHECK(!disconnect_callback.is_null()); | 144 DCHECK(!stop_callback.is_null()); |
140 DCHECK(!disconnect_window_); | 145 DCHECK(!window_); |
141 | 146 |
142 disconnect_callback_ = disconnect_callback; | 147 stop_callback_ = stop_callback; |
143 CreateWindow(); | 148 CreateWindow(title); |
144 | 149 |
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 return true; |
150 } | 151 } |
151 | 152 |
152 void DisconnectWindowGtk::Hide() { | 153 void ScreenCaptureNotificationUIGtk::HideWindow() { |
153 if (disconnect_window_) { | 154 if (window_) { |
154 gtk_widget_destroy(disconnect_window_); | 155 gtk_widget_destroy(window_); |
155 disconnect_window_ = NULL; | 156 window_ = NULL; |
156 } | 157 } |
157 | 158 |
158 disconnect_callback_.Reset(); | 159 stop_callback_.Reset(); |
159 } | 160 } |
160 | 161 |
161 void DisconnectWindowGtk::OnClicked(GtkWidget* button) { | 162 void ScreenCaptureNotificationUIGtk::OnClicked(GtkWidget* button) { |
162 disconnect_callback_.Run(); | 163 stop_callback_.Run(); |
163 Hide(); | 164 HideWindow(); |
164 } | 165 } |
165 | 166 |
166 gboolean DisconnectWindowGtk::OnDelete(GtkWidget* window, GdkEvent* event) { | 167 gboolean ScreenCaptureNotificationUIGtk::OnDelete( |
167 disconnect_callback_.Run(); | 168 GtkWidget* window, GdkEvent* event) { |
168 Hide(); | 169 stop_callback_.Run(); |
| 170 HideWindow(); |
169 | 171 |
170 return TRUE; | 172 return TRUE; |
171 } | 173 } |
172 | 174 |
173 namespace { | 175 namespace { |
174 // Helper function for creating a rectangular path with rounded corners, as | 176 // Helper function for creating a rectangular path with rounded corners, as |
175 // Cairo doesn't have this facility. |radius| is the arc-radius of each | 177 // Cairo doesn't have this facility. |radius| is the arc-radius of each |
176 // corner. The bounding rectangle extends from (0, 0) to (width, height). | 178 // corner. The bounding rectangle extends from (0, 0) to (width, height). |
177 void AddRoundRectPath(cairo_t* cairo_context, int width, int height, | 179 void AddRoundRectPath(cairo_t* cairo_context, int width, int height, |
178 int radius) { | 180 int radius) { |
179 cairo_new_sub_path(cairo_context); | 181 cairo_new_sub_path(cairo_context); |
180 cairo_arc(cairo_context, width - radius, radius, radius, -M_PI_2, 0); | 182 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); | 183 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); | 184 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); | 185 cairo_arc(cairo_context, radius, radius, radius, 2 * M_PI_2, 3 * M_PI_2); |
184 cairo_close_path(cairo_context); | 186 cairo_close_path(cairo_context); |
185 } | 187 } |
186 | 188 |
187 } // namespace | 189 } // namespace |
188 | 190 |
189 gboolean DisconnectWindowGtk::OnConfigure(GtkWidget* widget, | 191 gboolean ScreenCaptureNotificationUIGtk::OnConfigure(GtkWidget* widget, |
190 GdkEventConfigure* event) { | 192 GdkEventConfigure* event) { |
191 // Only generate bitmaps if the size has actually changed. | 193 // Only generate bitmaps if the size has actually changed. |
192 if (event->width == current_width_ && event->height == current_height_) | 194 if (event->width == current_width_ && event->height == current_height_) |
193 return FALSE; | 195 return FALSE; |
194 | 196 |
195 current_width_ = event->width; | 197 current_width_ = event->width; |
196 current_height_ = event->height; | 198 current_height_ = event->height; |
197 | 199 |
198 // Create the depth 1 pixmap for the window shape. | 200 // Create the depth 1 pixmap for the window shape. |
199 GdkPixmap* shape_mask = gdk_pixmap_new(NULL, current_width_, current_height_, | 201 GdkPixmap* shape_mask = |
200 1); | 202 gdk_pixmap_new(NULL, current_width_, current_height_, 1); |
201 cairo_t* cairo_context = gdk_cairo_create(shape_mask); | 203 cairo_t* cairo_context = gdk_cairo_create(shape_mask); |
202 | 204 |
203 // Set the arc radius for the corners. | 205 // Set the arc radius for the corners. |
204 const int kCornerRadius = 6; | 206 const int kCornerRadius = 6; |
205 | 207 |
206 // Initialize the whole bitmap to be transparent. | 208 // Initialize the whole bitmap to be transparent. |
207 cairo_set_source_rgba(cairo_context, 0, 0, 0, 0); | 209 cairo_set_source_rgba(cairo_context, 0, 0, 0, 0); |
208 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); | 210 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); |
209 cairo_paint(cairo_context); | 211 cairo_paint(cairo_context); |
210 | 212 |
211 // Paint an opaque round rect covering the whole area (leaving the extreme | 213 // Paint an opaque round rect covering the whole area (leaving the extreme |
212 // corners transparent). | 214 // corners transparent). |
213 cairo_set_source_rgba(cairo_context, 1, 1, 1, 1); | 215 cairo_set_source_rgba(cairo_context, 1, 1, 1, 1); |
214 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); | 216 cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE); |
215 AddRoundRectPath(cairo_context, current_width_, current_height_, | 217 AddRoundRectPath(cairo_context, current_width_, current_height_, |
216 kCornerRadius); | 218 kCornerRadius); |
217 cairo_fill(cairo_context); | 219 cairo_fill(cairo_context); |
218 | 220 |
219 cairo_destroy(cairo_context); | 221 cairo_destroy(cairo_context); |
220 gdk_window_shape_combine_mask(widget->window, shape_mask, 0, 0); | 222 gdk_window_shape_combine_mask(widget->window, shape_mask, 0, 0); |
221 g_object_unref(shape_mask); | 223 g_object_unref(shape_mask); |
222 | 224 |
223 // Create a full-color pixmap for the window background image. | 225 // Create a full-color pixmap for the window background image. |
224 GdkPixmap* background = gdk_pixmap_new(NULL, current_width_, current_height_, | 226 GdkPixmap* background = |
225 24); | 227 gdk_pixmap_new(NULL, current_width_, current_height_, 24); |
226 cairo_context = gdk_cairo_create(background); | 228 cairo_context = gdk_cairo_create(background); |
227 | 229 |
228 // Paint the whole bitmap one color. | 230 // Paint the whole bitmap one color. |
229 cairo_set_source_rgb(cairo_context, 0.91, 0.91, 0.91); | 231 cairo_set_source_rgb(cairo_context, 0.91, 0.91, 0.91); |
230 cairo_paint(cairo_context); | 232 cairo_paint(cairo_context); |
231 | 233 |
232 // Paint the round-rectangle edge. | 234 // Paint the round-rectangle edge. |
233 cairo_set_source_rgb(cairo_context, 0.13, 0.69, 0.11); | 235 cairo_set_source_rgb(cairo_context, 0.13, 0.69, 0.11); |
234 cairo_set_line_width(cairo_context, 6); | 236 cairo_set_line_width(cairo_context, 6); |
235 AddRoundRectPath(cairo_context, current_width_, current_height_, | 237 AddRoundRectPath(cairo_context, current_width_, current_height_, |
(...skipping 29 matching lines...) Expand all Loading... |
265 | 267 |
266 cairo_destroy(cairo_context); | 268 cairo_destroy(cairo_context); |
267 | 269 |
268 gdk_window_set_back_pixmap(widget->window, background, FALSE); | 270 gdk_window_set_back_pixmap(widget->window, background, FALSE); |
269 g_object_unref(background); | 271 g_object_unref(background); |
270 gdk_window_invalidate_rect(widget->window, NULL, TRUE); | 272 gdk_window_invalidate_rect(widget->window, NULL, TRUE); |
271 | 273 |
272 return FALSE; | 274 return FALSE; |
273 } | 275 } |
274 | 276 |
275 gboolean DisconnectWindowGtk::OnButtonPress(GtkWidget* widget, | 277 gboolean ScreenCaptureNotificationUIGtk::OnButtonPress(GtkWidget* widget, |
276 GdkEventButton* event) { | 278 GdkEventButton* event) { |
277 gtk_window_begin_move_drag(GTK_WINDOW(disconnect_window_), | 279 gtk_window_begin_move_drag(GTK_WINDOW(window_), |
278 event->button, | 280 event->button, |
279 event->x_root, | 281 event->x_root, |
280 event->y_root, | 282 event->y_root, |
281 event->time); | 283 event->time); |
282 return FALSE; | 284 return FALSE; |
283 } | 285 } |
284 | 286 |
285 scoped_ptr<DisconnectWindow> DisconnectWindow::Create( | 287 scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create() { |
286 const UiStrings* ui_strings) { | 288 return scoped_ptr<ScreenCaptureNotificationUI>( |
287 return scoped_ptr<DisconnectWindow>(new DisconnectWindowGtk(ui_strings)); | 289 new ScreenCaptureNotificationUIGtk()); |
288 } | 290 } |
289 | |
290 } // namespace remoting | |
OLD | NEW |