root/chrome/browser/ui/gtk/gtk_chrome_link_button.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. SetLinkButtonStyle
  2. gtk_chrome_link_button_destroy_text_resources
  3. G_DEFINE_TYPE
  4. gtk_chrome_link_button_style_changed
  5. gtk_chrome_link_button_expose
  6. gtk_chrome_link_button_enter
  7. gtk_chrome_link_button_leave
  8. gtk_chrome_link_button_destroy
  9. gtk_chrome_link_button_class_init
  10. gtk_chrome_link_button_init
  11. gtk_chrome_link_button_new
  12. gtk_chrome_link_button_new_with_markup
  13. gtk_chrome_link_button_set_use_gtk_theme
  14. gtk_chrome_link_button_set_label
  15. gtk_chrome_link_button_set_normal_color

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"

#include <stdlib.h>

#include "chrome/browser/ui/gtk/gtk_util.h"
#include "ui/gfx/gtk_compat.h"
#include "ui/gfx/gtk_util.h"

static const gchar* kLinkMarkup = "<u><span color=\"%s\">%s</span></u>";
static const gchar* kInsensitiveLinkMarkup = "<span color=\"%s\">%s</span>";

namespace {

// Set the GTK style on our custom link button. We don't want any border around
// the link text.
void SetLinkButtonStyle() {
  static bool style_was_set = false;

  if (style_was_set)
    return;
  style_was_set = true;

  gtk_rc_parse_string(
      "style \"chrome-link-button\" {"
      "  GtkButton::inner-border = {0, 0, 0, 0}"
      "  GtkButton::child-displacement-x = 0"
      "  GtkButton::child-displacement-y = 0"
      "  xthickness = 0"
      "  ythickness = 0"
      "}"
      "widget_class \"*.<GtkChromeLinkButton>\" style \"chrome-link-button\"");
}

static void gtk_chrome_link_button_destroy_text_resources(
    GtkChromeLinkButton* button) {
  g_free(button->native_markup);
  button->native_markup = NULL;
  g_free(button->normal_markup);
  button->normal_markup = NULL;
  g_free(button->pressed_markup);
  button->pressed_markup = NULL;
  g_free(button->insensitive_markup);
  button->insensitive_markup = NULL;

  g_free(button->text);
  button->text = NULL;
}

}  // namespace

G_BEGIN_DECLS
G_DEFINE_TYPE(GtkChromeLinkButton, gtk_chrome_link_button, GTK_TYPE_BUTTON)

static void gtk_chrome_link_button_set_text(GtkChromeLinkButton* button) {
  // If we were called before we were realized, abort. We'll be called for
  // real when |button| is realized.
  if (!gtk_widget_get_realized(GTK_WIDGET(button)))
    return;

  g_free(button->native_markup);
  button->native_markup = NULL;
  g_free(button->normal_markup);
  button->normal_markup = NULL;
  g_free(button->pressed_markup);
  button->pressed_markup = NULL;
  g_free(button->insensitive_markup);
  button->insensitive_markup = NULL;

  gchar* text = button->text;
  gboolean uses_markup = button->uses_markup;

  GtkStyle* style = gtk_rc_get_style(button->label);
  GdkColor insensitive_color = style->fg[GTK_STATE_INSENSITIVE];
  gchar insensitive_color_spec[9];
  snprintf(insensitive_color_spec, 9, "#%02X%02X%02X",
           insensitive_color.red / 257, insensitive_color.green / 257,
           insensitive_color.blue / 257);

  if (!uses_markup) {
    button->normal_markup = g_markup_printf_escaped(kLinkMarkup,
                                                    button->normal_color,
                                                    text);
    button->pressed_markup = g_markup_printf_escaped(kLinkMarkup, "red", text);
    button->insensitive_markup = g_markup_printf_escaped(kInsensitiveLinkMarkup,
                                                         insensitive_color_spec,
                                                         text);
  } else {
    button->normal_markup = g_strdup_printf(kLinkMarkup, button->normal_color,
                                            text);

    button->pressed_markup = g_strdup_printf(kLinkMarkup, "red", text);
    button->insensitive_markup = g_strdup_printf(kInsensitiveLinkMarkup,
                                                 insensitive_color_spec,
                                                 text);
  }

  // Get the current GTK theme's link button text color.
  GdkColor* native_color = NULL;
  gtk_widget_style_get(GTK_WIDGET(button), "link-color", &native_color, NULL);

  if (native_color) {
    gchar color_spec[9];
    snprintf(color_spec, 9, "#%02X%02X%02X", native_color->red / 257,
             native_color->green / 257, native_color->blue / 257);
    gdk_color_free(native_color);

    if (!uses_markup) {
      button->native_markup = g_markup_printf_escaped(kLinkMarkup,
          color_spec, text);
    } else {
      button->native_markup = g_strdup_printf(kLinkMarkup, color_spec, text);
    }
  } else {
    // If the theme doesn't have a link color, just use blue. This matches the
    // default for GtkLinkButton.
    button->native_markup = g_strdup(button->normal_markup);
  }

  gtk_label_set_markup(GTK_LABEL(button->label),
      button->using_native_theme ? button->native_markup :
      button->normal_markup);
}

