Home | History | Annotate | Download | only in gtk
      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 = &gtk_chrome_link_button_expose;
    211   button_class->enter = &gtk_chrome_link_button_enter;
    212   button_class->leave = &gtk_chrome_link_button_leave;
    213   object_class->destroy = &gtk_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