Implement the bookmarks panel with editing capabilities.

The bookmarks panel can display a tree of bookmarks and allows
editing and deleting of bookmarks via a context menu.

The XBEL implementation has been altered to work with a
reference count model to allow panels to update on their own.
This commit is contained in:
Christian Dywan 2007-12-31 23:49:22 +01:00
parent cd8b1e5b22
commit b6f248f344
7 changed files with 384 additions and 41 deletions

View file

@ -414,8 +414,7 @@ void on_menu_tabsClosed_item_activate(GtkWidget* menuitem, CBrowser* browser)
const gchar* uri = xbel_bookmark_get_href(item); const gchar* uri = 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_folder_remove_item(tabtrash, item); xbel_item_unref(item);
xbel_item_free(item);
update_browser_actions(curBrowser); 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); const gchar* uri = 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_folder_remove_item(tabtrash, item); xbel_item_unref(item);
xbel_item_free(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_free(tabtrash); xbel_item_unref(tabtrash);
tabtrash = xbel_folder_new(); tabtrash = xbel_folder_new();
update_browser_actions(browser); update_browser_actions(browser);
} }
@ -494,7 +492,10 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
GtkWidget* entry_title = gtk_entry_new(); GtkWidget* entry_title = gtk_entry_new();
gtk_entry_set_activates_default(GTK_ENTRY(entry_title), TRUE); gtk_entry_set_activates_default(GTK_ENTRY(entry_title), TRUE);
if(!newBookmark) 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_box_pack_start(GTK_BOX(hbox), entry_title, 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);
@ -507,30 +508,38 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
GtkWidget* entry_desc = gtk_entry_new(); GtkWidget* entry_desc = gtk_entry_new();
gtk_entry_set_activates_default(GTK_ENTRY(entry_desc), TRUE); gtk_entry_set_activates_default(GTK_ENTRY(entry_desc), TRUE);
if(!newBookmark) 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_box_pack_start(GTK_BOX(hbox), entry_desc, 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);
hbox = gtk_hbox_new(FALSE, 8); GtkWidget* entry_uri = NULL;
gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); if(xbel_item_is_bookmark(bookmark))
label = gtk_label_new_with_mnemonic("_Uri:"); {
gtk_size_group_add_widget(sizegroup, label); hbox = gtk_hbox_new(FALSE, 8);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
GtkWidget* entry_uri = gtk_entry_new(); label = gtk_label_new_with_mnemonic("_Uri:");
gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE); gtk_size_group_add_widget(sizegroup, label);
if(!newBookmark) gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_entry_set_text(GTK_ENTRY(entry_uri), xbel_bookmark_get_href(bookmark)); entry_uri = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), entry_uri, TRUE, TRUE, 0); gtk_entry_set_activates_default(GTK_ENTRY(entry_uri), TRUE);
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); if(!newBookmark)
gtk_widget_show_all(hbox); 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); 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))); 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_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 // FIXME: We want to choose a folder
if(newBookmark) if(newBookmark)
@ -539,6 +548,230 @@ static void browser_editBookmark_dialog_new(XbelItem* bookmark, CBrowser* browse
gtk_widget_destroy(dialog); 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", "<i>Separator</i>", 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 create_bookmark_menu(XbelItem*, GtkWidget*, CBrowser*);
static void on_bookmark_menu_folder_activate(GtkWidget* menuitem, CBrowser* browser) 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(); menuitem = gtk_separator_menu_item_new();
gtk_widget_show(menuitem); gtk_widget_show(menuitem);
gtk_menu_shell_append(GTK_MENU_SHELL(browser->menu_bookmarks), 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( browser->menu_window = gtk_menu_item_get_submenu(
GTK_MENU_ITEM(gtk_ui_manager_get_widget(ui_manager, "/menubar/Window"))); GTK_MENU_ITEM(gtk_ui_manager_get_widget(ui_manager, "/menubar/Window")));
menuitem = gtk_separator_menu_item_new(); menuitem = gtk_separator_menu_item_new();
@ -1142,6 +1377,40 @@ CBrowser* browser_new(CBrowser* oldBrowser)
// Dummy: This is the "fallback" panel for now // Dummy: This is the "fallback" panel for now
page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook) page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook)
, gtk_label_new("empty"), NULL); , 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 // Pageholder
browser->panel_pageholder = webView_new(&scrolled); browser->panel_pageholder = webView_new(&scrolled);
page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook) page = gtk_notebook_append_page(GTK_NOTEBOOK(browser->panels_notebook)
@ -1242,6 +1511,7 @@ CBrowser* browser_new(CBrowser* oldBrowser)
browser->actiongroup = oldBrowser->actiongroup; browser->actiongroup = oldBrowser->actiongroup;
browser->menubar = oldBrowser->menubar; browser->menubar = oldBrowser->menubar;
browser->menu_bookmarks = oldBrowser->menu_bookmarks; browser->menu_bookmarks = oldBrowser->menu_bookmarks;
browser->popup_bookmark = oldBrowser->popup_bookmark;
browser->menu_window = oldBrowser->menu_window; browser->menu_window = oldBrowser->menu_window;
browser->popup_webView = oldBrowser->popup_webView; browser->popup_webView = oldBrowser->popup_webView;
browser->popup_element = oldBrowser->popup_element; browser->popup_element = oldBrowser->popup_element;

View file

@ -26,6 +26,7 @@ typedef struct _CBrowser
// menus // menus
GtkWidget* menubar; GtkWidget* menubar;
GtkWidget* menu_bookmarks; GtkWidget* menu_bookmarks;
GtkWidget* popup_bookmark;
GtkWidget* menu_window; GtkWidget* menu_window;
GtkWidget* popup_webView; GtkWidget* popup_webView;
GtkWidget* popup_element; GtkWidget* popup_element;
@ -42,6 +43,7 @@ typedef struct _CBrowser
// panels // panels
GtkWidget* panels; GtkWidget* panels;
GtkWidget* panels_notebook; GtkWidget* panels_notebook;
GtkWidget* panel_bookmarks;
GtkWidget* panel_pageholder; GtkWidget* panel_pageholder;
GtkWidget* webViews; GtkWidget* webViews;
// findbox // findbox
@ -203,6 +205,21 @@ on_action_link_saveWith_activate(GtkAction*, CBrowser*);
void void
on_action_link_copy_activate(GtkAction*, CBrowser*); 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 void
on_menu_bookmarks_item_activate(GtkWidget*, CBrowser*); on_menu_bookmarks_item_activate(GtkWidget*, CBrowser*);
@ -438,6 +455,21 @@ static const GtkActionEntry entries[] = {
{ "BookmarksManage", STOCK_BOOKMARKS { "BookmarksManage", STOCK_BOOKMARKS
, "_Manage Bookmarks", "<Ctrl>b" , "_Manage Bookmarks", "<Ctrl>b"
, "hm?", NULL/*G_CALLBACK(on_action_bookmarks_manage_activate)*/ }, , "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" }, { "Tools", NULL, "_Tools" },

View file

@ -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_free(bookmarks); xbel_item_unref(bookmarks);
xbel_item_free(_session); xbel_item_unref(_session);
g_string_free(errorMessages, TRUE); g_string_free(errorMessages, TRUE);
return 0; return 0;
} }
@ -254,7 +254,7 @@ int main(int argc, char** argv)
browser = browser_new(browser); browser = browser_new(browser);
webView_open(browser->webView, xbel_bookmark_get_href(item)); webView_open(browser->webView, xbel_bookmark_get_href(item));
} }
xbel_item_free(_session); xbel_item_unref(_session);
gtk_main(); gtk_main();
@ -277,7 +277,7 @@ int main(int argc, char** argv)
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_free(bookmarks); 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;
@ -286,7 +286,7 @@ int main(int argc, char** argv)
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_free(tabtrash); xbel_item_unref(tabtrash);
g_free(configFile); g_free(configFile);
if(config->startup == CONFIG_STARTUP_SESSION) if(config->startup == CONFIG_STARTUP_SESSION)
{ {
@ -299,7 +299,7 @@ int main(int argc, char** argv)
} }
g_free(configFile); g_free(configFile);
} }
xbel_item_free(session); 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))

