/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* RDF viewer Evolution Executive Summary Component. * Bonoboised by Iain Holmes * Copyright (C) 2000 Ximian, Inc. * * Based on code from Portaloo * Channel retrieval tool * * (C) 1998 Alan Cox. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include 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; GtkWidget *g_update; GtkWidget *g_update_container; GtkAdjustment *adjustment; char *title; char *icon; char *location; int limit; gboolean showtitle; gboolean usetimer; int time; int timer; GString *str; char *buffer; GnomeVFSAsyncHandle *handle; xmlDocPtr cache; }; 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, "

\n"); } /********* ExecutiveSummaryComponent section **************/ static void view_destroyed (GtkObject *object, gpointer data) { RdfSummary *summary = (RdfSummary *) data; g_warning ("RDF: Point 1"); if (summary->handle) gnome_vfs_async_cancel (summary->handle); g_warning ("RDF: Point 2"); if (summary->cache != NULL) xmlFreeDoc (summary->cache); 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 (); } g_warning ("RDF: Point 3"); } /* 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; } 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; } if (strcasecmp (children->name, "usetimer") == 0) { xml_str = xmlNodeListGetString (doc, children->childs, 1); summary->usetimer = atoi (xml_str); xmlFree (xml_str); children = children->next; continue; } if (strcasecmp (children->name, "timer") == 0) { xml_str = xmlNodeListGetString (doc, children->childs, 1); summary->timer = 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.ximian.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); tmp_str = g_strdup_printf ("%d", summary->usetimer); xmlNewChild (doc->root, ns, "usetimer", tmp_str); g_free (tmp_str); tmp_str = g_strdup_printf ("%d", summary->timer); xmlNewChild (doc->root, ns, "timer", tmp_str); g_free (tmp_str); xmlDocDumpMemory (doc, &out_str, &out_len); 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 display_doc (RdfSummary *summary) { GString *html; html = g_string_new (""); tree_walk (summary->cache->root, summary, html); executive_summary_html_view_set_html (EXECUTIVE_SUMMARY_HTML_VIEW (summary->view), html->str); g_string_free (html, TRUE); } static void close_callback (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, RdfSummary *summary) { 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); if (summary->cache != NULL) xmlFreeDoc (summary->cache); 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 ("Cannot open location:
%s
", 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); /* Cache it for later */ summary->cache = doc; /* Draw it */ display_doc (summary); } 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 ("Cannot open location:
%s
", 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 ("Cannot open location:
%s
", 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 = "Loading RDF file. . .
Please wait
"; 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 (GtkWidget *widget, RdfSummary *summary) { if (widget == summary->g_update) { summary->usetimer = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); gtk_widget_set_sensitive (summary->g_update_container, summary->usetimer); } 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, *spinner, *button; 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); hbox = gtk_hbox_new (FALSE, 2); /* Update */ hbox = gtk_hbox_new (FALSE, 2); label = gtk_label_new (_("Update automatically")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); summary->g_update = gtk_check_button_new (); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (summary->g_update), summary->usetimer); gtk_signal_connect (GTK_OBJECT (summary->g_update), "toggled", GTK_SIGNAL_FUNC (item_changed), summary); gtk_box_pack_start (GTK_BOX (hbox), summary->g_update, TRUE, TRUE, 0); button = gtk_button_new_with_label (_("Update now")); gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (download), summary); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0); /* Timer */ hbox = gtk_hbox_new (FALSE, 2); summary->g_update_container = hbox; label = gtk_label_new (_("Update every ")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); summary->adjustment = GTK_ADJUSTMENT(gtk_adjustment_new (summary->time / 1000 / 60, 0.0, 1000.0, 1.0, 10.0, 1.0)); spinner = gtk_spin_button_new (summary->adjustment, 1.0, 0); gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0); label = gtk_label_new (_("minutes")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (container), hbox, FALSE, FALSE, 0); gtk_widget_set_sensitive (hbox, summary->usetimer); 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) { gboolean changed = FALSE; char *old_location; switch (action) { case Bonobo_PropertyControl_APPLY: old_location = 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))); if (strcmp (old_location, summary->location) != 0) changed = TRUE; summary->limit = atoi (gtk_entry_get_text (GTK_ENTRY (summary->g_limit))); summary->time = summary->adjustment->value * 60 * 1000; if (summary->timer) gtk_timeout_remove (summary->timer); summary->timer = gtk_timeout_add (summary->time, (GSourceFunc)download, summary); if (changed) g_idle_add ((GSourceFunc) download, summary); else g_idle_add ((GSourceFunc) display_doc, summary); g_free (old_location); 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; summary->cache = NULL; summary->usetimer = TRUE; summary->time = 600000; /* 10 minutes */ summary->timer = gtk_timeout_add (summary->time, (GSourceFunc) download, summary); 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)); /* Bonobo::PropertyControl */ 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_idle_add ((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; }