/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright 2009-2010 Red Hat, Inc, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Written by: Matthias Clasen */ #include #include "um-editable-button.h" #define EMPTY_TEXT "\xe2\x80\x94" struct _UmEditableButtonPrivate { GtkStack *stack; GtkLabel *label; GtkButton *button; gchar *text; gboolean editable; gint weight; gboolean weight_set; gdouble scale; gboolean scale_set; }; #define UM_EDITABLE_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), UM_TYPE_EDITABLE_BUTTON, UmEditableButtonPrivate)) #define PAGE_LABEL "_label" #define PAGE_BUTTON "_button" enum { PROP_0, PROP_TEXT, PROP_EDITABLE, PROP_SCALE, PROP_SCALE_SET, PROP_WEIGHT, PROP_WEIGHT_SET }; enum { START_EDITING, ACTIVATE, LAST_SIGNAL }; static guint signals [LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (UmEditableButton, um_editable_button, GTK_TYPE_BIN); void um_editable_button_set_text (UmEditableButton *button, const gchar *text) { UmEditableButtonPrivate *priv; gchar *tmp; GtkWidget *label; priv = button->priv; tmp = g_strdup (text); g_free (priv->text); priv->text = tmp; if (tmp == NULL || tmp[0] == '\0') tmp = EMPTY_TEXT; gtk_label_set_text (priv->label, tmp); label = gtk_bin_get_child (GTK_BIN (priv->button)); gtk_label_set_text (GTK_LABEL (label), tmp); g_object_notify (G_OBJECT (button), "text"); } const gchar * um_editable_button_get_text (UmEditableButton *button) { return button->priv->text; } void um_editable_button_set_editable (UmEditableButton *button, gboolean editable) { UmEditableButtonPrivate *priv; priv = button->priv; if (priv->editable != editable) { priv->editable = editable; gtk_stack_set_visible_child_name (priv->stack, editable ? PAGE_BUTTON : PAGE_LABEL); g_object_notify (G_OBJECT (button), "editable"); } } gboolean um_editable_button_get_editable (UmEditableButton *button) { return button->priv->editable; } static void update_fonts (UmEditableButton *button) { PangoAttrList *attrs; PangoAttribute *attr; GtkWidget *label; UmEditableButtonPrivate *priv = button->priv; attrs = pango_attr_list_new (); if (priv->scale_set) { attr = pango_attr_scale_new (priv->scale); pango_attr_list_insert (attrs, attr); } if (priv->weight_set) { attr = pango_attr_weight_new (priv->weight); pango_attr_list_insert (attrs, attr); } gtk_label_set_attributes (priv->label, attrs); label = gtk_bin_get_child (GTK_BIN (priv->button)); gtk_label_set_attributes (GTK_LABEL (label), attrs); pango_attr_list_unref (attrs); } void um_editable_button_set_weight (UmEditableButton *button, gint weight) { UmEditableButtonPrivate *priv = button->priv; if (priv->weight == weight && priv->weight_set) return; priv->weight = weight; priv->weight_set = TRUE; update_fonts (button); g_object_notify (G_OBJECT (button), "weight"); g_object_notify (G_OBJECT (button), "weight-set"); } gint um_editable_button_get_weight (UmEditableButton *button) { return button->priv->weight; } void um_editable_button_set_scale (UmEditableButton *button, gdouble scale) { UmEditableButtonPrivate *priv = button->priv; if (priv->scale == scale && priv->scale_set) return; priv->scale = scale; priv->scale_set = TRUE; update_fonts (button); g_object_notify (G_OBJECT (button), "scale"); g_object_notify (G_OBJECT (button), "scale-set"); } gdouble um_editable_button_get_scale (UmEditableButton *button) { return button->priv->scale; } static void um_editable_button_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { UmEditableButton *button = UM_EDITABLE_BUTTON (object); switch (prop_id) { case PROP_TEXT: um_editable_button_set_text (button, g_value_get_string (value)); break; case PROP_EDITABLE: um_editable_button_set_editable (button, g_value_get_boolean (value)); break; case PROP_WEIGHT: um_editable_button_set_weight (button, g_value_get_int (value)); break; case PROP_WEIGHT_SET: button->priv->weight_set = g_value_get_boolean (value); break; case PROP_SCALE: um_editable_button_set_scale (button, g_value_get_double (value)); break; case PROP_SCALE_SET: button->priv->scale_set = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void um_editable_button_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { UmEditableButton *button = UM_EDITABLE_BUTTON (object); switch (prop_id) { case PROP_TEXT: g_value_set_string (value, um_editable_button_get_text (button)); break; case PROP_EDITABLE: g_value_set_boolean (value, um_editable_button_get_editable (button)); break; case PROP_WEIGHT: g_value_set_int (value, um_editable_button_get_weight (button)); break; case PROP_WEIGHT_SET: g_value_set_boolean (value, button->priv->weight_set); break; case PROP_SCALE: g_value_set_double (value, um_editable_button_get_scale (button)); break; case PROP_SCALE_SET: g_value_set_boolean (value, button->priv->scale_set); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void um_editable_button_finalize (GObject *object) { UmEditableButton *button = (UmEditableButton*)object; g_free (button->priv->text); G_OBJECT_CLASS (um_editable_button_parent_class)->finalize (object); } static void um_editable_button_activate (UmEditableButton *button); static void um_editable_button_class_init (UmEditableButtonClass *class) { GObjectClass *object_class; GtkWidgetClass *widget_class; object_class = G_OBJECT_CLASS (class); widget_class = GTK_WIDGET_CLASS (class); object_class->set_property = um_editable_button_set_property; object_class->get_property = um_editable_button_get_property; object_class->finalize = um_editable_button_finalize; signals[START_EDITING] = g_signal_new ("start-editing", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (UmEditableButtonClass, start_editing), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); signals[ACTIVATE] = g_signal_new ("activate", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (UmEditableButtonClass, activate), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); widget_class->activate_signal = signals[ACTIVATE]; class->activate = um_editable_button_activate; g_object_class_install_property (object_class, PROP_TEXT, g_param_spec_string ("text", "Text", "The text of the button", NULL, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_EDITABLE, g_param_spec_boolean ("editable", "Editable", "Whether the text can be edited", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_WEIGHT, g_param_spec_int ("weight", "Font Weight", "The font weight to use", 0, G_MAXINT, PANGO_WEIGHT_NORMAL, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_WEIGHT_SET, g_param_spec_boolean ("weight-set", "Font Weight Set", "Whether a font weight is set", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SCALE, g_param_spec_double ("scale", "Font Scale", "The font scale to use", 0.0, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SCALE_SET, g_param_spec_boolean ("scale-set", "Font Scale Set", "Whether a font scale is set", FALSE, G_PARAM_READWRITE)); g_type_class_add_private (class, sizeof (UmEditableButtonPrivate)); } static void start_editing (UmEditableButton *button) { g_signal_emit (button, signals[START_EDITING], 0); } static void um_editable_button_activate (UmEditableButton *button) { UmEditableButtonPrivate *priv = button->priv; if (priv->editable) { gtk_widget_grab_focus (GTK_WIDGET (button->priv->button)); } } static void button_clicked (GtkWidget *widget, UmEditableButton *button) { start_editing (button); } static void update_button_padding (UmEditableButton *button, gpointer user_data) { UmEditableButtonPrivate *priv = button->priv; GtkStyleContext *context; GtkStateFlags state; GtkBorder padding, border; gint offset; context = gtk_widget_get_style_context (GTK_WIDGET (priv->button)); state = gtk_style_context_get_state (context); gtk_style_context_get_padding (context, state, &padding); gtk_style_context_get_border (context, state, &border); offset = padding.left + border.left; gtk_widget_set_margin_start (GTK_WIDGET (priv->label), offset); gtk_widget_set_margin_end (GTK_WIDGET (priv->label), offset); } static void um_editable_button_init (UmEditableButton *button) { UmEditableButtonPrivate *priv; priv = button->priv = UM_EDITABLE_BUTTON_GET_PRIVATE (button); priv->weight = PANGO_WEIGHT_NORMAL; priv->weight_set = FALSE; priv->scale = 1.0; priv->scale_set = FALSE; priv->stack = GTK_STACK (gtk_stack_new ()); priv->label = (GtkLabel*)gtk_label_new (EMPTY_TEXT); gtk_widget_set_halign (GTK_WIDGET (priv->label), GTK_ALIGN_START); gtk_stack_add_named (priv->stack, GTK_WIDGET (priv->label), PAGE_LABEL); priv->button = (GtkButton*)gtk_button_new_with_label (EMPTY_TEXT); gtk_widget_set_receives_default ((GtkWidget*)priv->button, TRUE); gtk_button_set_relief (priv->button, GTK_RELIEF_NONE); gtk_widget_set_halign (GTK_WIDGET (priv->button), GTK_ALIGN_FILL); gtk_widget_set_halign (gtk_bin_get_child (GTK_BIN (priv->button)), GTK_ALIGN_START); gtk_stack_add_named (priv->stack, GTK_WIDGET (priv->button), PAGE_BUTTON); g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked), button); g_signal_connect (button, "style-updated", G_CALLBACK (update_button_padding), NULL); gtk_container_add (GTK_CONTAINER (button), GTK_WIDGET (priv->stack)); gtk_widget_show ((GtkWidget*)priv->stack); gtk_widget_show ((GtkWidget*)priv->label); gtk_widget_show ((GtkWidget*)priv->button); gtk_stack_set_visible_child_name (priv->stack, PAGE_LABEL); } GtkWidget * um_editable_button_new (void) { return (GtkWidget *) g_object_new (UM_TYPE_EDITABLE_BUTTON, NULL); }