Well I'm back from holiday, and with me comes my first cut at plugin
support for glade. Currently tested with plugins for GtkWindow,
GtkLabel, and GtkHBox. At the moment I rely on a configuration file in
the users home directory .glade-custom. This file defines a list of
custom palettes. ATM, each palette entry explicitly specifies all the
widgets included in that palette, this is fine for now but I hope to
permit users to specify palette definition files in the future.
Each widget is described by a custom glade widget file (.cgw) which
describes the widget. For now the only format supported is GbWidget,
but there is room for GtkArg/Param support later.
For now all I have really done is expose the GbWidget code, and
palette_add_gbwidget() in palette.[ch].
So, to convert any GbWidget in glade/gbwidgets/* to a plugin you just
rename the init function to
GbWidget *gb_widget_init();
write the xml file to describe the widget, and add an entry to
~/.glade-custom to include it in a palette. A new palette is implicitly
created in palette_add_gbwidget() when you add the first widget to it,
so experimenting with new palette layouts is as simple as
reordering/editing the palette entries in the config file.
My final goal in this is to enable Gtk/Gnome widget writers to trivially
provide glade support. Ideally a developer could download the source
tarball for a new widget/widgetset, untar, ./configure, make install and
the new palette description file along with the various widget
description files would become visible to: glade, for inclusion in the
developers personal palette list; and libglade for so all users on a
system could then use libglade based applications using those widgets.
So my next tasks, which I'll start on Tuesday (now I'm back off holiday
and have my net connection back :), will be :
1/ Seperate user palette files from widget-library palette files.
2/ Look into libglade plugin support.
3/ Default gbwidget function support to reduce glade support effort
required of widget writers.
Andrae Muys
P.S. I think these are all the files required. I don't know
autoconf/automake so I'm unsure how to include these files into a proper
glade compile. Also note that the plugin support in main.c is bracketed
by #ifdef ENABLE_PLUGIN/#endif.
*** glade-0.5.11/glade/main.c Fri Aug 11 06:47:28 2000
--- glade-plugins/glade/main.c Wed Jan 17 09:12:08 2001
***************
*** 39,44 ****
--- 39,47 ----
#include "glade_project.h"
#include "glade_project_window.h"
#include "utils.h"
+ #ifdef ENABLE_PLUGIN
+ #include "plugin.h"
+ #endif
/* These are the arguments parsed from the command line. */
static gchar *arg_filename = NULL; /* The XML file to load on start-up. */
***************
*** 80,86 ****
int
main (int argc, char *argv[])
{
! gchar *home_dir, *rc_path;
#ifdef ENABLE_BONOBO
#ifndef USING_OAF
CORBA_Environment ev;
--- 83,89 ----
int
main (int argc, char *argv[])
{
! gchar *home_dir, *rc_path, *config_path;
#ifdef ENABLE_BONOBO
#ifndef USING_OAF
CORBA_Environment ev;
***************
*** 136,141 ****
--- 139,153 ----
parse_command_line (argc, argv);
+ #ifdef ENABLE_PLUGIN
+ g_message("Loading plugins...");
+ home_dir = g_get_home_dir();
+ config_path = g_strdup_printf ("%s/.glade-custom", home_dir);
+ if (glade_util_file_exists(config_path)) {
+ load_custom_widgets(config_path);
+ }
+ #endif
+
/* If the --write-source option is passed, we just write the source and exit
without even entering the GTK+ main loop. */
if (arg_write_source)
***************
*** 152,158 ****
#endif
return 0;
}
-
/* Currently the only command-line argument we handle is an XML file to load.
For Gnome we have to use popt, even though we have no options. */
--- 164,169 ----
/* Gtk+ User Interface Builder
* Copyright (C) 1998-2000 Damon Chaplin
*
* 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.
*/
#ifndef PLUGIN_H
#define PLUGIN_H
#include "glade.h"
#include <gnome.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/tree.h>
void load_custom_widgets(char *file_path);
#endif
/* Gtk+ User Interface Builder
* Copyright (C) 1998-2000 Damon Chaplin
*
* 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 "plugin.h"
#include "glade.h"
#include "palette.h"
#include "utils.h"
#include <gnome.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/tree.h>
#include <gmodule.h>
#include <string.h>
static void parse_palette (xmlNodePtr palette);
static void parse_widget (xmlNodePtr widget, const gchar *section);
static void load_widget(const gchar *name, const gchar *desc, const gchar *file_path,
const gchar *section);
void load_custom_widgets(char *file_path) {
xmlDocPtr doc;
xmlNodePtr palette;
if (!g_module_supported()) {
g_warning("gmodule not supported, custom widgets not loaded.");
return;
}
doc = xmlParseFile(file_path);
if (!doc) {
g_warning("No custom widget config file found.");
return;
}
if (!doc->root) {
g_warning("Invalid custom widget config file.");
return;
}
palette = doc->root->childs;
while(palette) {
parse_palette(palette);
palette = palette->next;
}
}
static void
parse_palette (xmlNodePtr palette) {
xmlNodePtr name;
xmlNodePtr list;
gchar *section;
xmlNodePtr widget;
name = palette->childs;
if (!name) {
g_warning("Invalid Palette List");
return;
}
list = name->next;
if (!list) {
g_warning("Invalid Palette List");
return;
}
if (!strcmp(name->name, "name")) {
section = name->childs->content;
} else {
g_warning("Invalid palette entry");
return;
}
if (!strcmp(list->name, "widget-list")) {
widget = list->childs;
while (widget) {
parse_widget (widget, section);
widget = widget->next;
}
} else {
g_warning("No widget list found.");
return;
}
}
static void
parse_widget (xmlNodePtr widget, const gchar *section) {
xmlNodePtr prop;
gchar *name = NULL;
gchar *desc = NULL;
gchar *file = NULL;
prop = widget->childs;
while(prop) {
if (!strcmp(prop->name, "name") && prop->childs) {
name = prop->childs->content;
} else if (!strcmp(prop->name, "description") && prop->childs) {
desc = prop->childs->content;
} else if (!strcmp(prop->name, "desc-file") && prop->childs) {
file = prop->childs->content;
}
prop = prop->next;
}
if (!name || !desc || !file) {
g_warning("Incomplete widget entry");
return;
}
g_message("Loading %s : \"%s\" from %s", name, desc, file);
if (!glade_util_file_exists(file)) {
g_warning("File %s Not Found.", file);
return;
}
load_widget(name, desc, file, section);
}
typedef GbWidget *(*GbWidgetInitFuncPtr)();
static void
load_widget(const gchar *name, const gchar *desc, const gchar *file_path,
const gchar *section) {
xmlDocPtr doc;
xmlNodePtr node;
gchar *libdir;
gchar *lib;
gchar *libpath;
GModule *module;
GbWidget *gbwidget;
GbWidgetInitFuncPtr initFunc;
doc = xmlParseFile(file_path);
if (!doc) {
g_warning("Failed to parse %s.", file_path);
return;
}
node = doc->root;
if (strcmp(node->name, "custom-widget") || !node->childs) {
g_warning("Invalid File Format");
return;
}
node = node->childs;
while (node) {
if (!strcmp(node->name, "name")) {
// Hash table based on this.
if (strcmp(name, node->childs->content)) {
g_warning("Name in widget description file dosn't
match name in catalogue. %s vs. %s.", name, node->childs->content);
g_warning("Using Catalogue name");
}
} else if (!strcmp(node->name, "parent")) {
// Unused?
} else if (!strcmp(node->name, "tooltip")) {
// gbwidget->tooltip = g_strdup(node->name);
} else if (!strcmp(node->name, "xpm")) {
// Load xpm and assign to pixmap_struct.
} else if (!strcmp(node->name, "library")) {
lib = node->childs->content;
} else if (!strcmp(node->name, "libdir")) {
libdir = node->childs->content;
} else if (!strcmp(node->name, "properties")) {
// Will be used for default property handling.
}
node = node->next;
}
libpath = g_module_build_path(libdir, lib);
g_message("Loading module %s", libpath);
module = g_module_open(libpath, 0);
if (!g_module_symbol(module, "gb_widget_init", &initFunc)) {
g_warning("Init function not found. gtk_type_new() not supported in
this release.");
return;
}
gbwidget = initFunc();
g_module_make_resident(module);
gb_widget_register_gbwidget(name, gbwidget);
palette_add_gbwidget(gbwidget, section, name);
}
plugins.tgz