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 "chrome/browser/ui/gtk/reload_button_gtk.h" | 5 #include "chrome/browser/ui/gtk/reload_button_gtk.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "grit/generated_resources.h" | 21 #include "grit/generated_resources.h" |
22 #include "grit/theme_resources.h" | 22 #include "grit/theme_resources.h" |
23 #include "grit/theme_resources_standard.h" | 23 #include "grit/theme_resources_standard.h" |
24 #include "ui/base/l10n/l10n_util.h" | 24 #include "ui/base/l10n/l10n_util.h" |
25 | 25 |
26 // The width of this button in GTK+ theme mode. The Stop and Refresh stock icons | 26 // The width of this button in GTK+ theme mode. The Stop and Refresh stock icons |
27 // can be different sizes; this variable is used to make sure that the button | 27 // can be different sizes; this variable is used to make sure that the button |
28 // doesn't change sizes when switching between the two. | 28 // doesn't change sizes when switching between the two. |
29 static int GtkButtonWidth = 0; | 29 static int GtkButtonWidth = 0; |
30 | 30 |
| 31 // The time in milliseconds between when the user clicks and the menu appears. |
| 32 static const int kReloadMenuTimerDelay = 500; |
| 33 |
| 34 // Content of the Reload drop-down menu. |
| 35 static const int kReloadMenuItems[] = { |
| 36 IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM, |
| 37 IDS_RELOAD_MENU_HARD_RELOAD_ITEM, |
| 38 IDS_RELOAD_MENU_CLEAR_AND_HARD_RELOAD_ITEM, |
| 39 }; |
| 40 |
31 //////////////////////////////////////////////////////////////////////////////// | 41 //////////////////////////////////////////////////////////////////////////////// |
32 // ReloadButton, public: | 42 // ReloadButton, public: |
33 | 43 |
34 ReloadButtonGtk::ReloadButtonGtk(LocationBarViewGtk* location_bar, | 44 ReloadButtonGtk::ReloadButtonGtk(LocationBarViewGtk* location_bar, |
35 Browser* browser) | 45 Browser* browser) |
36 : location_bar_(location_bar), | 46 : location_bar_(location_bar), |
37 browser_(browser), | 47 browser_(browser), |
38 intended_mode_(MODE_RELOAD), | 48 intended_mode_(MODE_RELOAD), |
39 visible_mode_(MODE_RELOAD), | 49 visible_mode_(MODE_RELOAD), |
40 theme_service_(browser ? | 50 theme_service_(browser ? |
41 GtkThemeService::GetFrom(browser->profile()) : NULL), | 51 GtkThemeService::GetFrom(browser->profile()) : NULL), |
42 reload_(theme_service_, IDR_RELOAD, IDR_RELOAD_P, IDR_RELOAD_H, 0), | 52 reload_(theme_service_, IDR_RELOAD, IDR_RELOAD_P, IDR_RELOAD_H, 0), |
43 stop_(theme_service_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, IDR_STOP_D), | 53 stop_(theme_service_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, IDR_STOP_D), |
44 widget_(gtk_chrome_button_new()), | 54 widget_(gtk_chrome_button_new()), |
45 stop_to_reload_timer_delay_(base::TimeDelta::FromMilliseconds(1350)), | 55 stop_to_reload_timer_delay_(base::TimeDelta::FromMilliseconds(1350)), |
| 56 weak_factory_(this), |
46 testing_mouse_hovered_(false), | 57 testing_mouse_hovered_(false), |
47 testing_reload_count_(0) { | 58 testing_reload_count_(0) { |
| 59 menu_model_.reset(new ui::SimpleMenuModel(this)); |
| 60 for (size_t i = 0; i < arraysize(kReloadMenuItems); i++) { |
| 61 menu_model_->AddItemWithStringId(kReloadMenuItems[i], kReloadMenuItems[i]); |
| 62 } |
| 63 |
48 gtk_widget_set_size_request(widget(), reload_.Width(), reload_.Height()); | 64 gtk_widget_set_size_request(widget(), reload_.Width(), reload_.Height()); |
49 | 65 |
50 gtk_widget_set_app_paintable(widget(), TRUE); | 66 gtk_widget_set_app_paintable(widget(), TRUE); |
51 | 67 |
52 g_signal_connect(widget(), "clicked", G_CALLBACK(OnClickedThunk), this); | 68 g_signal_connect(widget(), "clicked", G_CALLBACK(OnClickedThunk), this); |
53 g_signal_connect(widget(), "expose-event", G_CALLBACK(OnExposeThunk), this); | 69 g_signal_connect(widget(), "expose-event", G_CALLBACK(OnExposeThunk), this); |
54 g_signal_connect(widget(), "leave-notify-event", | 70 g_signal_connect(widget(), "leave-notify-event", |
55 G_CALLBACK(OnLeaveNotifyThunk), this); | 71 G_CALLBACK(OnLeaveNotifyThunk), this); |
56 gtk_widget_set_can_focus(widget(), FALSE); | 72 gtk_widget_set_can_focus(widget(), FALSE); |
57 | 73 |
58 gtk_widget_set_has_tooltip(widget(), TRUE); | 74 gtk_widget_set_has_tooltip(widget(), TRUE); |
59 g_signal_connect(widget(), "query-tooltip", G_CALLBACK(OnQueryTooltipThunk), | 75 g_signal_connect(widget(), "query-tooltip", G_CALLBACK(OnQueryTooltipThunk), |
60 this); | 76 this); |
61 | 77 |
| 78 g_signal_connect(widget(), "button-press-event", |
| 79 G_CALLBACK(OnButtonPressThunk), this); |
| 80 gtk_widget_add_events(widget(), GDK_POINTER_MOTION_MASK); |
| 81 g_signal_connect(widget(), "motion-notify-event", |
| 82 G_CALLBACK(OnMouseMoveThunk), this); |
| 83 |
| 84 // Popup the menu as left-aligned relative to this widget rather than the |
| 85 // default of right aligned. |
| 86 g_object_set_data(G_OBJECT(widget()), "left-align-popup", |
| 87 reinterpret_cast<void*>(true)); |
| 88 |
62 hover_controller_.Init(widget()); | 89 hover_controller_.Init(widget()); |
63 gtk_util::SetButtonTriggersNavigation(widget()); | 90 gtk_util::SetButtonTriggersNavigation(widget()); |
64 | 91 |
65 if (theme_service_) { | 92 if (theme_service_) { |
66 theme_service_->InitThemesFor(this); | 93 theme_service_->InitThemesFor(this); |
67 registrar_.Add(this, | 94 registrar_.Add(this, |
68 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | 95 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, |
69 content::Source<ThemeService>(theme_service_)); | 96 content::Source<ThemeService>(theme_service_)); |
70 } | 97 } |
71 | 98 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 DCHECK(chrome::NOTIFICATION_BROWSER_THEME_CHANGED == type); | 164 DCHECK(chrome::NOTIFICATION_BROWSER_THEME_CHANGED == type); |
138 | 165 |
139 GtkThemeService* provider = static_cast<GtkThemeService*>( | 166 GtkThemeService* provider = static_cast<GtkThemeService*>( |
140 content::Source<ThemeService>(source).ptr()); | 167 content::Source<ThemeService>(source).ptr()); |
141 DCHECK_EQ(provider, theme_service_); | 168 DCHECK_EQ(provider, theme_service_); |
142 GtkButtonWidth = 0; | 169 GtkButtonWidth = 0; |
143 UpdateThemeButtons(); | 170 UpdateThemeButtons(); |
144 } | 171 } |
145 | 172 |
146 //////////////////////////////////////////////////////////////////////////////// | 173 //////////////////////////////////////////////////////////////////////////////// |
| 174 // ReloadButtonGtk, MenuGtk::Delegate implementation: |
| 175 |
| 176 void ReloadButtonGtk::StoppedShowing() { |
| 177 reload_.set_paint_override(-1); |
| 178 } |
| 179 |
| 180 //////////////////////////////////////////////////////////////////////////////// |
| 181 // ReloadButtonGtk, SimpleMenuModel::Delegate implementation: |
| 182 |
| 183 bool ReloadButtonGtk::IsCommandIdChecked(int command_id) const { |
| 184 return false; |
| 185 } |
| 186 |
| 187 bool ReloadButtonGtk::IsCommandIdEnabled(int command_id) const { |
| 188 return true; |
| 189 } |
| 190 |
| 191 bool ReloadButtonGtk::IsCommandIdVisible(int command_id) const { |
| 192 return true; |
| 193 } |
| 194 |
| 195 bool ReloadButtonGtk::GetAcceleratorForCommandId(int command_id, |
| 196 ui::Accelerator* accelerator) { |
| 197 return false; |
| 198 } |
| 199 |
| 200 void ReloadButtonGtk::ExecuteCommand(int command_id) { |
| 201 switch (command_id) { |
| 202 case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM: |
| 203 DoReload(IDC_RELOAD); |
| 204 break; |
| 205 case IDS_RELOAD_MENU_HARD_RELOAD_ITEM: |
| 206 DoReload(IDC_RELOAD_IGNORING_CACHE); |
| 207 break; |
| 208 case IDS_RELOAD_MENU_CLEAR_AND_HARD_RELOAD_ITEM: |
| 209 ClearCache(); |
| 210 DoReload(IDC_RELOAD_IGNORING_CACHE); |
| 211 break; |
| 212 default: |
| 213 LOG(ERROR) << "Unknown reload menu command"; |
| 214 } |
| 215 } |
| 216 |
| 217 //////////////////////////////////////////////////////////////////////////////// |
147 // ReloadButtonGtk, private: | 218 // ReloadButtonGtk, private: |
148 | 219 |
149 void ReloadButtonGtk::OnClicked(GtkWidget* /* sender */) { | 220 void ReloadButtonGtk::OnClicked(GtkWidget* /* sender */) { |
| 221 weak_factory_.InvalidateWeakPtrs(); |
150 if (visible_mode_ == MODE_STOP) { | 222 if (visible_mode_ == MODE_STOP) { |
151 // Do nothing if Stop was disabled due to an attempt to change back to | 223 // Do nothing if Stop was disabled due to an attempt to change back to |
152 // RELOAD mode while hovered. | 224 // RELOAD mode while hovered. |
153 if (stop_.paint_override() == GTK_STATE_INSENSITIVE) | 225 if (stop_.paint_override() == GTK_STATE_INSENSITIVE) |
154 return; | 226 return; |
155 | 227 |
156 if (browser_) | 228 if (browser_) |
157 chrome::Stop(browser_); | 229 chrome::Stop(browser_); |
158 | 230 |
159 // The user has clicked, so we can feel free to update the button, | 231 // The user has clicked, so we can feel free to update the button, |
160 // even if the mouse is still hovering. | 232 // even if the mouse is still hovering. |
161 ChangeMode(MODE_RELOAD, true); | 233 ChangeMode(MODE_RELOAD, true); |
162 } else if (!double_click_timer_.IsRunning()) { | 234 } else if (!double_click_timer_.IsRunning()) { |
163 // Shift-clicking or Ctrl-clicking the reload button means we should ignore | 235 DoReload(0); |
164 // any cached content. | |
165 int command; | |
166 GdkModifierType modifier_state; | |
167 gtk_get_current_event_state(&modifier_state); | |
168 guint modifier_state_uint = modifier_state; | |
169 if (modifier_state_uint & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) { | |
170 command = IDC_RELOAD_IGNORING_CACHE; | |
171 // Mask off Shift and Control so they don't affect the disposition below. | |
172 modifier_state_uint &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK); | |
173 } else { | |
174 command = IDC_RELOAD; | |
175 } | |
176 | |
177 WindowOpenDisposition disposition = | |
178 event_utils::DispositionFromGdkState(modifier_state_uint); | |
179 if ((disposition == CURRENT_TAB) && location_bar_) { | |
180 // Forcibly reset the location bar, since otherwise it won't discard any | |
181 // ongoing user edits, since it doesn't realize this is a user-initiated | |
182 // action. | |
183 location_bar_->Revert(); | |
184 } | |
185 | |
186 // Start a timer - while this timer is running, the reload button cannot be | |
187 // changed to a stop button. We do not set |intended_mode_| to MODE_STOP | |
188 // here as the browser will do that when it actually starts loading (which | |
189 // may happen synchronously, thus the need to do this before telling the | |
190 // browser to execute the reload command). | |
191 double_click_timer_.Start(FROM_HERE, double_click_timer_delay_, this, | |
192 &ReloadButtonGtk::OnDoubleClickTimer); | |
193 | |
194 if (browser_) | |
195 chrome::ExecuteCommandWithDisposition(browser_, command, disposition); | |
196 ++testing_reload_count_; | |
197 } | 236 } |
198 } | 237 } |
199 | 238 |
200 gboolean ReloadButtonGtk::OnExpose(GtkWidget* widget, | 239 gboolean ReloadButtonGtk::OnExpose(GtkWidget* widget, |
201 GdkEventExpose* e) { | 240 GdkEventExpose* e) { |
202 TRACE_EVENT0("ui::gtk", "ReloadButtonGtk::OnExpose"); | 241 TRACE_EVENT0("ui::gtk", "ReloadButtonGtk::OnExpose"); |
203 if (theme_service_ && theme_service_->UsingNativeTheme()) | 242 if (theme_service_ && theme_service_->UsingNativeTheme()) |
204 return FALSE; | 243 return FALSE; |
205 return ((visible_mode_ == MODE_RELOAD) ? reload_ : stop_).OnExpose( | 244 return ((visible_mode_ == MODE_RELOAD) ? reload_ : stop_).OnExpose( |
206 widget, e, hover_controller_.GetCurrentValue()); | 245 widget, e, hover_controller_.GetCurrentValue()); |
207 } | 246 } |
208 | 247 |
209 gboolean ReloadButtonGtk::OnLeaveNotify(GtkWidget* /* widget */, | 248 gboolean ReloadButtonGtk::OnLeaveNotify(GtkWidget* /* widget */, |
210 GdkEventCrossing* /* event */) { | 249 GdkEventCrossing* /* event */) { |
211 ChangeMode(intended_mode_, true); | 250 ChangeMode(intended_mode_, true); |
212 return FALSE; | 251 return FALSE; |
213 } | 252 } |
214 | 253 |
215 gboolean ReloadButtonGtk::OnQueryTooltip(GtkWidget* /* sender */, | 254 gboolean ReloadButtonGtk::OnQueryTooltip(GtkWidget* /* sender */, |
216 gint /* x */, | 255 gint /* x */, |
217 gint /* y */, | 256 gint /* y */, |
218 gboolean /* keyboard_mode */, | 257 gboolean /* keyboard_mode */, |
219 GtkTooltip* tooltip) { | 258 GtkTooltip* tooltip) { |
220 // |location_bar_| can be NULL in tests. | 259 // |location_bar_| can be NULL in tests. |
221 if (!location_bar_) | 260 if (!location_bar_) |
222 return FALSE; | 261 return FALSE; |
223 | 262 |
| 263 int reload_tooltip = ReloadMenuEnabled() ? |
| 264 IDS_TOOLTIP_RELOAD_WITH_MENU : IDS_TOOLTIP_RELOAD; |
224 gtk_tooltip_set_text(tooltip, l10n_util::GetStringUTF8( | 265 gtk_tooltip_set_text(tooltip, l10n_util::GetStringUTF8( |
225 (visible_mode_ == MODE_RELOAD) ? | 266 (visible_mode_ == MODE_RELOAD) ? |
226 IDS_TOOLTIP_RELOAD : IDS_TOOLTIP_STOP).c_str()); | 267 reload_tooltip : IDS_TOOLTIP_STOP).c_str()); |
227 return TRUE; | 268 return TRUE; |
228 } | 269 } |
229 | 270 |
| 271 gboolean ReloadButtonGtk::OnButtonPress(GtkWidget* widget, |
| 272 GdkEventButton* event) { |
| 273 if (!ReloadMenuEnabled() || visible_mode_ == MODE_STOP) |
| 274 return FALSE; |
| 275 |
| 276 if (event->button == 3) |
| 277 ShowReloadMenu(event->button, event->time); |
| 278 |
| 279 if (event->button != 1) |
| 280 return FALSE; |
| 281 |
| 282 y_position_of_last_press_ = static_cast<int>(event->y); |
| 283 MessageLoop::current()->PostDelayedTask( |
| 284 FROM_HERE, |
| 285 base::Bind(&ReloadButtonGtk::ShowReloadMenu, |
| 286 weak_factory_.GetWeakPtr(), |
| 287 event->button, |
| 288 event->time), |
| 289 base::TimeDelta::FromMilliseconds(kReloadMenuTimerDelay)); |
| 290 return FALSE; |
| 291 } |
| 292 |
| 293 gboolean ReloadButtonGtk::OnMouseMove(GtkWidget* widget, |
| 294 GdkEventMotion* event) { |
| 295 // If we aren't waiting to show the back forward menu, do nothing. |
| 296 if (!weak_factory_.HasWeakPtrs()) |
| 297 return FALSE; |
| 298 |
| 299 // We only count moves about a certain threshold. |
| 300 GtkSettings* settings = gtk_widget_get_settings(widget); |
| 301 int drag_min_distance; |
| 302 g_object_get(settings, "gtk-dnd-drag-threshold", &drag_min_distance, NULL); |
| 303 if (event->y - y_position_of_last_press_ < drag_min_distance) |
| 304 return FALSE; |
| 305 |
| 306 // We will show the menu now. Cancel the delayed event. |
| 307 weak_factory_.InvalidateWeakPtrs(); |
| 308 ShowReloadMenu(/* button */ 1, event->time); |
| 309 return FALSE; |
| 310 } |
| 311 |
230 void ReloadButtonGtk::UpdateThemeButtons() { | 312 void ReloadButtonGtk::UpdateThemeButtons() { |
231 bool use_gtk = theme_service_ && theme_service_->UsingNativeTheme(); | 313 bool use_gtk = theme_service_ && theme_service_->UsingNativeTheme(); |
232 | 314 |
233 if (use_gtk) { | 315 if (use_gtk) { |
234 gtk_widget_ensure_style(widget()); | 316 gtk_widget_ensure_style(widget()); |
235 GtkStyle* style = gtk_widget_get_style(widget()); | 317 GtkStyle* style = gtk_widget_get_style(widget()); |
236 GtkIconSet* icon_set = gtk_style_lookup_icon_set( | 318 GtkIconSet* icon_set = gtk_style_lookup_icon_set( |
237 style, | 319 style, |
238 (visible_mode_ == MODE_RELOAD) ? GTK_STOCK_REFRESH : GTK_STOCK_STOP); | 320 (visible_mode_ == MODE_RELOAD) ? GTK_STOCK_REFRESH : GTK_STOCK_STOP); |
239 if (icon_set) { | 321 if (icon_set) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
276 gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(widget()), use_gtk); | 358 gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(widget()), use_gtk); |
277 } | 359 } |
278 | 360 |
279 void ReloadButtonGtk::OnDoubleClickTimer() { | 361 void ReloadButtonGtk::OnDoubleClickTimer() { |
280 ChangeMode(intended_mode_, false); | 362 ChangeMode(intended_mode_, false); |
281 } | 363 } |
282 | 364 |
283 void ReloadButtonGtk::OnStopToReloadTimer() { | 365 void ReloadButtonGtk::OnStopToReloadTimer() { |
284 ChangeMode(intended_mode_, true); | 366 ChangeMode(intended_mode_, true); |
285 } | 367 } |
| 368 |
| 369 void ReloadButtonGtk::ShowReloadMenu(int button, guint32 event_time) { |
| 370 if (!ReloadMenuEnabled() || visible_mode_ == MODE_STOP) |
| 371 return; |
| 372 |
| 373 menu_.reset(new MenuGtk(this, menu_model_.get())); |
| 374 reload_.set_paint_override(GTK_STATE_ACTIVE); |
| 375 menu_->PopupForWidget(widget(), button, event_time); |
| 376 } |
| 377 |
| 378 void ReloadButtonGtk::DoReload(int command) { |
| 379 // Shift-clicking or Ctrl-clicking the reload button means we should ignore |
| 380 // any cached content. |
| 381 GdkModifierType modifier_state; |
| 382 gtk_get_current_event_state(&modifier_state); |
| 383 guint modifier_state_uint = modifier_state; |
| 384 |
| 385 // Default reload behaviour. |
| 386 if (command == 0) { |
| 387 if (modifier_state_uint & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) { |
| 388 command = IDC_RELOAD_IGNORING_CACHE; |
| 389 // Mask off Shift and Control so they don't affect the disposition below. |
| 390 modifier_state_uint &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK); |
| 391 } else { |
| 392 command = IDC_RELOAD; |
| 393 } |
| 394 } |
| 395 |
| 396 WindowOpenDisposition disposition = |
| 397 event_utils::DispositionFromGdkState(modifier_state_uint); |
| 398 if ((disposition == CURRENT_TAB) && location_bar_) { |
| 399 // Forcibly reset the location bar, since otherwise it won't discard any |
| 400 // ongoing user edits, since it doesn't realize this is a user-initiated |
| 401 // action. |
| 402 location_bar_->Revert(); |
| 403 } |
| 404 |
| 405 // Start a timer - while this timer is running, the reload button cannot be |
| 406 // changed to a stop button. We do not set |intended_mode_| to MODE_STOP |
| 407 // here as the browser will do that when it actually starts loading (which |
| 408 // may happen synchronously, thus the need to do this before telling the |
| 409 // browser to execute the reload command). |
| 410 double_click_timer_.Start(FROM_HERE, double_click_timer_delay_, this, |
| 411 &ReloadButtonGtk::OnDoubleClickTimer); |
| 412 |
| 413 if (browser_) |
| 414 chrome::ExecuteCommandWithDisposition(browser_, command, disposition); |
| 415 ++testing_reload_count_; |
| 416 } |
| 417 |
| 418 bool ReloadButtonGtk::ReloadMenuEnabled() { |
| 419 if (!browser_) |
| 420 return false; |
| 421 return browser_->IsDebuggerAttachedForCurrentTab(); |
| 422 } |
| 423 |
| 424 void ReloadButtonGtk::ClearCache() { |
| 425 if (browser_) { |
| 426 chrome::ClearCache(browser_); |
| 427 } |
| 428 } |
| 429 |
OLD | NEW |