// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/gtk/go_button_gtk.h" #include "app/l10n_util.h" #include "base/logging.h" #include "base/message_loop.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/browser.h" #include "chrome/browser/gtk/gtk_chrome_button.h" #include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/browser/gtk/location_bar_view_gtk.h" #include "chrome/browser/profile.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/common/notification_service.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" // Limit the length of the tooltip text. This applies only to the text in the // omnibox (e.g. X in "Go to X"); const size_t kMaxTooltipTextLength = 400; GoButtonGtk::GoButtonGtk(LocationBarViewGtk* location_bar, Browser* browser) : location_bar_(location_bar), browser_(browser), button_delay_(0), stop_timer_(this), intended_mode_(MODE_GO), visible_mode_(MODE_GO), theme_provider_(browser ? GtkThemeProvider::GetFrom(browser->profile()) : NULL), go_(theme_provider_, IDR_GO, IDR_GO_P, IDR_GO_H, 0, IDR_GO_MASK), stop_(theme_provider_, IDR_STOP, IDR_STOP_P, IDR_STOP_H, 0, IDR_GO_MASK), widget_(gtk_chrome_button_new()) { gtk_widget_set_size_request(widget_.get(), go_.Width(), go_.Height()); gtk_widget_set_app_paintable(widget_.get(), TRUE); g_signal_connect(widget_.get(), "expose-event", G_CALLBACK(OnExpose), this); g_signal_connect(widget_.get(), "leave-notify-event", G_CALLBACK(OnLeave), this); g_signal_connect(widget_.get(), "clicked", G_CALLBACK(OnClicked), this); GTK_WIDGET_UNSET_FLAGS(widget_.get(), GTK_CAN_FOCUS); gtk_widget_set_has_tooltip(widget_.get(), TRUE); g_signal_connect(widget_.get(), "query-tooltip", G_CALLBACK(OnQueryTooltipThunk), this); hover_controller_.Init(widget()); gtk_util::SetButtonTriggersNavigation(widget()); if (theme_provider_) { theme_provider_->InitThemesFor(this); registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); } } GoButtonGtk::~GoButtonGtk() { widget_.Destroy(); } void GoButtonGtk::ChangeMode(Mode mode, bool force) { intended_mode_ = mode; // If the change is forced, or the user isn't hovering the icon, or it's safe // to change it to the other image type, make the change immediately; // otherwise we'll let it happen later. if (force || GTK_WIDGET_STATE(widget()) == GTK_STATE_NORMAL || ((mode == MODE_STOP) ? stop_timer_.empty() : (visible_mode_ != MODE_STOP))) { stop_timer_.RevokeAll(); visible_mode_ = mode; gtk_widget_queue_draw(widget_.get()); UpdateThemeButtons(); } } void GoButtonGtk::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { DCHECK(NotificationType::BROWSER_THEME_CHANGED == type); GtkThemeProvider* provider = static_cast( Source(source).ptr()); DCHECK(provider == theme_provider_); UpdateThemeButtons(); } Task* GoButtonGtk::CreateButtonTimerTask() { return stop_timer_.NewRunnableMethod(&GoButtonGtk::OnButtonTimer); } void GoButtonGtk::OnButtonTimer() { stop_timer_.RevokeAll(); ChangeMode(intended_mode_, true); } // static gboolean GoButtonGtk::OnExpose(GtkWidget* widget, GdkEventExpose* e, GoButtonGtk* button) { if (button->theme_provider_ && button->theme_provider_->UseGtkTheme()) { return FALSE; } else { double hover_state = button->hover_controller_.GetCurrentValue(); if (button->visible_mode_ == MODE_GO) { return button->go_.OnExpose(widget, e, hover_state); } else { return button->stop_.OnExpose(widget, e, hover_state); } } } // static gboolean GoButtonGtk::OnLeave(GtkWidget* widget, GdkEventCrossing* event, GoButtonGtk* button) { button->ChangeMode(button->intended_mode_, true); return FALSE; } // static gboolean GoButtonGtk::OnClicked(GtkButton* widget, GoButtonGtk* button) { if (button->visible_mode_ == MODE_STOP) { if (button->browser_) button->browser_->Stop(); // The user has clicked, so we can feel free to update the button, // even if the mouse is still hovering. button->ChangeMode(MODE_GO, true); } else if (button->visible_mode_ == MODE_GO && button->stop_timer_.empty()) { // If the go button is visible and not within the double click timer, go. if (button->browser_) { button->browser_->ExecuteCommandWithDisposition(IDC_GO, gtk_util::DispositionForCurrentButtonPressEvent()); } // Figure out the system double-click time. if (button->button_delay_ == 0) { GtkSettings* settings = gtk_settings_get_default(); g_object_get(G_OBJECT(settings), "gtk-double-click-time", &button->button_delay_, NULL); } // Stop any existing timers. button->stop_timer_.RevokeAll(); // Start a timer - while this timer is running, the go button // cannot be changed to a stop button. We do not set intended_mode_ // to MODE_STOP here as we want to wait for the browser to tell // us that it has started loading (and this may occur only after // some delay). MessageLoop::current()->PostDelayedTask(FROM_HERE, button->CreateButtonTimerTask(), button->button_delay_); } return TRUE; } gboolean GoButtonGtk::OnQueryTooltip(GtkTooltip* tooltip) { // |location_bar_| can be NULL in tests. if (!location_bar_) return FALSE; std::string text; if (visible_mode_ == MODE_GO) { std::wstring current_text_wstr(location_bar_->location_entry()->GetText()); if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) l10n_util::WrapStringWithLTRFormatting(¤t_text_wstr); string16 current_text = WideToUTF16Hack( l10n_util::TruncateString(current_text_wstr, kMaxTooltipTextLength)); AutocompleteEditModel* edit_model = location_bar_->location_entry()->model(); if (edit_model->CurrentTextIsURL()) { text = l10n_util::GetStringFUTF8(IDS_TOOLTIP_GO_SITE, current_text); } else { std::wstring keyword(edit_model->keyword()); TemplateURLModel* template_url_model = browser_->profile()->GetTemplateURLModel(); const TemplateURL* provider = (keyword.empty() || edit_model->is_keyword_hint()) ? template_url_model->GetDefaultSearchProvider() : template_url_model->GetTemplateURLForKeyword(keyword); if (!provider) return FALSE; // Don't show a tooltip. text = l10n_util::GetStringFUTF8(IDS_TOOLTIP_GO_SEARCH, WideToUTF16Hack(provider->AdjustedShortNameForLocaleDirection()), current_text); } } else { text = l10n_util::GetStringUTF8(IDS_TOOLTIP_STOP); } gtk_tooltip_set_text(tooltip, text.c_str()); return TRUE; } void GoButtonGtk::UpdateThemeButtons() { bool use_gtk = theme_provider_ && theme_provider_->UseGtkTheme(); if (use_gtk) { GdkPixbuf* pixbuf = NULL; if (intended_mode_ == MODE_GO) { pixbuf = theme_provider_->GetPixbufNamed(IDR_GO_NOBORDER_CENTER); } else { pixbuf = theme_provider_->GetPixbufNamed(IDR_STOP_NOBORDER_CENTER); } gtk_button_set_image( GTK_BUTTON(widget_.get()), gtk_image_new_from_pixbuf(pixbuf)); gtk_widget_set_size_request(widget_.get(), -1, -1); gtk_widget_set_app_paintable(widget_.get(), FALSE); gtk_widget_set_double_buffered(widget_.get(), TRUE); } else { gtk_widget_set_size_request(widget_.get(), go_.Width(), go_.Height()); gtk_widget_set_app_paintable(widget_.get(), TRUE); // We effectively double-buffer by virtue of having only one image... gtk_widget_set_double_buffered(widget_.get(), FALSE); } gtk_chrome_button_set_use_gtk_rendering( GTK_CHROME_BUTTON(widget_.get()), use_gtk); }