diff --git a/src/browser.c b/src/browser.c index 66a29d40..1e0a9d99 100644 --- a/src/browser.c +++ b/src/browser.c @@ -414,8 +414,7 @@ void on_menu_tabsClosed_item_activate(GtkWidget* menuitem, CBrowser* browser) const gchar* uri = xbel_bookmark_get_href(item); CBrowser* curBrowser = browser_new(browser); webView_open(curBrowser->webView, uri); - xbel_folder_remove_item(tabtrash, item); - xbel_item_free(item); + xbel_item_unref(item); update_browser_actions(curBrowser); } @@ -426,15 +425,14 @@ void on_action_tabsClosed_undo_activate(GtkAction* action, CBrowser* browser) const gchar* uri = xbel_bookmark_get_href(item); CBrowser* curBrowser = browser_new(browser); webView_open(curBrowser->webView, uri); - xbel_folder_remove_item(tabtrash, item); - xbel_item_free(item); + xbel_item_unref(item); update_browser_actions(curBrowser); } void on_action_tabsClosed_clear_activate(GtkAction* action, CBrowser* browser) { // Clear the closed tabs list - xbel_item_free(tabtrash); + xbel_item_unref(tabtrash); tabtrash = xbel_folder_new(); update_browser_actions(browser); } @@ -494,11 +492,14 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse GtkWidget* entry_title = gtk_entry_new(); gtk_entry_set_activates_default(GTK_ENTRY(entry_title), TRUE); if(!newBookmark) - gtk_entry_set_text(GTK_ENTRY(entry_title), xbel_item_get_title(bookmark)); + { + const gchar* title = xbel_item_get_title(bookmark); + gtk_entry_set_text(GTK_ENTRY(entry_title), title ? title : ""); + } gtk_box_pack_start(GTK_BOX(hbox), entry_title, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); gtk_widget_show_all(hbox); - + hbox = gtk_hbox_new(FALSE, 8); gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); label = gtk_label_new_with_mnemonic("_Description:"); @@ -507,30 +508,38 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse GtkWidget* entry_desc = gtk_entry_new(); gtk_entry_set_activates_default(GTK_ENTRY(entry_desc), TRUE); if(!newBookmark) - gtk_entry_set_text(GTK_ENTRY(entry_desc), xbel_item_get_desc(bookmark)); + { + const gchar* desc = xbel_item_get_desc(bookmark); + gtk_entry_set_text(GTK_ENTRY(entry_desc), desc ? desc : ""); + } gtk_box_pack_start(GTK_BOX(hbox), entry_desc, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); gtk_widget_show_all(hbox); - - hbox = gtk_hbox_new(FALSE, 8); - gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); - label = gtk_label_new_with_mnemonic("_Uri:"); - gtk_size_group_add_widget(sizegroup, label); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); - GtkWidget* entry_uri = gtk_entry_new(); - gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE); - if(!newBookmark) - gtk_entry_set_text(GTK_ENTRY(entry_uri), xbel_bookmark_get_href(bookmark)); - gtk_box_pack_start(GTK_BOX(hbox), entry_uri, TRUE, TRUE, 0); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); - gtk_widget_show_all(hbox); + + GtkWidget* entry_uri = NULL; + if(xbel_item_is_bookmark(bookmark)) + { + hbox = gtk_hbox_new(FALSE, 8); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); + label = gtk_label_new_with_mnemonic("_Uri:"); + gtk_size_group_add_widget(sizegroup, label); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + entry_uri = gtk_entry_new(); + gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE); + if(!newBookmark) + gtk_entry_set_text(GTK_ENTRY(entry_uri), xbel_bookmark_get_href(bookmark)); + gtk_box_pack_start(GTK_BOX(hbox), entry_uri, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); + gtk_widget_show_all(hbox); + } gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { 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))); - xbel_bookmark_set_href(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_uri))); + if(xbel_item_is_bookmark(bookmark)) + xbel_bookmark_set_href(bookmark, gtk_entry_get_text(GTK_ENTRY(entry_uri))); // FIXME: We want to choose a folder if(newBookmark) @@ -539,6 +548,230 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse gtk_widget_destroy(dialog); } +static void on_panel_bookmarks_row_activated(GtkTreeView* treeview + , GtkTreePath* path, GtkTreeViewColumn* column, CBrowser* browser) +{ + GtkTreeModel* model = gtk_tree_view_get_model(treeview); + GtkTreeIter iter; + if(gtk_tree_model_get_iter(model, &iter, path)) + { + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(xbel_item_is_bookmark(item)) + webView_open(get_nth_webView(-1, browser), xbel_bookmark_get_href(item)); + } +} + +static void panel_bookmarks_popup(GtkWidget* widget, GdkEventButton* event + , XbelItem* item, CBrowser* browser) +{ + gboolean isBookmark = xbel_item_is_bookmark(item); + gboolean isSeparator = xbel_item_is_separator(item); + + action_set_sensitive("BookmarkOpen", isBookmark, browser); + action_set_sensitive("BookmarkOpenTab", isBookmark, browser); + action_set_sensitive("BookmarkOpenWindow", isBookmark, browser); + + action_set_sensitive("BookmarkEdit", !isSeparator, browser); + + sokoke_widget_popup(widget, GTK_MENU(browser->popup_bookmark), event); +} + +static gboolean on_panel_bookmarks_button_release(GtkWidget* widget + , GdkEventButton* event, CBrowser* browser) +{ + if(event->button != 2 && event->button != 3) + return FALSE; + + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(event->button == 2 && xbel_item_is_bookmark(item)) + { + CBrowser* newBrowser = browser_new(browser); + const gchar* uri = xbel_bookmark_get_href(item); + webView_open(newBrowser->webView, uri); + } + else + panel_bookmarks_popup(widget, event, item, browser); + return TRUE; + } + return FALSE; +} + +void on_panel_bookmarks_popup(GtkWidget* widget, CBrowser* browser) +{ + GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + panel_bookmarks_popup(widget, NULL, item, browser); + } +} + +void on_action_bookmarkOpen_activate(GtkAction* action, CBrowser* browser) +{ + GtkTreeView* treeview = GTK_TREE_VIEW(browser->panel_bookmarks); + GtkTreeSelection* selection = gtk_tree_view_get_selection(treeview); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(xbel_item_is_bookmark(item)) + webView_open(get_nth_webView(-1, browser), xbel_bookmark_get_href(item)); + } +} + +void on_action_bookmarkOpenTab_activate(GtkAction* action, CBrowser* browser) +{ + GtkTreeView* treeview = GTK_TREE_VIEW(browser->panel_bookmarks); + GtkTreeSelection* selection = gtk_tree_view_get_selection(treeview); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(xbel_item_is_bookmark(item)) + { + CBrowser* newBrowser = browser_new(browser); + const gchar* uri = xbel_bookmark_get_href(item); + webView_open(newBrowser->webView, uri); + } + } +} + +void on_action_bookmarkOpenWindow_activate(GtkAction* action, CBrowser* browser) +{ + GtkTreeView* treeview = GTK_TREE_VIEW(browser->panel_bookmarks); + GtkTreeSelection* selection = gtk_tree_view_get_selection(treeview); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(xbel_item_is_bookmark(item)) + { + CBrowser* newBrowser = browser_new(NULL); + const gchar* uri = xbel_bookmark_get_href(item); + webView_open(newBrowser->webView, uri); + } + } +} + +void on_action_bookmarkEdit_activate(GtkAction* action, CBrowser* browser) +{ + GtkTreeView* treeview = GTK_TREE_VIEW(browser->panel_bookmarks); + GtkTreeSelection* selection = gtk_tree_view_get_selection(treeview); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + if(!xbel_item_is_separator(item)) + browser_editBookmark_dialog_new(item, browser); + } +} + +void on_action_bookmarkDelete_activate(GtkAction* action, CBrowser* browser) +{ + GtkTreeView* treeview = GTK_TREE_VIEW(browser->panel_bookmarks); + GtkTreeSelection* selection = gtk_tree_view_get_selection(treeview); + if(selection) + { + GtkTreeModel* model; + GtkTreeIter iter; + gtk_tree_selection_get_selected(selection, &model, &iter); + XbelItem* item; + gtk_tree_model_get(model, &iter, 0, &item, -1); + XbelItem* parent = xbel_item_get_parent(item); + xbel_folder_remove_item(parent, item); + xbel_item_unref(item); + } +} + +static void tree_store_insert_folder(GtkTreeStore* treestore, GtkTreeIter* parent + , XbelItem* folder) +{ + guint n = xbel_folder_get_n_items(folder); + guint i; + for(i = 0; i < n; i++) + { + XbelItem* item = xbel_folder_get_nth_item(folder, i); + GtkTreeIter iter; + gtk_tree_store_insert_with_values(treestore, &iter, parent, n, 0, item, -1); + xbel_item_ref(item); + if(xbel_item_is_folder(item)) + tree_store_insert_folder(treestore, &iter, item); + } +} + +static void on_bookmarks_item_render_icon(GtkTreeViewColumn* column + , GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter + , GtkWidget* treeview) +{ + XbelItem* item; + gtk_tree_model_get(model, iter, 0, &item, -1); + + if(!xbel_item_get_parent(item)) + { + gtk_tree_store_remove(GTK_TREE_STORE(model), iter); + xbel_item_unref(item); + return; + } + + // TODO: Would it be better to not do this on every redraw? + GdkPixbuf* pixbuf = NULL; + if(xbel_item_is_bookmark(item)) + pixbuf = gtk_widget_render_icon(treeview, STOCK_BOOKMARK + , GTK_ICON_SIZE_MENU, NULL); + else if(xbel_item_is_folder(item)) + pixbuf = gtk_widget_render_icon(treeview, GTK_STOCK_DIRECTORY + , GTK_ICON_SIZE_MENU, NULL); + g_object_set(renderer, "pixbuf", pixbuf, NULL); + if(pixbuf) + g_object_unref(pixbuf); +} + +static void on_bookmarks_item_render_text(GtkTreeViewColumn* column + , GtkCellRenderer* renderer, GtkTreeModel* model, GtkTreeIter* iter + , GtkWidget* treeview) +{ + XbelItem* item; + gtk_tree_model_get(model, iter, 0, &item, -1); + + if(!xbel_item_get_parent(item)) + { + gtk_tree_store_remove(GTK_TREE_STORE(model), iter); + xbel_item_unref(item); + return; + } + + if(xbel_item_is_separator(item)) + g_object_set(renderer + , "markup", "Separator", NULL); + else + g_object_set(renderer + , "markup", NULL, "text", xbel_item_get_title(item), NULL); +} + static void create_bookmark_menu(XbelItem*, GtkWidget*, CBrowser*); static void on_bookmark_menu_folder_activate(GtkWidget* menuitem, CBrowser* browser) @@ -972,6 +1205,8 @@ CBrowser* browser_new(CBrowser* oldBrowser) menuitem = gtk_separator_menu_item_new(); gtk_widget_show(menuitem); gtk_menu_shell_append(GTK_MENU_SHELL(browser->menu_bookmarks), menuitem); + browser->popup_bookmark = gtk_ui_manager_get_widget(ui_manager, "/popup_bookmark"); + g_object_ref(browser->popup_bookmark); browser->menu_window = gtk_menu_item_get_submenu( GTK_MENU_ITEM(gtk_ui_manager_get_widget(ui_manager, "/menubar/Window"))); menuitem = gtk_separator_menu_item_new(); @@ -1142,6 +1377,40 @@ CBrowser* browser_new(CBrowser* oldBrowser) // Dummy: This is the "fallback" panel for now page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook) , gtk_label_new("empty"), NULL); + // Bookmarks + GtkTreeViewColumn* column; + GtkCellRenderer* renderer_text; GtkCellRenderer* renderer_pixbuf; + GtkTreeStore* treestore = gtk_tree_store_new(1, G_TYPE_XBEL_ITEM); + GtkWidget* treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + column = gtk_tree_view_column_new(); + renderer_pixbuf = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer_pixbuf, FALSE); + gtk_tree_view_column_set_cell_data_func(column, renderer_pixbuf + , (GtkTreeCellDataFunc)on_bookmarks_item_render_icon, treeview, NULL); + renderer_text = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer_text, FALSE); + gtk_tree_view_column_set_cell_data_func(column, renderer_text + , (GtkTreeCellDataFunc)on_bookmarks_item_render_text, treeview, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + GtkWidget* scrolled = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled) + , GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrolled), treeview); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); + tree_store_insert_folder(GTK_TREE_STORE(treestore), NULL, bookmarks); + g_object_unref(treestore); + g_signal_connect(treeview, "row-activated" + , G_CALLBACK(on_panel_bookmarks_row_activated), browser); + g_signal_connect(treeview, "button-release-event" + , G_CALLBACK(on_panel_bookmarks_button_release), browser); + g_signal_connect(treeview, "popup-menu" + , G_CALLBACK(on_panel_bookmarks_popup), browser); + browser->panel_bookmarks = treeview; + page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook) + , scrolled, NULL); + action = gtk_action_group_get_action(browser->actiongroup, "PanelBookmarks"); + g_object_set_data(G_OBJECT(action), "iPage", GINT_TO_POINTER(page)); // Pageholder browser->panel_pageholder = webView_new(&scrolled); page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook) @@ -1242,6 +1511,7 @@ CBrowser* browser_new(CBrowser* oldBrowser) browser->actiongroup = oldBrowser->actiongroup; browser->menubar = oldBrowser->menubar; browser->menu_bookmarks = oldBrowser->menu_bookmarks; + browser->popup_bookmark = oldBrowser->popup_bookmark; browser->menu_window = oldBrowser->menu_window; browser->popup_webView = oldBrowser->popup_webView; browser->popup_element = oldBrowser->popup_element; diff --git a/src/browser.h b/src/browser.h index 0d08b352..f25cebd2 100644 --- a/src/browser.h +++ b/src/browser.h @@ -26,6 +26,7 @@ typedef struct _CBrowser // menus GtkWidget* menubar; GtkWidget* menu_bookmarks; + GtkWidget* popup_bookmark; GtkWidget* menu_window; GtkWidget* popup_webView; GtkWidget* popup_element; @@ -42,6 +43,7 @@ typedef struct _CBrowser // panels GtkWidget* panels; GtkWidget* panels_notebook; + GtkWidget* panel_bookmarks; GtkWidget* panel_pageholder; GtkWidget* webViews; // findbox @@ -203,6 +205,21 @@ on_action_link_saveWith_activate(GtkAction*, CBrowser*); void on_action_link_copy_activate(GtkAction*, CBrowser*); +void +on_action_bookmarkOpen_activate(GtkAction*, CBrowser*); + +void +on_action_bookmarkOpenTab_activate(GtkAction*, CBrowser*); + +void +on_action_bookmarkOpenWindow_activate(GtkAction*, CBrowser*); + +void +on_action_bookmarkEdit_activate(GtkAction*, CBrowser*); + +void +on_action_bookmarkDelete_activate(GtkAction*, CBrowser*); + void on_menu_bookmarks_item_activate(GtkWidget*, CBrowser*); @@ -438,6 +455,21 @@ static const GtkActionEntry entries[] = { { "BookmarksManage", STOCK_BOOKMARKS , "_Manage Bookmarks", "b" , "hm?", NULL/*G_CALLBACK(on_action_bookmarks_manage_activate)*/ }, + { "BookmarkOpen", GTK_STOCK_OPEN + , NULL, "" + , "hm?", G_CALLBACK(on_action_bookmarkOpen_activate) }, + { "BookmarkOpenTab", STOCK_TAB_NEW + , "Open in New _Tab", "" + , "hm?", G_CALLBACK(on_action_bookmarkOpenTab_activate) }, + { "BookmarkOpenWindow", STOCK_WINDOW_NEW + , "Open in New _Window", "" + , "hm?", G_CALLBACK(on_action_bookmarkOpenWindow_activate) }, + { "BookmarkEdit", GTK_STOCK_EDIT + , NULL, "" + , "hm?", G_CALLBACK(on_action_bookmarkEdit_activate) }, + { "BookmarkDelete", GTK_STOCK_DELETE + , NULL, "" + , "hm?", G_CALLBACK(on_action_bookmarkDelete_activate) }, { "Tools", NULL, "_Tools" }, diff --git a/src/main.c b/src/main.c index e23eece8..1edd038e 100755 --- a/src/main.c +++ b/src/main.c @@ -204,8 +204,8 @@ int main(int argc, char** argv) { config_free(config); search_engines_free(searchEngines); - xbel_item_free(bookmarks); - xbel_item_free(_session); + xbel_item_unref(bookmarks); + xbel_item_unref(_session); g_string_free(errorMessages, TRUE); return 0; } @@ -254,7 +254,7 @@ int main(int argc, char** argv) browser = browser_new(browser); webView_open(browser->webView, xbel_bookmark_get_href(item)); } - xbel_item_free(_session); + xbel_item_unref(_session); gtk_main(); @@ -277,7 +277,7 @@ int main(int argc, char** argv) g_warning("Bookmarks couldn't be saved. %s", error->message); g_error_free(error); } - xbel_item_free(bookmarks); + xbel_item_unref(bookmarks); g_free(configFile); configFile = g_build_filename(configPath, "tabtrash.xbel", NULL); error = NULL; @@ -286,7 +286,7 @@ int main(int argc, char** argv) g_warning("Tabtrash couldn't be saved. %s", error->message); g_error_free(error); } - xbel_item_free(tabtrash); + xbel_item_unref(tabtrash); g_free(configFile); if(config->startup == CONFIG_STARTUP_SESSION) { @@ -299,7 +299,7 @@ int main(int argc, char** argv) } g_free(configFile); } - xbel_item_free(session); + xbel_item_unref(session); configFile = g_build_filename(configPath, "config", NULL); error = NULL; if(!config_to_file(config, configFile, &error)) diff --git a/src/ui.h b/src/ui.h index e57b7139..a5832109 100644 --- a/src/ui.h +++ b/src/ui.h @@ -166,6 +166,14 @@ static const gchar* ui_markup = "" "" "" + "" + "" + "" + "" + "" + "" + "" + "" "" "" "" diff --git a/src/webView.c b/src/webView.c index 03845218..24a2809b 100644 --- a/src/webView.c +++ b/src/webView.c @@ -292,12 +292,18 @@ void on_webView_destroy(GtkWidget* widget, CBrowser* browser) browsers = g_list_delete_link(browsers, tmp); g_free(browser->elementUri); g_free(browser->statusMessage); - if(!g_list_length(browsers)) + // FIXME: Multiple windows are not taken into account + if(!g_list_nth(browsers, 0)) { g_object_unref(browser->actiongroup); + g_object_unref(browser->popup_bookmark); g_object_unref(browser->popup_webView); g_object_unref(browser->popup_element); g_object_unref(browser->popup_editable); + guint i; + guint n = xbel_folder_get_n_items(bookmarks); + for(i = 0; i < n; i++) + xbel_item_unref(xbel_folder_get_nth_item(bookmarks, i)); gtk_main_quit(); } } @@ -340,12 +346,11 @@ void webView_close(GtkWidget* webView, CBrowser* browser) if(n > 10) { XbelItem* item = xbel_folder_get_nth_item(tabtrash, n - 1); - xbel_folder_remove_item(tabtrash, item); - xbel_item_free(item); + xbel_item_unref(item); } } else - xbel_item_free(browser->sessionItem); + xbel_item_unref(browser->sessionItem); gtk_widget_destroy(browser->webView_menu); gtk_notebook_remove_page(GTK_NOTEBOOK(browser->webViews) , get_webView_index(webView, browser)); diff --git a/src/xbel.c b/src/xbel.c index f788e4ca..07b625da 100644 --- a/src/xbel.c +++ b/src/xbel.c @@ -38,6 +38,7 @@ 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) @@ -220,18 +221,41 @@ static gboolean xbel_folder_from_xmlDocPtr(XbelItem* folder, xmlDocPtr doc) } /** - * xbel_item_free: + * xbel_item_ref: * @item: a valid item * - * Free an XbelItem. If @item is a folder all of its children will also - * be freed automatically. + * Ref an XbelItem. * - * The item must not be contained in a folder or attempting to free it will fail. + * Ref means that the reference count is increased by one. + * + * This has no effect on children of a folder. **/ -void xbel_item_free(XbelItem* item) +void xbel_item_ref(XbelItem* item) { g_return_if_fail(item); - g_return_if_fail(!xbel_item_get_parent(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); @@ -240,7 +264,7 @@ void xbel_item_free(XbelItem* item) { XbelItem* _item = xbel_folder_get_nth_item(item, i); _item->parent = NULL; - xbel_item_free(_item); + xbel_item_unref(_item); } g_list_free(item->items); } @@ -260,7 +284,7 @@ void xbel_item_free(XbelItem* item) * * Copy an XbelItem. * - * The returned item must be freed eventually. + * The returned item must be unreffed eventually. * * Return value: a copy of @item **/ diff --git a/src/xbel.h b/src/xbel.h index cf593ea0..2fb6bf40 100644 --- a/src/xbel.h +++ b/src/xbel.h @@ -37,6 +37,7 @@ typedef enum // Note: This structure is entirely private. typedef struct { + guint refs; XbelItemKind kind; struct XbelItem* parent; @@ -60,7 +61,10 @@ XbelItem* xbel_folder_new(void); void -xbel_item_free(XbelItem*); +xbel_item_ref(XbelItem*); + +void +xbel_item_unref(XbelItem*); XbelItem* xbel_item_copy(XbelItem*);