static void gtk_chrome_link_button_style_changed(GtkChromeLinkButton* button) {
  // Regenerate the link with the possibly new colors after the user has
  // changed his GTK style.
  gtk_chrome_link_button_set_text(button);

  if (gtk_widget_get_visible(GTK_WIDGET(button)))
    gtk_widget_queue_draw(GTK_WIDGET(button));
}

static gboolean gtk_chrome_link_button_expose(GtkWidget* widget,
                                              GdkEventExpose* event) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(widget);
  GtkWidget* label = button->label;
  GtkStateType widget_state = gtk_widget_get_state(widget);

  if (widget_state != button->label_state) {
    switch (widget_state) {
      case GTK_STATE_NORMAL:
        gtk_label_set_markup(GTK_LABEL(label),
            button->using_native_theme ? button->native_markup :
                                         button->normal_markup);
        break;
      case GTK_STATE_ACTIVE:
        gtk_label_set_markup(GTK_LABEL(label), button->pressed_markup);
        break;
      case GTK_STATE_INSENSITIVE:
        gtk_label_set_markup(GTK_LABEL(label), button->insensitive_markup);
        break;
      default:
        break;
    }
    button->label_state = widget_state;
  }

  // Draw the link inside the button.
  gtk_container_propagate_expose(GTK_CONTAINER(widget), label, event);

  // Draw the focus rectangle.
  if (gtk_widget_has_focus(widget)) {
    GtkAllocation allocation;
    gtk_widget_get_allocation(widget, &allocation);
    gtk_paint_focus(gtk_widget_get_style(widget),
                    gtk_widget_get_window(widget),
                    gtk_widget_get_state(widget),
                    &event->area, widget, NULL,
                    allocation.x, allocation.y,
                    allocation.width, allocation.height);
  }

  return TRUE;
}

static void gtk_chrome_link_button_enter(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  GtkChromeLinkButton* link_button = GTK_CHROME_LINK_BUTTON(button);
  gdk_window_set_cursor(gtk_widget_get_window(widget),
                        link_button->hand_cursor);
}

static void gtk_chrome_link_button_leave(GtkButton* button) {
  GtkWidget* widget = GTK_WIDGET(button);
  gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
}

static void gtk_chrome_link_button_destroy(GtkObject* object) {
  GtkChromeLinkButton* button = GTK_CHROME_LINK_BUTTON(object);

  gtk_chrome_link_button_destroy_text_resources(button);

  button->hand_cursor = NULL;

  GTK_OBJECT_CLASS(gtk_chrome_link_button_parent_class)->destroy(object);
}

