• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
SetLinkButtonStyle()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 
gtk_chrome_link_button_destroy_text_resources(GtkChromeLinkButton * button)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
G_DEFINE_TYPE(GtkChromeLinkButton,gtk_chrome_link_button,GTK_TYPE_BUTTON)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 
gtk_chrome_link_button_style_changed(GtkChromeLinkButton * button)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 
gtk_chrome_link_button_expose(GtkWidget * widget,GdkEventExpose * event)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 
gtk_chrome_link_button_enter(GtkButton * button)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 
gtk_chrome_link_button_leave(GtkButton * button)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 
gtk_chrome_link_button_destroy(GtkObject * object)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 
gtk_chrome_link_button_class_init(GtkChromeLinkButtonClass * link_button_class)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 = &gtk_chrome_link_button_expose;
179   button_class->enter = &gtk_chrome_link_button_enter;
180   button_class->leave = &gtk_chrome_link_button_leave;
181   object_class->destroy = &gtk_chrome_link_button_destroy;
182 }
183 
gtk_chrome_link_button_init(GtkChromeLinkButton * button)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 
gtk_chrome_link_button_new(const char * text)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 
gtk_chrome_link_button_new_with_markup(const char * markup)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 
gtk_chrome_link_button_set_use_gtk_theme(GtkChromeLinkButton * button,gboolean use_gtk)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 
gtk_chrome_link_button_set_label(GtkChromeLinkButton * button,const char * text)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 
gtk_chrome_link_button_set_normal_color(GtkChromeLinkButton * button,const GdkColor * color)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