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.
5 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
9 #include "chrome/browser/ui/gtk/gtk_util.h"
10 #include "ui/gfx/gtk_compat.h"
11 #include "ui/gfx/gtk_util.h"
13 static const gchar
* kLinkMarkup
= "<u><span color=\"%s\">%s</span></u>";
14 static const gchar
* kInsensitiveLinkMarkup
= "<span color=\"%s\">%s</span>";
18 // Set the GTK style on our custom link button. We don't want any border around
20 void SetLinkButtonStyle() {
21 static bool style_was_set
= false;
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"
35 "widget_class \"*.<GtkChromeLinkButton>\" style \"chrome-link-button\"");
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
;
56 G_DEFINE_TYPE(GtkChromeLinkButton
, gtk_chrome_link_button
, GTK_TYPE_BUTTON
)
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
)))
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
;
73 gchar
* text
= button
->text
;
74 gboolean uses_markup
= button
->uses_markup
;
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);
84 button
->normal_markup
= g_markup_printf_escaped(kLinkMarkup
,
87 button
->pressed_markup
= g_markup_printf_escaped(kLinkMarkup
, "red", text
);
88 button
->insensitive_markup
= g_markup_printf_escaped(kInsensitiveLinkMarkup
,
89 insensitive_color_spec
,
92 button
->normal_markup
= g_strdup_printf(kLinkMarkup
, button
->normal_color
,
95 button
->pressed_markup
= g_strdup_printf(kLinkMarkup
, "red", text
);
96 button
->insensitive_markup
= g_strdup_printf(kInsensitiveLinkMarkup
,
97 insensitive_color_spec
,
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
);
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
);
112 button
->native_markup
= g_markup_printf_escaped(kLinkMarkup
,
115 button
->native_markup
= g_strdup_printf(kLinkMarkup
, color_spec
, text
);
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
);
123 gtk_label_set_markup(GTK_LABEL(button
->label
),
124 button
->using_native_theme
? button
->native_markup
:
125 button
->normal_markup
);
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
);
133 if (gtk_widget_get_visible(GTK_WIDGET(button
)))
134 gtk_widget_queue_draw(GTK_WIDGET(button
));
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
);
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
);
150 case GTK_STATE_ACTIVE
:
151 gtk_label_set_markup(GTK_LABEL(label
), button
->pressed_markup
);
153 case GTK_STATE_INSENSITIVE
:
154 gtk_label_set_markup(GTK_LABEL(label
), button
->insensitive_markup
);
159 button
->label_state
= widget_state
;
162 // Draw the link inside the button.
163 gtk_container_propagate_expose(GTK_CONTAINER(widget
), label
, event
);
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
);
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
);
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
);
192 static void gtk_chrome_link_button_destroy(GtkObject
* object
) {
193 GtkChromeLinkButton
* button
= GTK_CHROME_LINK_BUTTON(object
);
195 gtk_chrome_link_button_destroy_text_resources(button
);
197 button
->hand_cursor
= NULL
;
199 GTK_OBJECT_CLASS(gtk_chrome_link_button_parent_class
)->destroy(object
);
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
;
216 static void gtk_chrome_link_button_init(GtkChromeLinkButton
* button
) {
217 SetLinkButtonStyle();
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
);
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
);
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
;
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
;
256 void gtk_chrome_link_button_set_use_gtk_theme(GtkChromeLinkButton
* button
,
258 if (use_gtk
!= button
->using_native_theme
) {
259 button
->using_native_theme
= use_gtk
;
261 gtk_chrome_link_button_set_text(button
);
263 if (gtk_widget_get_visible(GTK_WIDGET(button
)))
264 gtk_widget_queue_draw(GTK_WIDGET(button
));
268 void gtk_chrome_link_button_set_label(GtkChromeLinkButton
* button
,
270 g_free(button
->text
);
271 button
->text
= g_strdup(text
);
273 gtk_chrome_link_button_set_text(button
);
275 if (gtk_widget_get_visible(GTK_WIDGET(button
)))
276 gtk_widget_queue_draw(GTK_WIDGET(button
));
279 void gtk_chrome_link_button_set_normal_color(GtkChromeLinkButton
* button
,
280 const GdkColor
* color
) {
282 snprintf(button
->normal_color
, 9, "#%02X%02X%02X", color
->red
/ 257,
283 color
->green
/ 257, color
->blue
/ 257);
285 strncpy(button
->normal_color
, "blue", 9);
288 gtk_chrome_link_button_set_text(button
);
290 if (gtk_widget_get_visible(GTK_WIDGET(button
)))
291 gtk_widget_queue_draw(GTK_WIDGET(button
));