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

Reply via email to