Logo Search packages:      
Sourcecode: kazehakase version File versions

kz-xbel.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *  $Id: kz-xbel.c,v 1.61 2005/02/13 13:20:34 ikezoe Exp $
 */

#include "kazehakase.h"
#include "kz-xbel.h"
#include "kz-xml.h"
#include "utils.h"
#include "kz-smart-bookmark.h"

#include <string.h>
#include <stdlib.h>

static gboolean kz_xbel_is_supported       (KzBookmarkFile  *bookmark,
                                  const gchar     *buf);
static void     kz_xbel_init               (KzBookmarkFile  *bookmark);
static gboolean kz_xbel_from_string        (KzBookmarkFile  *bookmark,
                                  const gchar     *buffer,
                                  guint            length,
                                  GError         **error);
static gchar   *kz_xbel_to_string          (KzBookmarkFile   *bookmark);

static void     kz_xbel_notify             (GObject     *object,
                                  GParamSpec  *pspec,
                                  KzXML       *xml);

static void     kz_xbel_build_tree         (KzBookmark  *bookmark);
static void     kz_xbel_insert_xml_node    (KzBookmark  *bookmark,
                                  KzBookmark  *parent,
                                  KzBookmark  *sibling);
static void     kz_xbel_remove_xml_node    (KzBookmark  *bookmark);
static void     kz_xbel_connect_signals    (KzBookmark  *bookmark);
static void     kz_xbel_disconnect_signals (KzBookmark  *bookmark);
static void     cb_bookmark_insert_child   (KzBookmark  *bookmark,
                                  KzBookmark  *child,
                                  KzBookmark  *sibling);
static void     cb_bookmark_remove_child   (KzBookmark  *bookmark,
                                  KzBookmark  *child);
static void     cb_bookmark_notify         (GObject     *object,
                                  GParamSpec  *spec);

static void     xml_node_set_title         (KzXMLNode   *parent,
                                  const gchar *title);


static GQuark xml_quark = 0;
static GQuark node_quark = 0;
static GQuark building_quark = 0;


#define BOOKMARK_SET_BUILDING(b) \
      g_object_set_qdata(G_OBJECT(b), building_quark, GINT_TO_POINTER(TRUE))
#define BOOKMARK_UNSET_BUILDING(b) \
      g_object_set_qdata(G_OBJECT(b), building_quark, GINT_TO_POINTER(FALSE))
#define BOOKMARK_IS_BUILDING(b) \
      GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(b), building_quark))


static KzBookmarkFileType xbel_file_type =
{
      priority_hint: 0,
      file_type:     "XBEL",
      init:          kz_xbel_init,
      is_supported:  kz_xbel_is_supported,
      from_string:   kz_xbel_from_string,
      to_string:     kz_xbel_to_string,
};


KzBookmarkFileType *
kz_xbel_get_file_types (gint idx)
{
      if (idx == 0)
            return &xbel_file_type;
      else
            return NULL;
}


static gboolean
kz_xbel_is_supported(KzBookmarkFile *bookmark, const gchar *buf)
{
      const gchar *pos;

      g_return_val_if_fail(buf, FALSE);

      if (strncmp(buf, "<?xml", 5)) return FALSE;

      pos = buf;

      /* find first element */
      do
            pos = strchr(pos + 1, '<');
      while (pos && pos[1] == '!');

      if (pos && !strncmp(pos, "<xbel", 5))
            return TRUE;

      return FALSE;
}


static void
kz_xbel_init (KzBookmarkFile *bookmark)
{
      KzXML *xml;

      if (!xml_quark)
            xml_quark = g_quark_from_string("KzXBEL::KzXML");
      if (!node_quark)
            node_quark = g_quark_from_string("KzXBEL::XMLNode");
      if (!building_quark)
            building_quark = g_quark_from_string("KzXBEL::Building");

      xml = kz_xml_new();
      g_object_set_qdata_full(G_OBJECT(bookmark), xml_quark,
                        xml, (GDestroyNotify) g_object_unref);

      g_object_set(G_OBJECT(bookmark),
                 "type", KZ_BOOKMARK_PURE_FOLDER,
                 NULL);

      g_signal_connect(bookmark, "notify",
                   G_CALLBACK(kz_xbel_notify), xml);
      g_signal_connect_after(bookmark, "insert-child",
                         G_CALLBACK(cb_bookmark_insert_child), xml);
      g_signal_connect_after(bookmark, "remove-child",
                         G_CALLBACK(cb_bookmark_remove_child), xml);
}


