Home | History | Annotate | Download | only in gtk
      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 = &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 
    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