/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* RDF viewer Evolution Executive Summary Component.
* Bonoboised by Iain Holmes <iain@ximian.com>
* Copyright (C) 2000 Ximian, Inc.
*
* Based on code from Portaloo
* Channel retrieval tool
*
* (C) 1998 Alan Cox.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <gnome.h>
#include <bonobo.h>
#include <gnome-xml/parser.h>
#include <gnome-xml/xmlmemory.h>
#include <evolution-services/executive-summary-component.h>
#include <evolution-services/executive-summary-html-view.h>
#include <liboaf/liboaf.h>
#include <libgnomevfs/gnome-vfs.h>
int xmlSubstituteEntitiesDefaultValue = 1; /* DV thinks of everything */
static int wipe_trackers = FALSE;
static int running_views = 0;
static BonoboGenericFactory *factory = NULL;
#define RDF_SUMMARY_ID "OAFIID:GNOME_Evolution_Summary_rdf_SummaryComponentFactory"
enum {
PROPERTY_TITLE,
PROPERTY_ICON
};
struct _RdfSummary {
BonoboObject *component;
BonoboObject *view;
BonoboPropertyBag *bag;
BonoboPropertyControl *property_control;
GtkWidget *rdf;
GtkWidget *g_limit;
GtkWidget *g_title;
char *title;
char *icon;
char *location;
int limit;
gboolean showtitle;
GString *str;
char *buffer;
GnomeVFSAsyncHandle *handle;
};
typedef struct _RdfSummary RdfSummary;
/************ RDF Parser *******************/
static char *
layer_find (xmlNodePtr node,
char *match,
char *fail)
{
while (node!=NULL) {
#ifdef RDF_DEBUG
xmlDebugDumpNode (stdout, node, 32);
printf("%s.\n", node->name);
#endif
if (strcasecmp (node->name, match)==0) {
if (node->childs != NULL && node->childs->content != NULL) {
return node->childs->content;
} else {
return fail;
}
}
node = node->next;
}
return fail;
}
static char *
layer_find_url (xmlNodePtr node,
char *match,
char *fail)
{
char *p = layer_find (node, match, fail);
char *r = p;
static char *wb = NULL;
char *w;
if (wb) {
free (wb);
}
wb = w = malloc (3 * strlen (p));
if (w == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
if (*r == ' ') r++; /* Fix UF bug */
while (*r) {
if (memcmp (r, "&", 5) == 0) {
*w++ = '&';
r += 5;
continue;
}
if (memcmp (r, "<", 4) == 0) {
*w++ = '<';
r += 4;
continue;
}
if (memcmp (r, ">", 4) == 0) {
*w++ = '>';
r += 4;
continue;
}
if (*r == '"' || *r == ' '){
*w++ = '%';
*w++ = "0123456789ABCDEF"[*r/16];
*w++ = "0123456789ABCDEF"[*r&15];
r++;
continue;
}
*w++ = *r++;
}
*w = 0;
return wb;
}
static void
tree_walk (xmlNodePtr root,
RdfSummary *summary,
GString *html)
{
BonoboArg *arg;
xmlNodePtr walk;
xmlNodePtr rewalk = root;
xmlNodePtr channel = NULL;
xmlNodePtr image = NULL;
xmlNodePtr item[16];
int items = 0;
int limit = summary->limit;
int i;
char *t;
char n[512];
char *tmp;
do {
walk = rewalk;
rewalk = NULL;
while (walk!=NULL){
#ifdef RDF_DEBUG
printf ("%p, %s\n", walk, walk->name);
#endif
if (strcasecmp (walk->name, "rdf") == 0) {
rewalk = walk->childs;
walk = walk->next;
continue;
}
if (strcasecmp (walk->name, "rss") == 0){
rewalk = walk->childs;
walk = walk->next;
continue;
}
/* This is the channel top level */
#ifdef RDF_DEBUG
printf ("Top level '%s'.\n", walk->name);
#endif
if (strcasecmp (walk->name, "channel") == 0) {
channel = walk;
rewalk = channel->childs;
}
if (strcasecmp (walk->name, "image") == 0) {
image = walk;
g_print ("Image\n");
}
if (strcasecmp (walk->name, "item") == 0 && items < 16) {
item[items++] = walk;
}
walk = walk->next;
}
}
while (rewalk);
if (channel == NULL) {
fprintf(stderr, "No channel definition.\n");
exit(1);
}
t = layer_find(channel->childs, "title", "No title");
arg = bonobo_arg_new (BONOBO_ARG_STRING);
if (summary->showtitle) {
BONOBO_ARG_SET_STRING (arg, t);
} else {
BONOBO_ARG_SET_STRING (arg, "");
}
bonobo_property_bag_set_value (summary->bag,
"window_title",
(const BonoboArg *) arg, NULL);
bonobo_arg_release (arg);
if (image && !wipe_trackers) {
char *icon;
icon = layer_find_url (image->childs, "url", "apple-red.png");
arg = bonobo_arg_new (BONOBO_ARG_STRING);
BONOBO_ARG_SET_STRING (arg, icon);
bonobo_property_bag_set_value (summary->bag,
"window_icon",
(const BonoboArg *) arg, NULL);
bonobo_arg_release (arg);
}
g_string_append (html, "<br clear=all><FONT size=\"-1\" face=\"helvetica\"><P><UL>\n");
items = MIN (limit, items);
for (i = 0; i < items; i++) {
char *p = layer_find (item[i]->childs, "title", "No information");
if (wipe_trackers) {
char *p = layer_find_url (item[i]->childs, "link", "");
char *x = strchr (p, '?');
unsigned char *r, *w;
int n;
if (x == NULL)
continue;
x++;
r = x;
w = x;
while (*r) {
if (*r == '+') {
*w++ = ' ';
} else if (*r == '%') {
sscanf (r+1, "%02x", &n);
*w++ = n;
r += 2;
} else {
*w++ = *r;
}
r++;
}
*w = 0;
tmp = g_strdup_printf ("<LI><A href=\"%s\">\n", x+4);
g_string_append (html, tmp);
g_free (tmp);
}
else {
tmp = g_strdup_printf ("<LI><A href=\"%s\">\n", layer_find_url(item[i]->childs, "link", ""));
g_string_append (html, tmp);
g_free (tmp);
}
tmp = g_strdup_printf ("%s\n</A>\n", p);
g_string_append (html, tmp);
g_free (tmp);
}
g_string_append (html, "</UL></FONT>\n");
}
/********* ExecutiveSummaryComponent section **************/
static void
view_destroyed (GtkObject *object,
gpointer data)
{
RdfSummary *summary = (RdfSummary *) data;
if (summary->handle)
gnome_vfs_async_cancel (summary->handle);
g_free (summary->title);
g_free (summary->icon);
g_free (summary);
running_views--;
g_print ("Running_views: %d\n", running_views);
if (running_views <= 0) {
gtk_main_quit ();
}
}
/* PersistStream callbacks */
static void
load_from_stream (BonoboPersistStream *ps,
Bonobo_Stream stream,
Bonobo_Persist_ContentType type,
gpointer data,
CORBA_Environment *ev)
{
RdfSummary *summary = (RdfSummary *) data;
char *str;
xmlChar *xml_str;
xmlDocPtr doc;
xmlNodePtr root, children;
if (*type && g_strcasecmp (type, "application/x-rdf-summary") != 0) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
bonobo_stream_client_read_string (stream, &str, ev);
if (ev->_major != CORBA_NO_EXCEPTION || str == NULL) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
g_print ("Hydrating with %s\n", str);
doc = xmlParseDoc ((xmlChar *) str);
if (doc == NULL) {
g_warning ("Bad data: %s!", str);
g_free (str);
return;
}
g_free (str);
root = doc->root;
children = root->childs;
while (children) {
if (strcasecmp (children->name, "location") == 0) {
xml_str = xmlNodeListGetString (doc, children->childs, 1);
summary->location = g_strdup (xml_str);
xmlFree (xml_str);
children = children->next;
continue;
}
if (strcasecmp (children->name, "limit") == 0) {
xml_str = xmlNodeListGetString (doc, children->childs, 1);
summary->limit = atoi (xml_str);
xmlFree (xml_str);
children = children->next;
continue;
}
if (strcasecmp (children->name, "showtitle") == 0) {
xml_str = xmlNodeListGetString (doc, children->childs, 1);
summary->showtitle = atoi (xml_str);
xmlFree (xml_str);
children = children->next;
continue;
}
g_print ("Unknown name: %s\n", children->name);
children = children->next;
}
xmlFreeDoc (doc);
}
static char *
summary_to_string (RdfSummary *summary)
{
xmlChar *out_str;
int out_len = 0;
xmlDocPtr doc;
xmlNodePtr root;
xmlNsPtr ns;
char *tmp_str;
doc = xmlNewDoc ("1.0");
ns = xmlNewGlobalNs (doc, "http://www.helixcode.com", "rdf");
doc->root = xmlNewDocNode (doc, ns, "rdf-summary", NULL);
xmlNewChild (doc->root, ns, "location", summary->location);
tmp_str = g_strdup_printf ("%d", summary->limit);
xmlNewChild (doc->root, ns, "limit", tmp_str);
g_free (tmp_str);
tmp_str = g_strdup_printf ("%d", summary->showtitle);
xmlNewChild (doc->root, ns, "showtitle", tmp_str);
g_free (tmp_str);
xmlDocDumpMemory (doc, &out_str, &out_len);
g_print ("%s\n", out_str);
return out_str;
}
static void
save_to_stream (BonoboPersistStream *ps,
const Bonobo_Stream stream,
Bonobo_Persist_ContentType type,
gpointer data,
CORBA_Environment *ev)
{
RdfSummary *summary = (RdfSummary *) data;
char *str;
if (*type && g_strcasecmp (type, "application/x-rdf-summary") != 0) {
CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
ex_Bonobo_Persist_WrongDataType, NULL);
return;
}
str = summary_to_string (summary);
if (str)
bonobo_stream_client_printf (stream, TRUE, ev, str);
xmlFree (str);
return;
}
static Bonobo_Persist_ContentTypeList *
content_types (BonoboPersistStream *ps,
void *closure,
CORBA_Environment *ev)
{
return bonobo_persist_generate_content_types (1, "application/x-rdf-summary");
}
static void
close_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
RdfSummary *summary)
{
GString *html;
xmlDocPtr doc;
char *xml;
if (summary == NULL)
return;
summary->handle = NULL;
g_free (summary->buffer);
xml = summary->str->str;
g_string_free (summary->str, FALSE);
doc = xmlParseMemory (xml, strlen (xml));
if (doc == NULL) {
char *emsg;
BonoboArg *arg;
arg = bonobo_arg_new (BONOBO_ARG_STRING);
BONOBO_ARG_SET_STRING (arg, _("Error"));
bonobo_property_bag_set_value (summary->bag,
"window_title",
(const BonoboArg *) arg,
NULL);
bonobo_arg_release (arg);
emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
summary->location);
executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
g_free (emsg);
g_free (xml);
return;
}
g_free (xml);
html = g_string_new ("");
tree_walk (doc->root, summary, html);
executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), html->str);
g_string_free (html, TRUE);
}
static void
read_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
gpointer buffer,
GnomeVFSFileSize bytes_requested,
GnomeVFSFileSize bytes_read,
RdfSummary *summary)
{
if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
char *emsg;
BonoboArg *arg;
arg = bonobo_arg_new (BONOBO_ARG_STRING);
BONOBO_ARG_SET_STRING (arg, _("Error"));
bonobo_property_bag_set_value (summary->bag,
"window_title",
(const BonoboArg *) arg,
NULL);
bonobo_arg_release (arg);
emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
summary->location);
executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
g_free (emsg);
gnome_vfs_async_close (handle,
(GnomeVFSAsyncCloseCallback) close_callback,
NULL);
g_print ("NULLING\n");
summary->handle = NULL;
}
if (bytes_read == 0) {
/* EOF */
gnome_vfs_async_close (handle,
(GnomeVFSAsyncCloseCallback) close_callback,
summary);
} else {
*((char *) buffer + bytes_read) = 0;
g_string_append (summary->str, (const char *) buffer);
gnome_vfs_async_read (handle, buffer, 4095,
(GnomeVFSAsyncReadCallback) read_callback,
summary);
}
}
static void
open_callback (GnomeVFSAsyncHandle *handle,
GnomeVFSResult result,
RdfSummary *summary)
{
GList *uri;
char *buffer;
if (result != GNOME_VFS_OK) {
char *emsg;
BonoboArg *arg;
arg = bonobo_arg_new (BONOBO_ARG_STRING);
BONOBO_ARG_SET_STRING (arg, _("Error"));
bonobo_property_bag_set_value (summary->bag,
"window_title",
(const BonoboArg *) arg,
NULL);
bonobo_arg_release (arg);
emsg = g_strdup_printf ("<b>Cannot open location:<br>%s</b>",
summary->location);
executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), emsg);
g_free (emsg);
summary->handle = NULL;
return;
}
summary->str = g_string_new ("");
summary->buffer = g_new (char, 4096);
gnome_vfs_async_read (handle, summary->buffer, 4095,
(GnomeVFSAsyncReadCallback) read_callback,
summary);
}
static int
download (RdfSummary *summary)
{
GnomeVFSAsyncHandle *handle;
char *html = "<b>Loading RDF file. . .<br>Please wait</b>";
executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view),
html);
gnome_vfs_async_open (&handle, summary->location, GNOME_VFS_OPEN_READ,
(GnomeVFSAsyncOpenCallback) open_callback,
summary);
summary->handle = handle;
return FALSE;
}
static void
get_prop (BonoboPropertyBag *bag,
BonoboArg *arg,
guint arg_id,
CORBA_Environment *ev,
gpointer user_data)
{
RdfSummary *summary = (RdfSummary *) user_data;
switch (arg_id) {
case PROPERTY_TITLE:
BONOBO_ARG_SET_STRING (arg, summary->title);
break;
case PROPERTY_ICON:
BONOBO_ARG_SET_STRING (arg, summary->icon);
break;
default:
break;
}
}
static void
set_prop (BonoboPropertyBag *bag,
const BonoboArg *arg,
guint arg_id,
CORBA_Environment *ev,
gpointer user_data)
{
RdfSummary *summary = (RdfSummary *) user_data;
switch (arg_id) {
case PROPERTY_TITLE:
if (summary->title)
g_free (summary->title);
summary->title = g_strdup (BONOBO_ARG_GET_STRING (arg));
g_print ("Notify listener!\n");
bonobo_property_bag_notify_listeners (bag, "window_title",
arg, NULL);
break;
case PROPERTY_ICON:
if (summary->icon)
g_free (summary->icon);
summary->icon = g_strdup (BONOBO_ARG_GET_STRING (arg));
g_print ("Notify listener 2\n");
bonobo_property_bag_notify_listeners (bag, "window_icon",
arg, NULL);
break;
default:
break;
}
}
static void
item_changed (GtkEntry *entry,
RdfSummary *summary)
{
bonobo_property_control_changed (summary->property_control, NULL);
}
static BonoboControl *
property_control (BonoboPropertyControl *property_control,
int page_num,
gpointer user_data)
{
BonoboControl *control;
RdfSummary *summary = (RdfSummary *) user_data;
GtkWidget *container, *label, *hbox;
char *climit;
container = gtk_vbox_new (FALSE, 2);
gtk_container_set_border_width (GTK_CONTAINER (container), 2);
hbox = gtk_hbox_new (FALSE, 2);
label = gtk_label_new ("Location:");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
summary->rdf = gtk_entry_new ();
if (summary->location)
gtk_entry_set_text (GTK_ENTRY (summary->rdf), summary->location);
gtk_signal_connect (GTK_OBJECT (summary->rdf), "changed",
GTK_SIGNAL_FUNC (item_changed), summary);
gtk_box_pack_start (GTK_BOX (hbox), summary->rdf, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0);
hbox = gtk_hbox_new (FALSE, 2);
label = gtk_label_new ("Maximum number of entries:");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
summary->g_limit = gtk_entry_new ();
climit = g_strdup_printf ("%d", summary->limit);
gtk_entry_set_text (GTK_ENTRY (summary->g_limit), climit);
g_free (climit);
gtk_signal_connect (GTK_OBJECT (summary->g_limit), "changed",
GTK_SIGNAL_FUNC (item_changed), summary);
gtk_box_pack_start (GTK_BOX (hbox), summary->g_limit, TRUE, TRUE, 0);
hbox = gtk_hbox_new (FALSE, 2);
/* FIXME: Do this better? */
label = gtk_label_new ("Show window title");
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
summary->g_title = gtk_check_button_new ();
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (summary->g_title),
summary->showtitle);
gtk_signal_connect (GTK_OBJECT (summary->g_title), "toggled",
GTK_SIGNAL_FUNC (item_changed), summary);
gtk_box_pack_start (GTK_BOX (hbox), summary->g_title, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0);
gtk_widget_show_all (container);
control = bonobo_control_new (container);
return control;
}
static void
property_action (GtkObject *property_control,
int page_num,
Bonobo_PropertyControl_Action action,
RdfSummary *summary)
{
switch (action) {
case Bonobo_PropertyControl_APPLY:
g_free (summary->location);
summary->showtitle = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (summary->g_title));
summary->location = g_strdup (gtk_entry_get_text (GTK_ENTRY (summary->rdf)));
summary->limit = atoi (gtk_entry_get_text (GTK_ENTRY (summary->g_limit)));
g_idle_add ((GSourceFunc) download, summary);
break;
case Bonobo_PropertyControl_HELP:
g_print ("HELP: Page %d!\n", page_num);
break;
default:
break;
}
}
static BonoboObject *
create_view (ExecutiveSummaryComponentFactory *_factory,
void *closure)
{
RdfSummary *summary;
BonoboObject *component, *view;
BonoboEventSource *event_source;
BonoboPersistStream *stream;
BonoboPropertyBag *bag;
BonoboPropertyControl *property;
summary = g_new (RdfSummary, 1);
summary->icon = g_strdup ("apple-green.png");
summary->title = g_strdup ("Downloading...");
summary->location = g_strdup ("http://news.gnome.org/gnome-news/rdf");
summary->limit = 10;
summary->showtitle = TRUE;
component = executive_summary_component_new ();
gtk_signal_connect (GTK_OBJECT (component), "destroy",
GTK_SIGNAL_FUNC (view_destroyed), summary);
summary->component = component;
/* Share the event source between the ExecutiveSummaryHtmlView and the
BonoboPropertyControl as we can only have one Bonobo::EventSource
interface aggregated */
event_source = bonobo_event_source_new ();
/* Summary::HtmlView */
view = executive_summary_html_view_new_full (event_source);
summary->view = view;
bonobo_object_add_interface (component, view);
/* Bonobo::PropertyBag */
bag = bonobo_property_bag_new_full (get_prop, set_prop,
event_source, summary);
summary->bag = bag;
bonobo_property_bag_add (bag,
"window_title", PROPERTY_TITLE,
BONOBO_ARG_STRING, NULL,
"The title of this component's window", 0);
bonobo_property_bag_add (bag,
"window_icon", PROPERTY_ICON,
BONOBO_ARG_STRING, NULL,
"The icon for this component's window", 0);
bonobo_object_add_interface (component, BONOBO_OBJECT(bag));
property = bonobo_property_control_new_full (property_control, 1,
event_source,
summary);
summary->property_control = property;
gtk_signal_connect (GTK_OBJECT (property), "action",
GTK_SIGNAL_FUNC (property_action), summary);
bonobo_object_add_interface (component, BONOBO_OBJECT(property));
/* Bonobo::PersistStream */
stream = bonobo_persist_stream_new (load_from_stream, save_to_stream,
NULL, content_types, summary);
bonobo_object_add_interface (component, BONOBO_OBJECT (stream));
running_views++;
gtk_timeout_add (5000, (GSourceFunc) download, summary);
return component;
}
static BonoboObject *
factory_fn (BonoboGenericFactory *_factory,
void *closure)
{
BonoboObject *component_factory;
component_factory = executive_summary_component_factory_new (create_view, NULL);
return component_factory;
}
static void
factory_init (void)
{
if (factory != NULL) {
return;
}
factory = bonobo_generic_factory_new (RDF_SUMMARY_ID, factory_fn, NULL);
if (factory == NULL) {
g_error ("Cannot initialize factory");
}
}
int
main (int argc,
char *argv[])
{
CORBA_ORB orb;
gnome_init_with_popt_table ("RDF-Summary", VERSION,
argc, argv, oaf_popt_options, 0, NULL);
orb = oaf_init (argc, argv);
gnome_vfs_init ();
if (bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL) == FALSE) {
g_error ("Could not initialize Bonobo");
}
factory_init ();
bonobo_main ();
return 0;
}