static gboolean
kz_xbel_from_string (KzBookmarkFile *bookmark,
                 const gchar *buffer, guint length,
                 GError **error)
{
      KzXML *xml;

      g_return_val_if_fail(KZ_IS_BOOKMARK_FILE(bookmark), FALSE);

      xml = g_object_get_qdata(G_OBJECT(bookmark), xml_quark);
      g_return_val_if_fail(KZ_IS_XML(xml), FALSE);

      if (!kz_xml_load_xml(xml, buffer, length) ||
          !kz_xml_get_root_element(xml))
      {
            KzXMLNode *node, *title_node, *title, *space;
            const gchar *bookmark_title;

            /*
             *  <xbel version=1.0 folded=no>\n (space1)
             *  <title>Bookmarks</title>\n       (space2)
             *  </xbel>
             */
            node = kz_xml_element_node_new("xbel");
            kz_xml_node_set_attr(node, "version", "1.0");
            kz_xml_node_set_attr(node, "folded", "no");     
            kz_xml_node_set_attr(node, "xmlns:kz", KAZEHAKASE_URI"2004");     
            /* kz_xml_node_set_attr(node, "id", "????????"); */
            kz_xml_node_append_child(xml->root, node);

            /* space1 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);

            {
                  title_node = kz_xml_element_node_new("title");
                  kz_xml_node_append_child(node, title_node);

                  bookmark_title = kz_bookmark_get_title(KZ_BOOKMARK(bookmark));
                  if(bookmark_title)
                        title = kz_xml_text_node_new(bookmark_title);
                  else
                        title = kz_xml_text_node_new("Bookmarks");
                  kz_xml_node_append_child(title_node, title);
            }

            /* space2 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);
      }

      kz_xbel_build_tree(KZ_BOOKMARK(bookmark));

      return TRUE;
}


static gchar *
kz_xbel_to_string (KzBookmarkFile *bookmark)
{
      KzXML *xml;

      g_return_val_if_fail(KZ_IS_BOOKMARK_FILE(bookmark), NULL);

      xml = g_object_get_qdata(G_OBJECT(bookmark), xml_quark);
      g_return_val_if_fail(KZ_IS_XML(xml), NULL);

      kz_xml_node_arrange_indent(xml->root, 0);

      return kz_xml_node_to_xml(xml->root);
}


static void
kz_xbel_notify (GObject *object, GParamSpec *pspec, KzXML *xml)
{
      KzBookmark *bookmark;
      KzXMLNode *node;
      const gchar *prop;
        GValue value = { 0 };

      g_return_if_fail(KZ_IS_BOOKMARK(object));
      g_return_if_fail(KZ_IS_XML(xml));

      bookmark = KZ_BOOKMARK(object);

      if (BOOKMARK_IS_BUILDING(bookmark)) return;

      node = kz_xml_get_root_element(xml);
      if (!node) return;
      g_return_if_fail(kz_xml_node_name_is(node, "xbel"));

      prop = g_param_spec_get_name(pspec);
      g_return_if_fail(prop);

        g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
        g_object_get_property(object, prop, &value);

      /*
       * FIXME! We should use hash table.
       */
      if (!strcmp(prop, "title"))
      {
            gchar *title = g_value_dup_string(&value);

            xml_node_set_title(node, title);
            g_free(title);
      }
      g_value_unset(&value);
}


static KzXMLNode *
xml_node_get_named_node (KzXMLNode *parent, const gchar *name)
{
      KzXMLNode *node;

      g_return_val_if_fail(parent, NULL);
      g_return_val_if_fail(name && *name, NULL);

      for (node = kz_xml_node_first_child(parent);
           node;
           node = kz_xml_node_next(node))
      {
            if (kz_xml_node_name_is(node, name))
                  return node;
      }

      return NULL;
}


static void
xml_node_set_location (KzXMLNode *node, KzBookmark *bookmark);
static void
xml_node_set_interval (KzXMLNode *node, KzBookmark *bookmark);

static void
parse_metadata_node (KzBookmark *bookmark, KzXMLNode *parent)
{
      KzXMLNode *node;

      g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

      if (!kz_xml_node_is_element(parent)) return;

      for (node = kz_xml_node_first_child(parent);
           node;
           node = kz_xml_node_next(node))
      {
            const GList *attrs, *a_node;
            const gchar *owner;
            const gchar *smart_regex  = NULL;
            const gchar *smart_uri    = NULL;
            const gchar *smart_encode = NULL;
            gboolean urlencode = FALSE;
            gint interval;

            if (!kz_xml_node_is_element(node)) continue;

            if (!kz_xml_node_name_is(node, "metadata")) continue;
                  
            /* We should ignore other owners such as Galeon. */
            owner = kz_xml_node_get_attr(node, "owner");
            if (!owner || strcmp(owner, KAZEHAKASE_URI))
                  continue;
                  
            attrs = kz_xml_node_get_attrs(node);

            for (a_node = attrs; a_node; a_node = g_list_next(a_node))
            {
                  KzXMLAttr *attr = a_node->data;

                  if (!strncmp(attr->name, "kz:update_interval", 118))
                  {
                        interval = atoi(attr->value);
                        kz_bookmark_file_set_interval(KZ_BOOKMARK_FILE(bookmark), interval);
                  }
                  else if (!strncmp(attr->name, "kz:smart_regex", 14))
                  {
                        smart_regex = attr->value;
                  }
                  else if (!strncmp(attr->name, "kz:smart_uri", 12))
                  {
                        smart_uri = attr->value;
                  }
                  else if (!strncmp(attr->name, "kz:smart_encode", 15))
                  {
                        smart_encode = attr->value;
                  }
                  else if (!strncmp(attr->name, "kz:smart_urlencode", 16))
                  {
                        urlencode = TRUE;
                  }
                  else if (!strncmp(attr->name, "kz:xmlrpc", 9))
                  {
                        kz_bookmark_file_set_xmlrpc(KZ_BOOKMARK_FILE(bookmark), attr->value);
                  }
                  else if (!strncmp(attr->name, "kz:xmlrpc_user", 14))
                  {
                        kz_bookmark_file_set_xmlrpc_user(KZ_BOOKMARK_FILE(bookmark), attr->value);
                  }
                  else if (!strncmp(attr->name, "kz:xmlrpc_pass", 14))
                  {
                        kz_bookmark_file_set_xmlrpc_pass(KZ_BOOKMARK_FILE(bookmark), attr->value);
                  }
                  else if (!strncmp(attr->name, "kz:current_position", 18))
                  {
                        gint pos = atoi(attr->value);
                        kz_bookmark_set_current(bookmark, pos);
                  }
                  else if (!strncmp(attr->name, "kz:lock", 7))
                  {
                        if (!strcmp(attr->value, "yes"))
                              kz_bookmark_set_lock(bookmark, TRUE);
                  }
                  else if (!strncmp(attr->name, "kz:auto_refresh", 15))
                  {
                        if (!strcmp(attr->value, "yes"))
                              kz_bookmark_set_auto_refresh(bookmark, TRUE);
                  }

            }

            if (smart_regex && smart_uri)
            {
                   kz_smart_bookmark_append_property(
                        KZ_SMART_BOOKMARK(bookmark),
                        smart_regex, smart_uri, 
                        smart_encode, urlencode);
            }
      }     
}

static KzXMLNode *
xml_node_find_metadata_node (KzXMLNode *parent)
{
      KzXMLNode *info_node, *node;

      info_node = xml_node_get_named_node(parent, "info");
      if (!info_node) return NULL;

      for (node = kz_xml_node_first_child(info_node);
           node;
           node = kz_xml_node_next(node))
      {
            const gchar *owner;
      
            if (!kz_xml_node_name_is(node, "metadata"))
                  continue;

            owner = kz_xml_node_get_attr(node, "owner");
            if (owner && !strcmp(owner, KAZEHAKASE_URI))
                  return node;
      }

      return NULL;
}


static gboolean
xml_node_has_smart_property (KzXMLNode *parent)
{
      const GList *attrs, *list;
      KzXMLNode *info_node, *node;
      gboolean ret = FALSE;
                  
      info_node = xml_node_get_named_node(parent, "info");
      if (!info_node) return FALSE;
      
      for (node = kz_xml_node_first_child(info_node);
           node;
           node = kz_xml_node_next(node))
      {
            const gchar *owner;
            if (!kz_xml_node_name_is(node, "metadata"))
                  continue;

            owner = kz_xml_node_get_attr(node, "owner");
            if (!owner || strcmp(owner, KAZEHAKASE_URI))
                  continue;

            attrs = kz_xml_node_get_attrs(node);

            for (list = attrs; list; list = g_list_next(list))
            {
                  KzXMLAttr *attr = list->data;

                  if (!strncmp(attr->name, "kz:smart", 7))
                  {
                        ret = TRUE;
                        break;
                  }
            }
            if (ret) break;
      }
      return ret;
}


static gboolean
xml_node_has_location (KzXMLNode *parent)
{
      KzXMLNode *child;

      g_return_val_if_fail(parent, FALSE);

      if (!kz_xml_node_name_is(parent, "folder"))
            return FALSE;

      child = xml_node_find_metadata_node(parent);
      if (!child) return FALSE;

      if (kz_xml_node_get_attr(child, "kz:location"))
            return TRUE;
      
      return FALSE;
}


static const gchar *
xml_node_get_location (KzXMLNode *parent)
{
      KzXMLNode *child;
      const gchar *attr;

      g_return_val_if_fail(parent, FALSE);

      if (!kz_xml_node_name_is(parent, "folder"))
            return NULL;

      child = xml_node_find_metadata_node(parent);
      if (!child) return NULL;

      /* Does metadata node have location attr? */
      attr = kz_xml_node_get_attr(child, "kz:location");
      if (!attr) return NULL;
      
      return attr;
}


static void
parse_child_node (KzBookmark *bookmark, KzXMLNode *parent)
{
      KzXMLNode *node;

      g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

      if (!kz_xml_node_is_element(parent)) return;

      for (node = kz_xml_node_first_child(parent);
           node;
           node = kz_xml_node_next(node))
      {
            if (!kz_xml_node_is_element(node)) continue;

            if (kz_xml_node_name_is(node, "title"))
            {
                  gchar *title;

                  if (kz_bookmark_get_title(bookmark) &&
                      !g_object_get_qdata(G_OBJECT(bookmark), xml_quark))
                  {
                        g_warning("title element is duplicated!");
                        continue;
                  }
                  title = kz_xml_node_to_str(node);
                  kz_bookmark_set_title(bookmark, title);
                  g_free(title);
            }
            else if (kz_xml_node_name_is(node, "desc"))
            {
                  gchar *desc;

                  if (kz_bookmark_get_description(bookmark))
                  {
                        g_warning("desc element is duplicated!");
                        continue;
                  }
                  desc = kz_xml_node_to_str(node);
                  kz_bookmark_set_description(bookmark, desc);
                  g_free(desc);
            }
            else if (kz_xml_node_name_is(node, "folder"))
            {
                  KzBookmark *folder;
                  const gchar *folded_str;
                  const gchar *id;
                  gboolean folded;

                  if (xml_node_has_location(node))
                  {
#warning FIXME! If the file does not exit, we should creat it.
                        const gchar *location = xml_node_get_location(node);

                        folder = KZ_BOOKMARK(kz_bookmark_file_new(location,
                                                NULL, NULL));
                        kz_bookmark_file_load_start(KZ_BOOKMARK_FILE(folder));
                  }
                  else
                  {
                        folder = kz_bookmark_pure_folder_new();
                  }

                  folded_str = kz_xml_node_get_attr(node, "folded");
                  folded = folded_str && !strcmp(folded_str, "yes");
                  kz_bookmark_set_folded(folder, folded);

                  id = kz_xml_node_get_attr(node, "id");
                  if (id)
                        kz_bookmark_set_id(folder, id);

                  BOOKMARK_SET_BUILDING(folder);
                  g_object_set_qdata(G_OBJECT(folder),
                                 node_quark, node);
                  kz_bookmark_append(bookmark, folder);
                  parse_child_node(folder, node);
                  BOOKMARK_UNSET_BUILDING(folder);

                  g_object_unref(folder);
            }
            else if (kz_xml_node_name_is(node, "bookmark"))
            {
                  KzBookmark *child_bookmark;
                  const GList *attrs = kz_xml_node_get_attrs(node);
                  const GList *list;

                  if (xml_node_has_smart_property(node))
                        child_bookmark = kz_smart_bookmark_new();
                  else
                        child_bookmark = kz_bookmark_new();

                  BOOKMARK_SET_BUILDING(bookmark);
                  g_object_set_qdata(G_OBJECT(child_bookmark),
                                 node_quark, node);
                  for (list = attrs; list; list = g_list_next(list))
                  {
                        KzXMLAttr *attr = list->data;
                        const gchar *name = attr->name;

                        if (!strcmp(name, "href"))
                        {
                              kz_bookmark_set_link(child_bookmark, attr->value);
                        }
                        else if (!strcmp(name, "added"))
                        {
                              guint t = 0;
                              if (str_isdigit(attr->value))
                                    t = atoi(attr->value);  
                              kz_bookmark_set_added_time(child_bookmark, t);
                        }
                        else if (!strcmp(name, "visited"))
                        {
                              guint t = 0;
                              if (str_isdigit(attr->value))
                                    t = atoi(attr->value);  
                              kz_bookmark_set_last_visited(child_bookmark, t);
                        }
                        else if (!strcmp(name, "id"))
                        {
                              kz_bookmark_set_id(child_bookmark, attr->value);
                        }
                  }

                  parse_child_node(child_bookmark, node);
                  /*
                   * To append child is after parsing child's nodes,
                   * because smart bookmark action needs bookmark title 
                   * in child's nodes.
                   */
                  kz_bookmark_append(bookmark, child_bookmark);
                  BOOKMARK_UNSET_BUILDING(bookmark);
                  g_object_unref(child_bookmark);
            }
            else if (kz_xml_node_name_is(node, "separator"))
            {
                  KzBookmark *separator = kz_bookmark_separator_new();

                  BOOKMARK_SET_BUILDING(separator);
                  g_object_set_qdata(G_OBJECT(separator),
                                 node_quark, node);
                  kz_bookmark_append(bookmark, separator);
                  BOOKMARK_UNSET_BUILDING(separator);
                  g_object_unref(separator);
            }
            else if (kz_xml_node_name_is(node, "alias"))
            {
                  g_warning("KzXBEL::alias element is not supported yet."
                          "Please implement me!");
            }
            else if (kz_xml_node_name_is(node, "info"))
            {
                  parse_metadata_node(bookmark, node);
            }
      }
}


static void
kz_xbel_build_tree (KzBookmark *bookmark)
{
      KzXML *xml;
      KzXMLNode *node;

      g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

      xml = g_object_get_qdata(G_OBJECT(bookmark), xml_quark);
      g_return_if_fail(KZ_IS_XML(xml));

      node = kz_xml_get_root_element (xml);
      if (!node) return;
      g_return_if_fail (kz_xml_node_name_is(node, "xbel"));

      BOOKMARK_SET_BUILDING(bookmark);
      g_object_set_qdata(G_OBJECT(bookmark),
                     node_quark, node);
      parse_child_node(bookmark, node);
      BOOKMARK_UNSET_BUILDING(bookmark);
}


static void
xml_node_append_title (KzXMLNode *node, KzBookmark *bookmark)
{
      const gchar *title = kz_bookmark_get_title(bookmark);
      KzXMLNode *title_node, *text_node, *space;

      /*
       *  <title>title</title>\n
       */

      /* title */
      title_node = kz_xml_element_node_new("title");
      kz_xml_node_append_child(node, title_node);
      text_node = kz_xml_text_node_new(title);
      kz_xml_node_append_child(title_node, text_node);

      /* space1 */
      space = kz_xml_text_node_new("\n");
      kz_xml_node_append_child(node, space);
}


static KzXMLNode *
xml_node_append_metadata_node (KzXMLNode *node)
{
      KzXMLNode *space, *info_node, *metadata_node;

      /*
       *  <info>\n      (space1)
       *  <metadata>\n  (space2)
       *  </metadata>\n (space3)
       *  </info>\n     (space4)
       */

      /* info */
      info_node = xml_node_get_named_node(node, "info");
      if (!info_node)
      {
            info_node = kz_xml_element_node_new("info");
            kz_xml_node_append_child(node, info_node);

            /* space1 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(info_node, space);

            /* space4 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);
      }

      /* metadata */
      metadata_node = xml_node_find_metadata_node(node);
      if (metadata_node) return metadata_node;

      metadata_node = kz_xml_element_node_new("metadata");
      kz_xml_node_set_attr(metadata_node, "owner", KAZEHAKASE_URI);
      kz_xml_node_append_child(info_node, metadata_node);

      /* space3 */
      space = kz_xml_text_node_new("\n");
      kz_xml_node_append_child(info_node, space);

      return metadata_node;
}


static void
xml_node_set_location (KzXMLNode *node, KzBookmark *bookmark)
{
      const gchar *location;
      KzXMLNode *metadata_node;

      location = kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(bookmark));
      g_return_if_fail(location);

      metadata_node = xml_node_find_metadata_node(node);
      
      if (!metadata_node)
            metadata_node = xml_node_append_metadata_node(node);

      kz_xml_node_set_attr(metadata_node, "kz:location", location);
}


static void
xml_node_set_interval (KzXMLNode *node, KzBookmark *bookmark)
{
      gchar *interval;
      KzXMLNode *metadata_node;

      interval = g_strdup_printf("%d", kz_bookmark_file_get_interval(KZ_BOOKMARK_FILE(bookmark)));

      metadata_node = xml_node_find_metadata_node(node);
      
      if (!metadata_node)
            metadata_node = xml_node_append_metadata_node(node);

      /* update_interval */
      kz_xml_node_set_attr(metadata_node, "kz:update_interval", interval);

      g_free(interval);
}


static void
xml_node_set_smart_list (KzXMLNode *node,
                   KzBookmark *bookmark,
                         const GList *smart_list)
{
      const GList *smart_node;      
      KzXMLNode *info_node, *child_node;

      /* get the info node, create it if not exist */
      info_node = xml_node_get_named_node(node, "info");
      if (!info_node)
      {
            KzXMLNode *space;

            info_node = kz_xml_element_node_new("info");
            kz_xml_node_append_child(node, info_node);

            /* space1 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(info_node, space);

            /* space4 */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);
      }

      /* remove all previous smart properties */
      child_node = kz_xml_node_first_child(info_node);
      while (child_node)
      {
            if (kz_xml_node_name_is(child_node, "metadata"))
            {
                  const gchar *owner, *smart_regex;
                  owner = kz_xml_node_get_attr(child_node, "owner");
                  smart_regex = kz_xml_node_get_attr(child_node,
                                             "kz:smart_regex");
                  if (owner && 
                     !strcmp(owner, KAZEHAKASE_URI) &&
                      smart_regex)
                  {
                        KzXMLNode *tmp = child_node;
                        child_node = kz_xml_node_next(child_node);
                        kz_xml_node_remove_child(info_node,
                                           tmp);
                        kz_xml_node_unref(tmp);

                        /* space is also removed */
                        if (kz_xml_node_is_space(child_node))
                        {
                              KzXMLNode *tmp = child_node;
                              child_node = kz_xml_node_next(child_node);
                              kz_xml_node_remove_child(info_node,
                                                 tmp);
                              kz_xml_node_unref(tmp);
                        }
                  }
                  else
                        child_node = kz_xml_node_next(child_node);
            }
            else
                  child_node = kz_xml_node_next(child_node);
      }

      /* append new smart properties */
      for (smart_node = smart_list; smart_node; smart_node = g_list_next(smart_node))
      {
            KzSmartBookmarkProperty *prop;
            KzXMLNode *metadata_node, *space;
            
            metadata_node = kz_xml_element_node_new("metadata");
            kz_xml_node_set_attr(metadata_node, "owner", KAZEHAKASE_URI);
            kz_xml_node_append_child(info_node, metadata_node);

            /* space */
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(info_node, space);

            prop = smart_node->data;

            if (prop->regex)
            {
                  kz_xml_node_set_attr(metadata_node,
                                   "kz:smart_regex", prop->regex);
            }
            if (prop->uri)
            {
                  kz_xml_node_set_attr(metadata_node,
                                   "kz:smart_uri", prop->uri);
            }
            if (prop->encode)
            {
                  kz_xml_node_set_attr(metadata_node,
                                   "kz:smart_encode", prop->encode);
            }
            if (prop->urlencode)
            {
                  kz_xml_node_set_attr(metadata_node,
                                   "kz:smart_urlencode", "yes");
            }
      }
}