View file

@ -166,6 +166,14 @@ static const gchar* ui_markup =
"<toolitem action='PanelExtensions'/>" "<toolitem action='PanelExtensions'/>"
"<toolitem action='PanelConsole'/>" "<toolitem action='PanelConsole'/>"
"</toolbar>" "</toolbar>"
"<popup name='popup_bookmark'>"
"<menuitem action='BookmarkOpen'/>"
"<menuitem action='BookmarkOpenTab'/>"
"<menuitem action='BookmarkOpenWindow'/>"
"<separator/>"
"<menuitem action='BookmarkEdit'/>"
"<menuitem action='BookmarkDelete'/>"
"</popup>"
"<popup name='popup_webView'>" "<popup name='popup_webView'>"
"<menuitem action='Back'/>" "<menuitem action='Back'/>"
"<menuitem action='Forward'/>" "<menuitem action='Forward'/>"

View file

@ -292,12 +292,18 @@ void on_webView_destroy(GtkWidget* widget, CBrowser* browser)
browsers = g_list_delete_link(browsers, tmp); browsers = g_list_delete_link(browsers, tmp);
g_free(browser->elementUri); g_free(browser->elementUri);
g_free(browser->statusMessage); 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->actiongroup);
g_object_unref(browser->popup_bookmark);
g_object_unref(browser->popup_webView); g_object_unref(browser->popup_webView);
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 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(); gtk_main_quit();
} }
} }
@ -340,12 +346,11 @@ void webView_close(GtkWidget* webView, CBrowser* browser)
if(n > 10) if(n > 10)
{ {
XbelItem* item = xbel_folder_get_nth_item(tabtrash, n - 1); XbelItem* item = xbel_folder_get_nth_item(tabtrash, n - 1);
xbel_folder_remove_item(tabtrash, item); xbel_item_unref(item);
xbel_item_free(item);
} }
} }
else else
xbel_item_free(browser->sessionItem); 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));

