OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 "views/widget/tooltip_manager_gtk.h" | 5 #include "views/widget/tooltip_manager_gtk.h" |
6 | 6 |
7 #include "app/gfx/font.h" | 7 #include "app/gfx/font.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/string_util.h" |
| 10 #include "views/focus/focus_manager.h" |
| 11 #include "views/widget/root_view.h" |
| 12 #include "views/widget/widget_gtk.h" |
| 13 |
| 14 // WARNING: this implementation is good for a start, but it doesn't give us |
| 15 // control of tooltip positioning both on mouse events and when showing from |
| 16 // keyboard. We may need to write our own to give us the control we need. |
9 | 17 |
10 namespace views { | 18 namespace views { |
11 | 19 |
| 20 static gfx::Font* LoadDefaultFont() { |
| 21 // Create a tooltip widget and extract the font from it (we have to realize |
| 22 // it to make sure the correct font gets set). |
| 23 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP); |
| 24 gtk_widget_set_name(window, "gtk-tooltip"); |
| 25 GtkWidget* label = gtk_label_new(""); |
| 26 gtk_widget_show(label); |
| 27 |
| 28 gtk_container_add(GTK_CONTAINER(window), label); |
| 29 gtk_widget_realize(window); |
| 30 |
| 31 GtkStyle* style = gtk_widget_get_style(label); |
| 32 PangoFontDescription* pfd = style->font_desc; |
| 33 gfx::Font* font = new gfx::Font(gfx::Font::CreateFont(pfd)); |
| 34 pango_font_description_free(pfd); |
| 35 |
| 36 gtk_widget_destroy(window); |
| 37 |
| 38 return font; |
| 39 } |
| 40 |
12 // static | 41 // static |
13 int TooltipManager::GetTooltipHeight() { | 42 int TooltipManager::GetTooltipHeight() { |
14 NOTIMPLEMENTED(); | 43 // This is only used to position the tooltip, and we don't yet support |
| 44 // positioning the tooltip, it isn't worth trying to implement this. |
15 return 0; | 45 return 0; |
16 } | 46 } |
17 | 47 |
18 // static | 48 // static |
19 gfx::Font TooltipManager::GetDefaultFont() { | 49 gfx::Font TooltipManager::GetDefaultFont() { |
20 NOTIMPLEMENTED(); | 50 static gfx::Font* font = NULL; |
21 return gfx::Font(); | 51 if (!font) |
| 52 font = LoadDefaultFont(); |
| 53 |
| 54 return *font; |
22 } | 55 } |
23 | 56 |
24 // static | 57 // static |
25 const std::wstring& TooltipManager::GetLineSeparator() { | 58 const std::wstring& TooltipManager::GetLineSeparator() { |
26 static std::wstring* line_separator = NULL; | 59 static std::wstring* line_separator = NULL; |
27 if (!line_separator) | 60 if (!line_separator) |
28 line_separator = new std::wstring(L"\n"); | 61 line_separator = new std::wstring(L"\n"); |
29 return *line_separator; | 62 return *line_separator; |
30 } | 63 } |
31 | 64 |
32 TooltipManagerGtk::TooltipManagerGtk(Widget* widget) : widget_(widget) { | 65 // Callback from gtk_container_foreach. If |*label_p| is NULL and |widget| is |
| 66 // a GtkLabel, |*label_p| is set to |widget|. Used to find the first GtkLabel |
| 67 // in a container. |
| 68 static void LabelLocatorCallback(GtkWidget* widget, |
| 69 gpointer label_p) { |
| 70 GtkWidget** label = static_cast<GtkWidget**>(label_p); |
| 71 if (!*label && GTK_IS_LABEL(widget)) |
| 72 *label = widget; |
| 73 } |
| 74 |
| 75 // By default GtkTooltip wraps at a longish string. We want more control over |
| 76 // that wrapping. The only way to do that is dig out the label and set |
| 77 // gtk_label_set_max_width_chars, which is what this code does. I also tried |
| 78 // setting a custom widget on the tooltip, but there is a bug in Gtk that |
| 79 // triggers continually hiding/showing the widget in that case. |
| 80 static void AdjustLabel(GtkTooltip* tooltip) { |
| 81 static const char kAdjustedLabelPropertyValue[] = "_adjusted_label_"; |
| 82 gpointer adjusted_value = g_object_get_data(G_OBJECT(tooltip), |
| 83 kAdjustedLabelPropertyValue); |
| 84 if (adjusted_value) |
| 85 return; |
| 86 |
| 87 adjusted_value = reinterpret_cast<gpointer>(1); |
| 88 g_object_set_data(G_OBJECT(tooltip), kAdjustedLabelPropertyValue, |
| 89 adjusted_value); |
| 90 |
| 91 GtkWidget* parent; |
| 92 { |
| 93 // Create a label so that we can get the parent. The Tooltip ends up taking |
| 94 // ownership of the label and deleting it. |
| 95 GtkWidget* label = gtk_label_new(""); |
| 96 gtk_tooltip_set_custom(tooltip, label); |
| 97 parent = gtk_widget_get_parent(label); |
| 98 gtk_tooltip_set_custom(tooltip, NULL); |
| 99 } |
| 100 if (parent) { |
| 101 // We found the parent, find the first label, which is where the tooltip |
| 102 // text ends up going. |
| 103 GtkLabel* real_label = NULL; |
| 104 gtk_container_foreach(GTK_CONTAINER(parent), LabelLocatorCallback, |
| 105 static_cast<gpointer>(&real_label)); |
| 106 if (real_label) |
| 107 gtk_label_set_max_width_chars(GTK_LABEL(real_label), 3000); |
| 108 } |
| 109 } |
| 110 |
| 111 TooltipManagerGtk::TooltipManagerGtk(WidgetGtk* widget) |
| 112 : widget_(widget), |
| 113 keyboard_view_(NULL) { |
| 114 } |
| 115 |
| 116 bool TooltipManagerGtk::ShowTooltip(int x, int y, bool for_keyboard, |
| 117 GtkTooltip* tooltip) { |
| 118 View* view = NULL; |
| 119 gfx::Point view_loc; |
| 120 if (keyboard_view_) { |
| 121 view = keyboard_view_; |
| 122 view_loc.SetPoint(view->width() / 2, view->height() / 2); |
| 123 } else if (!for_keyboard) { |
| 124 RootView* root_view = widget_->GetRootView(); |
| 125 view = root_view->GetViewForPoint(gfx::Point(x, y)); |
| 126 view_loc.SetPoint(x, y); |
| 127 View::ConvertPointFromWidget(view, &view_loc); |
| 128 } else { |
| 129 FocusManager* focus_manager = widget_->GetFocusManager(); |
| 130 if (focus_manager) { |
| 131 view = focus_manager->GetFocusedView(); |
| 132 if (view) |
| 133 view_loc.SetPoint(view->width() / 2, view->height() / 2); |
| 134 } |
| 135 } |
| 136 |
| 137 if (!view) |
| 138 return false; |
| 139 |
| 140 std::wstring text; |
| 141 if (!view->GetTooltipText(view_loc.x(), view_loc.y(), &text)) |
| 142 return false; |
| 143 |
| 144 AdjustLabel(tooltip); |
| 145 |
| 146 // Sets the area of the tooltip. This way if different views in the same |
| 147 // widget have tooltips the tooltip doesn't get stuck at the same location. |
| 148 gfx::Rect vis_bounds = view->GetVisibleBounds(); |
| 149 gfx::Point widget_loc(vis_bounds.x(), vis_bounds.y()); |
| 150 View::ConvertPointToWidget(view, &widget_loc); |
| 151 GdkRectangle tip_area = { widget_loc.x(), widget_loc.y(), |
| 152 vis_bounds.width(), vis_bounds.height() }; |
| 153 gtk_tooltip_set_tip_area(tooltip, &tip_area); |
| 154 |
| 155 int max_width, line_count; |
| 156 gfx::Point screen_loc(x, y); |
| 157 View::ConvertPointToScreen(widget_->GetRootView(), &screen_loc); |
| 158 TrimTooltipToFit(&text, &max_width, &line_count, screen_loc.x(), |
| 159 screen_loc.y()); |
| 160 gtk_tooltip_set_text(tooltip, WideToUTF8(text).c_str()); |
| 161 |
| 162 return true; |
33 } | 163 } |
34 | 164 |
35 void TooltipManagerGtk::UpdateTooltip() { | 165 void TooltipManagerGtk::UpdateTooltip() { |
36 NOTIMPLEMENTED(); | 166 // UpdateTooltip may be invoked after the widget has been destroyed. |
| 167 GtkWidget* widget = widget_->GetNativeView(); |
| 168 if (!widget) |
| 169 return; |
| 170 |
| 171 GdkDisplay* display = gtk_widget_get_display(widget); |
| 172 if (display) |
| 173 gtk_tooltip_trigger_tooltip_query(display); |
37 } | 174 } |
38 | 175 |
39 void TooltipManagerGtk::TooltipTextChanged(View* view) { | 176 void TooltipManagerGtk::TooltipTextChanged(View* view) { |
40 NOTIMPLEMENTED(); | 177 UpdateTooltip(); |
41 } | 178 } |
42 | 179 |
43 void TooltipManagerGtk::ShowKeyboardTooltip(View* view) { | 180 void TooltipManagerGtk::ShowKeyboardTooltip(View* view) { |
44 NOTIMPLEMENTED(); | 181 if (view == keyboard_view_) |
| 182 return; // We're already showing the tip for the specified view. |
| 183 |
| 184 // We have to hide the current tooltip, then show again. |
| 185 HideKeyboardTooltip(); |
| 186 |
| 187 std::wstring tooltip_text; |
| 188 if (!view->GetTooltipText(0, 0, &tooltip_text)) |
| 189 return; // The view doesn't have a tooltip, nothing to do. |
| 190 |
| 191 keyboard_view_ = view; |
| 192 if (!SendShowHelpSignal()) { |
| 193 keyboard_view_ = NULL; |
| 194 return; |
| 195 } |
45 } | 196 } |
46 | 197 |
47 void TooltipManagerGtk::HideKeyboardTooltip() { | 198 void TooltipManagerGtk::HideKeyboardTooltip() { |
48 NOTIMPLEMENTED(); | 199 if (!keyboard_view_) |
| 200 return; |
| 201 |
| 202 SendShowHelpSignal(); |
| 203 keyboard_view_ = NULL; |
| 204 } |
| 205 |
| 206 bool TooltipManagerGtk::SendShowHelpSignal() { |
| 207 GtkWidget* widget = widget_->window_contents(); |
| 208 GType itype = G_TYPE_FROM_INSTANCE(G_OBJECT(widget)); |
| 209 guint signal_id; |
| 210 GQuark detail; |
| 211 if (!g_signal_parse_name("show_help", itype, &signal_id, &detail, FALSE)) { |
| 212 NOTREACHED(); |
| 213 return false; |
| 214 } |
| 215 gboolean result; |
| 216 g_signal_emit(widget, signal_id, 0, GTK_WIDGET_HELP_TOOLTIP, &result); |
| 217 return true; |
49 } | 218 } |
50 | 219 |
51 } // namespace views | 220 } // namespace views |
OLD | NEW |