mirror of
https://gitlab.gnome.org/GNOME/gimp.git
synced 2025-07-04 17:59:37 +00:00

Sun Feb 14 01:27:29 GMT 1999 Austin Donnelly <austin@gimp.org> New manpage plus bumper fun pack of bugfixes. * gimprc.5.in: NEW FILE: beginnings of some docs on gimprc file format. * configure.in: generate gimprc.5 from gimprc.5.in * Makefile.am: install gimprc.5 * .cvsignore: ignore gimprc.5, it's auto generated. * gimp.1: fix a few paths and URLs. Mention the modules/ directory in user's gimpdir. * app/commands.c: cancel resize or scale dialogs when image they're for is destroyed, rather than segfaulting when Ok is clicked. Thanks to Peter Teichman <peter@zeno.dorm.duke.edu> for pointing this one out. Layer resize/scale still suffers from same problem, but Adam's working on L&C at the moment. * app/gdisplay.c: off-by one error on bounds check in making image title. * app/module_db.c: some would consider it foolish returning to code you've just unloaded. So don't do that. * app/plug_in.c: when superceeding a PDB function with a newer one of the same name, remove pointers to the old one from the plugins that originally registered them. Fixes Nick Lamb's pluginrc file corruption thing, and catches the (common?) error of copying a plugin to a different name but failing to change what it registers. Also, if registering a file loader/saver, make sure it has set an extension, prefix, or magic number it's interested in - that way code that relies on checking this doesn't get confused.
859 lines
20 KiB
C
859 lines
20 KiB
C
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* module_db.c (C) 1999 Austin Donnelly <austin@gimp.org>
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "appenv.h"
|
|
#include "module_db.h"
|
|
#include "gimpsignal.h"
|
|
#include "gimprc.h"
|
|
#include "datafiles.h"
|
|
#include "actionarea.h"
|
|
#include "gimpset.h"
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#include "libgimp/gimpmodule.h"
|
|
|
|
|
|
typedef enum {
|
|
ST_MODULE_ERROR, /* missing module_load function or other error */
|
|
ST_LOADED_OK, /* happy and running (normal state of affairs) */
|
|
ST_LOAD_FAILED, /* module_load returned GIMP_MODULE_UNLOAD */
|
|
ST_UNLOAD_REQUESTED, /* sent unload request, waiting for callback */
|
|
ST_UNLOADED_OK /* callback arrived, module not in memory anymore */
|
|
} module_state;
|
|
|
|
static const char * const statename[] = {
|
|
"ST_MODULE_ERROR",
|
|
"ST_LOADED_OK",
|
|
"ST_LOAD_FAILED",
|
|
"ST_UNLOAD_REQUESTED",
|
|
"ST_UNLOADED_OK"
|
|
};
|
|
|
|
|
|
/* one of these objects is kept per-module */
|
|
typedef struct {
|
|
GtkObject object;
|
|
gchar *fullpath; /* path to the module */
|
|
module_state state; /* what's happened to the module */
|
|
gboolean ondisk; /* TRUE if file still exists */
|
|
/* stuff from now on may be NULL depending on the state the module is in */
|
|
GimpModuleInfo *info; /* returned values from module_init */
|
|
gint refs; /* how many time we're running in the module */
|
|
GModule *module; /* handle on the module */
|
|
gchar *last_module_error;
|
|
GimpModuleInitFunc *init;
|
|
GimpModuleUnloadFunc *unload;
|
|
} module_info;
|
|
|
|
|
|
static guint module_info_get_type (void);
|
|
#define MODULE_INFO_TYPE module_info_get_type()
|
|
#define MODULE_INFO(obj) GTK_CHECK_CAST (obj, MODULE_INFO_TYPE, module_info)
|
|
#define IS_MODULE_INFO(obj) GTK_CHECK_TYPE (obj, MODULE_INFO_TYPE)
|
|
|
|
|
|
#define NUM_INFO_LINES 7
|
|
|
|
typedef struct {
|
|
GtkWidget *table;
|
|
GtkWidget *label[NUM_INFO_LINES];
|
|
GtkWidget *button_label;
|
|
module_info *last_update;
|
|
GtkWidget *button;
|
|
GtkWidget *list;
|
|
} browser_st;
|
|
|
|
/* global set of module_info pointers */
|
|
static GimpSet *modules;
|
|
|
|
|
|
/*#define DUMP_DB*/
|
|
|
|
|
|
/* prototypes */
|
|
static void module_initialize (char *filename);
|
|
static void mod_load (module_info *mod, gboolean verbose);
|
|
static void mod_unload (module_info *mod, gboolean verbose);
|
|
static module_info *module_find_by_path (const char *fullpath);
|
|
#ifdef DUMP_DB
|
|
static void print_module_info (gpointer data, gpointer user_data);
|
|
#endif
|
|
|
|
static void browser_popdown_callback (GtkWidget *w, gpointer client_data);
|
|
static void browser_destroy_callback (GtkWidget *w, gpointer client_data);
|
|
static void browser_info_update (module_info *, browser_st *);
|
|
static void browser_info_add (GimpSet *, module_info *, browser_st *);
|
|
static void browser_info_remove (GimpSet *, module_info *, browser_st *);
|
|
static void browser_info_init (browser_st *st, GtkWidget *table);
|
|
static void browser_select_callback (GtkWidget *widget, GtkWidget *child);
|
|
static void browser_load_unload_callback (GtkWidget *widget, gpointer data);
|
|
static void browser_refresh_callback (GtkWidget *widget, gpointer data);
|
|
static void make_list_item (gpointer data, gpointer user_data);
|
|
|
|
static void gimp_module_ref (module_info *mod);
|
|
static void gimp_module_unref (module_info *mod);
|
|
|
|
|
|
|
|
/**************************************************************/
|
|
/* Exported functions */
|
|
|
|
void
|
|
module_db_init (void)
|
|
{
|
|
/* Load and initialize gimp modules */
|
|
|
|
modules = gimp_set_new (MODULE_INFO_TYPE, FALSE);
|
|
|
|
if (g_module_supported ())
|
|
datafiles_read_directories (module_path,
|
|
module_initialize, 0 /* no flags */);
|
|
#ifdef DUMP_DB
|
|
gimp_set_foreach (modules, print_module_info, NULL);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
GtkWidget *
|
|
module_db_browser_new (void)
|
|
{
|
|
GtkWidget *shell;
|
|
GtkWidget *hbox;
|
|
GtkWidget *vbox;
|
|
GtkWidget *listbox;
|
|
GtkWidget *button;
|
|
browser_st *st;
|
|
ActionAreaItem action_items[] =
|
|
{
|
|
{ N_("OK"), browser_popdown_callback, NULL, NULL }
|
|
};
|
|
|
|
|
|
shell = gtk_dialog_new ();
|
|
gtk_window_set_wmclass (GTK_WINDOW (shell), "module_db_dialog", "Gimp");
|
|
gtk_window_set_title (GTK_WINDOW (shell), _("Module DB"));
|
|
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (shell)->vbox), hbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (hbox);
|
|
|
|
listbox = gtk_scrolled_window_new (NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (listbox),
|
|
GTK_POLICY_AUTOMATIC,
|
|
GTK_POLICY_AUTOMATIC);
|
|
gtk_box_pack_start (GTK_BOX (hbox), listbox, TRUE, TRUE, 0);
|
|
gtk_widget_set_usize (listbox, 125, 100);
|
|
gtk_widget_show (listbox);
|
|
|
|
st = g_new0 (browser_st, 1);
|
|
|
|
st->list = gtk_list_new ();
|
|
gtk_list_set_selection_mode (GTK_LIST (st->list), GTK_SELECTION_BROWSE);
|
|
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (listbox),
|
|
st->list);
|
|
|
|
gimp_set_foreach (modules, make_list_item, st);
|
|
|
|
gtk_widget_show (st->list);
|
|
|
|
vbox = gtk_vbox_new (FALSE, 10);
|
|
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
|
|
gtk_widget_show (vbox);
|
|
|
|
st->table = gtk_table_new (5, NUM_INFO_LINES, FALSE);
|
|
gtk_box_pack_start (GTK_BOX (vbox), st->table, FALSE, FALSE, 0);
|
|
gtk_widget_show (st->table);
|
|
|
|
hbox = gtk_hbox_new (FALSE, 10);
|
|
gtk_widget_show (hbox);
|
|
gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
|
|
|
|
button = gtk_button_new_with_label (_("Refresh"));
|
|
gtk_widget_show (button);
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
browser_refresh_callback, st);
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
|
|
|
|
st->button = gtk_button_new_with_label ("");
|
|
st->button_label = GTK_BIN (st->button)->child;
|
|
gtk_box_pack_start (GTK_BOX (hbox), st->button, TRUE, TRUE, 0);
|
|
gtk_widget_show (st->button);
|
|
gtk_signal_connect (GTK_OBJECT (st->button), "clicked",
|
|
browser_load_unload_callback, st);
|
|
|
|
browser_info_init (st, st->table);
|
|
browser_info_update (st->last_update, st);
|
|
|
|
gtk_object_set_user_data (GTK_OBJECT (st->list), st);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (st->list), "select_child",
|
|
browser_select_callback, NULL);
|
|
|
|
/* hook the gimpset signals so we can refresh the display
|
|
* appropriately. */
|
|
gimp_set_add_handler (modules, "modified", browser_info_update, st);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (modules), "add",
|
|
browser_info_add, st);
|
|
gtk_signal_connect (GTK_OBJECT (modules), "remove",
|
|
browser_info_remove, st);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (shell), "destroy",
|
|
browser_destroy_callback, st);
|
|
|
|
action_items[0].user_data = shell;
|
|
build_action_area (GTK_DIALOG (shell),
|
|
action_items,
|
|
sizeof( action_items)/sizeof( ActionAreaItem),
|
|
0);
|
|
|
|
return shell;
|
|
}
|
|
|
|
|
|
/**************************************************************/
|
|
/* module_info object glue */
|
|
|
|
|
|
typedef struct {
|
|
GtkObjectClass parent_class;
|
|
} module_infoClass;
|
|
|
|
enum {
|
|
MODIFIED,
|
|
LAST_SIGNAL
|
|
};
|
|
static guint module_info_signals[LAST_SIGNAL];
|
|
|
|
|
|
static void
|
|
module_info_destroy (GtkObject *object)
|
|
{
|
|
module_info *mod = MODULE_INFO (object);
|
|
|
|
if (mod->last_module_error)
|
|
g_free (mod->last_module_error);
|
|
g_free (mod->fullpath);
|
|
}
|
|
|
|
static void
|
|
module_info_class_init (module_infoClass *klass)
|
|
{
|
|
GtkObjectClass *object_class;
|
|
GtkType type;
|
|
|
|
object_class = GTK_OBJECT_CLASS(klass);
|
|
|
|
type = object_class->type;
|
|
|
|
object_class->destroy = module_info_destroy;
|
|
|
|
module_info_signals[MODIFIED] =
|
|
gimp_signal_new ("modified", 0, type, 0, gimp_sigtype_void);
|
|
|
|
gtk_object_class_add_signals(object_class, module_info_signals, LAST_SIGNAL);
|
|
}
|
|
|
|
static void
|
|
module_info_init (module_info *mod)
|
|
{
|
|
/* don't need to do anything */
|
|
}
|
|
|
|
static guint
|
|
module_info_get_type (void)
|
|
{
|
|
static guint module_info_type = 0;
|
|
|
|
if (!module_info_type)
|
|
{
|
|
static const GtkTypeInfo module_info_info =
|
|
{
|
|
"module_info",
|
|
sizeof (module_info),
|
|
sizeof (module_infoClass),
|
|
(GtkClassInitFunc) module_info_class_init,
|
|
(GtkObjectInitFunc) module_info_init,
|
|
/* reserved_1 */ NULL,
|
|
/* reserved_2 */ NULL,
|
|
(GtkClassInitFunc) NULL,
|
|
};
|
|
|
|
module_info_type = gtk_type_unique (gtk_object_get_type(),
|
|
&module_info_info);
|
|
}
|
|
|
|
return module_info_type;
|
|
}
|
|
|
|
/* exported API: */
|
|
|
|
static void
|
|
module_info_modified (module_info *mod)
|
|
{
|
|
gtk_signal_emit (GTK_OBJECT (mod), module_info_signals[MODIFIED]);
|
|
}
|
|
|
|
static module_info *
|
|
module_info_new (void)
|
|
{
|
|
return MODULE_INFO (gtk_type_new (module_info_get_type ()));
|
|
}
|
|
|
|
static void
|
|
module_info_free (module_info *mod)
|
|
{
|
|
gtk_object_unref (GTK_OBJECT (mod));
|
|
}
|
|
|
|
|
|
/**************************************************************/
|
|
/* helper functions */
|
|
|
|
|
|
/* name must be of the form lib*.so */
|
|
/* TODO: need support for WIN32-style dll names. Maybe this function
|
|
* should live in libgmodule? */
|
|
static gboolean
|
|
valid_module_name (const char *filename)
|
|
{
|
|
const char *basename;
|
|
int len;
|
|
|
|
basename = strrchr (filename, '/');
|
|
if (basename)
|
|
basename++;
|
|
else
|
|
basename = filename;
|
|
|
|
len = strlen (basename);
|
|
|
|
if (len < 3 + 1 + 3)
|
|
return FALSE;
|
|
|
|
if (strncmp (basename, "lib", 3))
|
|
return FALSE;
|
|
|
|
if (strcmp (basename + len - 3, ".so"))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
module_initialize (char *filename)
|
|
{
|
|
module_info *mod;
|
|
|
|
if (!valid_module_name (filename))
|
|
return;
|
|
|
|
/* don't load if we already know about it */
|
|
if (module_find_by_path (filename))
|
|
return;
|
|
|
|
mod = module_info_new ();
|
|
|
|
mod->fullpath = g_strdup (filename);
|
|
mod->ondisk = TRUE;
|
|
|
|
if ((be_verbose == TRUE) || (no_splash == TRUE))
|
|
g_print (_("load module: \"%s\"\n"), filename);
|
|
|
|
mod_load (mod, TRUE);
|
|
|
|
gimp_set_add (modules, mod);
|
|
}
|
|
|
|
static void
|
|
mod_load (module_info *mod, gboolean verbose)
|
|
{
|
|
gpointer symbol;
|
|
|
|
g_return_if_fail (mod->module == NULL);
|
|
|
|
mod->module = g_module_open (mod->fullpath, G_MODULE_BIND_LAZY);
|
|
if (!mod->module)
|
|
{
|
|
mod->state = ST_MODULE_ERROR;
|
|
|
|
if (mod->last_module_error)
|
|
g_free (mod->last_module_error);
|
|
mod->last_module_error = g_strdup (g_module_error ());
|
|
|
|
if (verbose)
|
|
g_warning (_("module load error: %s: %s"),
|
|
mod->fullpath, mod->last_module_error);
|
|
return;
|
|
}
|
|
|
|
/* find the module_init symbol */
|
|
if (!g_module_symbol (mod->module, "module_init", &symbol))
|
|
{
|
|
mod->state = ST_MODULE_ERROR;
|
|
|
|
if (mod->last_module_error)
|
|
g_free (mod->last_module_error);
|
|
mod->last_module_error = g_strdup (_("missing module_init() symbol"));
|
|
|
|
if (verbose)
|
|
g_warning (_("%s: module_init() symbol not found"), mod->fullpath);
|
|
|
|
g_module_close (mod->module);
|
|
mod->module = NULL;
|
|
mod->info = NULL;
|
|
return;
|
|
}
|
|
|
|
/* run module's initialisation */
|
|
mod->init = symbol;
|
|
mod->info = NULL;
|
|
if (mod->init (&mod->info) == GIMP_MODULE_UNLOAD)
|
|
{
|
|
mod->state = ST_LOAD_FAILED;
|
|
g_module_close (mod->module);
|
|
mod->module = NULL;
|
|
mod->info = NULL;
|
|
return;
|
|
}
|
|
|
|
/* module is now happy */
|
|
mod->state = ST_LOADED_OK;
|
|
|
|
/* do we have an unload function? */
|
|
if (g_module_symbol (mod->module, "module_unload", &symbol))
|
|
mod->unload = symbol;
|
|
else
|
|
mod->unload = NULL;
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
mod_unload_completed_callback (void *data)
|
|
{
|
|
module_info *mod = data;
|
|
|
|
g_return_if_fail (mod->state == ST_UNLOAD_REQUESTED);
|
|
|
|
if (mod->refs == 0)
|
|
{
|
|
g_module_close (mod->module);
|
|
mod->module = NULL;
|
|
}
|
|
mod->info = NULL;
|
|
|
|
mod->state = ST_UNLOADED_OK;
|
|
|
|
module_info_modified (mod);
|
|
}
|
|
|
|
static void
|
|
mod_unload (module_info *mod, gboolean verbose)
|
|
{
|
|
g_return_if_fail (mod->module != NULL);
|
|
g_return_if_fail (mod->unload != NULL);
|
|
|
|
if (mod->state == ST_UNLOAD_REQUESTED)
|
|
return;
|
|
|
|
mod->state = ST_UNLOAD_REQUESTED;
|
|
|
|
/* send the unload request. Need to ref the module so we don't
|
|
* accidentally unload it while this call is in progress (eg if the
|
|
* callback is called before the unload function returns). */
|
|
gimp_module_ref (mod);
|
|
mod->unload (mod->info->shutdown_data, mod_unload_completed_callback, mod);
|
|
gimp_module_unref (mod);
|
|
}
|
|
|
|
|
|
|
|
#ifdef DUMP_DB
|
|
static void
|
|
print_module_info (gpointer data, gpointer user_data)
|
|
{
|
|
module_info *i = data;
|
|
|
|
printf ("\n%s: %s\n",
|
|
i->fullpath, statename[i->state]);
|
|
printf (" module:%p lasterr:%s init:%p unload:%p\n",
|
|
i->module, i->last_module_error? i->last_module_error : "NONE",
|
|
i->init, i->unload);
|
|
if (i->info)
|
|
{
|
|
printf (" shutdown_data: %p\n"
|
|
" purpose: %s\n"
|
|
" author: %s\n"
|
|
" version: %s\n"
|
|
" copyright: %s\n"
|
|
" date: %s\n",
|
|
i->info->shutdown_data,
|
|
i->info->purpose, i->info->author, i->info->version,
|
|
i->info->copyright, i->info->date);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/**************************************************************/
|
|
/* UI functions */
|
|
|
|
static void
|
|
browser_popdown_callback (GtkWidget *w, gpointer client_data)
|
|
{
|
|
gtk_widget_destroy (GTK_WIDGET (client_data));
|
|
}
|
|
|
|
static void
|
|
browser_destroy_callback (GtkWidget *w, gpointer client_data)
|
|
{
|
|
gtk_signal_disconnect_by_data (GTK_OBJECT (modules), client_data);
|
|
g_free (client_data);
|
|
}
|
|
|
|
|
|
static void
|
|
browser_info_update (module_info *mod, browser_st *st)
|
|
{
|
|
int i;
|
|
const char *text[NUM_INFO_LINES - 1];
|
|
char *status;
|
|
|
|
/* only update the info if we're actually showing it */
|
|
if (mod != st->last_update)
|
|
return;
|
|
|
|
if (!mod)
|
|
{
|
|
for (i=0; i < NUM_INFO_LINES; i++)
|
|
gtk_label_set_text (GTK_LABEL (st->label[i]), "");
|
|
gtk_label_set_text (GTK_LABEL(st->button_label), _("<No modules>"));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (st->button), FALSE);
|
|
return;
|
|
}
|
|
|
|
if (mod->info)
|
|
{
|
|
text[0] = mod->info->purpose;
|
|
text[1] = mod->info->author;
|
|
text[2] = mod->info->version;
|
|
text[3] = mod->info->copyright;
|
|
text[4] = mod->info->date;
|
|
text[5] = mod->ondisk? _("on disk") : _("only in memory");
|
|
}
|
|
else
|
|
{
|
|
text[0] = "--";
|
|
text[1] = "--";
|
|
text[2] = "--";
|
|
text[3] = "--";
|
|
text[4] = "--";
|
|
text[5] = mod->ondisk? _("on disk") : _("nowhere (click 'refresh')");
|
|
}
|
|
|
|
|
|
if (mod->state == ST_MODULE_ERROR && mod->last_module_error)
|
|
{
|
|
gulong len = strlen (statename[mod->state]) + 2 +
|
|
strlen (mod->last_module_error) + 2;
|
|
|
|
status = g_malloc (len);
|
|
g_snprintf (status, len,
|
|
"%s (%s)", statename[mod->state], mod->last_module_error);
|
|
}
|
|
else
|
|
{
|
|
status = g_strdup (statename[mod->state]);
|
|
}
|
|
|
|
for (i=0; i < NUM_INFO_LINES - 1; i++)
|
|
{
|
|
gtk_label_set_text (GTK_LABEL (st->label[i]), text[i]);
|
|
}
|
|
|
|
gtk_label_set_text (GTK_LABEL (st->label[NUM_INFO_LINES-1]), status);
|
|
|
|
g_free (status);
|
|
|
|
/* work out what the button should do (if anything) */
|
|
switch (mod->state) {
|
|
case ST_MODULE_ERROR:
|
|
case ST_LOAD_FAILED:
|
|
case ST_UNLOADED_OK:
|
|
gtk_label_set_text (GTK_LABEL(st->button_label), _("Load"));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (st->button), mod->ondisk);
|
|
break;
|
|
|
|
case ST_UNLOAD_REQUESTED:
|
|
gtk_widget_set_sensitive (GTK_WIDGET (st->button), FALSE);
|
|
break;
|
|
|
|
case ST_LOADED_OK:
|
|
gtk_label_set_text (GTK_LABEL(st->button_label), _("Unload"));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (st->button),
|
|
mod->unload? TRUE : FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
browser_info_init (browser_st *st, GtkWidget *table)
|
|
{
|
|
GtkWidget *label;
|
|
int i;
|
|
char *text[] = {
|
|
N_("Purpose: "),
|
|
N_("Author: "),
|
|
N_("Version: "),
|
|
N_("Copyright: "),
|
|
N_("Date: "),
|
|
N_("Location: "),
|
|
N_("State: ")
|
|
};
|
|
|
|
for (i=0; i < sizeof(text) / sizeof(char *); i++)
|
|
{
|
|
label = gtk_label_new (gettext (text[i]));
|
|
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
|
|
gtk_table_attach (GTK_TABLE (table), label, 0, 1, i, i+1,
|
|
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
|
|
gtk_widget_show (label);
|
|
|
|
st->label[i] = gtk_label_new ("");
|
|
gtk_misc_set_alignment (GTK_MISC (st->label[i]), 0.0, 0.5);
|
|
gtk_table_attach (GTK_TABLE (st->table), st->label[i], 1, 2, i, i+1,
|
|
GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 2);
|
|
gtk_widget_show (st->label[i]);
|
|
}
|
|
}
|
|
|
|
static void
|
|
browser_select_callback (GtkWidget *widget, GtkWidget *child)
|
|
{
|
|
module_info *i;
|
|
browser_st *st;
|
|
|
|
i = gtk_object_get_user_data (GTK_OBJECT (child));
|
|
st = gtk_object_get_user_data (GTK_OBJECT (widget));
|
|
|
|
if (st->last_update == i)
|
|
return;
|
|
|
|
st->last_update = i;
|
|
|
|
browser_info_update (st->last_update, st);
|
|
}
|
|
|
|
|
|
static void
|
|
browser_load_unload_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
browser_st *st = data;
|
|
|
|
if (st->last_update->state == ST_LOADED_OK)
|
|
mod_unload (st->last_update, FALSE);
|
|
else
|
|
mod_load (st->last_update, FALSE);
|
|
|
|
module_info_modified (st->last_update);
|
|
}
|
|
|
|
|
|
static void
|
|
make_list_item (gpointer data, gpointer user_data)
|
|
{
|
|
module_info *info = data;
|
|
browser_st *st = user_data;
|
|
GtkWidget *list_item;
|
|
|
|
if (!st->last_update)
|
|
st->last_update = info;
|
|
|
|
list_item = gtk_list_item_new_with_label (info->fullpath);
|
|
|
|
gtk_widget_show (list_item);
|
|
gtk_object_set_user_data (GTK_OBJECT (list_item), info);
|
|
|
|
gtk_container_add (GTK_CONTAINER (st->list), list_item);
|
|
}
|
|
|
|
|
|
static void
|
|
browser_info_add (GimpSet *set, module_info *mod, browser_st *st)
|
|
{
|
|
make_list_item (mod, st);
|
|
}
|
|
|
|
|
|
static void
|
|
browser_info_remove (GimpSet *set, module_info *mod, browser_st *st)
|
|
{
|
|
GList *dlist, *free_list;
|
|
GtkWidget *list_item;
|
|
module_info *i;
|
|
|
|
dlist = gtk_container_children (GTK_CONTAINER (st->list));
|
|
free_list = dlist;
|
|
|
|
while (dlist)
|
|
{
|
|
list_item = dlist->data;
|
|
|
|
i = gtk_object_get_user_data (GTK_OBJECT (list_item));
|
|
g_return_if_fail (i != NULL);
|
|
|
|
if (i == mod)
|
|
{
|
|
gtk_container_remove (GTK_CONTAINER (st->list), list_item);
|
|
g_list_free(free_list);
|
|
return;
|
|
}
|
|
|
|
dlist = dlist->next;
|
|
}
|
|
|
|
g_warning ("tried to remove module that wasn't in brower's list");
|
|
g_list_free(free_list);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
module_db_module_ondisk (gpointer data, gpointer user_data)
|
|
{
|
|
module_info *mod = data;
|
|
struct stat statbuf;
|
|
int ret;
|
|
int old_ondisk = mod->ondisk;
|
|
GSList **kill_list = user_data;
|
|
|
|
ret = stat (mod->fullpath, &statbuf);
|
|
if (ret != 0)
|
|
mod->ondisk = FALSE;
|
|
else
|
|
mod->ondisk = TRUE;
|
|
|
|
/* if it's not on the disk, and it isn't in memory, mark it to be
|
|
* removed later. */
|
|
if (!mod->ondisk && !mod->module)
|
|
{
|
|
*kill_list = g_slist_append (*kill_list, mod);
|
|
mod = NULL;
|
|
}
|
|
|
|
if (mod && mod->ondisk != old_ondisk)
|
|
module_info_modified (mod);
|
|
}
|
|
|
|
|
|
static void
|
|
module_db_module_remove (gpointer data, gpointer user_data)
|
|
{
|
|
module_info *mod = data;
|
|
|
|
gimp_set_remove (modules, mod);
|
|
|
|
module_info_free (mod);
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
const char *search_key;
|
|
module_info *found;
|
|
} find_by_path_closure;
|
|
|
|
static void
|
|
module_db_path_cmp (gpointer data, gpointer user_data)
|
|
{
|
|
module_info *mod = data;
|
|
find_by_path_closure *cl = user_data;
|
|
|
|
if (!strcmp (mod->fullpath, cl->search_key))
|
|
cl->found = mod;
|
|
}
|
|
|
|
static module_info *
|
|
module_find_by_path (const char *fullpath)
|
|
{
|
|
find_by_path_closure cl;
|
|
|
|
cl.found = NULL;
|
|
cl.search_key = fullpath;
|
|
|
|
gimp_set_foreach (modules, module_db_path_cmp, &cl);
|
|
|
|
return cl.found;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
browser_refresh_callback (GtkWidget *widget, gpointer data)
|
|
{
|
|
GSList *kill_list = NULL;
|
|
|
|
/* remove modules we don't have on disk anymore */
|
|
gimp_set_foreach (modules, module_db_module_ondisk, &kill_list);
|
|
g_slist_foreach (kill_list, module_db_module_remove, NULL);
|
|
g_slist_free (kill_list);
|
|
kill_list = NULL;
|
|
|
|
/* walk filesystem and add new things we find */
|
|
datafiles_read_directories (module_path,
|
|
module_initialize, 0 /* no flags */);
|
|
}
|
|
|
|
|
|
static void
|
|
gimp_module_ref (module_info *mod)
|
|
{
|
|
g_return_if_fail (mod->refs >= 0);
|
|
g_return_if_fail (mod->module != NULL);
|
|
mod->refs++;
|
|
}
|
|
|
|
static void
|
|
gimp_module_unref (module_info *mod)
|
|
{
|
|
g_return_if_fail (mod->refs > 0);
|
|
g_return_if_fail (mod->module != NULL);
|
|
|
|
mod->refs--;
|
|
|
|
if (mod->refs == 0)
|
|
{
|
|
g_module_close (mod->module);
|
|
mod->module = NULL;
|
|
}
|
|
}
|
|
|
|
/* End of module_db.c */
|