/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball * * pikalayermodecombobox.c * Copyright (C) 2017 Michael Natterer * * 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 3 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikawidgets/pikawidgets.h" #include "widgets-types.h" #include "operations/layer-modes/pika-layer-modes.h" #include "pikalayermodecombobox.h" /** * SECTION: pikalayermodecombobox * @title: PikaLayerModeComboBox * @short_description: A #PikaEnumComboBox subclass for selecting a layer mode. * * A #GtkComboBox subclass for selecting a layer mode **/ enum { PROP_0, PROP_CONTEXT, PROP_LAYER_MODE, PROP_GROUP }; struct _PikaLayerModeComboBoxPrivate { PikaLayerModeContext context; PikaLayerMode layer_mode; PikaLayerModeGroup group; }; static void pika_layer_mode_combo_box_constructed (GObject *object); static void pika_layer_mode_combo_box_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void pika_layer_mode_combo_box_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void pika_layer_mode_combo_box_changed (GtkComboBox *gtk_combo); static void pika_layer_mode_combo_box_update_model (PikaLayerModeComboBox *combo, gboolean change_mode); static gboolean pika_layer_mode_combo_box_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data); G_DEFINE_TYPE_WITH_PRIVATE (PikaLayerModeComboBox, pika_layer_mode_combo_box, PIKA_TYPE_ENUM_COMBO_BOX) #define parent_class pika_layer_mode_combo_box_parent_class static void pika_layer_mode_combo_box_class_init (PikaLayerModeComboBoxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (klass); object_class->constructed = pika_layer_mode_combo_box_constructed; object_class->set_property = pika_layer_mode_combo_box_set_property; object_class->get_property = pika_layer_mode_combo_box_get_property; combo_class->changed = pika_layer_mode_combo_box_changed; g_object_class_install_property (object_class, PROP_CONTEXT, g_param_spec_flags ("context", NULL, NULL, PIKA_TYPE_LAYER_MODE_CONTEXT, PIKA_LAYER_MODE_CONTEXT_ALL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_LAYER_MODE, g_param_spec_enum ("layer-mode", NULL, NULL, PIKA_TYPE_LAYER_MODE, PIKA_LAYER_MODE_NORMAL, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_GROUP, g_param_spec_enum ("group", NULL, NULL, PIKA_TYPE_LAYER_MODE_GROUP, PIKA_LAYER_MODE_GROUP_DEFAULT, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); } static void pika_layer_mode_combo_box_init (PikaLayerModeComboBox *combo) { combo->priv = pika_layer_mode_combo_box_get_instance_private (combo); gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo), pika_layer_mode_combo_box_separator_func, GINT_TO_POINTER (PIKA_LAYER_MODE_SEPARATOR), NULL); } static void pika_layer_mode_combo_box_constructed (GObject *object) { PikaLayerModeComboBox *combo = PIKA_LAYER_MODE_COMBO_BOX (object); G_OBJECT_CLASS (parent_class)->constructed (object); pika_layer_mode_combo_box_update_model (combo, FALSE); pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), combo->priv->layer_mode); } static void pika_layer_mode_combo_box_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PikaLayerModeComboBox *combo = PIKA_LAYER_MODE_COMBO_BOX (object); switch (prop_id) { case PROP_CONTEXT: pika_layer_mode_combo_box_set_context (combo, g_value_get_flags (value)); break; case PROP_LAYER_MODE: pika_layer_mode_combo_box_set_mode (combo, g_value_get_enum (value)); break; case PROP_GROUP: pika_layer_mode_combo_box_set_group (combo, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void pika_layer_mode_combo_box_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PikaLayerModeComboBox *combo = PIKA_LAYER_MODE_COMBO_BOX (object); switch (prop_id) { case PROP_CONTEXT: g_value_set_flags (value, combo->priv->context); break; case PROP_LAYER_MODE: g_value_set_enum (value, combo->priv->layer_mode); break; case PROP_GROUP: g_value_set_enum (value, combo->priv->group); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void pika_layer_mode_combo_box_changed (GtkComboBox *gtk_combo) { PikaLayerModeComboBox *combo = PIKA_LAYER_MODE_COMBO_BOX (gtk_combo); PikaLayerMode mode; if (pika_int_combo_box_get_active (PIKA_INT_COMBO_BOX (combo), (gint *) &mode)) { combo->priv->layer_mode = mode; g_object_notify (G_OBJECT (combo), "layer-mode"); } } /** * pika_layer_mode_combo_box_new: * Foo. * * Returns: a new #PikaLayerModeComboBox. **/ GtkWidget * pika_layer_mode_combo_box_new (PikaLayerModeContext context) { return g_object_new (PIKA_TYPE_LAYER_MODE_COMBO_BOX, "context", context, NULL); } void pika_layer_mode_combo_box_set_context (PikaLayerModeComboBox *combo, PikaLayerModeContext context) { g_return_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo)); if (context != combo->priv->context) { g_object_freeze_notify (G_OBJECT (combo)); combo->priv->context = context; g_object_notify (G_OBJECT (combo), "context"); pika_layer_mode_combo_box_update_model (combo, TRUE); g_object_thaw_notify (G_OBJECT (combo)); } } PikaLayerModeContext pika_layer_mode_combo_box_get_context (PikaLayerModeComboBox *combo) { g_return_val_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo), PIKA_LAYER_MODE_CONTEXT_ALL); return combo->priv->context; } void pika_layer_mode_combo_box_set_mode (PikaLayerModeComboBox *combo, PikaLayerMode mode) { g_return_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo)); g_return_if_fail (mode == -1 || (pika_layer_mode_get_context (mode) & combo->priv->context)); if (mode == -1) { pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), -1); combo->priv->layer_mode = mode; } else if (mode != combo->priv->layer_mode) { GtkTreeModel *model; GtkTreeIter dummy; model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); g_object_freeze_notify (G_OBJECT (combo)); if (! pika_int_store_lookup_by_value (model, mode, &dummy)) { combo->priv->group = pika_layer_mode_get_group (mode); g_object_notify (G_OBJECT (combo), "group"); pika_layer_mode_combo_box_update_model (combo, FALSE); } pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), mode); g_object_thaw_notify (G_OBJECT (combo)); } } PikaLayerMode pika_layer_mode_combo_box_get_mode (PikaLayerModeComboBox *combo) { g_return_val_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo), PIKA_LAYER_MODE_NORMAL); return combo->priv->layer_mode; } void pika_layer_mode_combo_box_set_group (PikaLayerModeComboBox *combo, PikaLayerModeGroup group) { g_return_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo)); if (group != combo->priv->group) { g_object_freeze_notify (G_OBJECT (combo)); combo->priv->group = group; g_object_notify (G_OBJECT (combo), "group"); pika_layer_mode_combo_box_update_model (combo, TRUE); g_object_thaw_notify (G_OBJECT (combo)); } } PikaLayerModeGroup pika_layer_mode_combo_box_get_group (PikaLayerModeComboBox *combo) { g_return_val_if_fail (PIKA_IS_LAYER_MODE_COMBO_BOX (combo), PIKA_LAYER_MODE_GROUP_DEFAULT); return combo->priv->group; } /* private functions */ static void pika_enum_store_add_value (GtkListStore *store, GEnumClass *enum_class, GEnumValue *value) { GtkTreeIter iter = { 0, }; const gchar *desc; const gchar *abbrev; gchar *stripped; desc = pika_enum_value_get_desc (enum_class, value); abbrev = pika_enum_value_get_abbrev (enum_class, value); /* no mnemonics in combo boxes */ stripped = pika_strip_uline (desc); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, PIKA_INT_STORE_VALUE, value->value, PIKA_INT_STORE_LABEL, stripped, PIKA_INT_STORE_ABBREV, abbrev, -1); g_free (stripped); } static void pika_enum_store_add_separator (GtkListStore *store) { GtkTreeIter iter = { 0, }; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, PIKA_INT_STORE_VALUE, PIKA_LAYER_MODE_SEPARATOR, -1); } static GtkListStore * pika_enum_store_new_from_array (GType enum_type, gint n_values, const gint *values, PikaLayerModeContext context) { GtkListStore *store; GEnumClass *enum_class; GEnumValue *value; gboolean first_item = TRUE; gboolean prepend_separator = FALSE; gint i; g_return_val_if_fail (G_TYPE_IS_ENUM (enum_type), NULL); g_return_val_if_fail (n_values > 1, NULL); g_return_val_if_fail (values != NULL, NULL); store = g_object_new (PIKA_TYPE_ENUM_STORE, "enum-type", enum_type, NULL); enum_class = g_type_class_ref (enum_type); for (i = 0; i < n_values; i++) { if (values[i] != PIKA_LAYER_MODE_SEPARATOR) { if (pika_layer_mode_get_context (values[i]) & context) { value = g_enum_get_value (enum_class, values[i]); if (value) { if (prepend_separator) { pika_enum_store_add_separator (store); prepend_separator = FALSE; } pika_enum_store_add_value (store, enum_class, value); first_item = FALSE; } } } else { if (! first_item) prepend_separator = TRUE; } } g_type_class_unref (enum_class); return store; } static void pika_layer_mode_combo_box_update_model (PikaLayerModeComboBox *combo, gboolean change_mode) { GtkListStore *store; const PikaLayerMode *modes; gint n_modes; modes = pika_layer_mode_get_group_array (combo->priv->group, &n_modes); store = pika_enum_store_new_from_array (PIKA_TYPE_LAYER_MODE, n_modes, (gint *) modes, combo->priv->context); gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (store)); g_object_unref (store); if (change_mode) { PikaLayerMode new_mode; if (pika_layer_mode_get_for_group (combo->priv->layer_mode, combo->priv->group, &new_mode) && (pika_layer_mode_get_context (new_mode) & combo->priv->context)) { pika_int_combo_box_set_active (PIKA_INT_COMBO_BOX (combo), new_mode); } else { GtkTreeIter iter; /* switch to the first mode, which will be one of the "normal" */ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); } } } static gboolean pika_layer_mode_combo_box_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { gint value; gtk_tree_model_get (model, iter, PIKA_INT_STORE_VALUE, &value, -1); return value == GPOINTER_TO_INT (data); }