blob: 27ef1a26e2f552cb3cb4d0be9d497bc2961872ee [file] [log] [blame]
// 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 "ui/gfx/font.h"
#include "ui/gfx/screen.h"
#include "views/focus/focus_manager.h"
#include "views/view.h"
#include "views/widget/native_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(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 =
gfx::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(NativeWidgetGtk* 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) {
View* root_view = widget_->GetWidget()->GetRootView();
view = root_view->GetEventHandlerForPoint(gfx::Point(x, y));
view_loc.SetPoint(x, y);
View::ConvertPointFromWidget(view, &view_loc);
} else {
FocusManager* focus_manager = widget_->GetWidget()->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_->GetWidget()->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