336 lines
10 KiB
C
336 lines
10 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* pikamenushell.c
|
|
* Copyright (C) 2023 Jehan
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libpikabase/pikabase.h"
|
|
|
|
#include "widgets-types.h"
|
|
|
|
#include "pikamenu.h"
|
|
#include "pikamenumodel.h"
|
|
#include "pikamenushell.h"
|
|
#include "pikauimanager.h"
|
|
|
|
|
|
enum
|
|
{
|
|
MODEL_DELETED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
#define GET_PRIVATE(obj) (pika_menu_shell_get_private ((PikaMenuShell *) (obj)))
|
|
|
|
typedef struct _PikaMenuShellPrivate PikaMenuShellPrivate;
|
|
|
|
struct _PikaMenuShellPrivate
|
|
{
|
|
PikaUIManager *manager;
|
|
PikaMenuModel *model;
|
|
gchar *path_prefix;
|
|
|
|
GRegex *path_regex;
|
|
};
|
|
|
|
|
|
static PikaMenuShellPrivate *
|
|
pika_menu_shell_get_private (PikaMenuShell *menu_shell);
|
|
static void pika_menu_shell_private_finalize (PikaMenuShellPrivate *priv);
|
|
|
|
static void pika_menu_shell_model_changed (GMenuModel *self,
|
|
gint position,
|
|
gint removed,
|
|
gint added,
|
|
PikaMenuShell *shell);
|
|
|
|
static void pika_menu_shell_append_model_drop_top (PikaMenuShell *shell,
|
|
PikaMenuModel *model);
|
|
|
|
|
|
G_DEFINE_INTERFACE (PikaMenuShell, pika_menu_shell, GTK_TYPE_CONTAINER)
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
static void
|
|
pika_menu_shell_default_init (PikaMenuShellInterface *iface)
|
|
{
|
|
signals[MODEL_DELETED] =
|
|
g_signal_new ("model-deleted",
|
|
G_TYPE_FROM_INTERFACE (iface),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (PikaMenuShellInterface, model_deleted),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_object ("manager",
|
|
NULL, NULL,
|
|
PIKA_TYPE_UI_MANAGER,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY));
|
|
g_object_interface_install_property (iface,
|
|
g_param_spec_object ("model",
|
|
NULL, NULL,
|
|
PIKA_TYPE_MENU_MODEL,
|
|
PIKA_PARAM_READWRITE |
|
|
G_PARAM_READWRITE));
|
|
}
|
|
|
|
|
|
/* Public functions. */
|
|
|
|
void
|
|
pika_menu_shell_fill (PikaMenuShell *shell,
|
|
PikaMenuModel *model,
|
|
gboolean drop_top_submenu)
|
|
{
|
|
g_return_if_fail (GTK_IS_CONTAINER (shell));
|
|
|
|
gtk_container_foreach (GTK_CONTAINER (shell),
|
|
(GtkCallback) gtk_widget_destroy,
|
|
NULL);
|
|
|
|
if (drop_top_submenu)
|
|
pika_menu_shell_append_model_drop_top (shell, model);
|
|
else
|
|
pika_menu_shell_append (shell, model);
|
|
}
|
|
|
|
|
|
/* Protected functions. */
|
|
|
|
void
|
|
pika_menu_shell_append (PikaMenuShell *shell,
|
|
PikaMenuModel *model)
|
|
{
|
|
PikaMenuShellPrivate *priv = GET_PRIVATE (shell);
|
|
|
|
g_free (priv->path_prefix);
|
|
priv->path_prefix = g_strdup (pika_menu_model_get_path (PIKA_MENU_MODEL (model)));
|
|
|
|
if (priv->model)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (priv->model,
|
|
G_CALLBACK (pika_menu_shell_model_changed),
|
|
shell);
|
|
g_signal_emit (shell, signals[MODEL_DELETED], 0);
|
|
}
|
|
|
|
if (priv->model != model)
|
|
{
|
|
g_clear_object (&priv->model);
|
|
priv->model = g_object_ref (model);
|
|
}
|
|
|
|
if (model)
|
|
{
|
|
PIKA_MENU_SHELL_GET_INTERFACE (shell)->append (shell, model);
|
|
|
|
g_signal_connect_object (priv->model, "items-changed",
|
|
G_CALLBACK (pika_menu_shell_model_changed),
|
|
shell, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
pika_menu_shell_init (PikaMenuShell *shell)
|
|
{
|
|
PikaMenuShellPrivate *priv;
|
|
|
|
g_return_if_fail (PIKA_IS_MENU_SHELL (shell));
|
|
|
|
priv = GET_PRIVATE (shell);
|
|
|
|
priv->path_prefix = NULL;
|
|
priv->model = NULL;
|
|
priv->path_regex = g_regex_new ("/+", 0, 0, NULL);
|
|
}
|
|
|
|
/**
|
|
* pika_menu_shell_install_properties:
|
|
* @klass: the class structure for a type deriving from #GObject
|
|
*
|
|
* Installs the necessary properties for a class implementing
|
|
* #PikaMenuShell. Please call this function in the *_class_init()
|
|
* function of the child class.
|
|
**/
|
|
void
|
|
pika_menu_shell_install_properties (GObjectClass *klass)
|
|
{
|
|
g_object_class_override_property (klass, PIKA_MENU_SHELL_PROP_MANAGER, "manager");
|
|
g_object_class_override_property (klass, PIKA_MENU_SHELL_PROP_MODEL, "model");
|
|
}
|
|
|
|
void
|
|
pika_menu_shell_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaMenuShellPrivate *priv;
|
|
|
|
priv = GET_PRIVATE (object);
|
|
|
|
switch (property_id)
|
|
{
|
|
case PIKA_MENU_SHELL_PROP_MANAGER:
|
|
g_value_set_object (value, priv->manager);
|
|
break;
|
|
case PIKA_MENU_SHELL_PROP_MODEL:
|
|
g_value_set_object (value, priv->model);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
pika_menu_shell_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
PikaMenuShellPrivate *priv = GET_PRIVATE (object);
|
|
PikaMenuShell *shell = PIKA_MENU_SHELL (object);
|
|
|
|
|
|
switch (property_id)
|
|
{
|
|
case PIKA_MENU_SHELL_PROP_MANAGER:
|
|
g_set_weak_pointer (&priv->manager, g_value_get_object (value));
|
|
break;
|
|
case PIKA_MENU_SHELL_PROP_MODEL:
|
|
pika_menu_shell_append (shell, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PikaUIManager *
|
|
pika_menu_shell_get_manager (PikaMenuShell *shell)
|
|
{
|
|
return GET_PRIVATE (shell)->manager;
|
|
}
|
|
|
|
|
|
/* Private functions */
|
|
|
|
static PikaMenuShellPrivate *
|
|
pika_menu_shell_get_private (PikaMenuShell *menu_shell)
|
|
{
|
|
PikaMenuShellPrivate *priv;
|
|
static GQuark private_key = 0;
|
|
|
|
g_return_val_if_fail (PIKA_IS_MENU_SHELL (menu_shell), NULL);
|
|
|
|
if (! private_key)
|
|
private_key = g_quark_from_static_string ("pika-menu_shell-priv");
|
|
|
|
priv = g_object_get_qdata ((GObject *) menu_shell, private_key);
|
|
|
|
if (! priv)
|
|
{
|
|
priv = g_slice_new0 (PikaMenuShellPrivate);
|
|
|
|
g_object_set_qdata_full ((GObject *) menu_shell, private_key, priv,
|
|
(GDestroyNotify) pika_menu_shell_private_finalize);
|
|
}
|
|
|
|
return priv;
|
|
}
|
|
|
|
static void
|
|
pika_menu_shell_private_finalize (PikaMenuShellPrivate *priv)
|
|
{
|
|
g_clear_weak_pointer (&priv->manager);
|
|
g_free (priv->path_prefix);
|
|
g_clear_pointer (&priv->path_regex, g_regex_unref);
|
|
g_clear_object (&priv->model);
|
|
|
|
g_slice_free (PikaMenuShellPrivate, priv);
|
|
}
|
|
|
|
static void
|
|
pika_menu_shell_model_changed (GMenuModel *model,
|
|
gint position,
|
|
gint removed,
|
|
gint added,
|
|
PikaMenuShell *shell)
|
|
{
|
|
PikaMenuShellPrivate *priv = GET_PRIVATE (shell);
|
|
|
|
/* Lazy approach to handle model changes. A better implementation would only
|
|
* remove or delete the changed items using:
|
|
* PIKA_MENU_SHELL_GET_INTERFACE (shell)->add_ui()
|
|
* PIKA_MENU_SHELL_GET_INTERFACE (shell)->remove_ui
|
|
* TODO
|
|
*/
|
|
gtk_container_foreach (GTK_CONTAINER (shell),
|
|
(GtkCallback) gtk_widget_destroy,
|
|
NULL);
|
|
PIKA_MENU_SHELL_GET_INTERFACE (shell)->append (shell, priv->model);
|
|
}
|
|
|
|
static void
|
|
pika_menu_shell_append_model_drop_top (PikaMenuShell *shell,
|
|
PikaMenuModel *model)
|
|
{
|
|
PikaMenuShellPrivate *priv = GET_PRIVATE (shell);
|
|
GMenuModel *submenu = NULL;
|
|
|
|
g_return_if_fail (GTK_IS_CONTAINER (shell));
|
|
|
|
if (g_menu_model_get_n_items (G_MENU_MODEL (model)) == 1)
|
|
{
|
|
gchar *label = NULL;
|
|
|
|
submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), 0, G_MENU_LINK_SUBMENU);
|
|
|
|
if (submenu)
|
|
{
|
|
gchar *path_prefix;
|
|
|
|
g_menu_model_get_item_attribute (submenu, 0, G_MENU_ATTRIBUTE_LABEL, "s", &label);
|
|
|
|
path_prefix = g_strdup_printf ("%s/%s", priv->path_prefix, label);
|
|
g_free (priv->path_prefix);
|
|
priv->path_prefix = path_prefix;
|
|
}
|
|
|
|
g_free (label);
|
|
}
|
|
|
|
pika_menu_shell_append (shell, submenu != NULL ? PIKA_MENU_MODEL (submenu) : PIKA_MENU_MODEL (model));
|
|
|
|
g_clear_object (&submenu);
|
|
}
|