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 chrome::IsDebuggerAttachedToCurrentTab(browser_); | |
422 } | |
423 | |
424 void ReloadButtonGtk::ClearCache() { | |
425 if (browser_) { | |
Evan Stade
2012/07/11 02:13:30
no curlies
| |
426 chrome::ClearCache(browser_); | |
427 } | |
428 } | |
429 | |
OLD | NEW |