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/base/gtk/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 295