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