* Mark McLoughlin <mark@skynet.ie>
*/
-#include <config.h>
-
+#include "config.h"
+#include <string.h>
#include "gtkexpander.h"
#include "gtklabel.h"
+#include "gtkbuildable.h"
#include "gtkcontainer.h"
#include "gtkmarshalers.h"
#include "gtkmain.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include <gdk/gdkkeysyms.h>
+#include "gtkdnd.h"
+#include "gtkalias.h"
#define GTK_EXPANDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_EXPANDER, GtkExpanderPrivate))
PROP_LABEL,
PROP_USE_UNDERLINE,
PROP_USE_MARKUP,
- PROP_PADDING,
+ PROP_SPACING,
PROP_LABEL_WIDGET
};
GtkExpanderStyle expander_style;
guint animation_timeout;
+ guint expand_timer;
guint expanded : 1;
guint use_underline : 1;
guint prelight : 1;
};
-static void gtk_expander_class_init (GtkExpanderClass *klass);
-static void gtk_expander_init (GtkExpander *expander);
-
static void gtk_expander_set_property (GObject *object,
guint prop_id,
const GValue *value,
gboolean was_grabbed);
static void gtk_expander_state_changed (GtkWidget *widget,
GtkStateType previous_state);
+static gboolean gtk_expander_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static void gtk_expander_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time);
static void gtk_expander_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_expander_activate (GtkExpander *expander);
-static GtkBinClass *parent_class = NULL;
+static void get_expander_bounds (GtkExpander *expander,
+ GdkRectangle *rect);
-GType
-gtk_expander_get_type (void)
-{
- static GType expander_type = 0;
-
- if (!expander_type)
- {
- static const GTypeInfo expander_info =
- {
- sizeof (GtkExpanderClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_expander_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkExpander),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_expander_init,
- };
-
- expander_type = g_type_register_static (GTK_TYPE_BIN,
- "GtkExpander",
- &expander_info, 0);
- }
-
- return expander_type;
-}
+/* GtkBuildable */
+static void gtk_expander_buildable_init (GtkBuildableIface *iface);
+static void gtk_expander_buildable_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type);
+
+G_DEFINE_TYPE_WITH_CODE (GtkExpander, gtk_expander, GTK_TYPE_BIN,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+ gtk_expander_buildable_init))
static void
gtk_expander_class_init (GtkExpanderClass *klass)
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
- parent_class = g_type_class_peek_parent (klass);
-
gobject_class = (GObjectClass *) klass;
object_class = (GtkObjectClass *) klass;
widget_class = (GtkWidgetClass *) klass;
widget_class->focus = gtk_expander_focus;
widget_class->grab_notify = gtk_expander_grab_notify;
widget_class->state_changed = gtk_expander_state_changed;
+ widget_class->drag_motion = gtk_expander_drag_motion;
+ widget_class->drag_leave = gtk_expander_drag_leave;
container_class->add = gtk_expander_add;
container_class->remove = gtk_expander_remove;
P_("Expanded"),
P_("Whether the expander has been opened to reveal the child widget"),
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class,
PROP_LABEL,
P_("Label"),
P_("Text of the expander's label"),
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class,
PROP_USE_UNDERLINE,
- g_param_spec_boolean ("use_underline",
+ g_param_spec_boolean ("use-underline",
P_("Use underline"),
P_("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class,
PROP_USE_MARKUP,
- g_param_spec_boolean ("use_markup",
+ g_param_spec_boolean ("use-markup",
P_("Use markup"),
P_("The text of the label includes XML markup. See pango_parse_markup()"),
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (gobject_class,
- PROP_PADDING,
+ PROP_SPACING,
g_param_spec_int ("spacing",
P_("Spacing"),
P_("Space to put between the label and the child"),
0,
G_MAXINT,
0,
- G_PARAM_READWRITE));
+ GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_LABEL_WIDGET,
- g_param_spec_object ("label_widget",
+ g_param_spec_object ("label-widget",
P_("Label widget"),
P_("A widget to display in place of the usual expander label"),
GTK_TYPE_WIDGET,
- G_PARAM_READWRITE));
+ GTK_PARAM_READWRITE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("expander-size",
0,
G_MAXINT,
DEFAULT_EXPANDER_SIZE,
- G_PARAM_READABLE));
+ GTK_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
g_param_spec_int ("expander-spacing",
0,
G_MAXINT,
DEFAULT_EXPANDER_SPACING,
- G_PARAM_READABLE));
+ GTK_PARAM_READABLE));
widget_class->activate_signal =
- g_signal_new ("activate",
+ g_signal_new (I_("activate"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkExpanderClass, activate),
priv->use_markup = FALSE;
priv->button_down = FALSE;
priv->prelight = FALSE;
+ priv->expand_timer = 0;
+
+ gtk_drag_dest_set (GTK_WIDGET (expander), 0, NULL, 0, 0);
+ gtk_drag_dest_set_track_motion (GTK_WIDGET (expander), TRUE);
+}
+
+static void
+gtk_expander_buildable_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
+{
+ if (!type)
+ gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
+ else if (strcmp (type, "label") == 0)
+ gtk_expander_set_label_widget (GTK_EXPANDER (buildable), GTK_WIDGET (child));
+ else
+ GTK_BUILDER_WARN_INVALID_CHILD_TYPE (GTK_EXPANDER (buildable), type);
+}
+
+static void
+gtk_expander_buildable_init (GtkBuildableIface *iface)
+{
+ iface->add_child = gtk_expander_buildable_add_child;
}
static void
case PROP_USE_MARKUP:
gtk_expander_set_use_markup (expander, g_value_get_boolean (value));
break;
- case PROP_PADDING:
+ case PROP_SPACING:
gtk_expander_set_spacing (expander, g_value_get_int (value));
break;
case PROP_LABEL_WIDGET:
case PROP_USE_MARKUP:
g_value_set_boolean (value, priv->use_markup);
break;
- case PROP_PADDING:
+ case PROP_SPACING:
g_value_set_int (value, priv->spacing);
break;
case PROP_LABEL_WIDGET:
priv->animation_timeout = 0;
}
- GTK_OBJECT_CLASS (parent_class)->destroy (object);
+ GTK_OBJECT_CLASS (gtk_expander_parent_class)->destroy (object);
}
static void
GdkWindowAttr attributes;
gint attributes_mask;
gint border_width;
+ GdkRectangle expander_rect;
+ gint label_height;
priv = GTK_EXPANDER (widget)->priv;
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
border_width = GTK_CONTAINER (widget)->border_width;
-
+
+ get_expander_bounds (GTK_EXPANDER (widget), &expander_rect);
+
+ if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
+ {
+ GtkRequisition label_requisition;
+
+ gtk_widget_get_child_requisition (priv->label_widget, &label_requisition);
+ label_height = label_requisition.height;
+ }
+ else
+ label_height = 0;
+
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x + border_width;
attributes.y = widget->allocation.y + border_width;
- attributes.width = widget->allocation.width - 2 * border_width;
- attributes.height = widget->allocation.height - 2 * border_width;
+ attributes.width = MAX (widget->allocation.width - 2 * border_width, 1);
+ attributes.height = MAX (expander_rect.height, label_height - 2 * border_width);
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_BUTTON_PRESS_MASK |
gdk_window_set_user_data (priv->event_window, widget);
widget->style = gtk_style_attach (widget->style, widget->window);
- gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}
static void
priv->event_window = NULL;
}
- GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
+ GTK_WIDGET_CLASS (gtk_expander_parent_class)->unrealize (widget);
}
static void
2 * focus_width + 2 * focus_pad;
requisition->height = interior_focus ? (2 * focus_width + 2 * focus_pad) : 0;
- if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
+ if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
{
GtkRequisition label_requisition;
GdkRectangle *rect)
{
GtkWidget *widget;
- GtkBin *bin;
GtkExpanderPrivate *priv;
gint border_width;
gint expander_size;
gboolean ltr;
widget = GTK_WIDGET (expander);
- bin = GTK_BIN (expander);
priv = expander->priv;
border_width = GTK_CONTAINER (expander)->border_width;
rect->x += widget->allocation.width - 2 * border_width -
expander_spacing - expander_size;
- if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
+ if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
{
GtkAllocation label_allocation;
widget->allocation = *allocation;
- if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
+ if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
{
GtkAllocation label_allocation;
GtkRequisition label_requisition;
ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
- label_allocation.x = widget->allocation.x + border_width + focus_width + focus_pad;
if (ltr)
- label_allocation.x += expander_size + 2 * expander_spacing;
+ label_allocation.x = (widget->allocation.x +
+ border_width + focus_width + focus_pad +
+ expander_size + 2 * expander_spacing);
+ else
+ label_allocation.x = (widget->allocation.x + widget->allocation.width -
+ (label_requisition.width +
+ border_width + focus_width + focus_pad +
+ expander_size + 2 * expander_spacing));
+
label_allocation.y = widget->allocation.y + border_width + focus_width + focus_pad;
label_allocation.width = MIN (label_requisition.width,
get_expander_bounds (expander, &rect);
gdk_window_move_resize (priv->event_window,
- allocation->x + border_width, rect.y,
- MAX (allocation->width - 2 * border_width, 1), rect.width);
+ allocation->x + border_width,
+ allocation->y + border_width,
+ MAX (allocation->width - 2 * border_width, 1),
+ MAX (rect.height, label_height - 2 * border_width));
}
if (child_visible)
if (priv->label_widget)
gtk_widget_map (priv->label_widget);
- GTK_WIDGET_CLASS (parent_class)->map (widget);
+ GTK_WIDGET_CLASS (gtk_expander_parent_class)->map (widget);
if (priv->event_window)
gdk_window_show (priv->event_window);
if (priv->event_window)
gdk_window_hide (priv->event_window);
- GTK_WIDGET_CLASS (parent_class)->unmap (widget);
+ GTK_WIDGET_CLASS (gtk_expander_parent_class)->unmap (widget);
if (priv->label_widget)
gtk_widget_unmap (priv->label_widget);
area.y = widget->allocation.y + container->border_width;
area.width = widget->allocation.width - (2 * container->border_width);
- if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
+ if (priv->label_widget && gtk_widget_get_visible (priv->label_widget))
area.height = priv->label_widget->allocation.height;
else
area.height = 0;
{
GtkWidget *widget;
GtkExpanderPrivate *priv;
+ GdkRectangle rect;
gint x, y, width, height;
gboolean interior_focus;
gint border_width;
ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
- x = widget->allocation.x + border_width;
- y = widget->allocation.y + border_width;
-
- if (ltr && interior_focus)
- x += expander_spacing * 2 + expander_size;
-
width = height = 0;
- if (priv->label_widget && GTK_WIDGET_VISIBLE (priv->label_widget))
+ if (priv->label_widget)
{
- GtkAllocation label_allocation = priv->label_widget->allocation;
+ if (gtk_widget_get_visible (priv->label_widget))
+ {
+ GtkAllocation label_allocation = priv->label_widget->allocation;
- width = label_allocation.width;
- height = label_allocation.height;
- }
+ width = label_allocation.width;
+ height = label_allocation.height;
+ }
- if (!interior_focus)
+ width += 2 * focus_pad + 2 * focus_width;
+ height += 2 * focus_pad + 2 * focus_width;
+
+ x = widget->allocation.x + border_width;
+ y = widget->allocation.y + border_width;
+
+ if (ltr)
+ {
+ if (interior_focus)
+ x += expander_spacing * 2 + expander_size;
+ }
+ else
+ {
+ x += widget->allocation.width - 2 * border_width
+ - expander_spacing * 2 - expander_size - width;
+ }
+
+ if (!interior_focus)
+ {
+ width += expander_size + 2 * expander_spacing;
+ height = MAX (height, expander_size + 2 * expander_spacing);
+ }
+ }
+ else
{
- width += expander_size + 2 * expander_spacing;
- height = MAX (height, expander_size + 2 * expander_spacing);
+ get_expander_bounds (expander, &rect);
+
+ x = rect.x - focus_pad;
+ y = rect.y - focus_pad;
+ width = rect.width + 2 * focus_pad;
+ height = rect.height + 2 * focus_pad;
}
- width += 2 * focus_pad + 2 * focus_width;
- height += 2 * focus_pad + 2 * focus_width;
-
gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
area, widget, "expander",
x, y, width, height);
gtk_expander_expose (GtkWidget *widget,
GdkEventExpose *event)
{
- if (GTK_WIDGET_DRAWABLE (widget))
+ if (gtk_widget_is_drawable (widget))
{
GtkExpander *expander = GTK_EXPANDER (widget);
gtk_expander_paint (expander);
- if (GTK_WIDGET_HAS_FOCUS (expander))
+ if (gtk_widget_has_focus (widget))
gtk_expander_paint_focus (expander, &event->area);
- if (expander->priv->label_widget)
- gtk_container_propagate_expose (GTK_CONTAINER (widget),
- expander->priv->label_widget,
- event);
-
- GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
+ GTK_WIDGET_CLASS (gtk_expander_parent_class)->expose_event (widget, event);
}
return FALSE;
gtk_expander_state_changed (GtkWidget *widget,
GtkStateType previous_state)
{
- if (!GTK_WIDGET_IS_SENSITIVE (widget))
+ if (!gtk_widget_is_sensitive (widget))
GTK_EXPANDER (widget)->priv->button_down = FALSE;
}
return FALSE;
}
+static gboolean
+expand_timeout (gpointer data)
+{
+ GtkExpander *expander = GTK_EXPANDER (data);
+ GtkExpanderPrivate *priv = expander->priv;
+
+ priv->expand_timer = 0;
+ gtk_expander_set_expanded (expander, TRUE);
+
+ return FALSE;
+}
+
+static gboolean
+gtk_expander_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtkExpander *expander = GTK_EXPANDER (widget);
+ GtkExpanderPrivate *priv = expander->priv;
+
+ if (!priv->expanded && !priv->expand_timer)
+ {
+ GtkSettings *settings;
+ guint timeout;
+
+ settings = gtk_widget_get_settings (widget);
+ g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
+
+ priv->expand_timer = gdk_threads_add_timeout (timeout, (GSourceFunc) expand_timeout, expander);
+ }
+
+ return TRUE;
+}
+
+static void
+gtk_expander_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+ GtkExpander *expander = GTK_EXPANDER (widget);
+ GtkExpanderPrivate *priv = expander->priv;
+
+ if (priv->expand_timer)
+ {
+ g_source_remove (priv->expand_timer);
+ priv->expand_timer = 0;
+ }
+}
+
typedef enum
{
FOCUS_NONE,
gtk_expander_add (GtkContainer *container,
GtkWidget *widget)
{
- GTK_CONTAINER_CLASS (parent_class)->add (container, widget);
+ GTK_CONTAINER_CLASS (gtk_expander_parent_class)->add (container, widget);
gtk_widget_set_child_visible (widget, GTK_EXPANDER (container)->priv->expanded);
gtk_widget_queue_resize (GTK_WIDGET (container));
if (GTK_EXPANDER (expander)->priv->label_widget == widget)
gtk_expander_set_label_widget (expander, NULL);
else
- GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
+ GTK_CONTAINER_CLASS (gtk_expander_parent_class)->remove (container, widget);
}
static void
gtk_expander_set_expanded (expander, !expander->priv->expanded);
}
-
/**
* gtk_expander_new:
* @label: the text of the label
/**
* gtk_expander_new_with_mnemonic:
- * @label: the text of the label with an underscore in front of the
+ * @label: (allow-none): the text of the label with an underscore in front of the
* mnemonic character
- *
+ *
* Creates a new expander using @label as the text of the label.
* If characters in @label are preceded by an underscore, they are underlined.
* If you need a literal underscore character in a label, use '__' (two
{
return g_object_new (GTK_TYPE_EXPANDER,
"label", label,
- "use_underline", TRUE,
+ "use-underline", TRUE,
NULL);
}
GdkRectangle area;
gboolean finish = FALSE;
- GDK_THREADS_ENTER();
-
if (GTK_WIDGET_REALIZED (expander))
{
get_expander_bounds (expander, &area);
gtk_widget_queue_resize (GTK_WIDGET (expander));
}
- GDK_THREADS_LEAVE();
-
return !finish;
}
g_source_remove (priv->animation_timeout);
priv->animation_timeout =
- g_timeout_add (50,
+ gdk_threads_add_timeout (50,
(GSourceFunc) gtk_expander_animation_timeout,
expander);
}
if (priv->expanded != expanded)
{
+ GtkSettings *settings = gtk_widget_get_settings (GTK_WIDGET (expander));
+ gboolean enable_animations;
+
priv->expanded = expanded;
- if (GTK_WIDGET_REALIZED (expander))
+ g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
+
+ if (enable_animations && GTK_WIDGET_REALIZED (expander))
{
gtk_expander_start_animation (expander);
}
- else
+ else
{
priv->expander_style = expanded ? GTK_EXPANDER_EXPANDED :
GTK_EXPANDER_COLLAPSED;
/**
* gtk_expander_set_label:
* @expander: a #GtkExpander
- * @label: a string
+ * @label: (allow-none): a string
*
* Sets the text of the label of the expander to @label.
*
* gtk_expander_get_label:
* @expander: a #GtkExpander
*
- * Fetches the text from the label of the expander, as set by
- * gtk_expander_set_label(). If the label text has not
- * been set the return value will be %NULL. This will be the
- * case if you create an empty button with gtk_button_new() to
- * use as a container.
+ * Fetches the text from a label widget including any embedded
+ * underlines indicating mnemonics and Pango markup, as set by
+ * gtk_expander_set_label(). If the label text has not been set the
+ * return value will be %NULL. This will be the case if you create an
+ * empty button with gtk_button_new() to use as a container.
+ *
+ * Note that this function behaved differently in versions prior to
+ * 2.14 and used to return the label text stripped of embedded
+ * underlines indicating mnemonics and Pango markup. This problem can
+ * be avoided by fetching the label text directly from the label
+ * widget.
*
* Return value: The text of the label widget. This string is owned
* by the widget and must not be modified or freed.
priv = expander->priv;
- if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
- return gtk_label_get_text (GTK_LABEL (priv->label_widget));
+ if (GTK_IS_LABEL (priv->label_widget))
+ return gtk_label_get_label (GTK_LABEL (priv->label_widget));
else
return NULL;
}
{
priv->use_underline = use_underline;
- if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
+ if (GTK_IS_LABEL (priv->label_widget))
gtk_label_set_use_underline (GTK_LABEL (priv->label_widget), use_underline);
- g_object_notify (G_OBJECT (expander), "use_underline");
+ g_object_notify (G_OBJECT (expander), "use-underline");
}
}
{
priv->use_markup = use_markup;
- if (priv->label_widget && GTK_IS_LABEL (priv->label_widget))
+ if (GTK_IS_LABEL (priv->label_widget))
gtk_label_set_use_markup (GTK_LABEL (priv->label_widget), use_markup);
- g_object_notify (G_OBJECT (expander), "use_markup");
+ g_object_notify (G_OBJECT (expander), "use-markup");
}
}
/**
* gtk_expander_set_label_widget:
* @expander: a #GtkExpander
- * @label_widget: the new label widget
+ * @label_widget: (allow-none): the new label widget
*
* Set the label widget for the expander. This is the widget
* that will appear embedded alongside the expander arrow.
GtkWidget *label_widget)
{
GtkExpanderPrivate *priv;
+ GtkWidget *widget;
g_return_if_fail (GTK_IS_EXPANDER (expander));
g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
}
priv->label_widget = label_widget;
+ widget = GTK_WIDGET (expander);
if (label_widget)
{
priv->label_widget = label_widget;
- gtk_widget_set_parent (label_widget, GTK_WIDGET (expander));
+ gtk_widget_set_parent (label_widget, widget);
if (priv->prelight)
gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT);
}
- if (GTK_WIDGET_VISIBLE (expander))
- gtk_widget_queue_resize (GTK_WIDGET (expander));
+ if (gtk_widget_get_visible (widget))
+ gtk_widget_queue_resize (widget);
g_object_freeze_notify (G_OBJECT (expander));
- g_object_notify (G_OBJECT (expander), "label_widget");
+ g_object_notify (G_OBJECT (expander), "label-widget");
g_object_notify (G_OBJECT (expander), "label");
g_object_thaw_notify (G_OBJECT (expander));
}
return expander->priv->label_widget;
}
+
+#define __GTK_EXPANDER_C__
+#include "gtkaliasdef.c"