View file

@ -38,6 +38,7 @@
static XbelItem* xbel_item_new(XbelItemKind kind) static XbelItem* xbel_item_new(XbelItemKind kind)
{ {
XbelItem* item = g_new(XbelItem, 1); XbelItem* item = g_new(XbelItem, 1);
item->refs = 1;
item->parent = NULL; item->parent = NULL;
item->kind = kind; item->kind = kind;
if(kind == XBEL_ITEM_FOLDER) 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 * @item: a valid item
* *
* Free an XbelItem. If @item is a folder all of its children will also * Ref an XbelItem.
* be freed automatically.
* *
* 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(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)) if(xbel_item_is_folder(item))
{ {
guint n = xbel_folder_get_n_items(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); XbelItem* _item = xbel_folder_get_nth_item(item, i);
_item->parent = NULL; _item->parent = NULL;
xbel_item_free(_item); xbel_item_unref(_item);
} }
g_list_free(item->items); g_list_free(item->items);
} }
@ -260,7 +284,7 @@ void xbel_item_free(XbelItem* item)
* *
* Copy an XbelItem. * Copy an XbelItem.
* *
* The returned item must be freed eventually. * The returned item must be unreffed eventually.
* *
* Return value: a copy of @item * Return value: a copy of @item
**/ **/

View file

@ -37,6 +37,7 @@ typedef enum
// Note: This structure is entirely private. // Note: This structure is entirely private.
typedef struct typedef struct
{ {
guint refs;
XbelItemKind kind; XbelItemKind kind;
struct XbelItem* parent; struct XbelItem* parent;
@ -60,7 +61,10 @@ XbelItem*
xbel_folder_new(void); xbel_folder_new(void);
void void
xbel_item_free(XbelItem*); xbel_item_ref(XbelItem*);
void
xbel_item_unref(XbelItem*);
XbelItem* XbelItem*
xbel_item_copy(XbelItem*); xbel_item_copy(XbelItem*);