This is a prerequisite for removing the strong dependence on the child GtkToggleButton widget, removing the platform state override, and adding binding actions in later commits. Inheriting AdwBin was attempted but it didn't work, as AdwBin presumably messes with focusability and activation, making it insufficient for this use case. `hexpand=true` needs to be explicitly set. Part-of: <https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/841>
842 lines
24 KiB
C
842 lines
24 KiB
C
/* gcal-multi-choice.c
|
|
*
|
|
* GTK - The GIMP Toolkit
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define G_LOG_DOMAIN "GcalMultiChoice"
|
|
|
|
#include "config.h"
|
|
|
|
#include "gcal-multi-choice.h"
|
|
|
|
struct _GcalMultiChoice
|
|
{
|
|
GtkWidget parent;
|
|
|
|
GtkWidget *down_button;
|
|
GtkWidget *button;
|
|
GtkStack *stack;
|
|
GtkWidget *up_button;
|
|
GtkWidget *label1;
|
|
GtkWidget *label2;
|
|
|
|
gint value;
|
|
gint min_value;
|
|
gint max_value;
|
|
gchar *category;
|
|
gchar *prev_button_tooltip_text;
|
|
gchar *next_button_tooltip_text;
|
|
|
|
GtkWidget **choices;
|
|
gint n_choices;
|
|
GtkWidget *active;
|
|
GtkPopoverBin *popover_bin;
|
|
|
|
GcalMultiChoiceFormatCallback format_cb;
|
|
gpointer format_data;
|
|
GDestroyNotify format_destroy;
|
|
GcalMultiChoiceValueCallback prev_cb;
|
|
GcalMultiChoiceValueCallback next_cb;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_VALUE,
|
|
PROP_MIN_VALUE,
|
|
PROP_MAX_VALUE,
|
|
PROP_CHOICES,
|
|
PROP_POPOVER,
|
|
PROP_CATEGORY,
|
|
PROP_PREVIOUS_BUTTON_TOOLTIP,
|
|
PROP_NEXT_BUTTON_TOOLTIP,
|
|
N_PROPS,
|
|
};
|
|
|
|
enum
|
|
{
|
|
WRAPPED,
|
|
ACTIVATE,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0, };
|
|
static GParamSpec *properties[N_PROPS] = { NULL, };
|
|
|
|
static void gcal_multi_choice_accessible_init (GtkAccessibleInterface *iface);
|
|
|
|
static void gcal_multi_choice_accessible_range_init (GtkAccessibleRangeInterface *iface);
|
|
|
|
G_DEFINE_FINAL_TYPE_WITH_CODE (GcalMultiChoice, gcal_multi_choice, GTK_TYPE_WIDGET,
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE, gcal_multi_choice_accessible_init)
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACCESSIBLE_RANGE, gcal_multi_choice_accessible_range_init))
|
|
|
|
/*
|
|
* Auxiliary methods
|
|
*/
|
|
|
|
static gchar *
|
|
get_value_string (GcalMultiChoice *self,
|
|
gint value)
|
|
{
|
|
if (self->format_cb)
|
|
return self->format_cb (self, value, self->format_data);
|
|
else if (0 <= value && value < self->n_choices)
|
|
return g_strdup (gtk_label_get_label (GTK_LABEL (self->choices[value])));
|
|
else
|
|
return g_strdup_printf ("%d", value);
|
|
}
|
|
|
|
static void
|
|
apply_value (GcalMultiChoice *self,
|
|
GtkStackTransitionType transition)
|
|
{
|
|
GtkWidget *label;
|
|
const gchar *name;
|
|
gchar *text;
|
|
|
|
if (gtk_stack_get_visible_child (GTK_STACK (self->stack)) == self->label1)
|
|
{
|
|
name = "label2";
|
|
label = self->label2;
|
|
}
|
|
else
|
|
{
|
|
name = "label1";
|
|
label = self->label1;
|
|
}
|
|
|
|
text = get_value_string (self, self->value);
|
|
gtk_label_set_text (GTK_LABEL (label), text);
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT, text,
|
|
-1);
|
|
g_free (text);
|
|
|
|
gtk_stack_set_visible_child_full (GTK_STACK (self->stack), name, transition);
|
|
}
|
|
|
|
static void
|
|
set_value (GcalMultiChoice *self,
|
|
gint value,
|
|
GtkStackTransitionType transition)
|
|
{
|
|
value = CLAMP (value, self->min_value, self->max_value);
|
|
|
|
if (self->value == value)
|
|
return;
|
|
|
|
self->value = value;
|
|
|
|
apply_value (self, transition);
|
|
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_PROPERTY_VALUE_MAX, (gdouble) self->max_value,
|
|
GTK_ACCESSIBLE_PROPERTY_VALUE_MIN, (gdouble) self->min_value,
|
|
GTK_ACCESSIBLE_PROPERTY_VALUE_NOW, (gdouble) self->value,
|
|
-1);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VALUE]);
|
|
}
|
|
|
|
static void
|
|
go_up (GcalMultiChoice *self)
|
|
{
|
|
gboolean wrapped = FALSE;
|
|
gint value;
|
|
|
|
value = self->next_cb ? self->next_cb (self->value) : self->value + 1;
|
|
g_assert_cmpint (value, > ,self->value);
|
|
|
|
if (value > self->max_value)
|
|
{
|
|
value = self->min_value;
|
|
wrapped = TRUE;
|
|
}
|
|
|
|
set_value (self, value, GTK_STACK_TRANSITION_TYPE_NONE);
|
|
|
|
if (wrapped)
|
|
g_signal_emit (self, signals[WRAPPED], 0);
|
|
}
|
|
|
|
static void
|
|
go_down (GcalMultiChoice *self)
|
|
{
|
|
gint value;
|
|
gboolean wrapped = FALSE;
|
|
|
|
value = self->prev_cb ? self->prev_cb (self->value) : self->value - 1;
|
|
g_assert_cmpint (value, < , self->value);
|
|
|
|
if (value < self->min_value)
|
|
{
|
|
value = self->max_value;
|
|
wrapped = TRUE;
|
|
}
|
|
|
|
set_value (self, value, GTK_STACK_TRANSITION_TYPE_NONE);
|
|
|
|
if (wrapped)
|
|
g_signal_emit (self, signals[WRAPPED], 0);
|
|
}
|
|
|
|
static void
|
|
update_sensitivity (GcalMultiChoice *self)
|
|
{
|
|
GtkWidget *popover = gtk_popover_bin_get_popover (self->popover_bin);
|
|
GtkAccessibleTristate state;
|
|
|
|
gtk_widget_set_can_target (self->button, !!popover);
|
|
|
|
state = !!popover ? GTK_ACCESSIBLE_TRISTATE_TRUE : GTK_ACCESSIBLE_TRISTATE_FALSE;
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_PROPERTY_HAS_POPUP, state,
|
|
-1);
|
|
if (popover != NULL)
|
|
gtk_accessible_update_relation (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_RELATION_CONTROLS, popover, NULL,
|
|
-1);
|
|
else
|
|
gtk_accessible_reset_relation (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_RELATION_CONTROLS);
|
|
}
|
|
|
|
static void
|
|
button_clicked_cb (GtkWidget *button,
|
|
GcalMultiChoice *self)
|
|
{
|
|
GtkStateFlags state_flags;
|
|
|
|
state_flags = gtk_widget_get_state_flags (button);
|
|
|
|
if (button == self->down_button)
|
|
go_down (self);
|
|
else if (button == self->up_button)
|
|
go_up (self);
|
|
else
|
|
g_assert_not_reached ();
|
|
|
|
if (!gtk_widget_grab_focus (GTK_WIDGET (self)))
|
|
g_assert_not_reached ();
|
|
|
|
gtk_widget_set_state_flags (button, state_flags, TRUE);
|
|
}
|
|
|
|
static gboolean
|
|
key_pressed_cb (GcalMultiChoice *self,
|
|
guint keyval,
|
|
guint keycode,
|
|
GdkModifierType state,
|
|
GtkEventControllerKey *event_controller)
|
|
{
|
|
gboolean is_active;
|
|
gint old_value;
|
|
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
g_assert (GTK_IS_EVENT_CONTROLLER_KEY (event_controller));
|
|
|
|
switch (keyval)
|
|
{
|
|
case GDK_KEY_space:
|
|
case GDK_KEY_KP_Space:
|
|
case GDK_KEY_Return:
|
|
case GDK_KEY_ISO_Enter:
|
|
case GDK_KEY_KP_Enter:
|
|
is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button));
|
|
if (gtk_popover_bin_get_popover (self->popover_bin))
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), !is_active);
|
|
return TRUE;
|
|
|
|
case GDK_KEY_Up:
|
|
case GDK_KEY_KP_Up:
|
|
old_value = self->value;
|
|
go_up (self);
|
|
if (old_value == self->value)
|
|
gtk_widget_error_bell (GTK_WIDGET (self));
|
|
return TRUE;
|
|
|
|
case GDK_KEY_Down:
|
|
case GDK_KEY_KP_Down:
|
|
old_value = self->value;
|
|
go_down (self);
|
|
if (old_value == self->value)
|
|
gtk_widget_error_bell (GTK_WIDGET (self));
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
button_toggled_cb (GcalMultiChoice *self)
|
|
{
|
|
const gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->button));
|
|
|
|
if (gtk_popover_bin_get_popover (self->popover_bin))
|
|
{
|
|
if (active)
|
|
{
|
|
gtk_popover_bin_popup (self->popover_bin);
|
|
gtk_accessible_update_state (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_STATE_EXPANDED, TRUE,
|
|
-1);
|
|
}
|
|
else
|
|
{
|
|
gtk_accessible_reset_state (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_STATE_EXPANDED);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
menu_deactivate_cb (GcalMultiChoice *self)
|
|
{
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->button), FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
button_state_flags_changed_cb (GcalMultiChoice *self,
|
|
GtkStateFlags previous_state_flags)
|
|
{
|
|
GtkStateFlags state_flags;
|
|
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
state_flags = gtk_widget_get_state_flags (self->button);
|
|
|
|
gtk_widget_set_state_flags (GTK_WIDGET (self), state_flags, TRUE);
|
|
}
|
|
|
|
/*
|
|
* GObject overrides
|
|
*/
|
|
|
|
static void
|
|
gcal_multi_choice_dispose (GObject *object)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
|
|
|
|
g_clear_pointer ((GtkWidget **) &self->popover_bin, gtk_widget_unparent);
|
|
|
|
g_clear_pointer (&self->choices, g_free);
|
|
g_clear_pointer (&self->category, g_free);
|
|
g_clear_pointer (&self->prev_button_tooltip_text, g_free);
|
|
g_clear_pointer (&self->next_button_tooltip_text, g_free);
|
|
|
|
if (self->format_destroy)
|
|
g_clear_pointer (&self->format_data, self->format_destroy);
|
|
|
|
G_OBJECT_CLASS (gcal_multi_choice_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gcal_multi_choice_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_VALUE:
|
|
g_value_set_int (value, self->value);
|
|
break;
|
|
|
|
case PROP_MIN_VALUE:
|
|
g_value_set_int (value, self->min_value);
|
|
break;
|
|
|
|
case PROP_MAX_VALUE:
|
|
g_value_set_int (value, self->max_value);
|
|
break;
|
|
|
|
case PROP_POPOVER:
|
|
g_value_set_object (value, gcal_multi_choice_get_popover (self));
|
|
break;
|
|
|
|
case PROP_CATEGORY:
|
|
g_value_set_string (value, gcal_multi_choice_get_category (self));
|
|
break;
|
|
|
|
case PROP_PREVIOUS_BUTTON_TOOLTIP:
|
|
g_value_set_string (value, self->prev_button_tooltip_text);
|
|
break;
|
|
|
|
case PROP_NEXT_BUTTON_TOOLTIP:
|
|
g_value_set_string (value, self->next_button_tooltip_text);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gcal_multi_choice_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PROP_VALUE:
|
|
gcal_multi_choice_set_value (self, g_value_get_int (value));
|
|
break;
|
|
|
|
case PROP_MIN_VALUE:
|
|
self->min_value = g_value_get_int (value);
|
|
g_object_notify_by_pspec (object, properties[PROP_MIN_VALUE]);
|
|
gcal_multi_choice_set_value (self, self->value);
|
|
break;
|
|
|
|
case PROP_MAX_VALUE:
|
|
self->max_value = g_value_get_int (value);
|
|
g_object_notify_by_pspec (object, properties[PROP_MAX_VALUE]);
|
|
gcal_multi_choice_set_value (self, self->value);
|
|
break;
|
|
|
|
case PROP_CHOICES:
|
|
gcal_multi_choice_set_choices (self, (const gchar **)g_value_get_boxed (value));
|
|
break;
|
|
|
|
case PROP_POPOVER:
|
|
gcal_multi_choice_set_popover (self, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_CATEGORY:
|
|
gcal_multi_choice_set_category (self, g_value_get_string (value));
|
|
break;
|
|
|
|
case PROP_PREVIOUS_BUTTON_TOOLTIP:
|
|
if (g_set_str (&self->prev_button_tooltip_text, g_value_get_string (value)))
|
|
g_object_notify_by_pspec (object, properties[PROP_PREVIOUS_BUTTON_TOOLTIP]);
|
|
break;
|
|
|
|
case PROP_NEXT_BUTTON_TOOLTIP:
|
|
if (g_set_str (&self->next_button_tooltip_text, g_value_get_string (value)))
|
|
g_object_notify_by_pspec (object, properties[PROP_NEXT_BUTTON_TOOLTIP]);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GtkWidget overrides
|
|
*/
|
|
|
|
static void
|
|
gcal_multi_choice_state_flags_changed (GtkWidget *widget,
|
|
GtkStateFlags previous_state_flags)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (widget);
|
|
GtkStateFlags state_flags;
|
|
GtkWidget *popover;
|
|
|
|
state_flags = gtk_widget_get_state_flags (widget);
|
|
if (state_flags & GTK_STATE_FLAG_FOCUSED)
|
|
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_FOCUS_VISIBLE, FALSE);
|
|
|
|
if (!gtk_widget_is_sensitive (widget))
|
|
{
|
|
if ((popover = gtk_popover_bin_get_popover (self->popover_bin)))
|
|
gtk_widget_set_visible (popover, FALSE);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gcal_multi_choice_focus (GtkWidget *widget,
|
|
GtkDirectionType direction)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (widget);
|
|
GtkWidget *popover = gtk_popover_bin_get_popover (self->popover_bin);
|
|
|
|
if (popover && gtk_widget_get_visible (popover))
|
|
return gtk_widget_child_focus (popover, direction);
|
|
else
|
|
return gtk_widget_child_focus (self->button, direction);
|
|
}
|
|
|
|
static gboolean
|
|
gcal_multi_choice_grab_focus (GtkWidget *widget)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (widget);
|
|
|
|
return gtk_widget_grab_focus (self->button);
|
|
}
|
|
|
|
/*
|
|
* GtkAccessible overrides
|
|
*/
|
|
|
|
static gboolean
|
|
gcal_multi_choice_accessible_get_platform_state (GtkAccessible *accessible,
|
|
GtkAccessiblePlatformState state)
|
|
{
|
|
GcalMultiChoice *self = GCAL_MULTI_CHOICE (accessible);
|
|
|
|
return gtk_accessible_get_platform_state (GTK_ACCESSIBLE (self->button), state);
|
|
}
|
|
|
|
/*
|
|
* GtkAccessibleRange overrides
|
|
*/
|
|
|
|
static gboolean
|
|
gcal_multi_choice_accessible_range_set_current_value (GtkAccessibleRange *accessible_range,
|
|
gdouble value)
|
|
{
|
|
gcal_multi_choice_set_value (GCAL_MULTI_CHOICE (accessible_range), value);
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Init
|
|
*/
|
|
|
|
static void
|
|
gcal_multi_choice_class_init (GcalMultiChoiceClass *class)
|
|
{
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->dispose = gcal_multi_choice_dispose;
|
|
object_class->set_property = gcal_multi_choice_set_property;
|
|
object_class->get_property = gcal_multi_choice_get_property;
|
|
|
|
widget_class->state_flags_changed = gcal_multi_choice_state_flags_changed;
|
|
widget_class->focus = gcal_multi_choice_focus;
|
|
widget_class->grab_focus = gcal_multi_choice_grab_focus;
|
|
|
|
properties[PROP_VALUE] =
|
|
g_param_spec_int ("value", "Value", "Value",
|
|
G_MININT, G_MAXINT, 0,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
properties[PROP_MIN_VALUE] =
|
|
g_param_spec_int ("min-value", "Minimum Value", "Minimum Value",
|
|
G_MININT, G_MAXINT, 0,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
properties[PROP_MAX_VALUE] =
|
|
g_param_spec_int ("max-value", "Maximum Value", "Maximum Value",
|
|
G_MININT, G_MAXINT, 0,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
properties[PROP_CHOICES] =
|
|
g_param_spec_boxed ("choices", "Choices", "Choices",
|
|
G_TYPE_STRV,
|
|
G_PARAM_WRITABLE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
/**
|
|
* GcalMultiChoice:popover:
|
|
*
|
|
* The popover widget.
|
|
*/
|
|
properties[PROP_POPOVER] =
|
|
g_param_spec_object ("popover", NULL, NULL,
|
|
GTK_TYPE_POPOVER,
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
properties[PROP_CATEGORY] =
|
|
g_param_spec_string ("category", "Category", "Category",
|
|
"",
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
properties[PROP_PREVIOUS_BUTTON_TOOLTIP] =
|
|
g_param_spec_string ("previous-button-tooltip", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
properties[PROP_NEXT_BUTTON_TOOLTIP] =
|
|
g_param_spec_string ("next-button-tooltip", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
g_object_class_install_properties (object_class, N_PROPS, properties);
|
|
|
|
signals[WRAPPED] =
|
|
g_signal_new ("wrapped",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0,
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 0);
|
|
signals[ACTIVATE] =
|
|
g_signal_new ("activate",
|
|
G_TYPE_FROM_CLASS (object_class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
0,
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE]);
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/event-editor/gcal-multi-choice.ui");
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, down_button);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, up_button);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, button);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, stack);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, label1);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, label2);
|
|
gtk_widget_class_bind_template_child (widget_class, GcalMultiChoice, popover_bin);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, button_clicked_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, button_toggled_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, button_state_flags_changed_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb);
|
|
|
|
gtk_widget_class_set_css_name (widget_class, "navigator");
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
|
}
|
|
|
|
static void
|
|
gcal_multi_choice_accessible_init (GtkAccessibleInterface *iface)
|
|
{
|
|
iface->get_platform_state = gcal_multi_choice_accessible_get_platform_state;
|
|
}
|
|
|
|
static void
|
|
gcal_multi_choice_accessible_range_init (GtkAccessibleRangeInterface *iface)
|
|
{
|
|
iface->set_current_value = gcal_multi_choice_accessible_range_set_current_value;
|
|
}
|
|
|
|
static void
|
|
gcal_multi_choice_init (GcalMultiChoice *self)
|
|
{
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
|
|
update_sensitivity (self);
|
|
}
|
|
|
|
/*
|
|
* Public methods
|
|
*/
|
|
|
|
/**
|
|
* gcal_multi_choice_new:
|
|
*
|
|
* Creates a new #GcalMultiChoice.
|
|
*
|
|
* Returns: (transfer full): a newly created #GcalEventPopover
|
|
*/
|
|
GtkWidget *
|
|
gcal_multi_choice_new (void)
|
|
{
|
|
return GTK_WIDGET (g_object_new (GCAL_TYPE_MULTI_CHOICE, NULL));
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_set_value:
|
|
* @self: a #GcalMultiChoice
|
|
* @value: the value
|
|
*
|
|
* Sets the value for @self.
|
|
*/
|
|
void
|
|
gcal_multi_choice_set_value (GcalMultiChoice *self,
|
|
gint value)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
set_value (self, value, GTK_STACK_TRANSITION_TYPE_NONE);
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_get_value:
|
|
* @self: a #GcalMultiChoice
|
|
*
|
|
* Gets the value for @self.
|
|
*
|
|
* Returns: The value for @self.
|
|
*/
|
|
gint
|
|
gcal_multi_choice_get_value (GcalMultiChoice *self)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
return self->value;
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_set_choices:
|
|
* @self: a #GcalMultiChoice
|
|
* @choices: an array of strings
|
|
*
|
|
* Sets the available choices for @self.
|
|
*/
|
|
void
|
|
gcal_multi_choice_set_choices (GcalMultiChoice *self,
|
|
const gchar **choices)
|
|
{
|
|
gint i;
|
|
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
for (i = 0; i < self->n_choices; i++)
|
|
gtk_stack_remove (self->stack, self->choices[i]);
|
|
g_free (self->choices);
|
|
|
|
self->n_choices = g_strv_length ((gchar **)choices);
|
|
self->choices = g_new (GtkWidget *, self->n_choices);
|
|
for (i = 0; i < self->n_choices; i++)
|
|
{
|
|
self->choices[i] = gtk_label_new (choices[i]);
|
|
gtk_widget_set_visible (self->choices[i], TRUE);
|
|
gtk_stack_add_named (GTK_STACK (self->stack),
|
|
self->choices[i],
|
|
choices[i]);
|
|
}
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHOICES]);
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_set_format_callback:
|
|
* @self: a #GcalMultiChoice
|
|
* @callback: (nullable) (scope notified) (closure user_data) (destroy destroy): callback
|
|
* @user_data: user data passed to @callback
|
|
* @destroy: destroy notifier for @user_data
|
|
*
|
|
* Sets the format callback.
|
|
*/
|
|
void
|
|
gcal_multi_choice_set_format_callback (GcalMultiChoice *self,
|
|
GcalMultiChoiceFormatCallback callback,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
if (self->format_destroy)
|
|
self->format_destroy (self->format_data);
|
|
|
|
self->format_cb = callback;
|
|
self->format_data = user_data;
|
|
self->format_destroy = destroy;
|
|
|
|
apply_value (self, GTK_STACK_TRANSITION_TYPE_NONE);
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_set_popover:
|
|
* @self: a #GcalMultiChoice
|
|
* @popover: a popover widget.
|
|
*
|
|
* Sets the popover for @self.
|
|
*/
|
|
void
|
|
gcal_multi_choice_set_value_callbacks (GcalMultiChoice *self,
|
|
GcalMultiChoiceValueCallback prev_cb,
|
|
GcalMultiChoiceValueCallback next_cb)
|
|
{
|
|
self->prev_cb = prev_cb;
|
|
self->next_cb = next_cb;
|
|
}
|
|
|
|
void
|
|
gcal_multi_choice_set_popover (GcalMultiChoice *self,
|
|
GtkWidget *popover)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
g_assert (popover == NULL || GTK_IS_POPOVER (popover));
|
|
|
|
if (gtk_popover_bin_get_popover (self->popover_bin) == popover)
|
|
return;
|
|
|
|
gtk_popover_bin_set_popover (self->popover_bin, popover);
|
|
|
|
if (popover)
|
|
g_signal_connect_swapped (popover, "closed", G_CALLBACK (menu_deactivate_cb), self);
|
|
|
|
update_sensitivity (self);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_POPOVER]);
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_get_popover:
|
|
* @self: a #GcalMultiChoice
|
|
*
|
|
* Gets the popover for @self.
|
|
*
|
|
* Returns: (transfer none): a #GtkPopover
|
|
*/
|
|
GtkPopover *
|
|
gcal_multi_choice_get_popover (GcalMultiChoice *self)
|
|
{
|
|
GtkWidget *popover;
|
|
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
popover = gtk_popover_bin_get_popover (self->popover_bin);
|
|
|
|
return (GtkPopover *)popover;
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_set_category:
|
|
* @self: a #GcalMultiChoice
|
|
* @category: The category name
|
|
*
|
|
* Set the value of the category name
|
|
*/
|
|
void
|
|
gcal_multi_choice_set_category (GcalMultiChoice *self,
|
|
const gchar *category)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
if (g_strcmp0 (self->category, category) != 0)
|
|
{
|
|
self->category = g_strdup (category ? category : "");
|
|
|
|
gtk_accessible_update_property (GTK_ACCESSIBLE (self),
|
|
GTK_ACCESSIBLE_PROPERTY_LABEL, category,
|
|
-1);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CATEGORY]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gcal_multi_choice_get_category:
|
|
* @self: a #GcalMultiChoice
|
|
*
|
|
* Get the name of the category
|
|
*
|
|
* Returns: (transfer none): the category for the multi-choice.
|
|
*/
|
|
const gchar*
|
|
gcal_multi_choice_get_category (GcalMultiChoice *self)
|
|
{
|
|
g_assert (GCAL_IS_MULTI_CHOICE (self));
|
|
|
|
return self->category ? self->category : "";
|
|
}
|