/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * 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 3 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 * Library 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 * . */ #include "config.h" #include "pika.h" typedef struct { GType resource_type; PikaResourceChoosedCallback callback; gpointer owner_data; GDestroyNotify data_destroy; } PikaResourceAdaption; /* local */ static void pika_resource_data_free (PikaResourceAdaption *adaption); static PikaValueArray * pika_temp_resource_run (PikaProcedure *procedure, PikaProcedureConfig *config, gpointer run_data); static gboolean pika_resource_select_remove_after_run (const gchar *procedure_name); /* public */ /* Annotation, which appears in the libpika API doc. * Not a class, only functions. * The functions appear as class methods of PikaResource class. * Formerly, API had separate functions for each resource subclass. */ /** * SECTION: pikaresourceselect * @title: PikaResourceSelect * @short_description: A resource selection dialog. * * A resource selection dialog. * * An adapter and proxy between libpika and core. * (see Adapter and Proxy patterns in programming literature.) * * Proxy: to a remote dialog in core. * Is a dialog, but the dialog is remote (another process.) * Remote dialog is a chooser dialog of subclass of PikaResource, * e.g. PikaBrush, PikaFont, etc. * * Adapter: gets a callback via PDB procedure from remote dialog * and shuffles parameters to call a owner's callback on libpika side. * * Generic on type of PikaResource subclass. * That is, the type of PikaResource subclass is passed. * * Responsibilities: * * - implement a proxy to a chooser widget in core * * Collaborations: * * - called by PikaResourceSelectButton to popup as a sibling widget * - PDB procedures to/from core, which implements the remote dialog * (from via PDB temp callback, to via PDB procs such as pika_fonts_popup) * - plugins implementing their own GUI **/ /* This was extracted from pikabrushselect.c, pikafontselect.c, etc. * and those were deleted. */ /* Functions that dispatch on resource type. * * For now, the design is that core callbacks pass * attributes of the resource (not just the resource.) * FUTURE: core will use the same signature for all remote dialogs, * regardless of resource type. * Then all this dispatching code can be deleted. */ /* Create args for temp PDB callback. * Must match signature that core hardcodes in a subclass of pika_pdb_dialog. * * For example, see app/widgets/paletteselect.c * * When testing, the error "Unable to run callback... temp_proc... wrong type" * means a signature mismatch. * * Note the signature from core might be from an old design where * the core sent all data needed to draw the resource. * In the new design, libpika gets the attributes of the resource * from core in a separate PDB proc call back to core. * While core uses the old design and libpika uses the new design, * libpika simply ignores the extra args (adapts the signature.) */ static void create_callback_PDB_procedure_params (PikaProcedure *procedure, GType resource_type) { PIKA_PROC_ARG_RESOURCE (procedure, "resource", "Resource", "The resource", G_PARAM_READWRITE); /* Create args for the extra, superfluous args that core is passing.*/ if (g_type_is_a (resource_type, PIKA_TYPE_FONT)) { /* No other args. */ } else if (g_type_is_a (resource_type, PIKA_TYPE_GRADIENT)) { PIKA_PROC_ARG_INT (procedure, "gradient-width", "Gradient width", "The gradient width", 0, G_MAXINT, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_FLOAT_ARRAY (procedure, "gradient-data", "Gradient data", "The gradient data", G_PARAM_READWRITE); } else if (g_type_is_a (resource_type, PIKA_TYPE_BRUSH)) { PIKA_PROC_ARG_DOUBLE (procedure, "opacity", "Opacity", NULL, 0.0, 100.0, 100.0, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "spacing", "Spacing", NULL, -1, 1000, 20, G_PARAM_READWRITE); PIKA_PROC_ARG_ENUM (procedure, "paint-mode", "Paint mode", NULL, PIKA_TYPE_LAYER_MODE, PIKA_LAYER_MODE_NORMAL, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "mask-width", "Brush width", NULL, 0, 10000, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "mask-height", "Brush height", NULL, 0, 10000, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_BYTES (procedure, "mask-data", "Mask data", "The brush mask data", G_PARAM_READWRITE); } else if (g_type_is_a (resource_type, PIKA_TYPE_PALETTE)) { PIKA_PROC_ARG_INT (procedure, "num-colors", "Num colors", "Number of colors", 0, G_MAXINT, 0, G_PARAM_READWRITE); } else if (g_type_is_a (resource_type, PIKA_TYPE_PATTERN)) { PIKA_PROC_ARG_INT (procedure, "mask-width", "Mask width", "Pattern width", 0, 10000, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "mask-height", "Mask height", "Pattern height", 0, 10000, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_INT (procedure, "mask-bpp", "Mask bpp", "Pattern bytes per pixel", 0, 10000, 0, G_PARAM_READWRITE); PIKA_PROC_ARG_BYTES (procedure, "mask-data", "Mask data", "The pattern mask data", G_PARAM_READWRITE); } else { g_warning ("%s: unhandled resource type", G_STRFUNC); } PIKA_PROC_ARG_BOOLEAN (procedure, "closing", "Closing", "If the dialog was closing", FALSE, G_PARAM_READWRITE); } /* Open (create) a remote chooser dialog of resource. * * Dispatch on subclass of PikaResource. * Call a PDB procedure that communicates with core to create remote dialog. */ static gboolean popup_remote_chooser (const gchar *title, GBytes *parent_handle, PikaResource *resource, const gchar *temp_PDB_callback_name, GType resource_type) { gboolean result = FALSE; if (g_type_is_a (resource_type, PIKA_TYPE_BRUSH)) { result = pika_brushes_popup (temp_PDB_callback_name, title, PIKA_BRUSH (resource), parent_handle); } else if (g_type_is_a (resource_type, PIKA_TYPE_FONT)) { result = pika_fonts_popup (temp_PDB_callback_name, title, PIKA_FONT (resource), parent_handle); } else if (g_type_is_a (resource_type, PIKA_TYPE_GRADIENT)) { result = pika_gradients_popup (temp_PDB_callback_name, title, PIKA_GRADIENT (resource), parent_handle); } else if (g_type_is_a (resource_type, PIKA_TYPE_PALETTE)) { result = pika_palettes_popup (temp_PDB_callback_name, title, PIKA_PALETTE (resource), parent_handle); } else if (g_type_is_a (resource_type, PIKA_TYPE_PATTERN)) { result = pika_patterns_popup (temp_PDB_callback_name, title, PIKA_PATTERN (resource), parent_handle); } else { g_warning ("%s: unhandled resource type", G_STRFUNC); } return result; } /** * pika_resource_select_new: * @title: Title of the resource selection dialog. * @resource: The resource to set as the initial choice. * @resource_type: The type of the subclass of [class@Resource]. * @callback: (scope notified): The callback function to call when the user chooses a resource. * @owner_data: (closure callback): The run_data given to @callback. * @data_destroy: (destroy owner_data): The destroy function for @owner_data. * * Invoke a resource chooser dialog which may call @callback with the chosen * @resource and @owner_data. * * A proxy to a remote dialog in core, which knows the installed resources. * * Returns: (transfer none): the name of a temporary PDB procedure. The * string belongs to the resource selection dialog and will be * freed automatically when the dialog is closed. **/ const gchar * pika_resource_select_new (const gchar *title, GBytes *parent_handle, PikaResource *resource, GType resource_type, PikaResourceChoosedCallback callback, gpointer owner_data, GDestroyNotify data_destroy) { PikaPlugIn *plug_in = pika_get_plug_in (); PikaProcedure *procedure; gchar *temp_PDB_callback_name; PikaResourceAdaption *adaption; g_return_val_if_fail (resource != NULL, NULL); g_return_val_if_fail (callback != NULL, NULL); g_return_val_if_fail (g_type_is_a (resource_type, PIKA_TYPE_RESOURCE), NULL); temp_PDB_callback_name = pika_pdb_temp_procedure_name (pika_get_pdb ()); adaption = g_slice_new0 (PikaResourceAdaption); adaption->callback = callback; adaption->owner_data = owner_data; adaption->data_destroy = data_destroy; adaption->resource_type = resource_type; procedure = pika_procedure_new (plug_in, temp_PDB_callback_name, PIKA_PDB_PROC_TYPE_TEMPORARY, pika_temp_resource_run, adaption, (GDestroyNotify) pika_resource_data_free); create_callback_PDB_procedure_params (procedure, resource_type); pika_plug_in_add_temp_procedure (plug_in, procedure); g_object_unref (procedure); g_free (temp_PDB_callback_name); if (popup_remote_chooser (title, parent_handle, resource, pika_procedure_get_name (procedure), resource_type)) { /* Allow callbacks to be watched */ pika_plug_in_extension_enable (plug_in); return pika_procedure_get_name (procedure); } else { g_warning ("Failed to open remote resource select dialog."); pika_plug_in_remove_temp_procedure (plug_in, temp_PDB_callback_name); return NULL; } } /* pika_resource_select_set: * @callback_name: a callback name as returned by [func@resource_select_new]. * @resource: a [class@Resource] of type @resource_type. * * Set currently selected resource in remote chooser. */ void pika_resource_select_set (const gchar *callback_name, PikaResource *resource) { GType resource_type; g_return_if_fail (resource != NULL); resource_type = G_TYPE_FROM_INSTANCE (resource); g_return_if_fail (g_type_is_a (resource_type, PIKA_TYPE_RESOURCE)); if (g_type_is_a (resource_type, PIKA_TYPE_FONT)) pika_fonts_set_popup (callback_name, PIKA_FONT (resource)); else if (g_type_is_a (resource_type, PIKA_TYPE_GRADIENT)) pika_gradients_set_popup (callback_name, PIKA_GRADIENT (resource)); else if (g_type_is_a (resource_type, PIKA_TYPE_BRUSH)) pika_brushes_set_popup (callback_name, PIKA_BRUSH (resource)); else if (g_type_is_a (resource_type, PIKA_TYPE_PALETTE)) pika_palettes_set_popup (callback_name, PIKA_PALETTE (resource)); else if (g_type_is_a (resource_type, PIKA_TYPE_PATTERN)) pika_patterns_set_popup (callback_name, PIKA_PATTERN (resource)); else g_return_if_reached (); } /* private functions */ static void pika_resource_data_free (PikaResourceAdaption *adaption) { if (adaption->data_destroy) adaption->data_destroy (adaption->owner_data); g_slice_free (PikaResourceAdaption, adaption); } /* Run func for the temporary PDB procedure. * Called when user chooses a resource in remote dialog. */ static PikaValueArray * pika_temp_resource_run (PikaProcedure *procedure, PikaProcedureConfig *config, gpointer run_data) { PikaResourceAdaption *adaption = run_data; PikaResource *resource; gboolean closing; g_object_get (config, "resource", &resource, "closing", &closing, NULL); if (adaption->callback) adaption->callback (resource, closing, adaption->owner_data); if (closing) g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, (GSourceFunc) pika_resource_select_remove_after_run, g_strdup (pika_procedure_get_name (procedure)), g_free); g_object_unref (resource); return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); } static gboolean pika_resource_select_remove_after_run (const gchar *procedure_name) { pika_plug_in_remove_temp_procedure (pika_get_plug_in (), procedure_name); return G_SOURCE_REMOVE; }