static KzXMLNode *
create_xml_node (KzBookmark *bookmark)
{
      KzXMLNode *node = NULL, *space;

      g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

      if (kz_bookmark_is_separator(bookmark))
      {
            node = kz_xml_element_node_new("separator");
      }
      else if (KZ_IS_BOOKMARK_FILE(bookmark))
      {
            const gchar *location = kz_bookmark_file_get_location(KZ_BOOKMARK_FILE(bookmark));
            guint interval        = kz_bookmark_file_get_interval(KZ_BOOKMARK_FILE(bookmark));

            if (!location || !*location)
            {
                  /* warning? */
            }

            /*
             * <folder>\n
             * <title/>
             * ...
             * </folder>
             */

            node = kz_xml_element_node_new("folder");
            xml_node_append_title (node, bookmark);

            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);

            if (location)
                  xml_node_set_location(node, bookmark);
            if (interval)
                  xml_node_set_interval(node, bookmark);
      }
      else if (kz_bookmark_is_folder(bookmark))
      {
            KzXMLNode *metanode;
            gint pos = kz_bookmark_get_current(bookmark);
            gboolean lock = kz_bookmark_get_lock(bookmark);
            gboolean auto_refresh = kz_bookmark_get_auto_refresh(bookmark);
            /*
             * <folder>\n
             * <title/>
             * ...
             * </folder>
             */

            node = kz_xml_element_node_new("folder");

            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);

            xml_node_append_title (node, bookmark);
            
            if (pos || lock || auto_refresh)
            {
                  metanode = xml_node_find_metadata_node(node);
      
                  if (!metanode)
                        metanode = xml_node_append_metadata_node(node);

                  if (pos)
                  {
                        gchar *pos_str = g_strdup_printf("%d", pos);
                        kz_xml_node_set_attr(metanode, "kz:current_position", pos_str);
                  }
                  if (lock)
                  {
                        const gchar *str;
                        if (lock)
                              str = "yes";
                        else
                              str = "no";
                        kz_xml_node_set_attr(metanode, "kz:lock", str);
                  }
                  if (auto_refresh)
                  {
                        const gchar *str;
                        if (auto_refresh)
                              str = "yes";
                        else
                              str = "no";
                        kz_xml_node_set_attr(metanode, "kz:auto_refresh", str);
                  }

            }
      }
      else if (KZ_IS_SMART_BOOKMARK(bookmark))
      {
            const GList *smart_list;
            const gchar *uri = kz_bookmark_get_link(bookmark);
            
            smart_list = kz_smart_bookmark_get_smart_list(
                        KZ_SMART_BOOKMARK(bookmark));
      
            node = kz_xml_element_node_new("bookmark");
            if (uri)
                  kz_xml_node_set_attr(node, "href", uri);
            if (smart_list)
            {
                  xml_node_set_smart_list(node, 
                                    bookmark,
                                    smart_list);
            }
            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);

            xml_node_append_title (node, bookmark);
      }
      else
      {
            const gchar *uri = kz_bookmark_get_link(bookmark);
            const gchar *id = kz_bookmark_get_id(bookmark);
            guint visited = kz_bookmark_get_last_visited(bookmark);
            guint added = kz_bookmark_get_added_time(bookmark);
            /*
             * <bookmark>\n
             * <title/>
             * </bookmark>
             */

            node = kz_xml_element_node_new("bookmark");
            if (uri)
                  kz_xml_node_set_attr(node, "href", uri);
            if (id)
                  kz_xml_node_set_attr(node, "id", id);
            if (visited != 0)
            {
                  gchar *str = g_strdup_printf("%d", visited);
                  kz_xml_node_set_attr(node, "visited", str);
                  g_free(str);
            }
            if (added != 0)
            {
                  gchar *str = g_strdup_printf("%d", added);
                  kz_xml_node_set_attr(node, "added", str);
                  g_free(str);
            }

            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(node, space);

            xml_node_append_title (node, bookmark);
      }

      return node;
}