static void gtk_chrome_link_button_class_init(
    GtkChromeLinkButtonClass* link_button_class) {
  GtkWidgetClass* widget_class =
      reinterpret_cast<GtkWidgetClass*>(link_button_class);
  GtkButtonClass* button_class =
      reinterpret_cast<GtkButtonClass*>(link_button_class);
  GtkObjectClass* object_class =
      reinterpret_cast<GtkObjectClass*>(link_button_class);
  widget_class->expose_event = &gtk_chrome_link_button_expose;
  button_class->enter = &gtk_chrome_link_button_enter;
  button_class->leave = &gtk_chrome_link_button_leave;
  object_class->destroy = &gtk_chrome_link_button_destroy;
}

static void gtk_chrome_link_button_init(GtkChromeLinkButton* button) {
  SetLinkButtonStyle();

  // We put a label in a button so we can connect to the click event. We don't
  // let the button draw itself; catch all expose events to the button and pass
  // them through to the label.
  button->label = gtk_label_new(NULL);
  button->normal_markup = NULL;
  button->pressed_markup = NULL;
  button->label_state = GTK_STATE_NORMAL;
  strncpy(button->normal_color, "blue", 9);
  button->native_markup = NULL;
  button->using_native_theme = TRUE;
  button->hand_cursor = gfx::GetCursor(GDK_HAND2);
  button->text = NULL;

  gtk_container_add(GTK_CONTAINER(button), button->label);
  gtk_widget_set_app_paintable(GTK_WIDGET(button), TRUE);
  g_signal_connect(button, "realize",
                   G_CALLBACK(gtk_chrome_link_button_set_text), NULL);
  g_signal_connect(button, "style-set",
                   G_CALLBACK(gtk_chrome_link_button_style_changed), NULL);
}

GtkWidget* gtk_chrome_link_button_new(const char* text) {
  GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL));
  GTK_CHROME_LINK_BUTTON(lb)->text = g_strdup(text);
  GTK_CHROME_LINK_BUTTON(lb)->uses_markup = FALSE;

  return lb;
}

GtkWidget* gtk_chrome_link_button_new_with_markup(const char* markup) {
  GtkWidget* lb = GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_LINK_BUTTON, NULL));
  GTK_CHROME_LINK_BUTTON(lb)->text = g_strdup(markup);
  GTK_CHROME_LINK_BUTTON(lb)->uses_markup = TRUE;

  return lb;
}

void gtk_chrome_link_button_set_use_gtk_theme(GtkChromeLinkButton* button,
                                              gboolean use_gtk) {
  if (use_gtk != button->using_native_theme) {
    button->using_native_theme = use_gtk;

    gtk_chrome_link_button_set_text(button);

    if (gtk_widget_get_visible(GTK_WIDGET(button)))
      gtk_widget_queue_draw(GTK_WIDGET(button));
  }
}

void gtk_chrome_link_button_set_label(GtkChromeLinkButton* button,
                                      const char* text) {
  g_free(button->text);
  button->text = g_strdup(text);

  gtk_chrome_link_button_set_text(button);

  if (gtk_widget_get_visible(GTK_WIDGET(button)))
    gtk_widget_queue_draw(GTK_WIDGET(button));
}

void gtk_chrome_link_button_set_normal_color(GtkChromeLinkButton* button,
                                             const GdkColor* color) {
  if (color) {
    snprintf(button->normal_color, 9, "#%02X%02X%02X", color->red / 257,
             color->green / 257, color->blue / 257);
  } else {
    strncpy(button->normal_color, "blue", 9);
  }

  gtk_chrome_link_button_set_text(button);

  if (gtk_widget_get_visible(GTK_WIDGET(button)))
    gtk_widget_queue_draw(GTK_WIDGET(button));
}

G_END_DECLS

/* [<][>][^][v][top][bottom][index][help] */