blob: 92bbb15f879fbf9890d9027caf3dba6f7285f24b [file] [log] [blame]
// 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/base/gtk/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