Move XBEL implementation to katze.
The implementation of XBEL is moved to katze, including the appropriate api prefix and coding style changes. All relevant uses are updated.
This commit is contained in:
parent
e2328a4bfc
commit
d5a4f4cfd7
13 changed files with 1292 additions and 1141 deletions
|
@ -1,13 +1,16 @@
|
||||||
INCLUDES = \
|
INCLUDES = \
|
||||||
$(GTK_CFLAGS)
|
$(GTK_CFLAGS) \
|
||||||
|
$(LIBXML_CFLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = \
|
noinst_LTLIBRARIES = \
|
||||||
libkatze.la
|
libkatze.la
|
||||||
|
|
||||||
libkatze_la_LIBADD = \
|
libkatze_la_LIBADD = \
|
||||||
$(GTK_LIBS)
|
$(GTK_LIBS) \
|
||||||
|
$(LIBXML_LIBS)
|
||||||
|
|
||||||
libkatze_la_SOURCES = \
|
libkatze_la_SOURCES = \
|
||||||
katze.h \
|
katze.h \
|
||||||
katze-throbber.c katze-throbber.h \
|
katze-throbber.c katze-throbber.h \
|
||||||
katze-utils.c katze-utils.h
|
katze-utils.c katze-utils.h \
|
||||||
|
katze-xbel.c katze-xbel.h
|
||||||
|
|
958
katze/katze-xbel.c
Normal file
958
katze/katze-xbel.c
Normal file
|
@ -0,0 +1,958 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO:
|
||||||
|
* - Support info > metadata, alias, added, modified, visited
|
||||||
|
* - Compatibility: The Nokia 770 *requires* metadata and folder
|
||||||
|
* - Compatibility: Kazehakase's bookmarks
|
||||||
|
* - Compatibility: Epiphany's bookmarks
|
||||||
|
* - XML Indentation
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "katze-xbel.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
|
#include "katze-utils.h"
|
||||||
|
|
||||||
|
struct _KatzeXbelItemPrivate
|
||||||
|
{
|
||||||
|
guint refs;
|
||||||
|
KatzeXbelItemKind kind;
|
||||||
|
KatzeXbelItem* parent;
|
||||||
|
|
||||||
|
GList* items; // folder
|
||||||
|
gboolean folded; // foolder
|
||||||
|
gchar* title; // !separator
|
||||||
|
gchar* desc; // folder and bookmark
|
||||||
|
gchar* href; // bookmark
|
||||||
|
//time_t added; // !separator
|
||||||
|
//time_t modfied; // bookmark
|
||||||
|
//time_t visited; // bookmark
|
||||||
|
} ;
|
||||||
|
|
||||||
|
#define KATZE_XBEL_ITEM_GET_PRIVATE(item) \
|
||||||
|
item->priv
|
||||||
|
|
||||||
|
// Private: Create a new item of a certain type
|
||||||
|
static KatzeXbelItem*
|
||||||
|
katze_xbel_item_new (KatzeXbelItemKind kind)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = g_new (KatzeXbelItem, 1);
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item) = g_new (KatzeXbelItemPrivate, 1);
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
|
||||||
|
priv->refs = 1;
|
||||||
|
priv->parent = NULL;
|
||||||
|
priv->kind = kind;
|
||||||
|
if (kind == KATZE_XBEL_ITEM_KIND_FOLDER)
|
||||||
|
{
|
||||||
|
priv->items = NULL;
|
||||||
|
priv->folded = TRUE;
|
||||||
|
}
|
||||||
|
if (kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
|
||||||
|
{
|
||||||
|
priv->title = NULL;
|
||||||
|
priv->desc = NULL;
|
||||||
|
}
|
||||||
|
if (kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
|
||||||
|
priv->href = g_strdup ("");
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_bookmark_new:
|
||||||
|
*
|
||||||
|
* Create a new empty bookmark.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated bookmark
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_bookmark_new (void)
|
||||||
|
{
|
||||||
|
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_BOOKMARK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static KatzeXbelItem*
|
||||||
|
katze_xbel_bookmark_from_xmlNodePtr (xmlNodePtr cur)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (cur, NULL);
|
||||||
|
|
||||||
|
KatzeXbelItem* bookmark = katze_xbel_bookmark_new ();
|
||||||
|
xmlChar* key = xmlGetProp (cur, (xmlChar*)"href");
|
||||||
|
katze_xbel_bookmark_set_href (bookmark, (gchar*)key);
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent (cur);
|
||||||
|
katze_xbel_item_set_title (bookmark, g_strstrip ((gchar*)key));
|
||||||
|
}
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent (cur);
|
||||||
|
katze_xbel_item_set_desc (bookmark, g_strstrip ((gchar*)key));
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
return bookmark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_katze_xbel_separator_new:
|
||||||
|
*
|
||||||
|
* Create a new separator.
|
||||||
|
*
|
||||||
|
* The returned item must be freed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated separator.
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_separator_new (void)
|
||||||
|
{
|
||||||
|
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_new:
|
||||||
|
*
|
||||||
|
* Create a new empty folder.
|
||||||
|
*
|
||||||
|
* The returned item must be freed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a newly allocated folder.
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_folder_new (void)
|
||||||
|
{
|
||||||
|
return katze_xbel_item_new (KATZE_XBEL_ITEM_KIND_FOLDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: Create a folder from an xmlNodePtr
|
||||||
|
static KatzeXbelItem*
|
||||||
|
katze_xbel_folder_from_xmlNodePtr (xmlNodePtr cur)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (cur, NULL);
|
||||||
|
|
||||||
|
KatzeXbelItem* folder = katze_xbel_folder_new ();
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
|
||||||
|
xmlChar* key = xmlGetProp (cur, (xmlChar*)"folded");
|
||||||
|
if (key)
|
||||||
|
{
|
||||||
|
if (!g_ascii_strncasecmp ((gchar*)key, "yes", 3))
|
||||||
|
priv->folded = TRUE;
|
||||||
|
else if (!g_ascii_strncasecmp ((gchar*)key, "no", 2))
|
||||||
|
priv->folded = FALSE;
|
||||||
|
else
|
||||||
|
g_warning ("XBEL: Unknown value for folded.");
|
||||||
|
xmlFree (key);
|
||||||
|
}
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
if (!xmlStrcmp (cur->name, (const xmlChar*)"title"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent (cur);
|
||||||
|
katze_assign (priv->title, g_strstrip ((gchar*)key));
|
||||||
|
}
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"desc"))
|
||||||
|
{
|
||||||
|
xmlChar* key = xmlNodeGetContent (cur);
|
||||||
|
katze_assign (priv->desc, g_strstrip ((gchar*)key));
|
||||||
|
}
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = katze_xbel_folder_from_xmlNodePtr (cur);
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
|
||||||
|
priv->items = g_list_prepend (priv->items, item);
|
||||||
|
}
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = katze_xbel_bookmark_from_xmlNodePtr (cur);
|
||||||
|
priv->parent = folder;
|
||||||
|
priv->items = g_list_prepend (priv->items, item);
|
||||||
|
}
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = katze_xbel_separator_new ();
|
||||||
|
priv->parent = folder;
|
||||||
|
priv->items = g_list_prepend (priv->items, item);
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
// Prepending and reversing is faster than appending
|
||||||
|
priv->items = g_list_reverse (priv->items);
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private: Loads the contents from an xmlNodePtr into a folder.
|
||||||
|
static gboolean
|
||||||
|
katze_xbel_folder_from_xmlDocPtr (KatzeXbelItem* folder,
|
||||||
|
xmlDocPtr doc)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
|
||||||
|
g_return_val_if_fail (doc, FALSE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
|
||||||
|
xmlNodePtr cur = xmlDocGetRootElement (doc);
|
||||||
|
xmlChar* version = xmlGetProp (cur, (xmlChar*)"version");
|
||||||
|
if (xmlStrcmp (version, (xmlChar*)"1.0"))
|
||||||
|
g_warning ("XBEL version is not 1.0.");
|
||||||
|
xmlFree (version);
|
||||||
|
|
||||||
|
katze_assign (priv->title, (gchar*)xmlGetProp (cur, (xmlChar*)"title"));
|
||||||
|
katze_assign (priv->desc, (gchar*)xmlGetProp (cur, (xmlChar*)"desc"));
|
||||||
|
if ((cur = xmlDocGetRootElement (doc)) == NULL)
|
||||||
|
{
|
||||||
|
// Empty document
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (xmlStrcmp (cur->name, (const xmlChar*)"xbel"))
|
||||||
|
{
|
||||||
|
// Wrong document kind
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
cur = cur->xmlChildrenNode;
|
||||||
|
while (cur)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = NULL;
|
||||||
|
if (!xmlStrcmp (cur->name, (const xmlChar*)"folder"))
|
||||||
|
item = katze_xbel_folder_from_xmlNodePtr (cur);
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"bookmark"))
|
||||||
|
item = katze_xbel_bookmark_from_xmlNodePtr (cur);
|
||||||
|
else if (!xmlStrcmp (cur->name, (const xmlChar*)"separator"))
|
||||||
|
item = katze_xbel_separator_new ();
|
||||||
|
/*else if (!xmlStrcmp (cur->name, (const xmlChar*)"info"))
|
||||||
|
item = katze_xbel_parse_info (xbel, cur);*/
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
|
||||||
|
priv->items = g_list_prepend (priv->items, item);
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
// Prepending and reversing is faster than appending
|
||||||
|
priv->items = g_list_reverse (priv->items);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_ref:
|
||||||
|
* @item: a valid item
|
||||||
|
*
|
||||||
|
* Ref an KatzeXbelItem.
|
||||||
|
*
|
||||||
|
* Ref means that the reference count is increased by one.
|
||||||
|
*
|
||||||
|
* This has no effect on children of a folder.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_item_ref (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail (item);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
priv->refs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_unref:
|
||||||
|
* @item: a valid item
|
||||||
|
*
|
||||||
|
* Unref an KatzeXbelItem. If @item is a folder all of its children will also
|
||||||
|
* be unreffed automatically.
|
||||||
|
*
|
||||||
|
* Unref means that the reference count is decreased. If there are no
|
||||||
|
* references left, the memory will be freed and if needed removed from
|
||||||
|
* its containing folder.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_item_unref (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail (item);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
priv->refs--;
|
||||||
|
if (priv->refs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (priv->parent)
|
||||||
|
katze_xbel_folder_remove_item (priv->parent, item);
|
||||||
|
|
||||||
|
if (priv->kind == KATZE_XBEL_ITEM_KIND_FOLDER)
|
||||||
|
{
|
||||||
|
guint n = katze_xbel_folder_get_n_items (item);
|
||||||
|
guint i;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (_item)->parent = NULL;
|
||||||
|
katze_xbel_item_unref (_item);
|
||||||
|
}
|
||||||
|
g_list_free (priv->items);
|
||||||
|
}
|
||||||
|
if (priv->kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
|
||||||
|
{
|
||||||
|
g_free (priv->title);
|
||||||
|
g_free (priv->desc);
|
||||||
|
}
|
||||||
|
if (priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
|
||||||
|
g_free (priv->href);
|
||||||
|
g_free (item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_copy:
|
||||||
|
* @item: the item to copy
|
||||||
|
*
|
||||||
|
* Copy an KatzeXbelItem.
|
||||||
|
*
|
||||||
|
* The returned item must be unreffed eventually.
|
||||||
|
*
|
||||||
|
* Return value: a copy of @item
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_item_copy (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
KatzeXbelItem* copy = katze_xbel_item_new (priv->kind);
|
||||||
|
|
||||||
|
if (katze_xbel_item_is_folder (item))
|
||||||
|
{
|
||||||
|
guint n = katze_xbel_folder_get_n_items (item);
|
||||||
|
guint i;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
|
||||||
|
katze_xbel_folder_append_item (copy, katze_xbel_item_copy (_item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (priv->kind != KATZE_XBEL_ITEM_KIND_SEPARATOR)
|
||||||
|
{
|
||||||
|
katze_xbel_item_set_title (copy, priv->title);
|
||||||
|
katze_xbel_item_set_desc (copy, priv->desc);
|
||||||
|
}
|
||||||
|
if (priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK)
|
||||||
|
katze_xbel_bookmark_set_href (copy, priv->href);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
GType
|
||||||
|
katze_xbel_item_get_type (void)
|
||||||
|
{
|
||||||
|
static GType type = 0;
|
||||||
|
if (!type)
|
||||||
|
type = g_pointer_type_register_static ("katze_xbel_item");
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_append_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to append
|
||||||
|
*
|
||||||
|
* Append the given item to a folder.
|
||||||
|
*
|
||||||
|
* The item is actually moved and must not be contained in another folder.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_folder_append_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail (!katze_xbel_item_get_parent (item));
|
||||||
|
g_return_if_fail (katze_xbel_item_is_folder (folder));
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
priv->items = g_list_append (priv->items, item);
|
||||||
|
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_prepend_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to prepend
|
||||||
|
*
|
||||||
|
* Prepend the given item to a folder.
|
||||||
|
*
|
||||||
|
* The item is actually moved and must not be contained in another folder.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_folder_prepend_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail (!katze_xbel_item_get_parent (item));
|
||||||
|
g_return_if_fail (katze_xbel_item_is_folder (folder));
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
priv->items = g_list_prepend (priv->items, item);
|
||||||
|
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_remove_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @item: the item to remove
|
||||||
|
*
|
||||||
|
* Remove the given @item from a @folder.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_folder_remove_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_if_fail (item);
|
||||||
|
g_return_if_fail (katze_xbel_item_get_parent(folder) != item);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
// Fortunately we know that items are unique
|
||||||
|
priv->items = g_list_remove (priv->items, item);
|
||||||
|
|
||||||
|
KATZE_XBEL_ITEM_GET_PRIVATE (item)->parent = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_get_n_items:
|
||||||
|
* @folder: a folder
|
||||||
|
*
|
||||||
|
* Retrieve the number of items contained in the given @folder.
|
||||||
|
*
|
||||||
|
* Return value: number of items
|
||||||
|
**/
|
||||||
|
guint
|
||||||
|
katze_xbel_folder_get_n_items (KatzeXbelItem* folder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_item_is_folder (folder), 0);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
return g_list_length (priv->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_get_nth_item:
|
||||||
|
* @folder: a folder
|
||||||
|
* @n: the index of an item
|
||||||
|
*
|
||||||
|
* Retrieve an item contained in the given @folder by its index.
|
||||||
|
*
|
||||||
|
* Return value: the item at the given index or %NULL
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_folder_get_nth_item (KatzeXbelItem* folder,
|
||||||
|
guint n)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_item_is_folder(folder), NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
return (KatzeXbelItem*) g_list_nth_data (priv->items, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_is_empty:
|
||||||
|
* @folder: A folder.
|
||||||
|
*
|
||||||
|
* Determines wether or not a folder contains no items. This is significantly
|
||||||
|
* faster than katze_xbel_folder_get_n_items for this particular purpose.
|
||||||
|
*
|
||||||
|
* Return value: Wether the given @folder is folded.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_is_empty (KatzeXbelItem* folder)
|
||||||
|
{
|
||||||
|
return !katze_xbel_folder_get_nth_item (folder, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_get_folded:
|
||||||
|
* @folder: A folder.
|
||||||
|
*
|
||||||
|
* Determines wether or not a folder is folded. If a folder is not folded
|
||||||
|
* it should not be exposed in a user interface.
|
||||||
|
*
|
||||||
|
* New folders are folded by default.
|
||||||
|
*
|
||||||
|
* Return value: Wether the given @folder is folded.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_get_folded (KatzeXbelItem* folder)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_item_is_folder (folder), TRUE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
return priv->folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_get_kind:
|
||||||
|
* @item: A item.
|
||||||
|
*
|
||||||
|
* Determines the kind of an item.
|
||||||
|
*
|
||||||
|
* Return value: The kind of the given @item.
|
||||||
|
**/
|
||||||
|
KatzeXbelItemKind
|
||||||
|
katze_xbel_item_get_kind (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_get_parent:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the parent folder of an item.
|
||||||
|
*
|
||||||
|
* Return value: The parent folder of the given @item or %NULL.
|
||||||
|
**/
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_item_get_parent (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_get_title:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the title of an item.
|
||||||
|
*
|
||||||
|
* Return value: The title of the given @item or %NULL.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_item_get_title (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (!katze_xbel_item_is_separator (item), NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_get_desc:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Retrieves the description of an item.
|
||||||
|
*
|
||||||
|
* Return value: The description of the @item or %NULL.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_item_get_desc (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (!katze_xbel_item_is_separator (item), NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_bookmark_get_href:
|
||||||
|
* @bookmark: A bookmark.
|
||||||
|
*
|
||||||
|
* Retrieves the uri of a bookmark. The value is guaranteed to not be %NULL.
|
||||||
|
*
|
||||||
|
* Return value: The uri of the @bookmark.
|
||||||
|
**/
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_bookmark_get_href (KatzeXbelItem* bookmark)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_item_is_bookmark (bookmark), NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (bookmark);
|
||||||
|
return priv->href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_is_bookmark:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a bookmark.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the @item is a bookmark.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_item_is_bookmark (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, FALSE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->kind == KATZE_XBEL_ITEM_KIND_BOOKMARK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_is_separator:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a separator.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the @item is a separator.
|
||||||
|
**/
|
||||||
|
gboolean katze_xbel_item_is_separator (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, FALSE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->kind == KATZE_XBEL_ITEM_KIND_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_is_folder:
|
||||||
|
* @item: A valid item.
|
||||||
|
*
|
||||||
|
* Determines wether or not an item is a folder.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE if the item is a folder.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_item_is_folder (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, FALSE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
return priv->kind == KATZE_XBEL_ITEM_KIND_FOLDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_set_folded:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @folded: TRUE if the folder is folded.
|
||||||
|
*
|
||||||
|
* Sets the foldedness of the @folder.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_folder_set_folded (KatzeXbelItem* folder,
|
||||||
|
gboolean folded)
|
||||||
|
{
|
||||||
|
g_return_if_fail (katze_xbel_item_is_folder (folder));
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
priv->folded = folded;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_set_title:
|
||||||
|
* @item: A valid item.
|
||||||
|
* @title: A string to use for the title.
|
||||||
|
*
|
||||||
|
* Sets the title of the @item.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_item_set_title (KatzeXbelItem* item,
|
||||||
|
const gchar* title)
|
||||||
|
{
|
||||||
|
g_return_if_fail (!katze_xbel_item_is_separator (item));
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
katze_assign (priv->title, g_strdup (title));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_item_set_desc:
|
||||||
|
* @item: A valid item.
|
||||||
|
* @title: A string to use for the description.
|
||||||
|
*
|
||||||
|
* Sets the description of the @item.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_item_set_desc (KatzeXbelItem* item,
|
||||||
|
const gchar* desc)
|
||||||
|
{
|
||||||
|
g_return_if_fail (!katze_xbel_item_is_separator (item));
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
katze_assign (priv->desc, g_strdup (desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_bookmark_set_href:
|
||||||
|
* @bookmark: A bookmark.
|
||||||
|
* @href: A string containing a valid uri.
|
||||||
|
*
|
||||||
|
* Sets the uri of the bookmark.
|
||||||
|
*
|
||||||
|
* The uri must not be %NULL.
|
||||||
|
*
|
||||||
|
* This uri is not currently validated in any way. This may change in the future.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
katze_xbel_bookmark_set_href (KatzeXbelItem* bookmark,
|
||||||
|
const gchar* href)
|
||||||
|
{
|
||||||
|
g_return_if_fail (katze_xbel_item_is_bookmark (bookmark));
|
||||||
|
g_return_if_fail (href);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (bookmark);
|
||||||
|
katze_assign (priv->href, g_strdup (href));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_data (KatzeXbelItem* folder,
|
||||||
|
const gchar* data,
|
||||||
|
GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
|
||||||
|
g_return_val_if_fail (data, FALSE);
|
||||||
|
xmlDocPtr doc;
|
||||||
|
if((doc = xmlParseMemory (data, strlen (data))) == NULL)
|
||||||
|
{
|
||||||
|
// No valid xml or broken encoding
|
||||||
|
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
|
||||||
|
"Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!katze_xbel_folder_from_xmlDocPtr (folder, doc))
|
||||||
|
{
|
||||||
|
// Parsing failed
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
|
||||||
|
"Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_from_file:
|
||||||
|
* @folder: An empty folder.
|
||||||
|
* @file: A relative path to a file.
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Tries to load @file.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_file (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
|
||||||
|
g_return_val_if_fail (file, FALSE);
|
||||||
|
if (!g_file_test (file, G_FILE_TEST_EXISTS))
|
||||||
|
{
|
||||||
|
// File doesn't exist
|
||||||
|
*error = g_error_new (G_FILE_ERROR, G_FILE_ERROR_NOENT,
|
||||||
|
"File not found.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlDocPtr doc;
|
||||||
|
if ((doc = xmlParseFile (file)) == NULL)
|
||||||
|
{
|
||||||
|
// No valid xml or broken encoding
|
||||||
|
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
|
||||||
|
"Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!katze_xbel_folder_from_xmlDocPtr (folder, doc))
|
||||||
|
{
|
||||||
|
// Parsing failed
|
||||||
|
xmlFreeDoc (doc);
|
||||||
|
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
|
||||||
|
"Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
xmlFreeDoc (doc);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_from_data_dirs:
|
||||||
|
* @folder: An empty folder.
|
||||||
|
* @file: A relative path to a file.
|
||||||
|
* @full_path: return location for the full path of the file or %NULL
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Tries to load @file from the user data dir or any of the system data dirs.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_data_dirs (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
gchar** full_path,
|
||||||
|
GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_folder_is_empty (folder), FALSE);
|
||||||
|
g_return_val_if_fail (file, FALSE);
|
||||||
|
// FIXME: Essentially unimplemented
|
||||||
|
|
||||||
|
*error = g_error_new (KATZE_XBEL_ERROR, KATZE_XBEL_ERROR_READ,
|
||||||
|
"Malformed document.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar*
|
||||||
|
katze_xbel_xml_element (const gchar* name,
|
||||||
|
const gchar* value)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
return g_strdup ("");
|
||||||
|
gchar* valueEscaped = g_markup_escape_text (value, -1);
|
||||||
|
gchar* markup = g_strdup_printf ("<%s>%s</%s>\n",
|
||||||
|
name, valueEscaped, name);
|
||||||
|
g_free (valueEscaped);
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gchar*
|
||||||
|
katze_xbel_item_to_data (KatzeXbelItem* item)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (item, NULL);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (item);
|
||||||
|
|
||||||
|
gchar* markup = NULL;
|
||||||
|
switch (priv->kind)
|
||||||
|
{
|
||||||
|
case KATZE_XBEL_ITEM_KIND_FOLDER:
|
||||||
|
{
|
||||||
|
GString* _markup = g_string_new (NULL);
|
||||||
|
guint n = katze_xbel_folder_get_n_items (item);
|
||||||
|
guint i;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* _item = katze_xbel_folder_get_nth_item (item, i);
|
||||||
|
gchar* item_markup = katze_xbel_item_to_data (_item);
|
||||||
|
g_string_append (_markup, item_markup);
|
||||||
|
g_free (item_markup);
|
||||||
|
}
|
||||||
|
gchar* folded = priv->folded ? NULL : g_strdup_printf (" folded=\"no\"");
|
||||||
|
gchar* title = katze_xbel_xml_element ("title", priv->title);
|
||||||
|
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
|
||||||
|
markup = g_strdup_printf ("<folder%s>\n%s%s%s</folder>\n",
|
||||||
|
folded ? folded : "",
|
||||||
|
title, desc,
|
||||||
|
g_string_free (_markup, FALSE));
|
||||||
|
g_free (folded);
|
||||||
|
g_free (title);
|
||||||
|
g_free (desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KATZE_XBEL_ITEM_KIND_BOOKMARK:
|
||||||
|
{
|
||||||
|
gchar* href_escaped = g_markup_escape_text (priv->href, -1);
|
||||||
|
gchar* href = g_strdup_printf (" href=\"%s\"", href_escaped);
|
||||||
|
g_free (href_escaped);
|
||||||
|
gchar* title = katze_xbel_xml_element ("title", priv->title);
|
||||||
|
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
|
||||||
|
markup = g_strdup_printf ("<bookmark%s>\n%s%s%s</bookmark>\n",
|
||||||
|
href,
|
||||||
|
title, desc,
|
||||||
|
"");
|
||||||
|
g_free (href);
|
||||||
|
g_free (title);
|
||||||
|
g_free (desc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KATZE_XBEL_ITEM_KIND_SEPARATOR:
|
||||||
|
markup = g_strdup ("<separator/>\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_warning ("XBEL: Unknown item kind");
|
||||||
|
}
|
||||||
|
return markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_to_data:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @length: return location for the length of the created string or %NULL
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Retrieve the contents of @folder as a string.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gchar*
|
||||||
|
katze_xbel_folder_to_data (KatzeXbelItem* folder,
|
||||||
|
gsize* length,
|
||||||
|
GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (katze_xbel_item_is_folder (folder), FALSE);
|
||||||
|
|
||||||
|
KatzeXbelItemPrivate* priv = KATZE_XBEL_ITEM_GET_PRIVATE (folder);
|
||||||
|
|
||||||
|
GString* inner_markup = g_string_new (NULL);
|
||||||
|
guint n = katze_xbel_folder_get_n_items (folder);
|
||||||
|
guint i;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item (folder, i);
|
||||||
|
gchar* sItem = katze_xbel_item_to_data (item);
|
||||||
|
g_string_append (inner_markup, sItem);
|
||||||
|
g_free (sItem);
|
||||||
|
}
|
||||||
|
gchar* title = katze_xbel_xml_element ("title", priv->title);
|
||||||
|
gchar* desc = katze_xbel_xml_element ("desc", priv->desc);
|
||||||
|
gchar* outer_markup;
|
||||||
|
outer_markup = g_strdup_printf (
|
||||||
|
"%s%s<xbel version=\"1.0\">\n%s%s%s</xbel>\n",
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
|
||||||
|
"<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD "
|
||||||
|
"XML Bookmark Exchange Language 1.0//EN//XML\" "
|
||||||
|
"\"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n",
|
||||||
|
title,
|
||||||
|
desc,
|
||||||
|
g_string_free (inner_markup, FALSE));
|
||||||
|
g_free (title);
|
||||||
|
g_free (desc);
|
||||||
|
|
||||||
|
if (length)
|
||||||
|
*length = strlen (outer_markup);
|
||||||
|
return outer_markup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* katze_xbel_folder_to_file:
|
||||||
|
* @folder: A folder.
|
||||||
|
* @file: The destination filename.
|
||||||
|
* @error: return location for a GError or %NULL
|
||||||
|
*
|
||||||
|
* Write the contents of @folder to the specified file, creating it if necessary.
|
||||||
|
*
|
||||||
|
* Return value: %TRUE on success or %FALSE when an error occured.
|
||||||
|
**/
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_to_file (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
GError** error)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (file, FALSE);
|
||||||
|
gchar* data;
|
||||||
|
if (!(data = katze_xbel_folder_to_data (folder, NULL, error)))
|
||||||
|
return FALSE;
|
||||||
|
FILE* fp;
|
||||||
|
if(!(fp = fopen (file, "w")))
|
||||||
|
{
|
||||||
|
*error = g_error_new (G_FILE_ERROR, G_FILE_ERROR_ACCES,
|
||||||
|
"Writing failed.");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
fputs (data, fp);
|
||||||
|
fclose (fp);
|
||||||
|
g_free (data);
|
||||||
|
return TRUE;
|
||||||
|
}
|
176
katze/katze-xbel.h
Normal file
176
katze/katze-xbel.h
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2007-2008 Christian Dywan <christian@twotoasts.de>
|
||||||
|
|
||||||
|
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 __KATZE_XBEL_H__
|
||||||
|
#define __KATZE_XBEL_H__ 1
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define KATZE_TYPE_XBEL_ITEM \
|
||||||
|
(katze_xbel_item_get_type ())
|
||||||
|
|
||||||
|
typedef struct _KatzeXbelItem KatzeXbelItem;
|
||||||
|
typedef struct _KatzeXbelItemPrivate KatzeXbelItemPrivate;
|
||||||
|
|
||||||
|
struct _KatzeXbelItem
|
||||||
|
{
|
||||||
|
KatzeXbelItemPrivate* priv;
|
||||||
|
|
||||||
|
/* Padding for future expansion */
|
||||||
|
void (*_katze_reserved1) (void);
|
||||||
|
void (*_katze_reserved2) (void);
|
||||||
|
void (*_katze_reserved3) (void);
|
||||||
|
void (*_katze_reserved4) (void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KATZE_XBEL_ERROR g_quark_from_string("KATZE_XBEL_ERROR")
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KATZE_XBEL_ERROR_INVALID_URI, /* Malformed uri */
|
||||||
|
KATZE_XBEL_ERROR_INVALID_VALUE, /* Requested field not found */
|
||||||
|
KATZE_XBEL_ERROR_URI_NOT_FOUND, /* Requested uri not found */
|
||||||
|
KATZE_XBEL_ERROR_READ, /* Malformed document */
|
||||||
|
KATZE_XBEL_ERROR_UNKNOWN_ENCODING, /* Parsed text was in an unknown encoding */
|
||||||
|
KATZE_XBEL_ERROR_WRITE, /* Writing failed. */
|
||||||
|
} KatzeXbelError;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
KATZE_XBEL_ITEM_KIND_FOLDER,
|
||||||
|
KATZE_XBEL_ITEM_KIND_BOOKMARK,
|
||||||
|
KATZE_XBEL_ITEM_KIND_SEPARATOR
|
||||||
|
} KatzeXbelItemKind;
|
||||||
|
|
||||||
|
GType
|
||||||
|
katze_xbel_item_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_bookmark_new (void);
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_separator_new (void);
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_folder_new (void);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_item_ref (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_item_unref (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_item_copy (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_folder_append_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_folder_prepend_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_folder_remove_item (KatzeXbelItem* folder,
|
||||||
|
KatzeXbelItem* item);
|
||||||
|
|
||||||
|
guint
|
||||||
|
katze_xbel_folder_get_n_items (KatzeXbelItem* folder);
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_folder_get_nth_item (KatzeXbelItem* folder,
|
||||||
|
guint n);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_is_empty (KatzeXbelItem* folder);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_get_folded (KatzeXbelItem* folder);
|
||||||
|
|
||||||
|
KatzeXbelItemKind
|
||||||
|
katze_xbel_item_get_kind (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
KatzeXbelItem*
|
||||||
|
katze_xbel_item_get_parent (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_item_get_title (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_item_get_desc (KatzeXbelItem* item);
|
||||||
|
|
||||||
|
G_CONST_RETURN gchar*
|
||||||
|
katze_xbel_bookmark_get_href (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
/*time_t
|
||||||
|
katze_xbel_bookmark_get_added (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
time_t
|
||||||
|
katze_xbel_bookmark_get_modified (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
time_t
|
||||||
|
katze_xbel_bookmark_get_visited (KatzeXbelItem* bookmark);*/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_item_is_bookmark (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_item_is_separator (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_item_is_folder (KatzeXbelItem* bookmark);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_folder_set_folded (KatzeXbelItem* folder,
|
||||||
|
gboolean folded);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_item_set_title (KatzeXbelItem* item,
|
||||||
|
const gchar* title);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_item_set_desc (KatzeXbelItem* item,
|
||||||
|
const gchar* desc);
|
||||||
|
|
||||||
|
void
|
||||||
|
katze_xbel_bookmark_set_href (KatzeXbelItem* bookmark,
|
||||||
|
const gchar* href);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_data (KatzeXbelItem* folder,
|
||||||
|
const gchar* data,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_file (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_from_data_dirs (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
gchar** full_path,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
gchar*
|
||||||
|
katze_xbel_folder_to_data (KatzeXbelItem* folder,
|
||||||
|
gsize* length,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
katze_xbel_folder_to_file (KatzeXbelItem* folder,
|
||||||
|
const gchar* file,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
#endif /* !__KATZE_XBEL_H__ */
|
|
@ -14,5 +14,6 @@
|
||||||
|
|
||||||
#include "katze-throbber.h"
|
#include "katze-throbber.h"
|
||||||
#include "katze-utils.h"
|
#include "katze-utils.h"
|
||||||
|
#include "katze-xbel.h"
|
||||||
|
|
||||||
#endif /* __KATZE_THROBBER_H__ */
|
#endif /* __KATZE_H__ */
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
INCLUDES = \
|
INCLUDES = \
|
||||||
$(GTK_CFLAGS) \
|
$(GTK_CFLAGS) \
|
||||||
$(WEBKIT_CFLAGS) \
|
$(WEBKIT_CFLAGS) \
|
||||||
$(LIBXML_CFLAGS) \
|
|
||||||
$(LIBSEXY_CFLAGS) \
|
$(LIBSEXY_CFLAGS) \
|
||||||
-I../katze
|
-I../katze
|
||||||
|
|
||||||
LDADD = \
|
LDADD = \
|
||||||
$(GTK_LIBS) \
|
$(GTK_LIBS) \
|
||||||
$(WEBKIT_LIBS) \
|
$(WEBKIT_LIBS) \
|
||||||
$(LIBXML_LIBS) \
|
|
||||||
$(LIBSEXY_LIBS) \
|
$(LIBSEXY_LIBS) \
|
||||||
../katze/libkatze.la
|
../katze/libkatze.la
|
||||||
|
|
||||||
|
@ -25,7 +23,6 @@ midori_SOURCES = \
|
||||||
sokoke.c sokoke.h \
|
sokoke.c sokoke.h \
|
||||||
conf.c conf.h \
|
conf.c conf.h \
|
||||||
search.c search.h \
|
search.c search.h \
|
||||||
xbel.c xbel.h \
|
|
||||||
global.h \
|
global.h \
|
||||||
ui.h \
|
ui.h \
|
||||||
debug.h
|
debug.h
|
||||||
|
|
203
src/browser.c
203
src/browser.c
|
@ -17,7 +17,6 @@
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "webView.h"
|
#include "webView.h"
|
||||||
#include "webSearch.h"
|
#include "webSearch.h"
|
||||||
#include "xbel.h"
|
|
||||||
#include "../katze/katze.h"
|
#include "../katze/katze.h"
|
||||||
|
|
||||||
#include <gdk/gdkkeysyms.h>
|
#include <gdk/gdkkeysyms.h>
|
||||||
|
@ -379,20 +378,20 @@ void on_action_webSearch_activate(GtkAction* action, CBrowser* browser)
|
||||||
void on_menu_tabsClosed_activate(GtkWidget* widget, CBrowser* browser)
|
void on_menu_tabsClosed_activate(GtkWidget* widget, CBrowser* browser)
|
||||||
{
|
{
|
||||||
GtkWidget* menu = gtk_menu_new();
|
GtkWidget* menu = gtk_menu_new();
|
||||||
guint n = xbel_folder_get_n_items(tabtrash);
|
guint n = katze_xbel_folder_get_n_items(tabtrash);
|
||||||
GtkWidget* menuitem;
|
GtkWidget* menuitem;
|
||||||
guint i;
|
guint i;
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(tabtrash, i);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(tabtrash, i);
|
||||||
const gchar* title = xbel_item_get_title(item);
|
const gchar* title = katze_xbel_item_get_title(item);
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
menuitem = gtk_image_menu_item_new_with_label(title ? title : uri);
|
menuitem = gtk_image_menu_item_new_with_label(title ? title : uri);
|
||||||
// FIXME: Get the real icon
|
// FIXME: Get the real icon
|
||||||
GtkWidget* icon = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
|
GtkWidget* icon = gtk_image_new_from_stock(GTK_STOCK_FILE, GTK_ICON_SIZE_MENU);
|
||||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), icon);
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), icon);
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
|
||||||
g_object_set_data(G_OBJECT(menuitem), "XbelItem", item);
|
g_object_set_data(G_OBJECT(menuitem), "KatzeXbelItem", item);
|
||||||
g_signal_connect(menuitem, "activate", G_CALLBACK(on_menu_tabsClosed_item_activate), browser);
|
g_signal_connect(menuitem, "activate", G_CALLBACK(on_menu_tabsClosed_item_activate), browser);
|
||||||
gtk_widget_show(menuitem);
|
gtk_widget_show(menuitem);
|
||||||
}
|
}
|
||||||
|
@ -411,30 +410,30 @@ void on_menu_tabsClosed_activate(GtkWidget* widget, CBrowser* browser)
|
||||||
void on_menu_tabsClosed_item_activate(GtkWidget* menuitem, CBrowser* browser)
|
void on_menu_tabsClosed_item_activate(GtkWidget* menuitem, CBrowser* browser)
|
||||||
{
|
{
|
||||||
// Create a new webView with an uri which has been closed before
|
// Create a new webView with an uri which has been closed before
|
||||||
XbelItem* item = g_object_get_data(G_OBJECT(menuitem), "XbelItem");
|
KatzeXbelItem* item = g_object_get_data(G_OBJECT(menuitem), "KatzeXbelItem");
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
CBrowser* curBrowser = browser_new(browser);
|
CBrowser* curBrowser = browser_new(browser);
|
||||||
webView_open(curBrowser->webView, uri);
|
webView_open(curBrowser->webView, uri);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
update_browser_actions(curBrowser);
|
update_browser_actions(curBrowser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_action_tabsClosed_undo_activate(GtkAction* action, CBrowser* browser)
|
void on_action_tabsClosed_undo_activate(GtkAction* action, CBrowser* browser)
|
||||||
{
|
{
|
||||||
// Open the most recent tabtrash item
|
// Open the most recent tabtrash item
|
||||||
XbelItem* item = xbel_folder_get_nth_item(tabtrash, 0);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(tabtrash, 0);
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
CBrowser* curBrowser = browser_new(browser);
|
CBrowser* curBrowser = browser_new(browser);
|
||||||
webView_open(curBrowser->webView, uri);
|
webView_open(curBrowser->webView, uri);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
update_browser_actions(curBrowser);
|
update_browser_actions(curBrowser);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_action_tabsClosed_clear_activate(GtkAction* action, CBrowser* browser)
|
void on_action_tabsClosed_clear_activate(GtkAction* action, CBrowser* browser)
|
||||||
{
|
{
|
||||||
// Clear the closed tabs list
|
// Clear the closed tabs list
|
||||||
xbel_item_unref(tabtrash);
|
katze_xbel_item_unref(tabtrash);
|
||||||
tabtrash = xbel_folder_new();
|
tabtrash = katze_xbel_folder_new();
|
||||||
update_browser_actions(browser);
|
update_browser_actions(browser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +465,7 @@ void on_action_link_copy_activate(GtkAction* action, CBrowser* browser)
|
||||||
gtk_clipboard_set_text(clipboard, browser->elementUri, -1);
|
gtk_clipboard_set_text(clipboard, browser->elementUri, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browser)
|
static void browser_editBookmark_dialog_new(KatzeXbelItem* bookmark, CBrowser* browser)
|
||||||
{
|
{
|
||||||
gboolean newBookmark = !bookmark;
|
gboolean newBookmark = !bookmark;
|
||||||
GtkWidget* dialog = gtk_dialog_new_with_buttons(
|
GtkWidget* dialog = gtk_dialog_new_with_buttons(
|
||||||
|
@ -483,7 +482,7 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
GtkSizeGroup* sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
GtkSizeGroup* sizegroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
|
||||||
if(newBookmark)
|
if(newBookmark)
|
||||||
bookmark = xbel_bookmark_new();
|
bookmark = katze_xbel_bookmark_new();
|
||||||
|
|
||||||
GtkWidget* hbox = gtk_hbox_new(FALSE, 8);
|
GtkWidget* hbox = gtk_hbox_new(FALSE, 8);
|
||||||
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
@ -494,7 +493,7 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
gtk_entry_set_activates_default(GTK_ENTRY(entry_title), TRUE);
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_title), TRUE);
|
||||||
if(!newBookmark)
|
if(!newBookmark)
|
||||||
{
|
{
|
||||||
const gchar* title = xbel_item_get_title(bookmark);
|
const gchar* title = katze_xbel_item_get_title(bookmark);
|
||||||
gtk_entry_set_text(GTK_ENTRY(entry_title), title ? title : "");
|
gtk_entry_set_text(GTK_ENTRY(entry_title), title ? title : "");
|
||||||
}
|
}
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), entry_title, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), entry_title, TRUE, TRUE, 0);
|
||||||
|
@ -510,7 +509,7 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
gtk_entry_set_activates_default(GTK_ENTRY(entry_desc), TRUE);
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_desc), TRUE);
|
||||||
if(!newBookmark)
|
if(!newBookmark)
|
||||||
{
|
{
|
||||||
const gchar* desc = xbel_item_get_desc(bookmark);
|
const gchar* desc = katze_xbel_item_get_desc(bookmark);
|
||||||
gtk_entry_set_text(GTK_ENTRY(entry_desc), desc ? desc : "");
|
gtk_entry_set_text(GTK_ENTRY(entry_desc), desc ? desc : "");
|
||||||
}
|
}
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), entry_desc, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), entry_desc, TRUE, TRUE, 0);
|
||||||
|
@ -518,7 +517,7 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
gtk_widget_show_all(hbox);
|
gtk_widget_show_all(hbox);
|
||||||
|
|
||||||
GtkWidget* entry_uri = NULL;
|
GtkWidget* entry_uri = NULL;
|
||||||
if(xbel_item_is_bookmark(bookmark))
|
if(katze_xbel_item_is_bookmark(bookmark))
|
||||||
{
|
{
|
||||||
hbox = gtk_hbox_new(FALSE, 8);
|
hbox = gtk_hbox_new(FALSE, 8);
|
||||||
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
|
||||||
|
@ -528,7 +527,7 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
entry_uri = gtk_entry_new();
|
entry_uri = gtk_entry_new();
|
||||||
gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE);
|
gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE);
|
||||||
if(!newBookmark)
|
if(!newBookmark)
|
||||||
gtk_entry_set_text(GTK_ENTRY(entry_uri), xbel_bookmark_get_href(bookmark));
|
gtk_entry_set_text(GTK_ENTRY(entry_uri), katze_xbel_bookmark_get_href(bookmark));
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), entry_uri, TRUE, TRUE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), entry_uri, TRUE, TRUE, 0);
|
||||||
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
|
||||||
gtk_widget_show_all(hbox);
|
gtk_widget_show_all(hbox);
|
||||||
|
@ -537,14 +536,14 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
|
||||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
||||||
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
|
||||||
{
|
{
|
||||||
xbel_item_set_title(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_title)));
|
katze_xbel_item_set_title(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_title)));
|
||||||
xbel_item_set_desc(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_desc)));
|
katze_xbel_item_set_desc(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_desc)));
|
||||||
if(xbel_item_is_bookmark(bookmark))
|
if(katze_xbel_item_is_bookmark(bookmark))
|
||||||
xbel_bookmark_set_href(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_uri)));
|
katze_xbel_bookmark_set_href(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_uri)));
|
||||||
|
|
||||||
// FIXME: We want to choose a folder
|
// FIXME: We want to choose a folder
|
||||||
if(newBookmark)
|
if(newBookmark)
|
||||||
xbel_folder_append_item(bookmarks, bookmark);
|
katze_xbel_folder_append_item(bookmarks, bookmark);
|
||||||
}
|
}
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
}
|
}
|
||||||
|
@ -556,18 +555,18 @@ static void on_panel_bookmarks_row_activated(GtkTreeView* treeview
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
if(gtk_tree_model_get_iter(model, &iter, path))
|
if(gtk_tree_model_get_iter(model, &iter, path))
|
||||||
{
|
{
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(xbel_item_is_bookmark(item))
|
if(katze_xbel_item_is_bookmark(item))
|
||||||
webView_open(get_nth_webView(-1, browser), xbel_bookmark_get_href(item));
|
webView_open(get_nth_webView(-1, browser), katze_xbel_bookmark_get_href(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void panel_bookmarks_popup(GtkWidget* widget, GdkEventButton* event
|
static void panel_bookmarks_popup(GtkWidget* widget, GdkEventButton* event
|
||||||
, XbelItem* item, CBrowser* browser)
|
, KatzeXbelItem* item, CBrowser* browser)
|
||||||
{
|
{
|
||||||
gboolean isBookmark = xbel_item_is_bookmark(item);
|
gboolean isBookmark = katze_xbel_item_is_bookmark(item);
|
||||||
gboolean isSeparator = xbel_item_is_separator(item);
|
gboolean isSeparator = katze_xbel_item_is_separator(item);
|
||||||
|
|
||||||
action_set_sensitive("BookmarkOpen", isBookmark, browser);
|
action_set_sensitive("BookmarkOpen", isBookmark, browser);
|
||||||
action_set_sensitive("BookmarkOpenTab", isBookmark, browser);
|
action_set_sensitive("BookmarkOpenTab", isBookmark, browser);
|
||||||
|
@ -590,12 +589,12 @@ static gboolean on_panel_bookmarks_button_release(GtkWidget* widget
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(event->button == 2 && xbel_item_is_bookmark(item))
|
if(event->button == 2 && katze_xbel_item_is_bookmark(item))
|
||||||
{
|
{
|
||||||
CBrowser* newBrowser = browser_new(browser);
|
CBrowser* newBrowser = browser_new(browser);
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
webView_open(newBrowser->webView, uri);
|
webView_open(newBrowser->webView, uri);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -613,7 +612,7 @@ void on_panel_bookmarks_popup(GtkWidget* widget, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
panel_bookmarks_popup(widget, NULL, item, browser);
|
panel_bookmarks_popup(widget, NULL, item, browser);
|
||||||
}
|
}
|
||||||
|
@ -628,10 +627,10 @@ void on_action_bookmarkOpen_activate(GtkAction* action, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(xbel_item_is_bookmark(item))
|
if(katze_xbel_item_is_bookmark(item))
|
||||||
webView_open(get_nth_webView(-1, browser), xbel_bookmark_get_href(item));
|
webView_open(get_nth_webView(-1, browser), katze_xbel_bookmark_get_href(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,12 +643,12 @@ void on_action_bookmarkOpenTab_activate(GtkAction* action, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(xbel_item_is_bookmark(item))
|
if(katze_xbel_item_is_bookmark(item))
|
||||||
{
|
{
|
||||||
CBrowser* newBrowser = browser_new(browser);
|
CBrowser* newBrowser = browser_new(browser);
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
webView_open(newBrowser->webView, uri);
|
webView_open(newBrowser->webView, uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,12 +663,12 @@ void on_action_bookmarkOpenWindow_activate(GtkAction* action, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(xbel_item_is_bookmark(item))
|
if(katze_xbel_item_is_bookmark(item))
|
||||||
{
|
{
|
||||||
CBrowser* newBrowser = browser_new(NULL);
|
CBrowser* newBrowser = browser_new(NULL);
|
||||||
const gchar* uri = xbel_bookmark_get_href(item);
|
const gchar* uri = katze_xbel_bookmark_get_href(item);
|
||||||
webView_open(newBrowser->webView, uri);
|
webView_open(newBrowser->webView, uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -684,9 +683,9 @@ void on_action_bookmarkEdit_activate(GtkAction* action, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
if(!xbel_item_is_separator(item))
|
if(!katze_xbel_item_is_separator(item))
|
||||||
browser_editBookmark_dialog_new(item, browser);
|
browser_editBookmark_dialog_new(item, browser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,26 +699,26 @@ void on_action_bookmarkDelete_activate(GtkAction* action, CBrowser* browser)
|
||||||
GtkTreeModel* model;
|
GtkTreeModel* model;
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_selection_get_selected(selection, &model, &iter);
|
gtk_tree_selection_get_selected(selection, &model, &iter);
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
gtk_tree_model_get(model, &iter, 0, &item, -1);
|
||||||
XbelItem* parent = xbel_item_get_parent(item);
|
KatzeXbelItem* parent = katze_xbel_item_get_parent(item);
|
||||||
xbel_folder_remove_item(parent, item);
|
katze_xbel_folder_remove_item(parent, item);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tree_store_insert_folder(GtkTreeStore* treestore, GtkTreeIter* parent
|
static void tree_store_insert_folder(GtkTreeStore* treestore, GtkTreeIter* parent
|
||||||
, XbelItem* folder)
|
, KatzeXbelItem* folder)
|
||||||
{
|
{
|
||||||
guint n = xbel_folder_get_n_items(folder);
|
guint n = katze_xbel_folder_get_n_items(folder);
|
||||||
guint i;
|
guint i;
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(folder, i);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(folder, i);
|
||||||
GtkTreeIter iter;
|
GtkTreeIter iter;
|
||||||
gtk_tree_store_insert_with_values(treestore, &iter, parent, n, 0, item, -1);
|
gtk_tree_store_insert_with_values(treestore, &iter, parent, n, 0, item, -1);
|
||||||
xbel_item_ref(item);
|
katze_xbel_item_ref(item);
|
||||||
if(xbel_item_is_folder(item))
|
if(katze_xbel_item_is_folder(item))
|
||||||
tree_store_insert_folder(treestore, &iter, item);
|
tree_store_insert_folder(treestore, &iter, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,22 +727,22 @@ static void on_bookmarks_item_render_icon(GtkTreeViewColumn* column
|
||||||
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
||||||
, GtkWidget* treeview)
|
, GtkWidget* treeview)
|
||||||
{
|
{
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, iter, 0, &item, -1);
|
gtk_tree_model_get(model, iter, 0, &item, -1);
|
||||||
|
|
||||||
if(!xbel_item_get_parent(item))
|
if(!katze_xbel_item_get_parent(item))
|
||||||
{
|
{
|
||||||
gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
|
gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Would it be better to not do this on every redraw?
|
// TODO: Would it be better to not do this on every redraw?
|
||||||
GdkPixbuf* pixbuf = NULL;
|
GdkPixbuf* pixbuf = NULL;
|
||||||
if(xbel_item_is_bookmark(item))
|
if(katze_xbel_item_is_bookmark(item))
|
||||||
pixbuf = gtk_widget_render_icon(treeview, STOCK_BOOKMARK
|
pixbuf = gtk_widget_render_icon(treeview, STOCK_BOOKMARK
|
||||||
, GTK_ICON_SIZE_MENU, NULL);
|
, GTK_ICON_SIZE_MENU, NULL);
|
||||||
else if(xbel_item_is_folder(item))
|
else if(katze_xbel_item_is_folder(item))
|
||||||
pixbuf = gtk_widget_render_icon(treeview, GTK_STOCK_DIRECTORY
|
pixbuf = gtk_widget_render_icon(treeview, GTK_STOCK_DIRECTORY
|
||||||
, GTK_ICON_SIZE_MENU, NULL);
|
, GTK_ICON_SIZE_MENU, NULL);
|
||||||
g_object_set(renderer, "pixbuf", pixbuf, NULL);
|
g_object_set(renderer, "pixbuf", pixbuf, NULL);
|
||||||
|
@ -755,31 +754,31 @@ static void on_bookmarks_item_render_text(GtkTreeViewColumn* column
|
||||||
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
, GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter
|
||||||
, GtkWidget* treeview)
|
, GtkWidget* treeview)
|
||||||
{
|
{
|
||||||
XbelItem* item;
|
KatzeXbelItem* item;
|
||||||
gtk_tree_model_get(model, iter, 0, &item, -1);
|
gtk_tree_model_get(model, iter, 0, &item, -1);
|
||||||
|
|
||||||
if(!xbel_item_get_parent(item))
|
if(!katze_xbel_item_get_parent(item))
|
||||||
{
|
{
|
||||||
gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
|
gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(xbel_item_is_separator(item))
|
if(katze_xbel_item_is_separator(item))
|
||||||
g_object_set(renderer
|
g_object_set(renderer
|
||||||
, "markup", "<i>Separator</i>", NULL);
|
, "markup", "<i>Separator</i>", NULL);
|
||||||
else
|
else
|
||||||
g_object_set(renderer
|
g_object_set(renderer
|
||||||
, "markup", NULL, "text", xbel_item_get_title(item), NULL);
|
, "markup", NULL, "text", katze_xbel_item_get_title(item), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_bookmark_menu(XbelItem*, GtkWidget*, CBrowser*);
|
static void create_bookmark_menu(KatzeXbelItem*, GtkWidget*, CBrowser*);
|
||||||
|
|
||||||
static void on_bookmark_menu_folder_activate(GtkWidget* menuitem, CBrowser* browser)
|
static void on_bookmark_menu_folder_activate(GtkWidget* menuitem, CBrowser* browser)
|
||||||
{
|
{
|
||||||
GtkWidget* menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menuitem));
|
GtkWidget* menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menuitem));
|
||||||
gtk_container_foreach(GTK_CONTAINER(menu), (GtkCallback)gtk_widget_destroy, NULL);//...
|
gtk_container_foreach(GTK_CONTAINER(menu), (GtkCallback)gtk_widget_destroy, NULL);//...
|
||||||
XbelItem* folder = (XbelItem*)g_object_get_data(G_OBJECT(menuitem), "XbelItem");
|
KatzeXbelItem* folder = (KatzeXbelItem*)g_object_get_data(G_OBJECT(menuitem), "KatzeXbelItem");
|
||||||
create_bookmark_menu(folder, menu, browser);
|
create_bookmark_menu(folder, menu, browser);
|
||||||
// Remove all menuitems when the menu is hidden.
|
// Remove all menuitems when the menu is hidden.
|
||||||
// FIXME: We really *want* the line below, but it won't work like that
|
// FIXME: We really *want* the line below, but it won't work like that
|
||||||
|
@ -790,7 +789,7 @@ static void on_bookmark_menu_folder_activate(GtkWidget* menuitem, CBrowser* brow
|
||||||
static void on_bookmark_toolbar_folder_activate(GtkToolItem* toolitem, CBrowser* browser)
|
static void on_bookmark_toolbar_folder_activate(GtkToolItem* toolitem, CBrowser* browser)
|
||||||
{
|
{
|
||||||
GtkWidget* menu = gtk_menu_new();
|
GtkWidget* menu = gtk_menu_new();
|
||||||
XbelItem* folder = (XbelItem*)g_object_get_data(G_OBJECT(toolitem), "XbelItem");
|
KatzeXbelItem* folder = (KatzeXbelItem*)g_object_get_data(G_OBJECT(toolitem), "KatzeXbelItem");
|
||||||
create_bookmark_menu(folder, menu, browser);
|
create_bookmark_menu(folder, menu, browser);
|
||||||
// Remove all menuitems when the menu is hidden.
|
// Remove all menuitems when the menu is hidden.
|
||||||
// FIXME: We really *should* run the line below, but it won't work like that
|
// FIXME: We really *should* run the line below, but it won't work like that
|
||||||
|
@ -800,24 +799,24 @@ static void on_bookmark_toolbar_folder_activate(GtkToolItem* toolitem, CBrowser*
|
||||||
|
|
||||||
void on_menu_bookmarks_item_activate(GtkWidget* widget, CBrowser* browser)
|
void on_menu_bookmarks_item_activate(GtkWidget* widget, CBrowser* browser)
|
||||||
{
|
{
|
||||||
XbelItem* item = (XbelItem*)g_object_get_data(G_OBJECT(widget), "XbelItem");
|
KatzeXbelItem* item = (KatzeXbelItem*)g_object_get_data(G_OBJECT(widget), "KatzeXbelItem");
|
||||||
webView_open(get_nth_webView(-1, browser), xbel_bookmark_get_href(item));
|
webView_open(get_nth_webView(-1, browser), katze_xbel_bookmark_get_href(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_bookmark_menu(XbelItem* folder, GtkWidget* menu, CBrowser* browser)
|
static void create_bookmark_menu(KatzeXbelItem* folder, GtkWidget* menu, CBrowser* browser)
|
||||||
{
|
{
|
||||||
guint n = xbel_folder_get_n_items(folder);
|
guint n = katze_xbel_folder_get_n_items(folder);
|
||||||
guint i;
|
guint i;
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(folder, i);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(folder, i);
|
||||||
const gchar* title = xbel_item_is_separator(item) ? "" : xbel_item_get_title(item);
|
const gchar* title = katze_xbel_item_is_separator(item) ? "" : katze_xbel_item_get_title(item);
|
||||||
//const gchar* desc = xbel_item_is_separator(item) ? "" : xbel_item_get_desc(item);
|
//const gchar* desc = katze_xbel_item_is_separator(item) ? "" : katze_xbel_item_get_desc(item);
|
||||||
GtkWidget* menuitem = NULL;
|
GtkWidget* menuitem = NULL;
|
||||||
switch(xbel_item_get_kind(item))
|
switch(katze_xbel_item_get_kind(item))
|
||||||
{
|
{
|
||||||
case XBEL_ITEM_FOLDER:
|
case KATZE_XBEL_ITEM_KIND_FOLDER:
|
||||||
// FIXME: what about xbel_folder_is_folded?
|
// FIXME: what about katze_xbel_folder_is_folded?
|
||||||
menuitem = gtk_image_menu_item_new_with_label(title);
|
menuitem = gtk_image_menu_item_new_with_label(title);
|
||||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem)
|
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem)
|
||||||
, gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU));
|
, gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU));
|
||||||
|
@ -825,14 +824,14 @@ static void create_bookmark_menu(XbelItem* folder, GtkWidget* menu, CBrowser* br
|
||||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), _menu);
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), _menu);
|
||||||
g_signal_connect(menuitem, "activate"
|
g_signal_connect(menuitem, "activate"
|
||||||
, G_CALLBACK(on_bookmark_menu_folder_activate), browser);
|
, G_CALLBACK(on_bookmark_menu_folder_activate), browser);
|
||||||
g_object_set_data(G_OBJECT(menuitem), "XbelItem", item);
|
g_object_set_data(G_OBJECT(menuitem), "KatzeXbelItem", item);
|
||||||
break;
|
break;
|
||||||
case XBEL_ITEM_BOOKMARK:
|
case KATZE_XBEL_ITEM_KIND_BOOKMARK:
|
||||||
menuitem = menu_item_new(title, STOCK_BOOKMARK
|
menuitem = menu_item_new(title, STOCK_BOOKMARK
|
||||||
, G_CALLBACK(on_menu_bookmarks_item_activate), TRUE, browser);
|
, G_CALLBACK(on_menu_bookmarks_item_activate), TRUE, browser);
|
||||||
g_object_set_data(G_OBJECT(menuitem), "XbelItem", item);
|
g_object_set_data(G_OBJECT(menuitem), "KatzeXbelItem", item);
|
||||||
break;
|
break;
|
||||||
case XBEL_ITEM_SEPARATOR:
|
case KATZE_XBEL_ITEM_KIND_SEPARATOR:
|
||||||
menuitem = gtk_separator_menu_item_new();
|
menuitem = gtk_separator_menu_item_new();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -936,7 +935,7 @@ void on_location_changed(GtkWidget* widget, CBrowser* browser)
|
||||||
{
|
{
|
||||||
// Preserve changes to the uri
|
// Preserve changes to the uri
|
||||||
/*const gchar* newUri = gtk_entry_get_text(GTK_ENTRY(widget));
|
/*const gchar* newUri = gtk_entry_get_text(GTK_ENTRY(widget));
|
||||||
xbel_bookmark_set_href(browser->sessionItem, newUri);*/
|
katze_xbel_bookmark_set_href(browser->sessionItem, newUri);*/
|
||||||
// FIXME: If we want this feature, this is the wrong approach
|
// FIXME: If we want this feature, this is the wrong approach
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,9 +1036,9 @@ void on_notebook_switch_page(GtkWidget* widget, GtkNotebookPage* page
|
||||||
{
|
{
|
||||||
GtkWidget* webView = get_nth_webView(page_num, browser);
|
GtkWidget* webView = get_nth_webView(page_num, browser);
|
||||||
browser = get_browser_from_webView(webView);
|
browser = get_browser_from_webView(webView);
|
||||||
const gchar* uri = xbel_bookmark_get_href(browser->sessionItem);
|
const gchar* uri = katze_xbel_bookmark_get_href(browser->sessionItem);
|
||||||
gtk_entry_set_text(GTK_ENTRY(browser->location), uri);
|
gtk_entry_set_text(GTK_ENTRY(browser->location), uri);
|
||||||
const gchar* title = xbel_item_get_title(browser->sessionItem);
|
const gchar* title = katze_xbel_item_get_title(browser->sessionItem);
|
||||||
const gchar* effectiveTitle = title ? title : uri;
|
const gchar* effectiveTitle = title ? title : uri;
|
||||||
gchar* windowTitle = g_strconcat(effectiveTitle, " - ", PACKAGE_NAME, NULL);
|
gchar* windowTitle = g_strconcat(effectiveTitle, " - ", PACKAGE_NAME, NULL);
|
||||||
gtk_window_set_title(GTK_WINDOW(browser->window), windowTitle);
|
gtk_window_set_title(GTK_WINDOW(browser->window), windowTitle);
|
||||||
|
@ -1098,9 +1097,9 @@ CBrowser* browser_new(CBrowser* oldBrowser)
|
||||||
{
|
{
|
||||||
CBrowser* browser = g_new0(CBrowser, 1);
|
CBrowser* browser = g_new0(CBrowser, 1);
|
||||||
browsers = g_list_prepend(browsers, browser);
|
browsers = g_list_prepend(browsers, browser);
|
||||||
browser->sessionItem = xbel_bookmark_new();
|
browser->sessionItem = katze_xbel_bookmark_new();
|
||||||
xbel_item_set_title(browser->sessionItem, "about:blank");
|
katze_xbel_item_set_title(browser->sessionItem, "about:blank");
|
||||||
xbel_folder_append_item(session, browser->sessionItem);
|
katze_xbel_folder_append_item(session, browser->sessionItem);
|
||||||
|
|
||||||
GtkWidget* scrolled;
|
GtkWidget* scrolled;
|
||||||
|
|
||||||
|
@ -1289,26 +1288,26 @@ CBrowser* browser_new(CBrowser* oldBrowser)
|
||||||
gtk_toolbar_set_icon_size(GTK_TOOLBAR(browser->bookmarkbar), GTK_ICON_SIZE_MENU);
|
gtk_toolbar_set_icon_size(GTK_TOOLBAR(browser->bookmarkbar), GTK_ICON_SIZE_MENU);
|
||||||
gtk_toolbar_set_style(GTK_TOOLBAR(browser->bookmarkbar), GTK_TOOLBAR_BOTH_HORIZ);
|
gtk_toolbar_set_style(GTK_TOOLBAR(browser->bookmarkbar), GTK_TOOLBAR_BOTH_HORIZ);
|
||||||
create_bookmark_menu(bookmarks, browser->menu_bookmarks, browser);
|
create_bookmark_menu(bookmarks, browser->menu_bookmarks, browser);
|
||||||
for(i = 0; i < xbel_folder_get_n_items(bookmarks); i++)
|
for(i = 0; i < katze_xbel_folder_get_n_items(bookmarks); i++)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(bookmarks, i);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(bookmarks, i);
|
||||||
const gchar* title = xbel_item_is_separator(item)
|
const gchar* title = katze_xbel_item_is_separator(item)
|
||||||
? "" : xbel_item_get_title(item);
|
? "" : katze_xbel_item_get_title(item);
|
||||||
const gchar* desc = xbel_item_is_separator(item)
|
const gchar* desc = katze_xbel_item_is_separator(item)
|
||||||
? "" : xbel_item_get_desc(item);
|
? "" : katze_xbel_item_get_desc(item);
|
||||||
switch(xbel_item_get_kind(item))
|
switch(katze_xbel_item_get_kind(item))
|
||||||
{
|
{
|
||||||
case XBEL_ITEM_FOLDER:
|
case KATZE_XBEL_ITEM_KIND_FOLDER:
|
||||||
toolitem = tool_button_new(title, GTK_STOCK_DIRECTORY, TRUE, TRUE
|
toolitem = tool_button_new(title, GTK_STOCK_DIRECTORY, TRUE, TRUE
|
||||||
, G_CALLBACK(on_bookmark_toolbar_folder_activate), desc, browser);
|
, G_CALLBACK(on_bookmark_toolbar_folder_activate), desc, browser);
|
||||||
g_object_set_data(G_OBJECT(toolitem), "XbelItem", item);
|
g_object_set_data(G_OBJECT(toolitem), "KatzeXbelItem", item);
|
||||||
break;
|
break;
|
||||||
case XBEL_ITEM_BOOKMARK:
|
case KATZE_XBEL_ITEM_KIND_BOOKMARK:
|
||||||
toolitem = tool_button_new(title, STOCK_BOOKMARK, TRUE, TRUE
|
toolitem = tool_button_new(title, STOCK_BOOKMARK, TRUE, TRUE
|
||||||
, G_CALLBACK(on_menu_bookmarks_item_activate), desc, browser);
|
, G_CALLBACK(on_menu_bookmarks_item_activate), desc, browser);
|
||||||
g_object_set_data(G_OBJECT(toolitem), "XbelItem", item);
|
g_object_set_data(G_OBJECT(toolitem), "KatzeXbelItem", item);
|
||||||
break;
|
break;
|
||||||
case XBEL_ITEM_SEPARATOR:
|
case KATZE_XBEL_ITEM_KIND_SEPARATOR:
|
||||||
toolitem = gtk_separator_tool_item_new();
|
toolitem = gtk_separator_tool_item_new();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1381,7 +1380,7 @@ CBrowser* browser_new(CBrowser* oldBrowser)
|
||||||
// Bookmarks
|
// Bookmarks
|
||||||
GtkTreeViewColumn* column;
|
GtkTreeViewColumn* column;
|
||||||
GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf;
|
GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf;
|
||||||
GtkTreeStore* treestore = gtk_tree_store_new(1, G_TYPE_XBEL_ITEM);
|
GtkTreeStore* treestore = gtk_tree_store_new(1, KATZE_TYPE_XBEL_ITEM);
|
||||||
GtkWidget* treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));
|
GtkWidget* treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore));
|
||||||
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
|
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
|
||||||
column = gtk_tree_view_column_new();
|
column = gtk_tree_view_column_new();
|
||||||
|
@ -1566,7 +1565,7 @@ CBrowser* browser_new(CBrowser* oldBrowser)
|
||||||
katze_throbber_set_static_stock_id(KATZE_THROBBER(browser->webView_icon)
|
katze_throbber_set_static_stock_id(KATZE_THROBBER(browser->webView_icon)
|
||||||
, GTK_STOCK_FILE);
|
, GTK_STOCK_FILE);
|
||||||
gtk_box_pack_start(GTK_BOX(hbox), browser->webView_icon, FALSE, FALSE, 0);
|
gtk_box_pack_start(GTK_BOX(hbox), browser->webView_icon, FALSE, FALSE, 0);
|
||||||
browser->webView_name = gtk_label_new(xbel_item_get_title(browser->sessionItem));
|
browser->webView_name = gtk_label_new(katze_xbel_item_get_title(browser->sessionItem));
|
||||||
gtk_misc_set_alignment(GTK_MISC(browser->webView_name), 0.0, 0.5);
|
gtk_misc_set_alignment(GTK_MISC(browser->webView_name), 0.0, 0.5);
|
||||||
// TODO: make the tab initially look "unvisited" until it's focused
|
// TODO: make the tab initially look "unvisited" until it's focused
|
||||||
// TODO: gtk's tab scrolling is weird?
|
// TODO: gtk's tab scrolling is weird?
|
||||||
|
|
|
@ -73,7 +73,7 @@ typedef struct _CBrowser
|
||||||
//UNDEFINED favicon;
|
//UNDEFINED favicon;
|
||||||
guint security;
|
guint security;
|
||||||
gchar* statusMessage; // message from a webView
|
gchar* statusMessage; // message from a webView
|
||||||
XbelItem* sessionItem;
|
KatzeXbelItem* sessionItem;
|
||||||
} CBrowser;
|
} CBrowser;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#define __GLOBAL_H__ 1
|
#define __GLOBAL_H__ 1
|
||||||
|
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "xbel.h"
|
#include "../katze/katze.h"
|
||||||
|
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ CConfig* config;
|
||||||
GList* searchEngines; // Items of type 'SearchEngine'
|
GList* searchEngines; // Items of type 'SearchEngine'
|
||||||
GList* browsers; // Items of type 'CBrowser'
|
GList* browsers; // Items of type 'CBrowser'
|
||||||
GtkAccelGroup* accel_group;
|
GtkAccelGroup* accel_group;
|
||||||
XbelItem* bookmarks;
|
KatzeXbelItem* bookmarks;
|
||||||
XbelItem* session;
|
KatzeXbelItem* session;
|
||||||
XbelItem* tabtrash;
|
KatzeXbelItem* tabtrash;
|
||||||
|
|
||||||
// Custom stock items
|
// Custom stock items
|
||||||
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ void update_favicon(CBrowser* browser)
|
||||||
|
|
||||||
void update_security(CBrowser* browser)
|
void update_security(CBrowser* browser)
|
||||||
{
|
{
|
||||||
const gchar* uri = xbel_bookmark_get_href(browser->sessionItem);
|
const gchar* uri = katze_xbel_bookmark_get_href(browser->sessionItem);
|
||||||
// TODO: This check is bogus, until webkit tells us how secure a page is
|
// TODO: This check is bogus, until webkit tells us how secure a page is
|
||||||
if(g_str_has_prefix(uri, "https://"))
|
if(g_str_has_prefix(uri, "https://"))
|
||||||
{
|
{
|
||||||
|
@ -405,7 +405,7 @@ void update_browser_actions(CBrowser* browser)
|
||||||
action_set_sensitive("TabPrevious", active, browser);
|
action_set_sensitive("TabPrevious", active, browser);
|
||||||
action_set_sensitive("TabNext", active, browser);
|
action_set_sensitive("TabNext", active, browser);
|
||||||
|
|
||||||
gboolean tabtrashEmpty = xbel_folder_is_empty(tabtrash);
|
gboolean tabtrashEmpty = katze_xbel_folder_is_empty(tabtrash);
|
||||||
action_set_sensitive("UndoTabClose", !tabtrashEmpty, browser);
|
action_set_sensitive("UndoTabClose", !tabtrashEmpty, browser);
|
||||||
action_set_sensitive("TabsClosed", !tabtrashEmpty, browser);
|
action_set_sensitive("TabsClosed", !tabtrashEmpty, browser);
|
||||||
}
|
}
|
||||||
|
|
56
src/main.c
56
src/main.c
|
@ -17,7 +17,7 @@
|
||||||
#include "sokoke.h"
|
#include "sokoke.h"
|
||||||
#include "search.h"
|
#include "search.h"
|
||||||
#include "webView.h"
|
#include "webView.h"
|
||||||
#include "xbel.h"
|
#include "../katze/katze.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
@ -148,9 +148,9 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
||||||
bookmarks = xbel_folder_new();
|
bookmarks = katze_xbel_folder_new();
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_from_file(bookmarks, configFile, &error))
|
if(!katze_xbel_folder_from_file(bookmarks, configFile, &error))
|
||||||
{
|
{
|
||||||
if(error->code != G_FILE_ERROR_NOENT)
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
g_string_append_printf(errorMessages
|
g_string_append_printf(errorMessages
|
||||||
|
@ -158,12 +158,12 @@ int main(int argc, char** argv)
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
XbelItem* _session = xbel_folder_new();
|
KatzeXbelItem* _session = katze_xbel_folder_new();
|
||||||
if(config->startup == CONFIG_STARTUP_SESSION)
|
if(config->startup == CONFIG_STARTUP_SESSION)
|
||||||
{
|
{
|
||||||
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_from_file(_session, configFile, &error))
|
if(!katze_xbel_folder_from_file(_session, configFile, &error))
|
||||||
{
|
{
|
||||||
if(error->code != G_FILE_ERROR_NOENT)
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
g_string_append_printf(errorMessages
|
g_string_append_printf(errorMessages
|
||||||
|
@ -173,9 +173,9 @@ int main(int argc, char** argv)
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
}
|
}
|
||||||
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
||||||
tabtrash = xbel_folder_new();
|
tabtrash = katze_xbel_folder_new();
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_from_file(tabtrash, configFile, &error))
|
if(!katze_xbel_folder_from_file(tabtrash, configFile, &error))
|
||||||
{
|
{
|
||||||
if(error->code != G_FILE_ERROR_NOENT)
|
if(error->code != G_FILE_ERROR_NOENT)
|
||||||
g_string_append_printf(errorMessages
|
g_string_append_printf(errorMessages
|
||||||
|
@ -204,8 +204,8 @@ int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
config_free(config);
|
config_free(config);
|
||||||
search_engines_free(searchEngines);
|
search_engines_free(searchEngines);
|
||||||
xbel_item_unref(bookmarks);
|
katze_xbel_item_unref(bookmarks);
|
||||||
xbel_item_unref(_session);
|
katze_xbel_item_unref(_session);
|
||||||
g_string_free(errorMessages, TRUE);
|
g_string_free(errorMessages, TRUE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -220,23 +220,23 @@ int main(int argc, char** argv)
|
||||||
gchar* uri = argc > 1 ? strtok(g_strdup(argv[1]), "|") : NULL;
|
gchar* uri = argc > 1 ? strtok(g_strdup(argv[1]), "|") : NULL;
|
||||||
while(uri != NULL)
|
while(uri != NULL)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_bookmark_new();
|
KatzeXbelItem* item = katze_xbel_bookmark_new();
|
||||||
gchar* uriReady = magic_uri(uri, FALSE);
|
gchar* uriReady = magic_uri(uri, FALSE);
|
||||||
xbel_bookmark_set_href(item, uriReady);
|
katze_xbel_bookmark_set_href(item, uriReady);
|
||||||
g_free(uriReady);
|
g_free(uriReady);
|
||||||
xbel_folder_append_item(_session, item);
|
katze_xbel_folder_append_item(_session, item);
|
||||||
uri = strtok(NULL, "|");
|
uri = strtok(NULL, "|");
|
||||||
}
|
}
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
if(xbel_folder_is_empty(_session))
|
if(katze_xbel_folder_is_empty(_session))
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_bookmark_new();
|
KatzeXbelItem* item = katze_xbel_bookmark_new();
|
||||||
if(config->startup == CONFIG_STARTUP_BLANK)
|
if(config->startup == CONFIG_STARTUP_BLANK)
|
||||||
xbel_bookmark_set_href(item, "about:blank");
|
katze_xbel_bookmark_set_href(item, "about:blank");
|
||||||
else
|
else
|
||||||
xbel_bookmark_set_href(item, config->homepage);
|
katze_xbel_bookmark_set_href(item, config->homepage);
|
||||||
xbel_folder_prepend_item(_session, item);
|
katze_xbel_folder_prepend_item(_session, item);
|
||||||
}
|
}
|
||||||
g_free(configPath);
|
g_free(configPath);
|
||||||
|
|
||||||
|
@ -244,17 +244,17 @@ int main(int argc, char** argv)
|
||||||
stock_items_init();
|
stock_items_init();
|
||||||
browsers = NULL;
|
browsers = NULL;
|
||||||
|
|
||||||
session = xbel_folder_new();
|
session = katze_xbel_folder_new();
|
||||||
CBrowser* browser = NULL;
|
CBrowser* browser = NULL;
|
||||||
guint n = xbel_folder_get_n_items(_session);
|
guint n = katze_xbel_folder_get_n_items(_session);
|
||||||
guint i;
|
guint i;
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(_session, i);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(_session, i);
|
||||||
browser = browser_new(browser);
|
browser = browser_new(browser);
|
||||||
webView_open(browser->webView, xbel_bookmark_get_href(item));
|
webView_open(browser->webView, katze_xbel_bookmark_get_href(item));
|
||||||
}
|
}
|
||||||
xbel_item_unref(_session);
|
katze_xbel_item_unref(_session);
|
||||||
|
|
||||||
gtk_main();
|
gtk_main();
|
||||||
|
|
||||||
|
@ -272,34 +272,34 @@ int main(int argc, char** argv)
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
configFile = g_build_filename(configPath, "bookmarks.xbel", NULL);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_to_file(bookmarks, configFile, &error))
|
if(!katze_xbel_folder_to_file(bookmarks, configFile, &error))
|
||||||
{
|
{
|
||||||
g_warning("Bookmarks couldn't be saved. %s", error->message);
|
g_warning("Bookmarks couldn't be saved. %s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
xbel_item_unref(bookmarks);
|
katze_xbel_item_unref(bookmarks);
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
configFile = g_build_filename(configPath, "tabtrash.xbel", NULL);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_to_file(tabtrash, configFile, &error))
|
if(!katze_xbel_folder_to_file(tabtrash, configFile, &error))
|
||||||
{
|
{
|
||||||
g_warning("Tabtrash couldn't be saved. %s", error->message);
|
g_warning("Tabtrash couldn't be saved. %s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
xbel_item_unref(tabtrash);
|
katze_xbel_item_unref(tabtrash);
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
if(config->startup == CONFIG_STARTUP_SESSION)
|
if(config->startup == CONFIG_STARTUP_SESSION)
|
||||||
{
|
{
|
||||||
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
configFile = g_build_filename(configPath, "session.xbel", NULL);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!xbel_folder_to_file(session, configFile, &error))
|
if(!katze_xbel_folder_to_file(session, configFile, &error))
|
||||||
{
|
{
|
||||||
g_warning("Session couldn't be saved. %s", error->message);
|
g_warning("Session couldn't be saved. %s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
g_free(configFile);
|
g_free(configFile);
|
||||||
}
|
}
|
||||||
xbel_item_unref(session);
|
katze_xbel_item_unref(session);
|
||||||
configFile = g_build_filename(configPath, "config", NULL);
|
configFile = g_build_filename(configPath, "config", NULL);
|
||||||
error = NULL;
|
error = NULL;
|
||||||
if(!config_to_file(config, configFile, &error))
|
if(!config_to_file(config, configFile, &error))
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "sokoke.h"
|
#include "sokoke.h"
|
||||||
#include "xbel.h"
|
#include "../katze/katze.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ void on_webView_title_changed(GtkWidget* webView, WebKitWebFrame* frame
|
||||||
newTitle = title;
|
newTitle = title;
|
||||||
else
|
else
|
||||||
newTitle = webkit_web_frame_get_uri(frame);
|
newTitle = webkit_web_frame_get_uri(frame);
|
||||||
xbel_item_set_title(browser->sessionItem, newTitle);
|
katze_xbel_item_set_title(browser->sessionItem, newTitle);
|
||||||
gtk_label_set_text(GTK_LABEL(browser->webView_name), newTitle);
|
gtk_label_set_text(GTK_LABEL(browser->webView_name), newTitle);
|
||||||
sokoke_widget_set_tooltip_text(gtk_widget_get_parent(
|
sokoke_widget_set_tooltip_text(gtk_widget_get_parent(
|
||||||
gtk_widget_get_parent(browser->webView_name)), newTitle);
|
gtk_widget_get_parent(browser->webView_name)), newTitle);
|
||||||
|
@ -90,7 +90,7 @@ void on_webView_load_committed(GtkWidget* webView, WebKitWebFrame* frame
|
||||||
{
|
{
|
||||||
const gchar* uri = webkit_web_frame_get_uri(frame);
|
const gchar* uri = webkit_web_frame_get_uri(frame);
|
||||||
gchar* newUri = g_strdup(uri ? uri : "");
|
gchar* newUri = g_strdup(uri ? uri : "");
|
||||||
xbel_bookmark_set_href(browser->sessionItem, newUri);
|
katze_xbel_bookmark_set_href(browser->sessionItem, newUri);
|
||||||
if(webView == get_nth_webView(-1, browser))
|
if(webView == get_nth_webView(-1, browser))
|
||||||
{
|
{
|
||||||
gtk_entry_set_text(GTK_ENTRY(browser->location), newUri);
|
gtk_entry_set_text(GTK_ENTRY(browser->location), newUri);
|
||||||
|
@ -301,9 +301,9 @@ void on_webView_destroy(GtkWidget* widget, CBrowser* browser)
|
||||||
g_object_unref(browser->popup_element);
|
g_object_unref(browser->popup_element);
|
||||||
g_object_unref(browser->popup_editable);
|
g_object_unref(browser->popup_editable);
|
||||||
guint i;
|
guint i;
|
||||||
guint n = xbel_folder_get_n_items(bookmarks);
|
guint n = katze_xbel_folder_get_n_items(bookmarks);
|
||||||
for(i = 0; i < n; i++)
|
for(i = 0; i < n; i++)
|
||||||
xbel_item_unref(xbel_folder_get_nth_item(bookmarks, i));
|
katze_xbel_item_unref(katze_xbel_folder_get_nth_item(bookmarks, i));
|
||||||
gtk_main_quit();
|
gtk_main_quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,28 +329,28 @@ void webView_open(GtkWidget* webView, const gchar* uri)
|
||||||
CBrowser* browser = get_browser_from_webView(webView);
|
CBrowser* browser = get_browser_from_webView(webView);
|
||||||
if(browser)
|
if(browser)
|
||||||
{
|
{
|
||||||
xbel_bookmark_set_href(browser->sessionItem, uri);
|
katze_xbel_bookmark_set_href(browser->sessionItem, uri);
|
||||||
xbel_item_set_title(browser->sessionItem, "");
|
katze_xbel_item_set_title(browser->sessionItem, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void webView_close(GtkWidget* webView, CBrowser* browser)
|
void webView_close(GtkWidget* webView, CBrowser* browser)
|
||||||
{
|
{
|
||||||
browser = get_browser_from_webView(webView);
|
browser = get_browser_from_webView(webView);
|
||||||
const gchar* uri = xbel_bookmark_get_href(browser->sessionItem);
|
const gchar* uri = katze_xbel_bookmark_get_href(browser->sessionItem);
|
||||||
xbel_folder_remove_item(session, browser->sessionItem);
|
katze_xbel_folder_remove_item(session, browser->sessionItem);
|
||||||
if(uri && *uri)
|
if(uri && *uri)
|
||||||
{
|
{
|
||||||
xbel_folder_prepend_item(tabtrash, browser->sessionItem);
|
katze_xbel_folder_prepend_item(tabtrash, browser->sessionItem);
|
||||||
guint n = xbel_folder_get_n_items(tabtrash);
|
guint n = katze_xbel_folder_get_n_items(tabtrash);
|
||||||
if(n > 10)
|
if(n > 10)
|
||||||
{
|
{
|
||||||
XbelItem* item = xbel_folder_get_nth_item(tabtrash, n - 1);
|
KatzeXbelItem* item = katze_xbel_folder_get_nth_item(tabtrash, n - 1);
|
||||||
xbel_item_unref(item);
|
katze_xbel_item_unref(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
xbel_item_unref(browser->sessionItem);
|
katze_xbel_item_unref(browser->sessionItem);
|
||||||
gtk_widget_destroy(browser->webView_menu);
|
gtk_widget_destroy(browser->webView_menu);
|
||||||
gtk_notebook_remove_page(GTK_NOTEBOOK(browser->webViews)
|
gtk_notebook_remove_page(GTK_NOTEBOOK(browser->webViews)
|
||||||
, get_webView_index(webView, browser));
|
, get_webView_index(webView, browser));
|
||||||
|
|
825
src/xbel.c
825
src/xbel.c
|
@ -1,825 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is an implementation of XBEL based on Glib and libXML2.
|
|
||||||
*
|
|
||||||
* Design Goals:
|
|
||||||
* - XbelItem is the only opaque public data structure.
|
|
||||||
* - The interface should be intuitive and familiar to Glib users.
|
|
||||||
* - There should be no public exposure of libXML2 specific code.
|
|
||||||
* - Bookmarks should actually be easily exchangeable between programs.
|
|
||||||
*
|
|
||||||
* TODO:
|
|
||||||
* - Support info > metadata, alias, added, modified, visited
|
|
||||||
* - Compatibility: The Nokia 770 *requires* metadata and folder
|
|
||||||
* - Compatibility: Kazehakase's bookmarks
|
|
||||||
* - Compatibility: Epiphany's bookmarks
|
|
||||||
* - XML Indentation
|
|
||||||
* - Export and import to other formats
|
|
||||||
**/
|
|
||||||
|
|
||||||
#include "xbel.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <libxml/parser.h>
|
|
||||||
#include <libxml/tree.h>
|
|
||||||
|
|
||||||
// Private: Create a new item of a certain type
|
|
||||||
static XbelItem* xbel_item_new(XbelItemKind kind)
|
|
||||||
{
|
|
||||||
XbelItem* item = g_new(XbelItem, 1);
|
|
||||||
item->refs = 1;
|
|
||||||
item->parent = NULL;
|
|
||||||
item->kind = kind;
|
|
||||||
if(kind == XBEL_ITEM_FOLDER)
|
|
||||||
{
|
|
||||||
item->items = NULL;
|
|
||||||
item->folded = TRUE;
|
|
||||||
}
|
|
||||||
if(kind != XBEL_ITEM_SEPARATOR)
|
|
||||||
{
|
|
||||||
item->title = NULL;
|
|
||||||
item->desc = NULL;
|
|
||||||
}
|
|
||||||
if(kind == XBEL_ITEM_BOOKMARK)
|
|
||||||
item->href = g_strdup("");
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_bookmark_new:
|
|
||||||
*
|
|
||||||
* Create a new empty bookmark.
|
|
||||||
*
|
|
||||||
* Return value: a newly allocated bookmark
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_bookmark_new(void)
|
|
||||||
{
|
|
||||||
return xbel_item_new(XBEL_ITEM_BOOKMARK);
|
|
||||||
}
|
|
||||||
|
|
||||||
static XbelItem* xbel_bookmark_from_xmlNodePtr(xmlNodePtr cur)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(cur != NULL, NULL);
|
|
||||||
XbelItem* bookmark = xbel_bookmark_new();
|
|
||||||
xmlChar* key = xmlGetProp(cur, (xmlChar*)"href");
|
|
||||||
xbel_bookmark_set_href(bookmark, (gchar*)key);
|
|
||||||
cur = cur->xmlChildrenNode;
|
|
||||||
while(cur != NULL)
|
|
||||||
{
|
|
||||||
if(!xmlStrcmp(cur->name, (const xmlChar*)"title"))
|
|
||||||
{
|
|
||||||
xmlChar* key = xmlNodeGetContent(cur);
|
|
||||||
bookmark->title = (gchar*)g_strstrip((gchar*)key);
|
|
||||||
}
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"desc"))
|
|
||||||
{
|
|
||||||
xmlChar* key = xmlNodeGetContent(cur);
|
|
||||||
bookmark->desc = (gchar*)g_strstrip((gchar*)key);
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
return bookmark;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_separator_new:
|
|
||||||
*
|
|
||||||
* Create a new separator.
|
|
||||||
*
|
|
||||||
* The returned item must be freed eventually.
|
|
||||||
*
|
|
||||||
* Return value: a newly allocated separator.
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_separator_new(void)
|
|
||||||
{
|
|
||||||
return xbel_item_new(XBEL_ITEM_SEPARATOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_new:
|
|
||||||
*
|
|
||||||
* Create a new empty folder.
|
|
||||||
*
|
|
||||||
* The returned item must be freed eventually.
|
|
||||||
*
|
|
||||||
* Return value: a newly allocated folder.
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_folder_new(void)
|
|
||||||
{
|
|
||||||
return xbel_item_new(XBEL_ITEM_FOLDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private: Create a folder from an xmlNodePtr
|
|
||||||
static XbelItem* xbel_folder_from_xmlNodePtr(xmlNodePtr cur)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(cur != NULL, NULL);
|
|
||||||
XbelItem* folder = xbel_folder_new();
|
|
||||||
xmlChar* key = xmlGetProp(cur, (xmlChar*)"folded");
|
|
||||||
if(key)
|
|
||||||
{
|
|
||||||
if(!g_ascii_strncasecmp((gchar*)key, "yes", 3))
|
|
||||||
folder->folded = TRUE;
|
|
||||||
else if(!g_ascii_strncasecmp((gchar*)key, "no", 2))
|
|
||||||
folder->folded = FALSE;
|
|
||||||
else
|
|
||||||
g_warning("XBEL: Unknown value for folded.");
|
|
||||||
xmlFree(key);
|
|
||||||
}
|
|
||||||
cur = cur->xmlChildrenNode;
|
|
||||||
while(cur)
|
|
||||||
{
|
|
||||||
if(!xmlStrcmp(cur->name, (const xmlChar*)"title"))
|
|
||||||
{
|
|
||||||
xmlChar* key = xmlNodeGetContent(cur);
|
|
||||||
folder->title = (gchar*)g_strstrip((gchar*)key);
|
|
||||||
}
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"desc"))
|
|
||||||
{
|
|
||||||
xmlChar* key = xmlNodeGetContent(cur);
|
|
||||||
folder->desc = (gchar*)g_strstrip((gchar*)key);
|
|
||||||
}
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"folder"))
|
|
||||||
{
|
|
||||||
XbelItem* item = xbel_folder_from_xmlNodePtr(cur);
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_prepend(folder->items, item);
|
|
||||||
}
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"bookmark"))
|
|
||||||
{
|
|
||||||
XbelItem* item = xbel_bookmark_from_xmlNodePtr(cur);
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_prepend(folder->items, item);
|
|
||||||
}
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"separator"))
|
|
||||||
{
|
|
||||||
XbelItem* item = xbel_separator_new();
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_prepend(folder->items, item);
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
// Prepending and reversing is faster than appending
|
|
||||||
folder->items = g_list_reverse(folder->items);
|
|
||||||
return folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private: Loads the contents from an xmlNodePtr into a folder.
|
|
||||||
static gboolean xbel_folder_from_xmlDocPtr(XbelItem* folder, xmlDocPtr doc)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
|
||||||
g_return_val_if_fail(doc != NULL, FALSE);
|
|
||||||
xmlNodePtr cur = xmlDocGetRootElement(doc);
|
|
||||||
xmlChar* version = xmlGetProp(cur, (xmlChar*)"version");
|
|
||||||
if(xmlStrcmp(version, (xmlChar*)"1.0"))
|
|
||||||
g_warning("XBEL version is not 1.0.");
|
|
||||||
xmlFree(version);
|
|
||||||
folder->title = (gchar*)xmlGetProp(cur, (xmlChar*)"title");
|
|
||||||
folder->desc = (gchar*)xmlGetProp(cur, (xmlChar*)"desc");
|
|
||||||
if((cur = xmlDocGetRootElement(doc)) == NULL)
|
|
||||||
{
|
|
||||||
// Empty document
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if(xmlStrcmp(cur->name, (const xmlChar*)"xbel"))
|
|
||||||
{
|
|
||||||
// Wrong document kind
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
cur = cur->xmlChildrenNode;
|
|
||||||
while(cur != NULL)
|
|
||||||
{
|
|
||||||
XbelItem* item = NULL;
|
|
||||||
if(!xmlStrcmp(cur->name, (const xmlChar*)"folder"))
|
|
||||||
item = xbel_folder_from_xmlNodePtr(cur);
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"bookmark"))
|
|
||||||
item = xbel_bookmark_from_xmlNodePtr(cur);
|
|
||||||
else if(!xmlStrcmp(cur->name, (const xmlChar*)"separator"))
|
|
||||||
item = xbel_separator_new();
|
|
||||||
/*else if(!xmlStrcmp(cur->name, (const xmlChar*)"info"))
|
|
||||||
item = xbel_parse_info(xbel, cur);*/
|
|
||||||
if(item != NULL)
|
|
||||||
{
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_prepend(folder->items, item);
|
|
||||||
}
|
|
||||||
cur = cur->next;
|
|
||||||
}
|
|
||||||
// Prepending and reversing is faster than appending
|
|
||||||
folder->items = g_list_reverse(folder->items);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_ref:
|
|
||||||
* @item: a valid item
|
|
||||||
*
|
|
||||||
* Ref an XbelItem.
|
|
||||||
*
|
|
||||||
* Ref means that the reference count is increased by one.
|
|
||||||
*
|
|
||||||
* This has no effect on children of a folder.
|
|
||||||
**/
|
|
||||||
void xbel_item_ref(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_if_fail(item);
|
|
||||||
item->refs++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_unref:
|
|
||||||
* @item: a valid item
|
|
||||||
*
|
|
||||||
* Unref an XbelItem. If @item is a folder all of its children will also
|
|
||||||
* be unreffed automatically.
|
|
||||||
*
|
|
||||||
* Unref means that the reference count is decreased. If there are no
|
|
||||||
* references left, the memory will be freed and if needed removed from
|
|
||||||
* its containing folder.
|
|
||||||
**/
|
|
||||||
void xbel_item_unref(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_if_fail(item);
|
|
||||||
item->refs--;
|
|
||||||
if(item->refs)
|
|
||||||
return;
|
|
||||||
XbelItem* parent = xbel_item_get_parent(item);
|
|
||||||
if(parent)
|
|
||||||
xbel_folder_remove_item(parent, item);
|
|
||||||
if(xbel_item_is_folder(item))
|
|
||||||
{
|
|
||||||
guint n = xbel_folder_get_n_items(item);
|
|
||||||
guint i;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
|
||||||
_item->parent = NULL;
|
|
||||||
xbel_item_unref(_item);
|
|
||||||
}
|
|
||||||
g_list_free(item->items);
|
|
||||||
}
|
|
||||||
if(item->kind != XBEL_ITEM_SEPARATOR)
|
|
||||||
{
|
|
||||||
g_free(item->title);
|
|
||||||
g_free(item->desc);
|
|
||||||
}
|
|
||||||
if(item->kind == XBEL_ITEM_BOOKMARK)
|
|
||||||
g_free(item->href);
|
|
||||||
g_free(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_copy:
|
|
||||||
* @item: the item to copy
|
|
||||||
*
|
|
||||||
* Copy an XbelItem.
|
|
||||||
*
|
|
||||||
* The returned item must be unreffed eventually.
|
|
||||||
*
|
|
||||||
* Return value: a copy of @item
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_item_copy(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, NULL);
|
|
||||||
XbelItem* copy = xbel_folder_new();
|
|
||||||
if(xbel_item_is_folder(item))
|
|
||||||
{
|
|
||||||
guint n = xbel_folder_get_n_items(item);
|
|
||||||
guint i;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
|
||||||
xbel_folder_append_item(copy, xbel_item_copy(_item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(item->kind != XBEL_ITEM_SEPARATOR)
|
|
||||||
{
|
|
||||||
xbel_item_set_title(copy, item->title);
|
|
||||||
xbel_item_set_desc(copy, item->desc);
|
|
||||||
}
|
|
||||||
if(item->kind == XBEL_ITEM_BOOKMARK)
|
|
||||||
xbel_bookmark_set_href(copy, item->href);
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
GType xbel_item_get_type()
|
|
||||||
{
|
|
||||||
static GType type = 0;
|
|
||||||
if(!type)
|
|
||||||
type = g_pointer_type_register_static("xbel_item");
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_append_item:
|
|
||||||
* @folder: a folder
|
|
||||||
* @item: the item to append
|
|
||||||
*
|
|
||||||
* Append the given item to a folder.
|
|
||||||
*
|
|
||||||
* The item is actually moved and must not be contained in another folder.
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
void xbel_folder_append_item(XbelItem* folder, XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_if_fail(!xbel_item_get_parent(item));
|
|
||||||
g_return_if_fail(xbel_item_is_folder(folder));
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_append(folder->items, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_prepend_item:
|
|
||||||
* @folder: a folder
|
|
||||||
* @item: the item to prepend
|
|
||||||
*
|
|
||||||
* Prepend the given item to a folder.
|
|
||||||
*
|
|
||||||
* The item is actually moved and must not be contained in another folder.
|
|
||||||
*
|
|
||||||
**/
|
|
||||||
void xbel_folder_prepend_item(XbelItem* folder, XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_if_fail(!xbel_item_get_parent(item));
|
|
||||||
g_return_if_fail(xbel_item_is_folder(folder));
|
|
||||||
item->parent = (struct XbelItem*)folder;
|
|
||||||
folder->items = g_list_prepend(folder->items, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_remove_item:
|
|
||||||
* @folder: a folder
|
|
||||||
* @item: the item to remove
|
|
||||||
*
|
|
||||||
* Remove the given @item from a @folder.
|
|
||||||
**/
|
|
||||||
void xbel_folder_remove_item(XbelItem* folder, XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_if_fail(item);
|
|
||||||
g_return_if_fail(xbel_item_get_parent(folder) != item);
|
|
||||||
item->parent = NULL;
|
|
||||||
// Fortunately we know that items are unique
|
|
||||||
folder->items = g_list_remove(folder->items, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_get_n_items:
|
|
||||||
* @folder: a folder
|
|
||||||
*
|
|
||||||
* Retrieve the number of items contained in the given @folder.
|
|
||||||
*
|
|
||||||
* Return value: number of items
|
|
||||||
**/
|
|
||||||
guint xbel_folder_get_n_items(XbelItem* folder)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_item_is_folder(folder), 0);
|
|
||||||
return g_list_length(folder->items);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_get_nth_item:
|
|
||||||
* @folder: a folder
|
|
||||||
* @n: the index of an item
|
|
||||||
*
|
|
||||||
* Retrieve an item contained in the given @folder by its index.
|
|
||||||
*
|
|
||||||
* Return value: the item at the given index or %NULL
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_folder_get_nth_item(XbelItem* folder, guint n)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_item_is_folder(folder), NULL);
|
|
||||||
return (XbelItem*)g_list_nth_data(folder->items, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_is_empty:
|
|
||||||
* @folder: A folder.
|
|
||||||
*
|
|
||||||
* Determines wether or not a folder contains no items. This is significantly
|
|
||||||
* faster than xbel_folder_get_n_items for this particular purpose.
|
|
||||||
*
|
|
||||||
* Return value: Wether the given @folder is folded.
|
|
||||||
**/
|
|
||||||
gboolean xbel_folder_is_empty(XbelItem* folder)
|
|
||||||
{
|
|
||||||
return !xbel_folder_get_nth_item(folder, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_get_folded:
|
|
||||||
* @folder: A folder.
|
|
||||||
*
|
|
||||||
* Determines wether or not a folder is folded. If a folder is not folded
|
|
||||||
* it should not be exposed in a user interface.
|
|
||||||
*
|
|
||||||
* New folders are folded by default.
|
|
||||||
*
|
|
||||||
* Return value: Wether the given @folder is folded.
|
|
||||||
**/
|
|
||||||
gboolean xbel_folder_get_folded(XbelItem* folder)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_item_is_folder(folder), TRUE);
|
|
||||||
return folder->folded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_get_kind:
|
|
||||||
* @item: A item.
|
|
||||||
*
|
|
||||||
* Determines the kind of an item.
|
|
||||||
*
|
|
||||||
* Return value: The kind of the given @item.
|
|
||||||
**/
|
|
||||||
XbelItemKind xbel_item_get_kind(XbelItem* item)
|
|
||||||
{
|
|
||||||
return item->kind;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_get_parent:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Retrieves the parent folder of an item.
|
|
||||||
*
|
|
||||||
* Return value: The parent folder of the given @item or %NULL.
|
|
||||||
**/
|
|
||||||
XbelItem* xbel_item_get_parent(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, NULL);
|
|
||||||
return (XbelItem*)item->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_get_title:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Retrieves the title of an item.
|
|
||||||
*
|
|
||||||
* Return value: The title of the given @item or %NULL.
|
|
||||||
**/
|
|
||||||
G_CONST_RETURN gchar* xbel_item_get_title(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(!xbel_item_is_separator(item), NULL);
|
|
||||||
return item->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_get_desc:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Retrieves the description of an item.
|
|
||||||
*
|
|
||||||
* Return value: The description of the @item or %NULL.
|
|
||||||
**/
|
|
||||||
G_CONST_RETURN gchar* xbel_item_get_desc(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(!xbel_item_is_separator(item), NULL);
|
|
||||||
return item->desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_bookmark_get_href:
|
|
||||||
* @bookmark: A bookmark.
|
|
||||||
*
|
|
||||||
* Retrieves the uri of a bookmark. The value is guaranteed to not be %NULL.
|
|
||||||
*
|
|
||||||
* Return value: The uri of the @bookmark.
|
|
||||||
**/
|
|
||||||
G_CONST_RETURN gchar* xbel_bookmark_get_href(XbelItem* bookmark)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_item_is_bookmark(bookmark), NULL);
|
|
||||||
return bookmark->href;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_is_bookmark:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Determines wether or not an item is a bookmark.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE if the @item is a bookmark.
|
|
||||||
**/
|
|
||||||
gboolean xbel_item_is_bookmark(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, FALSE);
|
|
||||||
return item->kind == XBEL_ITEM_BOOKMARK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_is_separator:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Determines wether or not an item is a separator.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE if the @item is a separator.
|
|
||||||
**/
|
|
||||||
gboolean xbel_item_is_separator(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, FALSE);
|
|
||||||
return item->kind == XBEL_ITEM_SEPARATOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_is_folder:
|
|
||||||
* @item: A valid item.
|
|
||||||
*
|
|
||||||
* Determines wether or not an item is a folder.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE if the item is a folder.
|
|
||||||
**/
|
|
||||||
gboolean xbel_item_is_folder(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, FALSE);
|
|
||||||
return item->kind == XBEL_ITEM_FOLDER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_set_folded:
|
|
||||||
* @folder: A folder.
|
|
||||||
* @folded: TRUE if the folder is folded.
|
|
||||||
*
|
|
||||||
* Sets the foldedness of the @folder.
|
|
||||||
**/
|
|
||||||
void xbel_folder_set_folded(XbelItem* folder, gboolean folded)
|
|
||||||
{
|
|
||||||
g_return_if_fail(xbel_item_is_folder(folder));
|
|
||||||
folder->folded = folded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_set_title:
|
|
||||||
* @item: A valid item.
|
|
||||||
* @title: A string to use for the title.
|
|
||||||
*
|
|
||||||
* Sets the title of the @item.
|
|
||||||
**/
|
|
||||||
void xbel_item_set_title(XbelItem* item, const gchar* title)
|
|
||||||
{
|
|
||||||
g_return_if_fail(!xbel_item_is_separator(item));
|
|
||||||
g_free(item->title);
|
|
||||||
item->title = g_strdup(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_item_set_desc:
|
|
||||||
* @item: A valid item.
|
|
||||||
* @title: A string to use for the description.
|
|
||||||
*
|
|
||||||
* Sets the description of the @item.
|
|
||||||
**/
|
|
||||||
void xbel_item_set_desc(XbelItem* item, const gchar* desc)
|
|
||||||
{
|
|
||||||
g_return_if_fail(!xbel_item_is_separator(item));
|
|
||||||
g_free(item->desc);
|
|
||||||
item->desc = g_strdup(desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_bookmark_set_href:
|
|
||||||
* @bookmark: A bookmark.
|
|
||||||
* @href: A string containing a valid uri.
|
|
||||||
*
|
|
||||||
* Sets the uri of the bookmark.
|
|
||||||
*
|
|
||||||
* The uri must not be %NULL.
|
|
||||||
*
|
|
||||||
* This uri is not currently validated in any way. This may change in the future.
|
|
||||||
**/
|
|
||||||
void xbel_bookmark_set_href(XbelItem* bookmark, const gchar* href)
|
|
||||||
{
|
|
||||||
g_return_if_fail(xbel_item_is_bookmark(bookmark));
|
|
||||||
g_return_if_fail(href);
|
|
||||||
g_free(bookmark->href);
|
|
||||||
bookmark->href = g_strdup(href);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean xbel_folder_from_data(XbelItem* folder, const gchar* data, GError** error)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
|
||||||
g_return_val_if_fail(data, FALSE);
|
|
||||||
xmlDocPtr doc;
|
|
||||||
if((doc = xmlParseMemory(data, strlen(data))) == NULL)
|
|
||||||
{
|
|
||||||
// No valid xml or broken encoding
|
|
||||||
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
|
||||||
, "Malformed document.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if(!xbel_folder_from_xmlDocPtr(folder, doc))
|
|
||||||
{
|
|
||||||
// Parsing failed
|
|
||||||
xmlFreeDoc(doc);
|
|
||||||
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
|
||||||
, "Malformed document.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
xmlFreeDoc(doc);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_from_file:
|
|
||||||
* @folder: An empty folder.
|
|
||||||
* @file: A relative path to a file.
|
|
||||||
* @error: return location for a GError or %NULL
|
|
||||||
*
|
|
||||||
* Tries to load @file.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE on success or %FALSE when an error occured.
|
|
||||||
**/
|
|
||||||
gboolean xbel_folder_from_file(XbelItem* folder, const gchar* file, GError** error)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
|
||||||
g_return_val_if_fail(file, FALSE);
|
|
||||||
if(!g_file_test(file, G_FILE_TEST_EXISTS))
|
|
||||||
{
|
|
||||||
// File doesn't exist
|
|
||||||
*error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_NOENT
|
|
||||||
, "File not found.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
xmlDocPtr doc;
|
|
||||||
if((doc = xmlParseFile(file)) == NULL)
|
|
||||||
{
|
|
||||||
// No valid xml or broken encoding
|
|
||||||
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
|
||||||
, "Malformed document.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if(!xbel_folder_from_xmlDocPtr(folder, doc))
|
|
||||||
{
|
|
||||||
// Parsing failed
|
|
||||||
xmlFreeDoc(doc);
|
|
||||||
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
|
||||||
, "Malformed document.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
xmlFreeDoc(doc);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_from_data_dirs:
|
|
||||||
* @folder: An empty folder.
|
|
||||||
* @file: A relative path to a file.
|
|
||||||
* @full_path: return location for the full path of the file or %NULL
|
|
||||||
* @error: return location for a GError or %NULL
|
|
||||||
*
|
|
||||||
* Tries to load @file from the user data dir or any of the system data dirs.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE on success or %FALSE when an error occured.
|
|
||||||
**/
|
|
||||||
gboolean xbel_folder_from_data_dirs(XbelItem* folder, const gchar* file
|
|
||||||
, gchar** full_path, GError** error)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_folder_is_empty(folder), FALSE);
|
|
||||||
g_return_val_if_fail(file, FALSE);
|
|
||||||
// FIXME: Essentially unimplemented
|
|
||||||
|
|
||||||
*error = g_error_new(XBEL_ERROR, XBEL_ERROR_READ
|
|
||||||
, "Malformed document.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gchar* xbel_xml_element(const gchar* name, const gchar* value)
|
|
||||||
{
|
|
||||||
if(!value)
|
|
||||||
return g_strdup("");
|
|
||||||
gchar* valueEscaped = g_markup_escape_text(value, -1);
|
|
||||||
gchar* XML = g_strdup_printf("<%s>%s</%s>\n", name, valueEscaped, name);
|
|
||||||
g_free(valueEscaped);
|
|
||||||
return XML;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gchar* xbel_item_to_data(XbelItem* item)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(item, NULL);
|
|
||||||
gchar* XML = NULL;
|
|
||||||
switch(xbel_item_get_kind(item))
|
|
||||||
{
|
|
||||||
case XBEL_ITEM_FOLDER:
|
|
||||||
{
|
|
||||||
GString* _XML = g_string_new(NULL);
|
|
||||||
guint n = xbel_folder_get_n_items(item);
|
|
||||||
guint i;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
XbelItem* _item = xbel_folder_get_nth_item(item, i);
|
|
||||||
gchar* itemXML = xbel_item_to_data(_item);
|
|
||||||
g_string_append(_XML, itemXML);
|
|
||||||
g_free(itemXML);
|
|
||||||
}
|
|
||||||
gchar* folded = item->folded ? NULL : g_strdup_printf(" folded=\"no\"");
|
|
||||||
gchar* title = xbel_xml_element("title", item->title);
|
|
||||||
gchar* desc = xbel_xml_element("desc", item->desc);
|
|
||||||
XML = g_strdup_printf("<folder%s>\n%s%s%s</folder>\n"
|
|
||||||
, folded ? folded : ""
|
|
||||||
, title
|
|
||||||
, desc
|
|
||||||
, g_string_free(_XML, FALSE));
|
|
||||||
g_free(folded);
|
|
||||||
g_free(title);
|
|
||||||
g_free(desc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XBEL_ITEM_BOOKMARK:
|
|
||||||
{
|
|
||||||
gchar* hrefEscaped = g_markup_escape_text(item->href, -1);
|
|
||||||
gchar* href = g_strdup_printf(" href=\"%s\"", hrefEscaped);
|
|
||||||
g_free(hrefEscaped);
|
|
||||||
gchar* title = xbel_xml_element("title", item->title);
|
|
||||||
gchar* desc = xbel_xml_element("desc", item->desc);
|
|
||||||
XML = g_strdup_printf("<bookmark%s>\n%s%s%s</bookmark>\n"
|
|
||||||
, href
|
|
||||||
, title
|
|
||||||
, desc
|
|
||||||
, "");
|
|
||||||
g_free(href);
|
|
||||||
g_free(title);
|
|
||||||
g_free(desc);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XBEL_ITEM_SEPARATOR:
|
|
||||||
XML = g_strdup("<separator/>\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_warning("XBEL: Unknown item kind");
|
|
||||||
}
|
|
||||||
return XML;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_to_data:
|
|
||||||
* @folder: A folder.
|
|
||||||
* @length: return location for the length of the created string or %NULL
|
|
||||||
* @error: return location for a GError or %NULL
|
|
||||||
*
|
|
||||||
* Retrieve the contents of @folder as a string.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE on success or %FALSE when an error occured.
|
|
||||||
**/
|
|
||||||
gchar* xbel_folder_to_data(XbelItem* folder, gsize* length, GError** error)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(xbel_item_is_folder(folder), FALSE);
|
|
||||||
// FIXME: length is never filled
|
|
||||||
GString* innerXML = g_string_new(NULL);
|
|
||||||
guint n = xbel_folder_get_n_items(folder);
|
|
||||||
guint i;
|
|
||||||
for(i = 0; i < n; i++)
|
|
||||||
{
|
|
||||||
gchar* sItem = xbel_item_to_data(xbel_folder_get_nth_item(folder, i));
|
|
||||||
g_string_append(innerXML, sItem);
|
|
||||||
g_free(sItem);
|
|
||||||
}
|
|
||||||
gchar* title = xbel_xml_element("title", folder->title);
|
|
||||||
gchar* desc = xbel_xml_element("desc", folder->desc);
|
|
||||||
gchar* outerXML;
|
|
||||||
outerXML = g_strdup_printf("%s%s<xbel version=\"1.0\">\n%s%s%s</xbel>\n"
|
|
||||||
, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
||||||
, "<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML\" \"http://www.python.org/topics/xml/dtds/xbel-1.0.dtd\">\n"
|
|
||||||
, title
|
|
||||||
, desc
|
|
||||||
, g_string_free(innerXML, FALSE));
|
|
||||||
g_free(title);
|
|
||||||
g_free(desc);
|
|
||||||
return outerXML;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* xbel_folder_to_file:
|
|
||||||
* @folder: A folder.
|
|
||||||
* @file: The destination filename.
|
|
||||||
* @error: return location for a GError or %NULL
|
|
||||||
*
|
|
||||||
* Write the contents of @folder to the specified file, creating it if necessary.
|
|
||||||
*
|
|
||||||
* Return value: %TRUE on success or %FALSE when an error occured.
|
|
||||||
**/
|
|
||||||
gboolean xbel_folder_to_file(XbelItem* folder, const gchar* file, GError** error)
|
|
||||||
{
|
|
||||||
g_return_val_if_fail(file, FALSE);
|
|
||||||
gchar* data;
|
|
||||||
if(!(data = xbel_folder_to_data(folder, NULL, error)))
|
|
||||||
return FALSE;
|
|
||||||
FILE* fp;
|
|
||||||
if(!(fp = fopen(file, "w")))
|
|
||||||
{
|
|
||||||
*error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_ACCES
|
|
||||||
, "Writing failed.");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
fputs(data, fp);
|
|
||||||
fclose(fp);
|
|
||||||
g_free(data);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
158
src/xbel.h
158
src/xbel.h
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2007 Christian Dywan <christian@twotoasts.de>
|
|
||||||
|
|
||||||
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 __XBEL_H__
|
|
||||||
#define __XBEL_H__ 1
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <glib-object.h>
|
|
||||||
|
|
||||||
#define XBEL_ERROR g_quark_from_string("XBEL_ERROR")
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
XBEL_ERROR_INVALID_URI, /* Malformed uri */
|
|
||||||
XBEL_ERROR_INVALID_VALUE, /* Requested field not found */
|
|
||||||
XBEL_ERROR_URI_NOT_FOUND, /* Requested uri not found */
|
|
||||||
XBEL_ERROR_READ, /* Malformed document */
|
|
||||||
XBEL_ERROR_UNKNOWN_ENCODING, /* Parsed text was in an unknown encoding */
|
|
||||||
XBEL_ERROR_WRITE, /* Writing failed. */
|
|
||||||
} XBELError;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
XBEL_ITEM_FOLDER,
|
|
||||||
XBEL_ITEM_BOOKMARK,
|
|
||||||
XBEL_ITEM_SEPARATOR
|
|
||||||
} XbelItemKind;
|
|
||||||
|
|
||||||
// Note: This structure is entirely private.
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
guint refs;
|
|
||||||
XbelItemKind kind;
|
|
||||||
struct XbelItem* parent;
|
|
||||||
|
|
||||||
GList* items; // folder
|
|
||||||
gboolean folded; // foolder
|
|
||||||
gchar* title; // !separator
|
|
||||||
gchar* desc; // folder and bookmark
|
|
||||||
gchar* href; // bookmark
|
|
||||||
//time_t added; // !separator
|
|
||||||
//time_t modfied; // bookmark
|
|
||||||
//time_t visited; // bookmark
|
|
||||||
} XbelItem;
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_bookmark_new(void);
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_separator_new(void);
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_folder_new(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_item_ref(XbelItem*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_item_unref(XbelItem*);
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_item_copy(XbelItem*);
|
|
||||||
|
|
||||||
GType
|
|
||||||
xbel_item_get_type();
|
|
||||||
|
|
||||||
#define G_TYPE_XBEL_ITEM xbel_item_get_type()
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_folder_append_item(XbelItem*, XbelItem*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_folder_prepend_item(XbelItem*, XbelItem*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_folder_remove_item(XbelItem*, XbelItem*);
|
|
||||||
|
|
||||||
guint
|
|
||||||
xbel_folder_get_n_items(XbelItem*);
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_folder_get_nth_item(XbelItem*, guint);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_is_empty(XbelItem*);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_get_folded(XbelItem*);
|
|
||||||
|
|
||||||
XbelItemKind
|
|
||||||
xbel_item_get_kind(XbelItem*);
|
|
||||||
|
|
||||||
XbelItem*
|
|
||||||
xbel_item_get_parent(XbelItem*);
|
|
||||||
|
|
||||||
G_CONST_RETURN gchar*
|
|
||||||
xbel_item_get_title(XbelItem*);
|
|
||||||
|
|
||||||
G_CONST_RETURN gchar*
|
|
||||||
xbel_item_get_desc(XbelItem*);
|
|
||||||
|
|
||||||
G_CONST_RETURN gchar*
|
|
||||||
xbel_bookmark_get_href(XbelItem*);
|
|
||||||
|
|
||||||
/*time_t
|
|
||||||
xbel_bookmark_get_added(XbelItem*);
|
|
||||||
|
|
||||||
time_t
|
|
||||||
xbel_bookmark_get_modified(XbelItem*);
|
|
||||||
|
|
||||||
time_t
|
|
||||||
xbel_bookmark_get_visited(XbelItem*);*/
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_item_is_bookmark(XbelItem*);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_item_is_separator(XbelItem*);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_item_is_folder(XbelItem*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_folder_set_folded(XbelItem*, gboolean);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_item_set_title(XbelItem*, const gchar*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_item_set_desc(XbelItem*, const gchar*);
|
|
||||||
|
|
||||||
void
|
|
||||||
xbel_bookmark_set_href(XbelItem*, const gchar*);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_from_data(XbelItem*, const gchar*, GError**);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_from_file(XbelItem*, const gchar*, GError**);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_from_data_dirs(XbelItem*, const gchar*, gchar**, GError**);
|
|
||||||
|
|
||||||
gchar*
|
|
||||||
xbel_folder_to_data(XbelItem*, gsize*, GError**);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
xbel_folder_to_file(XbelItem*, const gchar*, GError**);
|
|
||||||
|
|
||||||
#endif /* !__XBEL_H__ */
|
|
Loading…
Reference in a new issue