Initial version of the Feed Panel extension
This commit is contained in:
parent
e58550f0c0
commit
7419d17fb6
9 changed files with 2310 additions and 0 deletions
343
extensions/feed-panel/feed-atom.c
Normal file
343
extensions/feed-panel/feed-atom.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "feed-atom.h"
|
||||
|
||||
#define atom_get_link_attribute(item, attribute) \
|
||||
(gchar*)g_object_get_data (G_OBJECT (item), attribute)
|
||||
|
||||
#define atom_set_link_attribute(item, attribute, value) \
|
||||
g_object_set_data (G_OBJECT (item), attribute, value)
|
||||
|
||||
static gboolean
|
||||
atom_is_valid (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
|
||||
node = fparser->node;
|
||||
|
||||
if (!(xmlStrcmp (node->name, BAD_CAST "feed")) &&
|
||||
!(xmlStrcmp (node->ns->href, BAD_CAST "http://www.w3.org/2005/Atom"))
|
||||
)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
atom_update (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr child;
|
||||
gint64 date;
|
||||
gint64 newdate;
|
||||
|
||||
date = katze_item_get_added (fparser->item);
|
||||
|
||||
node = fparser->node;
|
||||
child = node->children;
|
||||
while (child)
|
||||
{
|
||||
if (child->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
if (!(xmlStrcmp (child->name, BAD_CAST "updated")))
|
||||
{
|
||||
fparser->node = child;
|
||||
newdate = feed_get_element_date (fparser);
|
||||
fparser->node = node;
|
||||
return (date != newdate);
|
||||
}
|
||||
}
|
||||
child = child->next;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
atom_preferred_link (const gchar* old,
|
||||
const gchar* new)
|
||||
{
|
||||
guint i;
|
||||
gint iold;
|
||||
gint inew;
|
||||
gchar* rels[5] =
|
||||
{
|
||||
"enclosure",
|
||||
"via",
|
||||
"related",
|
||||
"alternate",
|
||||
"self",
|
||||
};
|
||||
|
||||
iold = inew = -1;
|
||||
for (i = 0; i < 5; i++)
|
||||
{
|
||||
if (old && g_str_equal (old, rels[i]))
|
||||
iold = i;
|
||||
if (new && g_str_equal (new, rels[i]))
|
||||
inew = i;
|
||||
}
|
||||
return (inew > iold);
|
||||
}
|
||||
|
||||
static void
|
||||
atom_get_link (KatzeItem* item,
|
||||
xmlNodePtr node)
|
||||
{
|
||||
gchar* oldtype;
|
||||
gchar* newtype;
|
||||
gchar* oldrel;
|
||||
gchar* newrel;
|
||||
gchar* href;
|
||||
gboolean oldishtml;
|
||||
gboolean newishtml;
|
||||
gboolean newlink;
|
||||
|
||||
newlink = FALSE;
|
||||
oldtype = atom_get_link_attribute (item, "linktype");
|
||||
oldrel = atom_get_link_attribute (item, "linkrel");
|
||||
|
||||
newtype = (gchar*)xmlGetProp (node, BAD_CAST "type");
|
||||
newrel = (gchar*)xmlGetProp (node, BAD_CAST "rel");
|
||||
href = (gchar*)xmlGetProp (node, BAD_CAST "href");
|
||||
|
||||
if (!newrel)
|
||||
newrel = g_strdup ("alternate");
|
||||
|
||||
oldishtml = (oldtype && g_str_equal (oldtype, "text/html"));
|
||||
newishtml = (newtype && g_str_equal (newtype, "text/html"));
|
||||
|
||||
/* prefer HTML links over anything else.
|
||||
* if the previous link was already HTML, decide which link
|
||||
* we prefer.
|
||||
*/
|
||||
if ((newishtml && oldishtml) || (!newishtml && !oldishtml))
|
||||
newlink = atom_preferred_link (oldrel, newrel);
|
||||
else
|
||||
newlink = newishtml;
|
||||
|
||||
if (newlink)
|
||||
{
|
||||
katze_item_set_uri (item, href);
|
||||
atom_set_link_attribute (item, "linkrel", newrel);
|
||||
atom_set_link_attribute (item, "linktype", newrel);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlFree (href);
|
||||
xmlFree (newrel);
|
||||
xmlFree (newtype);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
atom_preparse_entry (FeedParser* fparser)
|
||||
{
|
||||
fparser->item = katze_item_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
atom_parse_entry (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
gchar* content;
|
||||
gint64 date;
|
||||
|
||||
node = fparser->node;
|
||||
content = NULL;
|
||||
|
||||
if (!xmlStrcmp (node->name, BAD_CAST "id"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_token (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_name (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "summary"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_text (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "updated"))
|
||||
{
|
||||
date = feed_get_element_date (fparser);
|
||||
katze_item_set_added (fparser->item, date);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "icon"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_icon (fparser->item, content);
|
||||
}
|
||||
/* FIXME content can be used in some cases where there
|
||||
* is no summary, but it needs additional work,
|
||||
* as it can be HTML, or base64 encoded.
|
||||
* see the spec.
|
||||
*/
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "content"))
|
||||
{
|
||||
/* Only retrieve content if there is no summary */
|
||||
if (!katze_item_get_text (fparser->item))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_text (fparser->item, content);
|
||||
}
|
||||
}
|
||||
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
|
||||
atom_get_link (fparser->item, node);
|
||||
|
||||
g_free (content);
|
||||
}
|
||||
|
||||
static void
|
||||
atom_postparse_entry (FeedParser* fparser)
|
||||
{
|
||||
if (!*fparser->error)
|
||||
{
|
||||
/*
|
||||
* Verify that the required Atom elements are added
|
||||
* (as per the spec)
|
||||
*/
|
||||
if (!katze_item_get_token (fparser->item) ||
|
||||
!katze_item_get_name (fparser->item) ||
|
||||
!katze_item_get_uri (fparser->item) ||
|
||||
!katze_item_get_added (fparser->item))
|
||||
{
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find required Atom entry elements in XML data."));
|
||||
}
|
||||
}
|
||||
|
||||
if (KATZE_IS_ITEM (fparser->item))
|
||||
{
|
||||
atom_set_link_attribute (fparser->item, "linkrel", NULL);
|
||||
atom_set_link_attribute (fparser->item, "linktype", NULL);
|
||||
|
||||
if (*fparser->error)
|
||||
{
|
||||
g_object_unref (fparser->item);
|
||||
fparser->item = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
atom_parse_feed (FeedParser* fparser)
|
||||
{
|
||||
FeedParser* eparser;
|
||||
xmlNodePtr node;
|
||||
gchar* content;
|
||||
gint64 date;
|
||||
|
||||
node = fparser->node;
|
||||
content = NULL;
|
||||
|
||||
if (!xmlStrcmp (node->name, BAD_CAST "id"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_token (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_name (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "subtitle"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_text (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "updated"))
|
||||
{
|
||||
date = feed_get_element_date (fparser);
|
||||
katze_item_set_added (fparser->item, date);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "icon"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_icon (fparser->item, content);
|
||||
}
|
||||
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
|
||||
{
|
||||
atom_get_link (fparser->item, node);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "entry"))
|
||||
{
|
||||
eparser = g_new0 (FeedParser, 1);
|
||||
eparser->doc = fparser->doc;
|
||||
eparser->node = fparser->node;
|
||||
eparser->error = fparser->error;
|
||||
eparser->preparse = atom_preparse_entry;
|
||||
eparser->parse = atom_parse_entry;
|
||||
eparser->postparse = atom_postparse_entry;
|
||||
|
||||
feed_parse_node (eparser);
|
||||
|
||||
if (KATZE_IS_ITEM (eparser->item))
|
||||
{
|
||||
KatzeItem* item;
|
||||
if (!(item = feed_item_exists (KATZE_ARRAY (fparser->item), eparser->item)))
|
||||
katze_array_add_item (KATZE_ARRAY (fparser->item), eparser->item);
|
||||
else
|
||||
{
|
||||
g_object_unref (eparser->item);
|
||||
katze_array_move_item (KATZE_ARRAY (fparser->item), item, 0);
|
||||
}
|
||||
}
|
||||
g_free (eparser);
|
||||
|
||||
}
|
||||
g_free (content);
|
||||
}
|
||||
|
||||
static void
|
||||
atom_postparse_feed (FeedParser* fparser)
|
||||
{
|
||||
if (KATZE_IS_ARRAY (fparser->item))
|
||||
{
|
||||
atom_set_link_attribute (fparser->item, "linkrel", NULL);
|
||||
atom_set_link_attribute (fparser->item, "linktype", NULL);
|
||||
}
|
||||
|
||||
if (!*fparser->error)
|
||||
{
|
||||
/*
|
||||
* Verify that the required Atom elements are added
|
||||
* (as per the spec)
|
||||
*/
|
||||
if (!katze_item_get_token (fparser->item) ||
|
||||
!katze_item_get_name (fparser->item) ||
|
||||
!katze_item_get_added (fparser->item))
|
||||
{
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find required Atom feed elements in XML data."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedParser*
|
||||
atom_init_parser (void)
|
||||
{
|
||||
FeedParser* fparser;
|
||||
|
||||
fparser = g_new0 (FeedParser, 1);
|
||||
g_return_val_if_fail (fparser, NULL);
|
||||
|
||||
fparser->isvalid = atom_is_valid;
|
||||
fparser->update = atom_update;
|
||||
fparser->parse = atom_parse_feed;
|
||||
fparser->postparse = atom_postparse_feed;
|
||||
|
||||
return fparser;
|
||||
}
|
||||
|
25
extensions/feed-panel/feed-atom.h
Normal file
25
extensions/feed-panel/feed-atom.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __FEED_ATOM_H__
|
||||
#define __FEED_ATOM_H__
|
||||
|
||||
#include "feed-parse.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
FeedParser*
|
||||
atom_init_parser (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __FEED_ATOM_H__ */
|
||||
|
850
extensions/feed-panel/feed-panel.c
Normal file
850
extensions/feed-panel/feed-panel.c
Normal file
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "feed-panel.h"
|
||||
|
||||
#include <midori/midori.h>
|
||||
#include <midori/sokoke.h>
|
||||
#include <time.h>
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define STOCK_FEED_PANEL "feed-panel"
|
||||
|
||||
struct _FeedPanel
|
||||
{
|
||||
GtkVBox parent_instance;
|
||||
|
||||
GtkWidget* toolbar;
|
||||
GtkWidget* treeview;
|
||||
GtkWidget* webview;
|
||||
GtkWidget* delete;
|
||||
GdkPixbuf* pixbuf;
|
||||
KatzeNet* net;
|
||||
};
|
||||
|
||||
struct _FeedPanelClass
|
||||
{
|
||||
GtkVBoxClass parent_class;
|
||||
};
|
||||
|
||||
static void
|
||||
feed_panel_viewable_iface_init (MidoriViewableIface* iface);
|
||||
|
||||
static void
|
||||
feed_panel_insert_item (FeedPanel* panel,
|
||||
GtkTreeStore* treestore,
|
||||
GtkTreeIter* parent,
|
||||
KatzeItem* item);
|
||||
|
||||
static void
|
||||
feed_panel_disconnect_feed (FeedPanel* panel,
|
||||
KatzeArray* feed);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (FeedPanel, feed_panel, GTK_TYPE_VBOX,
|
||||
G_IMPLEMENT_INTERFACE (MIDORI_TYPE_VIEWABLE,
|
||||
feed_panel_viewable_iface_init));
|
||||
|
||||
enum
|
||||
{
|
||||
ADD_FEED,
|
||||
REMOVE_FEED,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
static void
|
||||
feed_panel_treeview_render_icon_cb (GtkTreeViewColumn* column,
|
||||
GtkCellRenderer* renderer,
|
||||
GtkTreeModel* model,
|
||||
GtkTreeIter* iter,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GdkPixbuf* pixbuf;
|
||||
KatzeItem* item;
|
||||
KatzeItem* pitem;
|
||||
const gchar* uri;
|
||||
|
||||
gtk_tree_model_get (model, iter, 0, &item, -1);
|
||||
g_assert (KATZE_IS_ITEM (item));
|
||||
|
||||
if (!KATZE_IS_ARRAY (item))
|
||||
{
|
||||
pitem = katze_item_get_parent (item);
|
||||
g_assert (KATZE_IS_ITEM (pitem));
|
||||
}
|
||||
else
|
||||
pitem = item;
|
||||
|
||||
uri = katze_item_get_uri (pitem);
|
||||
pixbuf = katze_net_load_icon (panel->net, uri, NULL, NULL, NULL);
|
||||
if (!pixbuf)
|
||||
pixbuf = panel->pixbuf;
|
||||
|
||||
g_object_set (renderer, "pixbuf", pixbuf, NULL);
|
||||
|
||||
if (pixbuf != panel->pixbuf)
|
||||
g_object_unref (pixbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_treeview_render_text_cb (GtkTreeViewColumn* column,
|
||||
GtkCellRenderer* renderer,
|
||||
GtkTreeModel* model,
|
||||
GtkTreeIter* iter,
|
||||
GtkWidget* treeview)
|
||||
{
|
||||
KatzeItem* item;
|
||||
const gchar* title;
|
||||
|
||||
gtk_tree_model_get (model, iter, 0, &item, -1);
|
||||
g_assert (KATZE_IS_ITEM (item));
|
||||
|
||||
title = katze_item_get_name (item);
|
||||
if (!title)
|
||||
title = katze_item_get_text (item);
|
||||
if (!title)
|
||||
title = katze_item_get_uri (item);
|
||||
|
||||
g_object_set (renderer, "text", title, NULL);
|
||||
g_object_unref (item);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_add_item_cb (KatzeArray* feed,
|
||||
KatzeItem* child,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter child_iter;
|
||||
KatzeItem* item;
|
||||
gint i;
|
||||
|
||||
g_return_if_fail (KATZE_IS_ARRAY (feed));
|
||||
g_return_if_fail (KATZE_IS_ITEM (child));
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
|
||||
|
||||
if (KATZE_IS_ARRAY (child))
|
||||
{
|
||||
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child_iter,
|
||||
NULL, G_MAXINT, 0, child, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
i = 0;
|
||||
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i++))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
if (item == KATZE_ITEM (feed))
|
||||
{
|
||||
gtk_tree_store_insert_with_values (GTK_TREE_STORE (model), &child_iter,
|
||||
&iter, 0, 0, child, -1);
|
||||
|
||||
g_object_unref (child);
|
||||
g_object_unref (item);
|
||||
break;
|
||||
}
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
feed_panel_insert_item (panel, GTK_TREE_STORE (model), &child_iter, child);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_remove_iter (GtkTreeModel* model,
|
||||
KatzeItem* removed_item)
|
||||
{
|
||||
guint i;
|
||||
GtkTreeIter iter;
|
||||
|
||||
i = 0;
|
||||
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
|
||||
{
|
||||
KatzeItem* item;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
|
||||
if (item == removed_item)
|
||||
{
|
||||
gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
|
||||
g_object_unref (item);
|
||||
break;
|
||||
}
|
||||
g_object_unref (item);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_remove_item_cb (KatzeArray* feed,
|
||||
KatzeItem* child,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
|
||||
g_assert (KATZE_IS_ARRAY (feed));
|
||||
g_assert (KATZE_IS_ITEM (child));
|
||||
|
||||
if (KATZE_IS_ARRAY (child))
|
||||
feed_panel_disconnect_feed (panel, KATZE_ARRAY (child));
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
|
||||
feed_panel_remove_iter (model, child);
|
||||
g_object_unref (child);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_move_item_cb (KatzeArray* feed,
|
||||
KatzeItem* child,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
guint i;
|
||||
|
||||
g_assert (KATZE_IS_ARRAY (feed));
|
||||
g_assert (KATZE_IS_ITEM (child));
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
|
||||
|
||||
i = 0;
|
||||
while (gtk_tree_model_iter_nth_child (model, &iter, NULL, i))
|
||||
{
|
||||
KatzeItem* item;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
|
||||
if (item == child)
|
||||
{
|
||||
gtk_tree_store_move_after (GTK_TREE_STORE (model), &iter, NULL);
|
||||
g_object_unref (item);
|
||||
break;
|
||||
}
|
||||
g_object_unref (item);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_disconnect_feed (FeedPanel* panel,
|
||||
KatzeArray* feed)
|
||||
{
|
||||
KatzeItem* item;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (KATZE_IS_ARRAY (feed));
|
||||
|
||||
g_signal_handlers_disconnect_by_func (feed,
|
||||
feed_panel_add_item_cb, panel);
|
||||
g_signal_handlers_disconnect_by_func (feed,
|
||||
feed_panel_remove_item_cb, panel);
|
||||
g_signal_handlers_disconnect_by_func (feed,
|
||||
feed_panel_move_item_cb, panel);
|
||||
|
||||
i = 0;
|
||||
while ((item = katze_array_get_nth_item (feed, i++)))
|
||||
{
|
||||
if (KATZE_IS_ARRAY (item))
|
||||
feed_panel_disconnect_feed (panel, KATZE_ARRAY (item));
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_insert_item (FeedPanel* panel,
|
||||
GtkTreeStore* treestore,
|
||||
GtkTreeIter* parent,
|
||||
KatzeItem* item)
|
||||
{
|
||||
g_return_if_fail (KATZE_IS_ITEM (item));
|
||||
|
||||
if (KATZE_IS_ARRAY (item))
|
||||
{
|
||||
g_signal_connect_after (item, "add-item",
|
||||
G_CALLBACK (feed_panel_add_item_cb), panel);
|
||||
g_signal_connect_after (item, "move-item",
|
||||
G_CALLBACK (feed_panel_move_item_cb), panel);
|
||||
|
||||
if (!parent)
|
||||
{
|
||||
g_signal_connect (item, "remove-item",
|
||||
G_CALLBACK (feed_panel_remove_item_cb), panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_row_activated_cb (GtkTreeView* treeview,
|
||||
GtkTreePath* path,
|
||||
GtkTreeViewColumn* column,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
KatzeItem* item;
|
||||
const gchar* uri;
|
||||
|
||||
model = gtk_tree_view_get_model (treeview);
|
||||
|
||||
if (gtk_tree_model_get_iter (model, &iter, path))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
uri = katze_item_get_uri (item);
|
||||
if (uri && *uri)
|
||||
{
|
||||
MidoriWebSettings* settings;
|
||||
MidoriBrowser* browser;
|
||||
gint n;
|
||||
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
n = midori_browser_add_item (browser, item);
|
||||
|
||||
settings = katze_object_get_object (browser, "settings");
|
||||
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
|
||||
midori_browser_set_current_page (browser, n);
|
||||
g_object_unref (settings);
|
||||
|
||||
}
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_cursor_or_row_changed_cb (GtkTreeView* treeview,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
KatzeItem* item;
|
||||
gboolean sensitive = FALSE;
|
||||
|
||||
if (katze_tree_view_get_selected_iter (treeview, &model, &iter))
|
||||
{
|
||||
const gchar* uri;
|
||||
const gchar* text;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
|
||||
uri = katze_item_get_uri (item);
|
||||
|
||||
if (uri)
|
||||
{
|
||||
if (KATZE_IS_ARRAY (item))
|
||||
{
|
||||
gint64 date;
|
||||
SoupDate* sdate;
|
||||
|
||||
text = NULL;
|
||||
date = katze_item_get_added (item);
|
||||
if (date)
|
||||
{
|
||||
sdate = soup_date_new_from_time_t ((time_t) date);
|
||||
text = g_strdup_printf ("Last updated %s.", soup_date_to_string (sdate, SOUP_DATE_HTTP));
|
||||
soup_date_free (sdate);
|
||||
}
|
||||
webkit_web_view_load_html_string (
|
||||
WEBKIT_WEB_VIEW (panel->webview), text ? text : "", uri);
|
||||
g_free ((gchar*) text);
|
||||
|
||||
sensitive = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
text = katze_item_get_text (item);
|
||||
if (text)
|
||||
{
|
||||
webkit_web_view_load_html_string (
|
||||
WEBKIT_WEB_VIEW (panel->webview), text, uri);
|
||||
}
|
||||
}
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
if (GTK_IS_WIDGET (panel->delete))
|
||||
gtk_widget_set_sensitive (panel->delete, sensitive);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_popup_item (GtkWidget* menu,
|
||||
const gchar* stock_id,
|
||||
const gchar* label,
|
||||
KatzeItem* item,
|
||||
gpointer callback,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
const gchar* uri;
|
||||
GtkWidget* menuitem;
|
||||
|
||||
uri = katze_item_get_uri (item);
|
||||
|
||||
menuitem = gtk_image_menu_item_new_from_stock (stock_id, NULL);
|
||||
if (label)
|
||||
gtk_label_set_text_with_mnemonic (GTK_LABEL (gtk_bin_get_child (
|
||||
GTK_BIN (menuitem))), label);
|
||||
g_object_set_data (G_OBJECT (menuitem), "KatzeItem", item);
|
||||
g_signal_connect (menuitem, "activate", G_CALLBACK (callback), panel);
|
||||
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
|
||||
gtk_widget_show (menuitem);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_open_activate_cb (GtkWidget* menuitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
KatzeItem* item;
|
||||
const gchar* uri;
|
||||
|
||||
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
|
||||
uri = katze_item_get_uri (item);
|
||||
|
||||
if (uri && *uri)
|
||||
{
|
||||
MidoriBrowser* browser;
|
||||
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
midori_browser_set_current_uri (browser, uri);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_open_in_tab_activate_cb (GtkWidget* menuitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
KatzeItem* item;
|
||||
const gchar* uri;
|
||||
guint n;
|
||||
|
||||
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
|
||||
|
||||
if ((uri = katze_item_get_uri (item)) && *uri)
|
||||
{
|
||||
MidoriWebSettings* settings;
|
||||
MidoriBrowser* browser;
|
||||
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
n = midori_browser_add_item (browser, item);
|
||||
|
||||
settings = katze_object_get_object (browser, "settings");
|
||||
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
|
||||
midori_browser_set_current_page (browser, n);
|
||||
g_object_unref (settings);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_open_in_window_activate_cb (GtkWidget* menuitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
KatzeItem* item;
|
||||
const gchar* uri;
|
||||
|
||||
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
|
||||
uri = katze_item_get_uri (item);
|
||||
|
||||
if (uri && *uri)
|
||||
{
|
||||
MidoriBrowser* browser;
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
g_signal_emit_by_name (browser, "new-window", uri);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_delete_activate_cb (GtkWidget* menuitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
KatzeItem* item;
|
||||
|
||||
g_return_if_fail (FEED_IS_PANEL (panel));
|
||||
|
||||
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
|
||||
g_signal_emit (panel, signals[REMOVE_FEED], 0, item);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_popup (GtkWidget* widget,
|
||||
GdkEventButton* event,
|
||||
KatzeItem* item,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkWidget* menu;
|
||||
|
||||
menu = gtk_menu_new ();
|
||||
if (!KATZE_IS_ARRAY (item))
|
||||
{
|
||||
feed_panel_popup_item (menu, GTK_STOCK_OPEN, NULL,
|
||||
item, feed_panel_open_activate_cb, panel);
|
||||
feed_panel_popup_item (menu, STOCK_TAB_NEW, _("Open in New _Tab"),
|
||||
item, feed_panel_open_in_tab_activate_cb, panel);
|
||||
feed_panel_popup_item (menu, STOCK_WINDOW_NEW, _("Open in New _Window"),
|
||||
item, feed_panel_open_in_window_activate_cb, panel);
|
||||
}
|
||||
else
|
||||
{
|
||||
feed_panel_popup_item (menu, GTK_STOCK_DELETE, NULL,
|
||||
item, feed_panel_delete_activate_cb, panel);
|
||||
}
|
||||
|
||||
sokoke_widget_popup (widget, GTK_MENU (menu),
|
||||
event, SOKOKE_MENU_POSITION_CURSOR);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
feed_panel_button_release_event_cb (GtkWidget* widget,
|
||||
GdkEventButton* event,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if (event->button != 2 && event->button != 3)
|
||||
return FALSE;
|
||||
|
||||
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
|
||||
{
|
||||
KatzeItem* item;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
|
||||
if (event->button == 2)
|
||||
{
|
||||
const gchar* uri = katze_item_get_uri (item);
|
||||
|
||||
if (uri && *uri)
|
||||
{
|
||||
MidoriWebSettings* settings;
|
||||
MidoriBrowser* browser;
|
||||
gint n;
|
||||
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
n = midori_browser_add_item (browser, item);
|
||||
|
||||
settings = katze_object_get_object (browser, "settings");
|
||||
if (!katze_object_get_boolean (settings, "open-tabs-in-the-background"))
|
||||
midori_browser_set_current_page (browser, n);
|
||||
g_object_unref (settings);
|
||||
}
|
||||
}
|
||||
else
|
||||
feed_panel_popup (widget, event, item, panel);
|
||||
|
||||
g_object_unref (item);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_popup_menu_cb (GtkWidget* widget,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
KatzeItem* item;
|
||||
|
||||
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (widget), &model, &iter))
|
||||
{
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
feed_panel_popup (widget, NULL, item, panel);
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
feed_panel_add_feeds (FeedPanel* panel,
|
||||
KatzeItem* feed)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
|
||||
model = gtk_tree_view_get_model (GTK_TREE_VIEW (panel->treeview));
|
||||
g_assert (GTK_IS_TREE_MODEL (model));
|
||||
|
||||
feed_panel_insert_item (panel, GTK_TREE_STORE (model), NULL, feed);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
webview_button_press_event_cb (GtkWidget* widget,
|
||||
GdkEventButton* event)
|
||||
{
|
||||
/* Disable the popup menu */
|
||||
return (event->button == 3);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
webview_navigation_request_cb (WebKitWebView* web_view,
|
||||
WebKitWebFrame* frame,
|
||||
WebKitNetworkRequest* request,
|
||||
WebKitWebNavigationAction* navigation_action,
|
||||
WebKitWebPolicyDecision* policy_decision,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
if (webkit_web_navigation_action_get_reason (navigation_action) ==
|
||||
WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED)
|
||||
{
|
||||
MidoriBrowser* browser;
|
||||
const gchar* uri;
|
||||
gint n;
|
||||
|
||||
browser = midori_browser_get_for_widget (GTK_WIDGET (panel));
|
||||
uri = webkit_network_request_get_uri (request);
|
||||
n = midori_browser_add_uri (browser, uri);
|
||||
midori_browser_set_current_page (browser, n);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
feed_panel_get_label (MidoriViewable* viewable)
|
||||
{
|
||||
return _("Feeds");
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
feed_panel_get_stock_id (MidoriViewable* viewable)
|
||||
{
|
||||
return STOCK_FEED_PANEL;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_add_clicked_cb (GtkWidget* toolitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
g_return_if_fail (FEED_IS_PANEL (panel));
|
||||
|
||||
g_signal_emit (panel, signals[ADD_FEED], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_delete_clicked_cb (GtkWidget* toolitem,
|
||||
FeedPanel* panel)
|
||||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
g_return_if_fail (FEED_IS_PANEL (panel));
|
||||
|
||||
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (panel->treeview),
|
||||
&model, &iter))
|
||||
{
|
||||
KatzeItem* item;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &item, -1);
|
||||
g_signal_emit (panel, signals[REMOVE_FEED], 0, item);
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget*
|
||||
feed_panel_get_toolbar (MidoriViewable* viewable)
|
||||
{
|
||||
FeedPanel* panel = FEED_PANEL (viewable);
|
||||
|
||||
if (!panel->toolbar)
|
||||
{
|
||||
GtkWidget* toolbar;
|
||||
GtkToolItem* toolitem;
|
||||
|
||||
toolbar = gtk_toolbar_new ();
|
||||
gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
|
||||
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
|
||||
panel->toolbar = toolbar;
|
||||
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ADD);
|
||||
gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem), _("Add new feed"));
|
||||
gtk_tool_item_set_is_important (toolitem, TRUE);
|
||||
g_signal_connect (toolitem, "clicked",
|
||||
G_CALLBACK (feed_panel_add_clicked_cb), panel);
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
|
||||
gtk_widget_show (GTK_WIDGET (toolitem));
|
||||
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_DELETE);
|
||||
gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem), _("Delete feed"));
|
||||
g_signal_connect (toolitem, "clicked",
|
||||
G_CALLBACK (feed_panel_delete_clicked_cb), panel);
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
|
||||
gtk_widget_show (GTK_WIDGET (toolitem));
|
||||
panel->delete = GTK_WIDGET (toolitem);;
|
||||
|
||||
feed_panel_cursor_or_row_changed_cb (
|
||||
GTK_TREE_VIEW (panel->treeview), panel);
|
||||
g_signal_connect (panel->delete, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed), &panel->delete);
|
||||
}
|
||||
|
||||
return panel->toolbar;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_finalize (GObject* object)
|
||||
{
|
||||
FeedPanel* panel = FEED_PANEL (object);
|
||||
|
||||
g_object_unref (panel->pixbuf);
|
||||
g_object_unref (panel->net);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_viewable_iface_init (MidoriViewableIface* iface)
|
||||
{
|
||||
iface->get_stock_id = feed_panel_get_stock_id;
|
||||
iface->get_label = feed_panel_get_label;
|
||||
iface->get_toolbar = feed_panel_get_toolbar;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_class_init (FeedPanelClass* class)
|
||||
{
|
||||
GObjectClass* gobject_class;
|
||||
|
||||
signals[ADD_FEED] = g_signal_new (
|
||||
"add-feed",
|
||||
G_TYPE_FROM_CLASS (class),
|
||||
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
signals[REMOVE_FEED] = g_signal_new (
|
||||
"remove-feed",
|
||||
G_TYPE_FROM_CLASS (class),
|
||||
(GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
g_cclosure_marshal_VOID__POINTER,
|
||||
G_TYPE_NONE, 1,
|
||||
G_TYPE_POINTER);
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (class);
|
||||
gobject_class->finalize = feed_panel_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_panel_init (FeedPanel* panel)
|
||||
{
|
||||
GtkTreeStore* model;
|
||||
GtkWidget* treewin;
|
||||
GtkWidget* treeview;
|
||||
GtkWidget* webview;
|
||||
GtkWidget* paned;
|
||||
GtkTreeViewColumn* column;
|
||||
GtkCellRenderer* renderer_pixbuf;
|
||||
GtkCellRenderer* renderer_text;
|
||||
GtkIconFactory *factory;
|
||||
GtkIconSource *icon_source;
|
||||
GtkIconSet *icon_set;
|
||||
WebKitWebSettings* settings;
|
||||
PangoFontDescription* font_desc;
|
||||
const gchar* family;
|
||||
gint size;
|
||||
GtkStockItem items[] =
|
||||
{
|
||||
{ STOCK_FEED_PANEL, N_("_Feeds"), 0, 0, NULL }
|
||||
};
|
||||
|
||||
factory = gtk_icon_factory_new ();
|
||||
gtk_stock_add (items, G_N_ELEMENTS (items));
|
||||
icon_set = gtk_icon_set_new ();
|
||||
icon_source = gtk_icon_source_new ();
|
||||
gtk_icon_source_set_icon_name (icon_source, STOCK_NEWS_FEED);
|
||||
gtk_icon_set_add_source (icon_set, icon_source);
|
||||
gtk_icon_source_free (icon_source);
|
||||
gtk_icon_factory_add (factory, STOCK_FEED_PANEL, icon_set);
|
||||
gtk_icon_set_unref (icon_set);
|
||||
gtk_icon_factory_add_default (factory);
|
||||
g_object_unref (factory);
|
||||
|
||||
panel->net = katze_net_new ();
|
||||
|
||||
model = gtk_tree_store_new (1, KATZE_TYPE_ITEM);
|
||||
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
|
||||
panel->treeview = treeview;
|
||||
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
|
||||
column = gtk_tree_view_column_new ();
|
||||
renderer_pixbuf = gtk_cell_renderer_pixbuf_new ();
|
||||
gtk_tree_view_column_pack_start (column, renderer_pixbuf, FALSE);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer_pixbuf,
|
||||
(GtkTreeCellDataFunc)feed_panel_treeview_render_icon_cb,
|
||||
panel, NULL);
|
||||
renderer_text = gtk_cell_renderer_text_new ();
|
||||
gtk_tree_view_column_pack_start (column, renderer_text, FALSE);
|
||||
gtk_tree_view_column_set_cell_data_func (column, renderer_text,
|
||||
(GtkTreeCellDataFunc)feed_panel_treeview_render_text_cb,
|
||||
treeview, NULL);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
|
||||
g_object_unref (model);
|
||||
g_object_connect (treeview,
|
||||
"signal::row-activated",
|
||||
feed_panel_row_activated_cb, panel,
|
||||
"signal::cursor-changed",
|
||||
feed_panel_cursor_or_row_changed_cb, panel,
|
||||
"signal::columns-changed",
|
||||
feed_panel_cursor_or_row_changed_cb, panel,
|
||||
"signal::button-release-event",
|
||||
feed_panel_button_release_event_cb, panel,
|
||||
"signal::popup-menu",
|
||||
feed_panel_popup_menu_cb, panel,
|
||||
NULL);
|
||||
gtk_widget_show (treeview);
|
||||
|
||||
webview = webkit_web_view_new ();
|
||||
font_desc = treeview->style->font_desc;
|
||||
family = pango_font_description_get_family (font_desc);
|
||||
size = pango_font_description_get_size (font_desc) / PANGO_SCALE;
|
||||
settings = webkit_web_settings_new ();
|
||||
g_object_set (settings, "default-font-family", family,
|
||||
"default-font-size", size, NULL);
|
||||
g_object_set (webview, "settings", settings, NULL);
|
||||
gtk_widget_set_size_request (webview, -1, 50);
|
||||
g_object_connect (webview,
|
||||
"signal::navigation-policy-decision-requested",
|
||||
webview_navigation_request_cb, panel,
|
||||
"signal::button-press-event",
|
||||
webview_button_press_event_cb, NULL,
|
||||
"signal::button-release-event",
|
||||
webview_button_press_event_cb, NULL,
|
||||
NULL);
|
||||
panel->webview = webview;
|
||||
|
||||
treewin = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (treewin),
|
||||
GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (treewin),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_container_add (GTK_CONTAINER (treewin), treeview);
|
||||
gtk_widget_show (treewin);
|
||||
|
||||
paned = gtk_vpaned_new ();
|
||||
gtk_paned_pack1 (GTK_PANED (paned), treewin, TRUE, FALSE);
|
||||
gtk_paned_pack2 (GTK_PANED (paned), webview, TRUE, FALSE);
|
||||
gtk_box_pack_start (GTK_BOX (panel), paned, TRUE, TRUE, 0);
|
||||
gtk_widget_show (webview);
|
||||
gtk_widget_show (paned);
|
||||
|
||||
panel->pixbuf = gtk_widget_render_icon (treeview,
|
||||
STOCK_NEWS_FEED, GTK_ICON_SIZE_MENU, NULL);
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
feed_panel_new (void)
|
||||
{
|
||||
FeedPanel* panel = g_object_new (FEED_TYPE_PANEL, NULL);
|
||||
|
||||
return GTK_WIDGET (panel);
|
||||
}
|
||||
|
51
extensions/feed-panel/feed-panel.h
Normal file
51
extensions/feed-panel/feed-panel.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __FEED_PANEL_H__
|
||||
#define __FEED_PANEL_H__
|
||||
|
||||
#include <midori/midori.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FEED_TYPE_PANEL \
|
||||
(feed_panel_get_type ())
|
||||
#define FEED_PANEL(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), FEED_TYPE_PANEL, FeedPanel))
|
||||
#define FEED_PANEL_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), FEED_TYPE_PANEL, FeedPanelClass))
|
||||
#define FEED_IS_PANEL(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), FEED_TYPE_PANEL))
|
||||
#define FEED_IS_PANEL_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), FEED_TYPE_PANEL))
|
||||
#define FEED_PANEL_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), FEED_TYPE_PANEL, FeedPanelClass))
|
||||
|
||||
typedef struct _FeedPanel FeedPanel;
|
||||
typedef struct _FeedPanelClass FeedPanelClass;
|
||||
|
||||
void
|
||||
feed_panel_add_feeds (FeedPanel* panel,
|
||||
KatzeItem* feed);
|
||||
|
||||
void
|
||||
feed_panel_set_editable (FeedPanel* panel,
|
||||
gboolean editable);
|
||||
|
||||
GType
|
||||
feed_panel_get_type (void);
|
||||
|
||||
GtkWidget*
|
||||
feed_panel_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __FEED_PANEL_H__ */
|
218
extensions/feed-panel/feed-parse.c
Normal file
218
extensions/feed-panel/feed-parse.c
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "feed-parse.h"
|
||||
#include <time.h>
|
||||
|
||||
gchar*
|
||||
feed_get_element_string (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
|
||||
node = fparser->node;
|
||||
|
||||
if (!node->children ||
|
||||
xmlIsBlankNode (node->children) ||
|
||||
node->children->type != XML_TEXT_NODE
|
||||
)
|
||||
{
|
||||
/* Some servers add required elements with no content,
|
||||
* create a dummy string to handle it.
|
||||
*/
|
||||
return g_strdup (" ");
|
||||
}
|
||||
|
||||
return (gchar* )xmlNodeListGetString (fparser->doc, node->children, 1);
|
||||
}
|
||||
|
||||
gint64
|
||||
feed_get_element_date (FeedParser* fparser)
|
||||
{
|
||||
time_t date;
|
||||
gchar* content;
|
||||
|
||||
date = 0;
|
||||
content = feed_get_element_string (fparser);
|
||||
|
||||
if (content)
|
||||
{
|
||||
SoupDate* sdate;
|
||||
|
||||
sdate = soup_date_new_from_string (content);
|
||||
date = soup_date_to_time_t (sdate);
|
||||
soup_date_free (sdate);
|
||||
g_free (content);
|
||||
}
|
||||
return ((gint64)date);
|
||||
}
|
||||
|
||||
KatzeItem*
|
||||
feed_item_exists (KatzeArray* array,
|
||||
KatzeItem* item)
|
||||
{
|
||||
const gchar* guid;
|
||||
gchar* hstr;
|
||||
guint hash;
|
||||
|
||||
guid = katze_item_get_token (item);
|
||||
if (!guid)
|
||||
{
|
||||
hstr = g_strjoin (NULL,
|
||||
katze_item_get_name (item),
|
||||
katze_item_get_uri (item),
|
||||
katze_item_get_text (item),
|
||||
NULL);
|
||||
hash = g_str_hash (hstr);
|
||||
g_free (hstr);
|
||||
|
||||
hstr = g_strdup_printf ("%u", hash);
|
||||
katze_item_set_token (item, hstr);
|
||||
g_free (hstr);
|
||||
|
||||
guid = katze_item_get_token (item);
|
||||
}
|
||||
|
||||
return (katze_array_find_token (array, guid));
|
||||
}
|
||||
|
||||
void
|
||||
feed_parse_node (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr child;
|
||||
|
||||
if (!*fparser->error)
|
||||
{
|
||||
if (fparser->preparse)
|
||||
(*fparser->preparse) (fparser);
|
||||
|
||||
if (fparser->parse)
|
||||
{
|
||||
node = fparser->node;
|
||||
child = node->last;
|
||||
|
||||
while (child)
|
||||
{
|
||||
if (child->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
fparser->node = child;
|
||||
|
||||
(*fparser->parse) (fparser);
|
||||
|
||||
if (*fparser->error)
|
||||
break;
|
||||
}
|
||||
child = child->prev;
|
||||
}
|
||||
fparser->node = node;
|
||||
}
|
||||
|
||||
if (fparser->postparse)
|
||||
(*fparser->postparse) (fparser);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_parse_doc (xmlDocPtr doc,
|
||||
GSList* parsers,
|
||||
KatzeArray* array,
|
||||
GError** error)
|
||||
{
|
||||
FeedParser* fparser;
|
||||
xmlNodePtr root;
|
||||
gboolean isvalid;
|
||||
|
||||
root = xmlDocGetRootElement (doc);
|
||||
|
||||
if (!root)
|
||||
{
|
||||
*error = g_error_new (FEED_PARSE_ERROR,
|
||||
FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find root element in feed XML data."));
|
||||
return;
|
||||
}
|
||||
|
||||
while (parsers)
|
||||
{
|
||||
fparser = (FeedParser*)parsers->data;
|
||||
fparser->error = error;
|
||||
fparser->doc = doc;
|
||||
fparser->node = root;
|
||||
|
||||
if (fparser && fparser->isvalid)
|
||||
{
|
||||
isvalid = (*fparser->isvalid) (fparser);
|
||||
|
||||
if (*fparser->error)
|
||||
return;
|
||||
|
||||
if (isvalid)
|
||||
{
|
||||
fparser->item = KATZE_ITEM (array);
|
||||
|
||||
if (fparser->update &&
|
||||
(*fparser->update) (fparser))
|
||||
feed_parse_node (fparser);
|
||||
}
|
||||
}
|
||||
|
||||
fparser->error = NULL;
|
||||
fparser->doc = NULL;
|
||||
fparser->node = NULL;
|
||||
|
||||
if (isvalid)
|
||||
return;
|
||||
|
||||
parsers = g_slist_next (parsers);
|
||||
}
|
||||
|
||||
*error = g_error_new (FEED_PARSE_ERROR,
|
||||
FEED_PARSE_ERROR_INVALID_FORMAT,
|
||||
_("Unsupported feed format."));
|
||||
}
|
||||
|
||||
gboolean
|
||||
parse_feed (gchar* data,
|
||||
gint64 length,
|
||||
GSList* parsers,
|
||||
KatzeArray* array,
|
||||
GError** error)
|
||||
{
|
||||
xmlDocPtr doc;
|
||||
xmlErrorPtr xerror;
|
||||
|
||||
LIBXML_TEST_VERSION
|
||||
|
||||
doc = xmlReadMemory (
|
||||
data, length, "feedfile.xml", NULL,
|
||||
XML_PARSE_NOWARNING | XML_PARSE_NOERROR /*| XML_PARSE_RECOVER*/
|
||||
);
|
||||
|
||||
if (doc)
|
||||
{
|
||||
feed_parse_doc (doc, parsers, array, error);
|
||||
xmlFreeDoc (doc);
|
||||
}
|
||||
else
|
||||
{
|
||||
xerror = xmlGetLastError ();
|
||||
*error = g_error_new (FEED_PARSE_ERROR,
|
||||
FEED_PARSE_ERROR_PARSE,
|
||||
_("Failed to parse XML feed: %s"),
|
||||
xerror->message);
|
||||
xmlResetLastError ();
|
||||
}
|
||||
xmlCleanupParser ();
|
||||
xmlMemoryDump ();
|
||||
|
||||
return *error ? FALSE : TRUE;
|
||||
}
|
||||
|
77
extensions/feed-panel/feed-parse.h
Normal file
77
extensions/feed-panel/feed-parse.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __FEED_PARSE_H__
|
||||
#define __FEED_PARSE_H__
|
||||
|
||||
#include <midori/midori.h>
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <libsoup/soup.h>
|
||||
#include <libxml/parser.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FEED_PARSE_ERROR g_quark_from_string("FEED_PARSE_ERROR")
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FEED_PARSE_ERROR_PARSE,
|
||||
FEED_PARSE_ERROR_INVALID_FORMAT,
|
||||
FEED_PARSE_ERROR_INVALID_VERSION,
|
||||
FEED_PARSE_ERROR_MISSING_ELEMENT
|
||||
|
||||
} FeedBarError;
|
||||
|
||||
typedef struct _FeedParser
|
||||
{
|
||||
xmlDocPtr doc; /* The XML document */
|
||||
xmlNodePtr node; /* The XML node at a specific point */
|
||||
KatzeItem* item;
|
||||
GError** error;
|
||||
|
||||
gboolean (*isvalid) (struct _FeedParser* fparser);
|
||||
gboolean (*update) (struct _FeedParser* fparser);
|
||||
void (*preparse) (struct _FeedParser* fparser);
|
||||
void (*parse) (struct _FeedParser* fparser);
|
||||
void (*postparse) (struct _FeedParser* fparser);
|
||||
|
||||
} FeedParser;
|
||||
|
||||
#define feed_parser_set_error(fparser, err, msg) \
|
||||
*(fparser)->error = g_error_new ( \
|
||||
FEED_PARSE_ERROR, (err), (msg))
|
||||
gchar*
|
||||
feed_get_element_string (FeedParser* fparser);
|
||||
|
||||
gint64
|
||||
feed_get_element_date (FeedParser* fparser);
|
||||
|
||||
KatzeItem*
|
||||
feed_item_exists (KatzeArray* array,
|
||||
KatzeItem* item);
|
||||
void
|
||||
feed_parse_node (FeedParser* fparser);
|
||||
|
||||
gboolean
|
||||
parse_feed (gchar* data,
|
||||
gint64 length,
|
||||
GSList* parsers,
|
||||
KatzeArray* array,
|
||||
GError** error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __FEED_PARSE_H__ */
|
||||
|
249
extensions/feed-panel/feed-rss.c
Normal file
249
extensions/feed-panel/feed-rss.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "feed-rss.h"
|
||||
|
||||
static gboolean
|
||||
rss_is_valid (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr child;
|
||||
xmlChar* str;
|
||||
gboolean valid;
|
||||
|
||||
node = fparser->node;
|
||||
|
||||
if (!(xmlStrcmp (node->name, BAD_CAST "rss")))
|
||||
{
|
||||
if ((str = xmlGetProp (node, BAD_CAST "version")))
|
||||
{
|
||||
valid = !xmlStrcmp (str, BAD_CAST "2.0");
|
||||
xmlFree (str);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
child = node->children;
|
||||
while (child)
|
||||
{
|
||||
if (child->type == XML_ELEMENT_NODE &&
|
||||
!(xmlStrcmp (child->name, BAD_CAST "channel")))
|
||||
{
|
||||
fparser->node = child;
|
||||
return TRUE;
|
||||
}
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find channel element in RSS XML data."));
|
||||
}
|
||||
else
|
||||
{
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_INVALID_VERSION,
|
||||
_("Unsupported RSS version found."));
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rss_update (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr child;
|
||||
gint64 date;
|
||||
gint64 newdate;
|
||||
|
||||
date = katze_item_get_added (fparser->item);
|
||||
|
||||
node = fparser->node;
|
||||
child = node->children;
|
||||
while (child)
|
||||
{
|
||||
if (child->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
if (!(xmlStrcmp (child->name, BAD_CAST "lastBuildDate")))
|
||||
{
|
||||
fparser->node = child;
|
||||
newdate = feed_get_element_date (fparser);
|
||||
fparser->node = node;
|
||||
return (date != newdate);
|
||||
}
|
||||
}
|
||||
child = child->next;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
rss_preparse_item (FeedParser* fparser)
|
||||
{
|
||||
fparser->item = katze_item_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
rss_parse_item (FeedParser* fparser)
|
||||
{
|
||||
xmlNodePtr node;
|
||||
gchar* content;
|
||||
gint64 date;
|
||||
|
||||
node = fparser->node;
|
||||
content = NULL;
|
||||
|
||||
if (!xmlStrcmp (node->name, BAD_CAST "guid"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_token (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "title"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_name (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "description"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_text (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "pubDate"))
|
||||
{
|
||||
date = feed_get_element_date (fparser);
|
||||
katze_item_set_added (fparser->item, date);
|
||||
}
|
||||
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_uri (fparser->item, content);
|
||||
}
|
||||
g_free (content);
|
||||
}
|
||||
|
||||
static void
|
||||
rss_postparse_item (FeedParser* fparser)
|
||||
{
|
||||
if (!*fparser->error)
|
||||
{
|
||||
/*
|
||||
* Verify that the required Atom elements are added
|
||||
* (as per the spec)
|
||||
*/
|
||||
if (!katze_item_get_name (fparser->item) &&
|
||||
!katze_item_get_text (fparser->item))
|
||||
{
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find required RSS item elements in XML data."));
|
||||
}
|
||||
}
|
||||
|
||||
if (*fparser->error && KATZE_IS_ITEM (fparser->item))
|
||||
{
|
||||
g_object_unref (fparser->item);
|
||||
fparser->item = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rss_parse_channel (FeedParser* fparser)
|
||||
{
|
||||
FeedParser* eparser;
|
||||
xmlNodePtr node;
|
||||
gchar* content;
|
||||
gint64 date;
|
||||
|
||||
node = fparser->node;
|
||||
content = NULL;
|
||||
|
||||
if (!xmlStrcmp (node->name, BAD_CAST "title"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_name (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "description"))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_text (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "lastBuildDate"))
|
||||
{
|
||||
date = feed_get_element_date (fparser);
|
||||
katze_item_set_added (fparser->item, date);
|
||||
}
|
||||
else if (!(xmlStrcmp (node->name, BAD_CAST "link")))
|
||||
{
|
||||
content = feed_get_element_string (fparser);
|
||||
katze_item_set_uri (fparser->item, content);
|
||||
}
|
||||
else if (!xmlStrcmp (node->name, BAD_CAST "item"))
|
||||
{
|
||||
eparser = g_new0 (FeedParser, 1);
|
||||
eparser->doc = fparser->doc;
|
||||
eparser->node = fparser->node;
|
||||
eparser->error = fparser->error;
|
||||
eparser->preparse = rss_preparse_item;
|
||||
eparser->parse = rss_parse_item;
|
||||
eparser->postparse = rss_postparse_item;
|
||||
|
||||
feed_parse_node (eparser);
|
||||
|
||||
if (KATZE_IS_ITEM (eparser->item))
|
||||
{
|
||||
KatzeItem* item;
|
||||
if (!(item = feed_item_exists (KATZE_ARRAY (fparser->item), eparser->item)))
|
||||
katze_array_add_item (KATZE_ARRAY (fparser->item), eparser->item);
|
||||
else
|
||||
{
|
||||
g_object_unref (eparser->item);
|
||||
katze_array_move_item (KATZE_ARRAY (fparser->item), item, 0);
|
||||
}
|
||||
}
|
||||
g_free (eparser);
|
||||
|
||||
}
|
||||
g_free (content);
|
||||
}
|
||||
|
||||
static void
|
||||
rss_postparse_channel (FeedParser* fparser)
|
||||
{
|
||||
if (!*fparser->error)
|
||||
{
|
||||
/*
|
||||
* Verify that the required Atom elements are added
|
||||
* (as per the spec)
|
||||
*/
|
||||
if (!katze_item_get_name (fparser->item) ||
|
||||
!katze_item_get_text (fparser->item) ||
|
||||
!katze_item_get_uri (fparser->item))
|
||||
{
|
||||
feed_parser_set_error (fparser, FEED_PARSE_ERROR_MISSING_ELEMENT,
|
||||
_("Failed to find required RSS channel elements in XML data."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedParser*
|
||||
rss_init_parser (void)
|
||||
{
|
||||
FeedParser* fparser;
|
||||
|
||||
fparser = g_new0 (FeedParser, 1);
|
||||
g_return_val_if_fail (fparser, NULL);
|
||||
|
||||
fparser->isvalid = rss_is_valid;
|
||||
fparser->update = rss_update;
|
||||
fparser->parse = rss_parse_channel;
|
||||
fparser->postparse = rss_postparse_channel;
|
||||
|
||||
return fparser;
|
||||
}
|
||||
|
25
extensions/feed-panel/feed-rss.h
Normal file
25
extensions/feed-panel/feed-rss.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dayul@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __FEED_RSS_H__
|
||||
#define __FEED_RSS_H__
|
||||
|
||||
#include "feed-parse.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
FeedParser*
|
||||
rss_init_parser (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __FEED_RSS_H__ */
|
||||
|
472
extensions/feed-panel/main.c
Normal file
472
extensions/feed-panel/main.c
Normal file
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
Copyright (C) 2009 Dale Whittaker <dale@users.sf.net>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "feed-panel.h"
|
||||
#include "feed-atom.h"
|
||||
#include "feed-rss.h"
|
||||
|
||||
#include <midori/midori.h>
|
||||
|
||||
#define EXTENSION_NAME "FeedPanel"
|
||||
#define UPDATE_FREQ 10
|
||||
|
||||
#define feed_get_flags(feed) \
|
||||
GPOINTER_TO_INT (g_object_get_data (G_OBJECT ((feed)), "flags"))
|
||||
|
||||
#define feed_set_flags(feed, flags) \
|
||||
g_object_set_data (G_OBJECT ((feed)), "flags", \
|
||||
GINT_TO_POINTER ((flags)))
|
||||
|
||||
#define feed_has_flags(feed, flags) \
|
||||
((flags) & feed_get_flags ((feed)))
|
||||
|
||||
#define feed_add_flags(feed, flags) \
|
||||
feed_set_flags ((feed), (feed_get_flags ((feed)) | (flags)))
|
||||
|
||||
#define feed_remove_flags(feed, flags) \
|
||||
feed_set_flags ((feed), (feed_get_flags ((feed)) & ~(flags)))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MidoriBrowser* browser;
|
||||
MidoriExtension* extension;
|
||||
GtkWidget* panel;
|
||||
KatzeArray* feeds;
|
||||
KatzeNet* net;
|
||||
GSList* parsers;
|
||||
|
||||
guint source_id;
|
||||
gboolean is_running;
|
||||
|
||||
} FeedPrivate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MidoriExtension* extension;
|
||||
GSList* parsers;
|
||||
KatzeArray* feed;
|
||||
|
||||
} FeedNetPrivate;
|
||||
|
||||
enum
|
||||
{
|
||||
FEED_NEW,
|
||||
FEED_READ,
|
||||
FEED_REMOVE
|
||||
};
|
||||
|
||||
static void
|
||||
feed_app_add_browser_cb (MidoriApp* app,
|
||||
MidoriBrowser* browser,
|
||||
MidoriExtension* extension);
|
||||
|
||||
static void
|
||||
feed_deactivate_cb (MidoriExtension* extension,
|
||||
FeedPrivate* priv)
|
||||
{
|
||||
if (priv)
|
||||
{
|
||||
MidoriApp* app = midori_extension_get_app (extension);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (app,
|
||||
feed_app_add_browser_cb, extension);
|
||||
g_signal_handlers_disconnect_by_func (extension,
|
||||
feed_deactivate_cb, priv);
|
||||
|
||||
if (priv->source_id)
|
||||
g_source_remove (priv->source_id);
|
||||
g_slist_foreach (priv->parsers, (GFunc)g_free, NULL);
|
||||
g_slist_free (priv->parsers);
|
||||
if (priv->feeds)
|
||||
g_object_unref (priv->net);
|
||||
if (priv->feeds)
|
||||
g_object_unref (priv->feeds);
|
||||
gtk_widget_destroy (priv->panel);
|
||||
g_free (priv);
|
||||
}
|
||||
}
|
||||
|
||||
static KatzeArray*
|
||||
feed_add_item (KatzeArray* feeds,
|
||||
const gchar* uri)
|
||||
{
|
||||
KatzeArray* feed;
|
||||
|
||||
feed = NULL;
|
||||
|
||||
if (uri)
|
||||
{
|
||||
feed = katze_array_new (KATZE_TYPE_ITEM);
|
||||
g_object_set_data_full (G_OBJECT (feed), "feeduri",
|
||||
(gpointer) g_strdup ((uri)), g_free);
|
||||
katze_array_add_item (feeds, feed);
|
||||
}
|
||||
return feed;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_save_items (MidoriExtension* extension,
|
||||
KatzeArray* feed)
|
||||
{
|
||||
KatzeItem* item;
|
||||
gchar** sfeeds;
|
||||
gint i;
|
||||
gint n;
|
||||
|
||||
g_return_if_fail (KATZE_IS_ARRAY (feed));
|
||||
|
||||
n = katze_array_get_length (feed);
|
||||
sfeeds = g_new (gchar*, n + 1);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
item = katze_array_get_nth_item (feed, i);
|
||||
sfeeds[i] = (gchar*) g_object_get_data (G_OBJECT (item), "feeduri");
|
||||
}
|
||||
sfeeds[n] = NULL;
|
||||
|
||||
midori_extension_set_string_list (extension, "feeds", sfeeds, n);
|
||||
g_free (sfeeds);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_handle_net_error (FeedNetPrivate* netpriv,
|
||||
const gchar* msg)
|
||||
{
|
||||
GtkWidget* dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new (
|
||||
NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
|
||||
_("Error"));
|
||||
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
|
||||
"%s", msg);
|
||||
gtk_window_set_title (GTK_WINDOW (dialog), EXTENSION_NAME);
|
||||
gtk_widget_show (dialog);
|
||||
g_signal_connect_swapped (dialog, "response",
|
||||
G_CALLBACK (gtk_widget_destroy), dialog);
|
||||
|
||||
if (feed_has_flags (netpriv->feed, FEED_NEW))
|
||||
{
|
||||
KatzeArray* parent;
|
||||
KatzeItem* child;
|
||||
|
||||
child = KATZE_ITEM (netpriv->feed);
|
||||
parent = katze_item_get_parent (child);
|
||||
katze_array_remove_item (parent, child);
|
||||
feed_save_items (netpriv->extension, parent);
|
||||
}
|
||||
feed_remove_flags (netpriv->feed, FEED_READ);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
feed_status_cb (KatzeNetRequest* request,
|
||||
FeedNetPrivate* netpriv)
|
||||
{
|
||||
if (request->status == KATZE_NET_FAILED ||
|
||||
request->status == KATZE_NET_NOT_FOUND)
|
||||
{
|
||||
gchar* msg;
|
||||
|
||||
msg = g_strdup_printf (_("Error loading feed %s"),
|
||||
katze_item_get_uri (KATZE_ITEM (netpriv->feed)));
|
||||
feed_handle_net_error (netpriv, msg);
|
||||
g_free (msg);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
feed_transfer_cb (KatzeNetRequest* request,
|
||||
FeedNetPrivate* netpriv)
|
||||
{
|
||||
GError* error;
|
||||
|
||||
if (request->status == KATZE_NET_MOVED)
|
||||
return;
|
||||
|
||||
g_return_if_fail (KATZE_IS_ARRAY (netpriv->feed));
|
||||
|
||||
error = NULL;
|
||||
|
||||
if (request->data)
|
||||
{
|
||||
if (!parse_feed (request->data, request->length,
|
||||
netpriv->parsers, netpriv->feed, &error))
|
||||
{
|
||||
feed_handle_net_error (netpriv, error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (feed_has_flags (netpriv->feed, FEED_REMOVE))
|
||||
{
|
||||
KatzeArray* parent;
|
||||
|
||||
/* deferred remove */
|
||||
parent = katze_item_get_parent (KATZE_ITEM (netpriv->feed));
|
||||
katze_array_remove_item (parent, netpriv->feed);
|
||||
feed_save_items (netpriv->extension, parent);
|
||||
}
|
||||
else
|
||||
feed_set_flags (netpriv->feed, 0);
|
||||
}
|
||||
}
|
||||
|
||||
netpriv->parsers = NULL;
|
||||
netpriv->feed = NULL;
|
||||
g_free (netpriv);
|
||||
}
|
||||
|
||||
static void
|
||||
update_feed (FeedPrivate* priv,
|
||||
KatzeItem* feed)
|
||||
{
|
||||
if (!(feed_has_flags (feed, FEED_READ)))
|
||||
{
|
||||
FeedNetPrivate* netpriv;
|
||||
gchar* uri;
|
||||
|
||||
uri = (gchar*) g_object_get_data (G_OBJECT (feed), "feeduri");
|
||||
feed_add_flags (feed, FEED_READ);
|
||||
katze_item_set_uri (KATZE_ITEM (feed), uri);
|
||||
netpriv = g_new0 (FeedNetPrivate, 1);
|
||||
netpriv->parsers = priv->parsers;
|
||||
netpriv->extension = priv->extension;
|
||||
netpriv->feed = KATZE_ARRAY (feed);
|
||||
|
||||
katze_net_load_uri (priv->net,
|
||||
katze_item_get_uri (feed),
|
||||
(KatzeNetStatusCb) feed_status_cb,
|
||||
(KatzeNetTransferCb) feed_transfer_cb,
|
||||
netpriv);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_feeds (FeedPrivate* priv)
|
||||
{
|
||||
KatzeItem* feed;
|
||||
gint i;
|
||||
gint n;
|
||||
|
||||
if (!priv->is_running)
|
||||
{
|
||||
priv->is_running = TRUE;
|
||||
n = katze_array_get_length (priv->feeds);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
feed = katze_array_get_nth_item (priv->feeds, i);
|
||||
update_feed (priv, feed);
|
||||
}
|
||||
}
|
||||
priv->is_running = FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
secondary_icon_released_cb (GtkAction* action,
|
||||
GtkWidget* widget,
|
||||
FeedPrivate* priv)
|
||||
{
|
||||
const gchar* uri;
|
||||
|
||||
g_assert (KATZE_IS_ARRAY (priv->feeds));
|
||||
|
||||
uri = midori_location_action_get_uri (MIDORI_LOCATION_ACTION (action));
|
||||
|
||||
if (uri && *uri)
|
||||
{
|
||||
KatzeArray* feed;
|
||||
|
||||
feed = feed_add_item (priv->feeds, uri);
|
||||
feed_save_items (priv->extension, priv->feeds);
|
||||
feed_add_flags (feed, FEED_NEW);
|
||||
update_feed (priv, KATZE_ITEM (feed));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
panel_add_feed_cb (FeedPanel* panel,
|
||||
FeedPrivate* priv)
|
||||
{
|
||||
GtkWidget* dialog;
|
||||
GtkSizeGroup* sizegroup;
|
||||
GtkWidget* hbox;
|
||||
GtkWidget* label;
|
||||
GtkWidget* entry;
|
||||
|
||||
dialog = gtk_dialog_new_with_buttons (
|
||||
_("New feed"), GTK_WINDOW (priv->browser),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
|
||||
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||
GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
|
||||
NULL);
|
||||
gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_ADD);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 5);
|
||||
sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
|
||||
|
||||
hbox = gtk_hbox_new (FALSE, 8);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
|
||||
label = gtk_label_new_with_mnemonic (_("_Address:"));
|
||||
gtk_size_group_add_widget (sizegroup, label);
|
||||
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
|
||||
entry = gtk_entry_new ();
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), "");
|
||||
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
||||
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), hbox);
|
||||
gtk_widget_show_all (hbox);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
const gchar* uri;
|
||||
|
||||
g_assert (KATZE_IS_ARRAY (priv->feeds));
|
||||
|
||||
uri = gtk_entry_get_text (GTK_ENTRY (entry));
|
||||
if (uri && *uri)
|
||||
{
|
||||
KatzeArray* feed;
|
||||
|
||||
feed = feed_add_item (priv->feeds, uri);
|
||||
feed_save_items (priv->extension, priv->feeds);
|
||||
feed_add_flags (feed, FEED_NEW);
|
||||
update_feed (priv, KATZE_ITEM (feed));
|
||||
}
|
||||
}
|
||||
gtk_widget_destroy (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
panel_remove_feed_cb (FeedPanel* panel,
|
||||
KatzeArray* feed,
|
||||
FeedPrivate* priv)
|
||||
{
|
||||
g_assert (KATZE_IS_ARRAY (priv->feeds));
|
||||
|
||||
if (feed_has_flags (feed, FEED_READ))
|
||||
feed_add_flags (feed, FEED_REMOVE);
|
||||
else
|
||||
{
|
||||
feed_add_flags (feed, FEED_READ);
|
||||
katze_array_remove_item (priv->feeds, feed);
|
||||
feed_save_items (priv->extension, priv->feeds);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
feed_app_add_browser_cb (MidoriApp* app,
|
||||
MidoriBrowser* browser,
|
||||
MidoriExtension* extension)
|
||||
{
|
||||
GtkWidget* panel;
|
||||
GtkWidget* addon;
|
||||
GtkActionGroup* action_group;
|
||||
GtkAction* action;
|
||||
KatzeNet* net;
|
||||
KatzeArray* feeds;
|
||||
KatzeArray* feed;
|
||||
FeedPrivate* priv;
|
||||
gchar** sfeeds;
|
||||
gsize i;
|
||||
gsize n;
|
||||
|
||||
priv = g_new0 (FeedPrivate, 1);
|
||||
|
||||
panel = katze_object_get_object (browser, "panel");
|
||||
addon = feed_panel_new ();
|
||||
gtk_widget_show (addon);
|
||||
midori_panel_append_page (MIDORI_PANEL (panel), MIDORI_VIEWABLE (addon));
|
||||
g_object_unref (panel);
|
||||
|
||||
net = katze_net_new ();
|
||||
feeds = katze_array_new (KATZE_TYPE_ARRAY);
|
||||
feed_panel_add_feeds (FEED_PANEL (addon), KATZE_ITEM (feeds));
|
||||
|
||||
priv->extension = extension;
|
||||
priv->browser = browser;
|
||||
priv->panel = addon;
|
||||
priv->net = net;
|
||||
priv->feeds = feeds;
|
||||
priv->parsers = g_slist_prepend (priv->parsers, atom_init_parser ());
|
||||
priv->parsers = g_slist_prepend (priv->parsers, rss_init_parser ());
|
||||
|
||||
sfeeds = midori_extension_get_string_list (extension, "feeds", &n);
|
||||
g_assert (n == 0 || sfeeds);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (sfeeds[i])
|
||||
{
|
||||
feed = feed_add_item (feeds, sfeeds[i]);
|
||||
update_feed (priv, KATZE_ITEM (feed));
|
||||
}
|
||||
}
|
||||
action_group = midori_browser_get_action_group (browser);
|
||||
action = gtk_action_group_get_action (action_group, "Location");
|
||||
|
||||
g_signal_connect (addon, "add-feed",
|
||||
G_CALLBACK (panel_add_feed_cb), priv);
|
||||
g_signal_connect (addon, "remove-feed",
|
||||
G_CALLBACK (panel_remove_feed_cb), priv);
|
||||
g_signal_connect (action, "secondary-icon-released",
|
||||
G_CALLBACK (secondary_icon_released_cb), priv);
|
||||
g_signal_connect (extension, "deactivate",
|
||||
G_CALLBACK (feed_deactivate_cb), priv);
|
||||
|
||||
priv->source_id = g_timeout_add_seconds (UPDATE_FREQ * 60,
|
||||
(GSourceFunc) update_feeds, priv);
|
||||
}
|
||||
|
||||
static void
|
||||
feed_activate_cb (MidoriExtension* extension,
|
||||
MidoriApp* app)
|
||||
{
|
||||
KatzeArray* browsers;
|
||||
MidoriBrowser* browser;
|
||||
guint i;
|
||||
|
||||
browsers = katze_object_get_object (app, "browsers");
|
||||
i = 0;
|
||||
while ((browser = katze_array_get_nth_item (browsers, i++)))
|
||||
feed_app_add_browser_cb (app, browser, extension);
|
||||
g_object_unref (browsers);
|
||||
|
||||
g_signal_connect (app, "add-browser",
|
||||
G_CALLBACK (feed_app_add_browser_cb), extension);
|
||||
}
|
||||
|
||||
MidoriExtension*
|
||||
extension_init (void)
|
||||
{
|
||||
MidoriExtension* extension;
|
||||
gchar* sfeed[2];
|
||||
|
||||
extension = g_object_new (MIDORI_TYPE_EXTENSION,
|
||||
"name", _("Feed Panel"),
|
||||
"description", _("Read Atom/ RSS feeds"),
|
||||
"version", "0.1",
|
||||
"authors", "Dale Whittaker <dayul@users.sf.net>",
|
||||
NULL);
|
||||
|
||||
sfeed[0] = NULL;
|
||||
midori_extension_install_string_list (extension, "feeds", sfeed, 1);
|
||||
|
||||
g_signal_connect (extension, "activate",
|
||||
G_CALLBACK (feed_activate_cb), NULL);
|
||||
|
||||
return extension;
|
||||
}
|
Loading…
Reference in a new issue