| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" | |
| 6 | |
| 7 #include <stdlib.h> | |
| 8 | |
| 9 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 10 #include "ui/gfx/gtk_compat.h" | |
| 11 #include "ui/gfx/gtk_util.h" | |
| 12 | |
| 13 static const gchar* kLinkMarkup = "<u><span color=\"%s\">%s</span></u>"; | |
| 14 static const gchar* kInsensitiveLinkMarkup = "<span color=\"%s\">%s</span>"; | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 // Set the GTK style on our custom link button. We don't want any border around | |
| 19 // the link text. | |
| 20 void SetLinkButtonStyle() { | |
| 21 static bool style_was_set = false; | |
| 22 | |
| 23 if (style_was_set) | |
| 24 return; | |
| 25 style_was_set = true; | |
| 26 | |
| 27 gtk_rc_parse_string( | |
| 28 "style \"chrome-link-button\" {" | |
| 29 " GtkButton::inner-border = {0, 0, 0, 0}" | |
| 30 " GtkButton::child-displacement-x = 0" | |
| 31 " GtkButton::child-displacement-y = 0" | |
| 32 " xthickness = 0" | |
| 33 " ythickness = 0" | |
| 34 "}" | |
| 35 "widget_class \"*.<GtkChromeLinkButton>\" style \"chrome-link-button\""); | |
| 36 } | |
| 37 | |
| 38 static void gtk_chrome_link_button_destroy_text_resources( | |
| 39 GtkChromeLinkButton* button) { | |
| 40 g_free(button->native_markup); | |
| 41 button->native_markup = NULL; | |
| 42 g_free(button->normal_markup); | |
| 43 button->normal_markup = NULL; | |
| 44 g_free(button->pressed_markup); | |
| 45 button->pressed_markup = NULL; | |
| 46 g_free(button->insensitive_markup); | |
| 47 button->insensitive_markup = NULL; | |
| 48 | |
| 49 g_free(button->text); | |
| 50 button->text = NULL; | |
| 51 } | |
| 52 | |
| 53 } // namespace | |
| 54 | |
| 55 G_BEGIN_DECLS | |
| 56 G_DEFINE_TYPE(GtkChromeLinkButton, gtk_chrome_link_button, GTK_TYPE_BUTTON) | |
| 57 | |
| 58 static void gtk_chrome_link_button_set_text(GtkChromeLinkButton* button) { | |
| 59 // If we were called before we were realized, abort. We'll be called for | |
| 60 // real when |button| is realized. | |
| 61 if (!gtk_widget_get_realized(GTK_WIDGET(button))) | |
| 62 return; | |
| 63 | |
| 64 g_free(button->native_markup); | |
| 65 button->native_markup = NULL; | |
| 66 g_free(button->normal_markup); | |
| 67 button->normal_markup = NULL; | |
| 68 g_free(button->pressed_markup); | |
| 69 button->pressed_markup = NULL; | |
| 70 g_free(button->insensitive_markup); | |
| 71 button->insensitive_markup = NULL; | |
| 72 | |
| 73 gchar* text = button->text; | |
| 74 gboolean uses_markup = button->uses_markup; | |
| 75 | |
| 76 GtkStyle* style = gtk_rc_get_style(button->label); | |
| 77 GdkColor insensitive_color = style->fg[GTK_STATE_INSENSITIVE]; | |
| 78 gchar insensitive_color_spec[9]; | |
| 79 snprintf(insensitive_color_spec, 9, "#%02X%02X%02X", | |
| 80 insensitive_color.red / 257, insensitive_color.green / 257, | |
| 81 insensitive_color.blue / 257); | |
| 82 | |
| 83 if (!uses_markup) { | |
| 84 button->normal_markup = g_markup_printf_escaped(kLinkMarkup, | |
| 85 button->normal_color, | |
| 86 text); | |
| 87 button->pressed_markup = g_markup_printf_escaped(kLinkMarkup, "red", text); | |
| 88 button->insensitive_markup = g_markup_printf_escaped(kInsensitiveLinkMarkup, | |
| 89 insensitive_color_spec, | |
| 90 text); | |
| 91 } else { | |
| 92 button->normal_markup = g_strdup_printf(kLinkMarkup, button->normal_color, | |
| 93 text); | |
| 94 | |
| 95 button->pressed_markup = g_strdup_printf(kLinkMarkup, "red", text); | |
| 96 button->insensitive_markup = g_strdup_printf(kInsensitiveLinkMarkup, | |
| 97 insensitive_color_spec, | |
| 98 text); | |
| 99 } | |
| 100 | |
| 101 // Get the current GTK theme's link button text color. | |
| 102 GdkColor* native_color = NULL; | |
| 103 gtk_widget_style_get(GTK_WIDGET(button), "link-color", &native_color, NULL); | |
| 104 | |
| 105 if (native_color) { | |
| 106 gchar color_spec[9]; | |
| 107 snprintf(color_spec, 9, "#%02X%02X%02X", native_color->red / 257, | |
| 108 native_color->green / 257, native_color->blue / 257); | |
| 109 gdk_color_free(native_color); | |
| 110 | |
| 111 if (!uses_markup) { | |
| 112 button->native_markup = g_markup_printf_escaped(kLinkMarkup, | |
| 113 color_spec, text); | |
| 114 } else { | |
| 115 button->native_markup = g_strdup_printf(kLinkMarkup, color_spec, text); | |
| 116 } | |
| 117 } else { | |
| 118 // If the theme doesn't have a link color, just use blue. This matches the | |
| 119 // default for GtkLinkButton. | |
| 120 button->native_markup = g_strdup(button->normal_markup); | |
| 121 } | |
| 122 | |
| 123 gtk_label_set_markup(GTK_LABEL(button->label), | |
| 124 button->using_native_theme ? button->native_markup : | |
| 125 button->normal_markup); | |
| 126 } | |
| 127 | |
| 128 static void gtk_chrome_link_button_style_changed(GtkChromeLinkButton* button) { | |
| 129 // Regenerate the link with the possibly new colors after the user has | |
| 130 // changed his GTK style. | |
| 131 gtk_chrome_link_button_set_text(button); | |
| 132 | |
| 133 if (gtk_widget_get_visible(GTK_WIDGET(button))) | |
| 134 gtk_widget_queue_draw(GTK_WIDGET(button)); | |
| 135 } | |
| 136 | |
| 137 static gboolean gtk_chrome_link_button_expose(GtkWidget* widget, | |
| 138 GdkEventExpose* event) { | |
| 139 GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(widget); | |
| 140 GtkWidget* label = button->label; | |
| 141 GtkStateType widget_state = gtk_widget_get_state(widget); | |
| 142 | |
| 143 if (widget_state != button->label_state) { | |
| 144 switch (widget_state) { | |
| 145 case GTK_STATE_NORMAL: | |
| 146 gtk_label_set_markup(GTK_LABEL(label), | |
| 147 button->using_native_theme ? button->native_markup : | |
| 148 button->normal_markup); | |
| 149 break; | |
| 150 case GTK_STATE_ACTIVE: | |
| 151 gtk_label_set_markup(GTK_LABEL(label), button->pressed_markup); | |
| 152 break; | |
| 153 case GTK_STATE_INSENSITIVE: | |
| 154 gtk_label_set_markup(GTK_LABEL(label), button->insensitive_markup); | |
| 155 break; | |
| 156 default: | |
| 157 break; | |
| 158 } | |
| 159 button->label_state = widget_state; | |
| 160 } | |
| 161 | |
| 162 // Draw the link inside the button. | |
| 163 gtk_container_propagate_expose(GTK_CONTAINER(widget), label, event); | |
| 164 | |
| 165 // Draw the focus rectangle. | |
| 166 if (gtk_widget_has_focus(widget)) { | |
| 167 GtkAllocation allocation; | |
| 168 gtk_widget_get_allocation(widget, &allocation); | |
| 169 gtk_paint_focus(gtk_widget_get_style(widget), | |
| 170 gtk_widget_get_window(widget), | |
| 171 gtk_widget_get_state(widget), | |
| 172 &event->area, widget, NULL, | |
| 173 allocation.x, allocation.y, | |
| 174 allocation.width, allocation.height); | |
| 175 } | |
| 176 | |
| 177 return TRUE; | |
| 178 } | |
| 179 | |
| 180 static void gtk_chrome_link_button_enter(GtkButton* button) { | |
| 181 GtkWidget* widget = GTK_WIDGET(button); | |
| 182 GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button); | |
| 183 gdk_window_set_cursor(gtk_widget_get_window(widget), | |
| 184 link_button->hand_cursor); | |
| 185 } | |
| 186 | |
| 187 static void gtk_chrome_link_button_leave(GtkButton* button) { | |
| 188 GtkWidget* widget = GTK_WIDGET(button); | |
| 189 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL); | |
| 190 } | |
| 191 | |
| 192 static void gtk_chrome_link_button_destroy(GtkObject* object) { | |
| 193 GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(object); | |
| 194 | |
| 195 gtk_chrome_link_button_destroy_text_resources(button); | |
| 196 | |
| 197 button->hand_cursor = NULL; | |
| 198 | |
| 199 GTK_OBJECT_CLASS(gtk_chrome_link_button_parent_class)->destroy(object); | |
| 200 } | |
| 201 | |
| 202 static void gtk_chrome_link_button_class_init( | |
| 203 GtkChromeLinkButtonClass* link_button_class) { | |
| 204 GtkWidgetClass* widget_class = | |
| 205 reinterpret_cast<GtkWidgetClass*>(link_button_class); | |
| 206 GtkButtonClass* button_class = | |
| 207 reinterpret_cast<GtkButtonClass*>(link_button_class); | |
| 208 GtkObjectClass* object_class = | |
| 209 reinterpret_cast<GtkObjectClass*>(link_button_class); | |
| 210 widget_class->expose_event = >k_chrome_link_button_expose; | |
| 211 button_class->enter = >k_chrome_link_button_enter; | |
| 212 button_class->leave = >k_chrome_link_button_leave; | |
| 213 object_class->destroy = >k_chrome_link_button_destroy; | |
| 214 } | |
| 215 | |
| 216 static void gtk_chrome_link_button_init(GtkChromeLinkButton* button) { | |
| 217 SetLinkButtonStyle(); | |
| 218 | |
| 219 // We put a label in a button so we can connect to the click event. We don't | |
| 220 // let the button draw itself; catch all expose events to the button and pass | |
| 221 // them through to the label. | |
| 222 button->label = gtk_label_new(NULL); | |
| 223 button->normal_markup = NULL; | |
| 224 button->pressed_markup = NULL; | |
| 225 button->label_state = GTK_STATE_NORMAL; | |
| 226 strncpy(button->normal_color, "blue", 9); | |
| 227 button->native_markup = NULL; | |
| 228 button->using_native_theme = TRUE; | |
| 229 button->hand_cursor = gfx::GetCursor(GDK_HAND2); | |
| 230 button->text = NULL; | |
| 231 | |
| 232 gtk_container_add(GTK_CONTAINER(button), button->label); | |
| 233 gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE); | |
| 234 g_signal_connect(button, "realize", | |
| 235 G_CALLBACK(gtk_chrome_link_button_set_text), NULL); | |
| 236 g_signal_connect(button, "style-set", | |
| 237 G_CALLBACK(gtk_chrome_link_button_style_changed), NULL); | |
| 238 } | |
| 239 | |
| 240 GtkWidget* gtk_chrome_link_button_new(const char* text) { | |
| 241 GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL)); | |
| 242 GTK_CHROME_LINK_BUTTON(lb)->text = g_strdup(text); | |
| 243 GTK_CHROME_LINK_BUTTON(lb)->uses_markup = FALSE; | |
| 244 | |
| 245 return lb; | |
| 246 } | |
| 247 | |
| 248 GtkWidget* gtk_chrome_link_button_new_with_markup(const char* markup) { | |
| 249 GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL)); | |
| 250 GTK_CHROME_LINK_BUTTON(lb)->text = g_strdup(markup); | |
| 251 GTK_CHROME_LINK_BUTTON(lb)->uses_markup = TRUE; | |
| 252 | |
| 253 return lb; | |
| 254 } | |
| 255 | |
| 256 void gtk_chrome_link_button_set_use_gtk_theme(GtkChromeLinkButton* button, | |
| 257 gboolean use_gtk) { | |
| 258 if (use_gtk != button->using_native_theme) { | |
| 259 button->using_native_theme = use_gtk; | |
| 260 | |
| 261 gtk_chrome_link_button_set_text(button); | |
| 262 | |
| 263 if (gtk_widget_get_visible(GTK_WIDGET(button))) | |
| 264 gtk_widget_queue_draw(GTK_WIDGET(button)); | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 void gtk_chrome_link_button_set_label(GtkChromeLinkButton* button, | |
| 269 const char* text) { | |
| 270 g_free(button->text); | |
| 271 button->text = g_strdup(text); | |
| 272 | |
| 273 gtk_chrome_link_button_set_text(button); | |
| 274 | |
| 275 if (gtk_widget_get_visible(GTK_WIDGET(button))) | |
| 276 gtk_widget_queue_draw(GTK_WIDGET(button)); | |
| 277 } | |
| 278 | |
| 279 void gtk_chrome_link_button_set_normal_color(GtkChromeLinkButton* button, | |
| 280 const GdkColor* color) { | |
| 281 if (color) { | |
| 282 snprintf(button->normal_color, 9, "#%02X%02X%02X", color->red / 257, | |
| 283 color->green / 257, color->blue / 257); | |
| 284 } else { | |
| 285 strncpy(button->normal_color, "blue", 9); | |
| 286 } | |
| 287 | |
| 288 gtk_chrome_link_button_set_text(button); | |
| 289 | |
| 290 if (gtk_widget_get_visible(GTK_WIDGET(button))) | |
| 291 gtk_widget_queue_draw(GTK_WIDGET(button)); | |
| 292 } | |
| 293 | |
| 294 G_END_DECLS | |
| OLD | NEW |