// Copyright (c) 2010 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 "views/widget/tooltip_manager_gtk.h" #include "base/logging.h" #include "base/utf_string_conversions.h" #include "gfx/font.h" #include "views/focus/focus_manager.h" #include "views/screen.h" #include "views/widget/root_view.h" #include "views/widget/widget_gtk.h" // WARNING: this implementation is good for a start, but it doesn't give us // control of tooltip positioning both on mouse events and when showing from // keyboard. We may need to write our own to give us the control we need. namespace views { static gfx::Font* LoadDefaultFont() { // Create a tooltip widget and extract the font from it (we have to realize // it to make sure the correct font gets set). GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP); gtk_widget_set_name(window, "gtk-tooltip"); GtkWidget* label = gtk_label_new(""); gtk_widget_show(label); gtk_container_add(GTK_CONTAINER(window), label); gtk_widget_realize(window); GtkStyle* style = gtk_widget_get_style(label); gfx::Font* font = new gfx::Font(gfx::Font::CreateFont(style->font_desc)); gtk_widget_destroy(window); return font; } // static int TooltipManager::GetTooltipHeight() { // This is only used to position the tooltip, and we don't yet support // positioning the tooltip, it isn't worth trying to implement this. return 0; } // static gfx::Font TooltipManager::GetDefaultFont() { static gfx::Font* font = NULL; if (!font) font = LoadDefaultFont(); return *font; } // static const std::wstring& TooltipManager::GetLineSeparator() { static std::wstring* line_separator = NULL; if (!line_separator) line_separator = new std::wstring(L"\n"); return *line_separator; } // static int TooltipManager::GetMaxWidth(int x, int y) { gfx::Rect monitor_bounds = Screen::GetMonitorAreaNearestPoint(gfx::Point(x, y)); // GtkLabel (gtk_label_ensure_layout) forces wrapping at this size. We mirror // the size here otherwise tooltips wider than the size used by gtklabel end // up with extraneous empty lines. return monitor_bounds.width() == 0 ? 800 : (monitor_bounds.width() + 1) / 2; } TooltipManagerGtk::TooltipManagerGtk(WidgetGtk* widget) : widget_(widget), keyboard_view_(NULL), tooltip_window_(widget->window_contents()) { } bool TooltipManagerGtk::ShowTooltip(int x, int y, bool for_keyboard, GtkTooltip* tooltip) { View* view = NULL; gfx::Point view_loc; if (keyboard_view_) { view = keyboard_view_; view_loc.SetPoint(view->width() / 2, view->height() / 2); } else if (!for_keyboard) { RootView* root_view = widget_->GetRootView(); view = root_view->GetViewForPoint(gfx::Point(x, y)); view_loc.SetPoint(x, y); View::ConvertPointFromWidget(view, &view_loc); } else { FocusManager* focus_manager = widget_->GetFocusManager(); if (focus_manager) { view = focus_manager->GetFocusedView(); if (view) view_loc.SetPoint(view->width() / 2, view->height() / 2); } } if (!view) return false; std::wstring text; if (!view->GetTooltipText(view_loc, &text)) return false; // Sets the area of the tooltip. This way if different views in the same // widget have tooltips the tooltip doesn't get stuck at the same location. gfx::Rect vis_bounds = view->GetVisibleBounds(); gfx::Point widget_loc(vis_bounds.origin()); View::ConvertPointToWidget(view, &widget_loc); GdkRectangle tip_area = { widget_loc.x(), widget_loc.y(), vis_bounds.width(), vis_bounds.height() }; gtk_tooltip_set_tip_area(tooltip, &tip_area); int max_width, line_count; gfx::Point screen_loc(x, y); View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); TrimTooltipToFit(&text, &max_width, &line_count, screen_loc.x(), screen_loc.y()); tooltip_window_.SetTooltipText(text); return true; } void TooltipManagerGtk::UpdateTooltip() { // UpdateTooltip may be invoked after the widget has been destroyed. GtkWidget* widget = widget_->GetNativeView(); if (!widget) return; GdkDisplay* display = gtk_widget_get_display(widget); if (display) gtk_tooltip_trigger_tooltip_query(display); } void TooltipManagerGtk::TooltipTextChanged(View* view) { UpdateTooltip(); } void TooltipManagerGtk::ShowKeyboardTooltip(View* view) { if (view == keyboard_view_) return; // We're already showing the tip for the specified view. // We have to hide the current tooltip, then show again. HideKeyboardTooltip(); std::wstring tooltip_text; if (!view->GetTooltipText(gfx::Point(), &tooltip_text)) return; // The view doesn't have a tooltip, nothing to do. keyboard_view_ = view; if (!SendShowHelpSignal()) { keyboard_view_ = NULL; return; } } void TooltipManagerGtk::HideKeyboardTooltip() { if (!keyboard_view_) return; SendShowHelpSignal(); keyboard_view_ = NULL; } bool TooltipManagerGtk::SendShowHelpSignal() { GtkWidget* widget = widget_->window_contents(); GType itype = G_TYPE_FROM_INSTANCE(G_OBJECT(widget)); guint signal_id; GQuark detail; if (!g_signal_parse_name("show_help", itype, &signal_id, &detail, FALSE)) { NOTREACHED(); return false; } gboolean result; g_signal_emit(widget, signal_id, 0, GTK_WIDGET_HELP_TOOLTIP, &result); return true; } } // namespace views