/* * Copyright (C) 2003, 2004 Marco Pesenti Gritti * Copyright (C) 2003, 2004 Christian Persch * Copyright (C) 2004 Peter Harvey * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Id$ */ #include "config.h" #include "ephy-bookmarks-menu.h" #include "ephy-bookmarks-ui.h" #include "ephy-bookmark-action.h" #include "ephy-open-tabs-action.h" #include "ephy-topic-action.h" #include "ephy-nodes-cover.h" #include "ephy-node-common.h" #include "ephy-link.h" #include "ephy-shell.h" #include "ephy-string.h" #include "ephy-gui.h" #include "ephy-debug.h" #include #define MIN_MENU_SIZE 7 #define MAX_MENU_SIZE 21 enum { BUILD_SUBDIVIS = 1 << 0, BUILD_SUBMENUS = 1 << 1, BUILD_CHILD_SUBDIVIS = 1 << 2, BUILD_CHILD_SUBMENUS = 1 << 3 }; /* Construct a block of bookmark actions, postfixing the names with a topic id * to allow different. */ static void append_bookmarks (GString *string, const GPtrArray *bookmarks, int instance) { EphyNode *child; char name[EPHY_BOOKMARK_ACTION_NAME_BUFFER_SIZE]; long i; for (i = 0; i < bookmarks->len; i++) { child = g_ptr_array_index (bookmarks, i); EPHY_BOOKMARK_ACTION_NAME_PRINTF (name, child); g_string_append_printf (string, "", name, name, instance); } } /* Build a menu of the given bookmarks categorised by the given topics. * Shows categorisation using subdivisions, submenus, or a mix of both. */ static void append_menu (GString *string, const GPtrArray *topics, const GPtrArray *bookmarks, guint flags) { GPtrArray *uncovered; guint i, j; gboolean use_subdivis = flags & BUILD_SUBDIVIS; gboolean use_submenus = flags & BUILD_SUBMENUS; if (use_subdivis || use_submenus) { GPtrArray *subset, *covering, *subdivisions, *submenus, *unused; GArray *sizes = 0; EphyNode *topic; gint size, total; gboolean separate = FALSE; char name[EPHY_TOPIC_ACTION_NAME_BUFFER_SIZE]; /* Get the subtopics, uncovered bookmarks, and subtopic sizes. */ sizes = g_array_sized_new (FALSE, FALSE, sizeof(int), topics->len); uncovered = g_ptr_array_sized_new (bookmarks->len); covering = ephy_nodes_get_covering (topics, bookmarks, 0, uncovered, sizes); /* Preallocate arrays for submenus, subdivisions, and bookmark subsets. */ subdivisions = g_ptr_array_sized_new (topics->len); submenus = g_ptr_array_sized_new (topics->len); subset = g_ptr_array_sized_new (bookmarks->len); unused = g_ptr_array_sized_new (bookmarks->len); /* Get the total number of items in the menu. */ total = uncovered->len; for (i = 0; i < covering->len; i++) total += g_array_index (sizes, int, i); /* Seperate covering into list of submenus and subdivisions */ for (i = 0; i < covering->len; i++) { topic = g_ptr_array_index (covering, i); size = g_array_index (sizes, int, i); if (!use_submenus || (use_subdivis && (size < MIN_MENU_SIZE || total < MAX_MENU_SIZE))) { g_ptr_array_add (subdivisions, topic); } else { g_ptr_array_add (submenus, topic); total = total - size + 1; } } /* Sort the list of submenus and subdivisions. */ g_ptr_array_sort (submenus, ephy_bookmarks_compare_topic_pointers); g_ptr_array_sort (subdivisions, ephy_bookmarks_compare_topic_pointers); if (flags & BUILD_CHILD_SUBDIVIS) flags |= BUILD_SUBDIVIS; if (flags & BUILD_CHILD_SUBMENUS) flags |= BUILD_SUBMENUS; /* Create each of the submenus. */ for (i = 0; i < submenus->len; i++) { topic = g_ptr_array_index (submenus, i); ephy_nodes_get_covered (topic, bookmarks, subset); EPHY_TOPIC_ACTION_NAME_PRINTF (name, topic); g_string_append_printf (string, "", name, name); append_menu (string, topics, subset, flags); g_string_append (string, ""); separate = TRUE; } /* Build a list of bookmarks which don't appear in any subdivision yet. */ for (i = 0; i < bookmarks->len; i++) { g_ptr_array_add (unused, g_ptr_array_index (bookmarks, i)); } /* Create each of the subdivisions. */ for (i = 0; i < subdivisions->len; i++) { topic = g_ptr_array_index (subdivisions, i); ephy_nodes_get_covered (topic, unused, subset); g_ptr_array_sort (subset, ephy_bookmarks_compare_bookmark_pointers); if (separate) g_string_append (string, ""); append_bookmarks (string, subset, i+1); separate = TRUE; /* Record that each bookmark has been added. */ for (j = 0; j < subset->len; j++) { g_ptr_array_remove_fast (unused, g_ptr_array_index (subset, j)); } } g_array_free (sizes, TRUE); g_ptr_array_free (covering, TRUE); g_ptr_array_free (subdivisions, TRUE); g_ptr_array_free (submenus, TRUE); g_ptr_array_free (subset, TRUE); g_ptr_array_free (unused, TRUE); if (separate && uncovered->len) g_string_append (string, ""); } else { uncovered = g_ptr_array_sized_new (bookmarks->len); for (i = 0; i < bookmarks->len; i++) g_ptr_array_add (uncovered, g_ptr_array_index (bookmarks, i)); g_ptr_array_sort (uncovered, ephy_bookmarks_compare_bookmark_pointers); } /* Create the final subdivision (uncovered bookmarks). */ g_ptr_array_sort (uncovered, ephy_bookmarks_compare_bookmark_pointers); append_bookmarks (string, uncovered, 0); g_ptr_array_free (uncovered, TRUE); } void ephy_bookmarks_menu_build (GString *string, EphyNode *parent) { GPtrArray *children, *topics; EphyBookmarks *eb; EphyNode *node; gint priority; guint flags, id, i; eb = ephy_shell_get_bookmarks (ephy_shell); children = ephy_node_get_children (ephy_bookmarks_get_keywords (eb)); topics = g_ptr_array_sized_new (children->len); for (i = 0; i < children->len; i++) { node = g_ptr_array_index (children, i); priority = ephy_node_get_property_int (node, EPHY_NODE_KEYWORD_PROP_PRIORITY); if (priority == EPHY_NODE_NORMAL_PRIORITY) g_ptr_array_add (topics, node); } /* If no parent was supplied, use the default 'All' */ node = parent ? parent : ephy_bookmarks_get_bookmarks(eb); children = ephy_node_get_children (node); /* Determine what kind of menu we want. */ id = ephy_node_get_id (node); switch(id) { case FAVORITES_NODE_ID: flags = 0; break; case BOOKMARKS_NODE_ID: flags = BUILD_SUBMENUS | BUILD_CHILD_SUBDIVIS; break; default: flags = BUILD_SUBMENUS | BUILD_SUBDIVIS | BUILD_CHILD_SUBDIVIS; /* flags = BUILD_SUBDIVIS; */ break; } /* If this menu is the 'All' menu, be sure to include the 'local' topic. */ if (id == BOOKMARKS_NODE_ID) { EphyNode *local_node; local_node = ephy_bookmarks_get_local (eb); if (local_node != NULL) { g_ptr_array_add (topics, ephy_bookmarks_get_local (eb)); } append_menu (string, topics, children, flags); g_ptr_array_free (topics, TRUE); } /* Otherwise, build the menu with "Open in tabs". */ else { char name[EPHY_OPEN_TABS_ACTION_NAME_BUFFER_SIZE]; append_menu (string, topics, children, flags); g_ptr_array_free (topics, TRUE); if (children->len > 1) { EPHY_OPEN_TABS_ACTION_NAME_PRINTF (name, node); g_string_append_printf (string, "", name); } } }