static void
kz_xbel_connect_signals (KzBookmark *bookmark)
{
      g_signal_connect(bookmark, "notify",
                   G_CALLBACK(cb_bookmark_notify), NULL);

      if (KZ_IS_BOOKMARK_FILE(bookmark)) return;
      if (!kz_bookmark_is_folder(bookmark)) return;

      {
            g_signal_connect_after(bookmark, "insert-child",
                               G_CALLBACK(cb_bookmark_insert_child),
                               NULL);
            g_signal_connect_after(bookmark, "remove-child",
                               G_CALLBACK(cb_bookmark_remove_child),
                               NULL);
      }

      /* for children */
      {
            GList *node, *children;

            children = kz_bookmark_get_children(bookmark);
            for (node = children; node; node = g_list_next(node))
            {
                  KzBookmark *child = node->data;
                  kz_xbel_connect_signals(child);
            }
            g_list_free(children);
      }
}


static void
kz_xbel_disconnect_signals (KzBookmark *bookmark)
{
      g_signal_handlers_disconnect_by_func
            (bookmark,
             G_CALLBACK(cb_bookmark_notify), NULL);

      if (KZ_IS_BOOKMARK_FILE(bookmark)) return;
      if (!kz_bookmark_is_folder(bookmark)) return;

      {
            g_signal_handlers_disconnect_by_func
                  (bookmark,
                   G_CALLBACK(cb_bookmark_insert_child), NULL);
            g_signal_handlers_disconnect_by_func
                  (bookmark,
                   G_CALLBACK(cb_bookmark_remove_child), NULL);
      }

      /* for children */
      {
            GList *node, *children;

            children = kz_bookmark_get_children(bookmark);
            for (node = children; node; node = g_list_next(node))
            {
                  KzBookmark *child = node->data;
                  kz_xbel_disconnect_signals(child);
            }
            g_list_free(children);
      }
}


static void
cb_bookmark_insert_child (KzBookmark *bookmark,
                    KzBookmark *child, KzBookmark *sibling)
{
      kz_xbel_insert_xml_node(child, bookmark, sibling);
      kz_xbel_connect_signals(child);
}


static void
cb_bookmark_remove_child (KzBookmark *bookmark, KzBookmark *child)
{
      kz_xbel_disconnect_signals(child);
      kz_xbel_remove_xml_node(child);
}


static void
xml_node_set_title (KzXMLNode *parent, const gchar *title)
{
      KzXMLNode *title_node = NULL, *node, *child;

      g_return_if_fail(parent);

      node = kz_xml_node_first_child(parent);

      /* find title node */
      for (; node; node = kz_xml_node_next(node))
      {
            if (kz_xml_node_name_is(node, "title"))
            {
                  title_node = node;
                  break;
            }
      }

      g_return_if_fail(title_node);

      /* remove old title */
      child = kz_xml_node_first_child(title_node);
      while(child)
      {
            KzXMLNode *next = kz_xml_node_next(child);

            child = kz_xml_node_remove_child(title_node, child);
            kz_xml_node_unref(child);
            child = next;
      }

      /* set new title */
      child = kz_xml_text_node_new(title);
      kz_xml_node_append_child(title_node, child);
}


static void
xml_node_set_description (KzXMLNode *parent, const gchar *desc)
{
      KzXMLNode *desc_node = NULL, *node, *child;

      g_return_if_fail(parent);

      node = kz_xml_node_first_child(parent);

      /* find desc node */
      for (; node; node = kz_xml_node_next(node))
      {
            if (kz_xml_node_name_is(node, "desc"))
            {
                  desc_node = node;
                  break;
            }
      }

      /* remove old title, or create new desc node */
      if (desc_node)
      {
            child = kz_xml_node_first_child(node);
            while(child)
            {
                  KzXMLNode *next = kz_xml_node_next(child);

                  child = kz_xml_node_remove_child(node, child);
                  kz_xml_node_unref(child);
                  child = next;
            }
      }
      else
      {
            KzXMLNode *space;

            desc_node = kz_xml_element_node_new("desc");
            kz_xml_node_append_child(parent, desc_node);

            space = kz_xml_text_node_new("\n");
            kz_xml_node_append_child(parent, space);
      }

      /* set new description */
      child = kz_xml_text_node_new(desc);
      kz_xml_node_append_child(desc_node, child);
}


static void
cb_bookmark_notify (GObject *object, GParamSpec *pspec)
{
      KzBookmark *bookmark;
      KzXMLNode *node;
      const gchar *prop;
        GValue value = { 0 };

      g_return_if_fail(KZ_IS_BOOKMARK(object));
      bookmark = KZ_BOOKMARK(object);

      if (BOOKMARK_IS_BUILDING(bookmark)) return;

      node = g_object_get_qdata(G_OBJECT(bookmark), node_quark);
      g_return_if_fail(node);
      g_return_if_fail(kz_xml_node_name_is(node, "bookmark") ||
                   kz_xml_node_name_is(node, "folder"));

      prop = g_param_spec_get_name(pspec);
      g_return_if_fail(prop);

        g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
        g_object_get_property(object, prop, &value);

      /*
       * FIXME! We should use hash table.
       */
      if (!strcmp(prop, "id"))
      {
            gchar *id = g_value_dup_string(&value);

            kz_xml_node_set_attr(node, "id", id);
            g_free(id);
      }
      else if (!strcmp(prop, "title"))
      {
            gchar *title = g_value_dup_string(&value);

            xml_node_set_title(node, title);
            g_free(title);
      }
      else if (!strcmp(prop, "link"))
      {
            gchar *uri = g_value_dup_string(&value);

            kz_xml_node_set_attr(node, "href", uri);
            g_free(uri);
      }
      else if (!strcmp(prop, "description"))
      {
            gchar *desc = g_value_dup_string(&value);

            xml_node_set_description(node, desc);
            g_free(desc);
      }
      else if (!strcmp(prop, "location"))
      {
            xml_node_set_location(node, bookmark);
      }
      else if (!strcmp(prop, "interval"))
      {
            xml_node_set_interval(node, bookmark);
      }
      else if (!strcmp(prop, "last-visited"))
      {
            guint t = g_value_get_uint(&value);
            gchar *t_str = g_strdup_printf("%d", t);
            kz_xml_node_set_attr(node, "visited", t_str);
            g_free(t_str);
      }
      else if (!strcmp(prop, "added-time"))
      {
            guint t = g_value_get_uint(&value);
            gchar *t_str = g_strdup_printf("%d", t);
            kz_xml_node_set_attr(node, "added", t_str);
            g_free(t_str);
      }
      else if (!strcmp(prop, "smart-list"))
      {
            const GList *smart_list;
            smart_list =  g_value_get_pointer(&value);
            xml_node_set_smart_list(node, bookmark, smart_list);
      }
      else if (!strcmp(prop, "xmlrpc"))
      {
            KzXMLNode *metanode;
            gchar *xmlrpc = g_value_dup_string(&value);
            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:xmlrpc", xmlrpc);
            g_free(xmlrpc);
      }
      else if (!strcmp(prop, "xmlrpc-user"))
      {
            KzXMLNode *metanode;
            gchar *xmlrpc = g_value_dup_string(&value);
            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:xmlrpc_user", xmlrpc);
            g_free(xmlrpc);
      }
      else if (!strcmp(prop, "xmlrpc-pass"))
      {
            KzXMLNode *metanode;
            gchar *xmlrpc = g_value_dup_string(&value);
            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:xmlrpc_user", xmlrpc);
            g_free(xmlrpc);
      }
      else if (!strcmp(prop, "current"))
      {
            KzXMLNode *metanode;
            guint pos = g_value_get_uint(&value);
            gchar *pos_str = g_strdup_printf("%d", pos);
            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:current_position", pos_str);
            g_free(pos_str);
      }
      else if (!strcmp(prop, "lock"))
      {
            KzXMLNode *metanode;
            const gchar *str;
            gboolean lock = g_value_get_boolean(&value);
            
            if (lock)
                  str = "yes";
            else
                  str = "no";

            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:lock", str);
      }
      else if (!strcmp(prop, "auto-refresh"))
      {
            KzXMLNode *metanode;
            const gchar *str;
            gboolean auto_refresh = g_value_get_boolean(&value);
            
            if (auto_refresh)
                  str = "yes";
            else
                  str = "no";

            metanode = xml_node_find_metadata_node(node);
      
            if (!metanode)
                  metanode = xml_node_append_metadata_node(node);

            kz_xml_node_set_attr(metanode, "kz:auto_refresh", str);
      }
      g_value_unset(&value);
}


#if 0
void
kz_xbel_save (KzXBEL *xbel)
{
      const gchar *location;;

      g_return_if_fail(KZ_IS_XBEL(xbel));

      if (xbel->xml)
            kz_xml_node_arrange_indent(xbel->xml->root, 0);

      location = kz_bookmark_get_location(KZ_BOOKMARK(xbel));
      if (location && *location)
            kz_xml_save(xbel->xml, location);
}
#endif


static void
kz_xbel_insert_xml_node (KzBookmark *bookmark,
                   KzBookmark *parent, KzBookmark *sibling)
{
      KzXMLNode *node, *space;
      KzXMLNode *parent_node, *sibling_node = NULL;

      g_return_if_fail(KZ_IS_BOOKMARK(parent));
      g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
      g_return_if_fail(!sibling || KZ_IS_BOOKMARK(sibling));

      node = g_object_get_qdata(G_OBJECT(bookmark), node_quark);
      if (node) return;

      parent_node = g_object_get_qdata(G_OBJECT(parent),
                               node_quark);
      if (!parent_node)
      {
            KzXML *xml;

            g_return_if_fail(KZ_IS_BOOKMARK(parent));

            xml = g_object_get_qdata(G_OBJECT(parent), xml_quark);
            g_return_if_fail(KZ_IS_XML(xml));

            parent_node = kz_xml_get_root_element(xml);
            g_return_if_fail (kz_xml_node_name_is(parent_node, "xbel"));
      }

      if (sibling)
            sibling_node = g_object_get_qdata(G_OBJECT(sibling),
                                      node_quark);

      node = create_xml_node(bookmark);
      g_object_set_qdata(G_OBJECT(bookmark), node_quark, node);

      kz_xml_node_insert_before(parent_node, node, sibling_node);
      space = kz_xml_text_node_new("\n");
      kz_xml_node_insert_before(parent_node, space, kz_xml_node_next(node));

      if (kz_bookmark_is_folder(bookmark) && !KZ_IS_BOOKMARK_FILE(bookmark))
      {
            GList *node, *children;

            children = kz_bookmark_get_children(bookmark);
            for (node = children; node; node = g_list_next(node))
            {
                  KzBookmark *child = node->data;
                  kz_xbel_insert_xml_node (child, bookmark, NULL);
            }
            g_list_free(children);
      }
}


static void
kz_xbel_remove_xml_node (KzBookmark *bookmark)
{
      KzXMLNode *node, *parent;

      g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

      if (kz_bookmark_is_folder(bookmark))
      {
            GList *node, *children;

            children = kz_bookmark_get_children(bookmark);
            for (node = children; node; node = g_list_next(node))
            {
                  KzBookmark *child = node->data;
                  kz_xbel_remove_xml_node (child);
            }
            g_list_free(children);
      }

      node = g_object_get_qdata(G_OBJECT(bookmark), node_quark);
      if (!node) return;

      parent = kz_xml_node_parent(node);
      if (parent)
      {
            KzXMLNode *space;

            space = kz_xml_node_next(node);
            if (space && kz_xml_node_is_space(space))
            {
                  space = kz_xml_node_remove_child(parent, space);
                  kz_xml_node_unref(space);
            }

            node = kz_xml_node_remove_child(parent, node);
      }

      kz_xml_node_unref(node);
      g_object_set_qdata(G_OBJECT(bookmark), node_quark, NULL);
}

Generated by  Doxygen 1.6.0   Back to index