static void feed_list_node_check_if_folder_is_empty (const gchar *nodeId) { GtkTreeIter *iter; int count; debug1 (DEBUG_GUI, "folder empty check for node id \"%s\"", nodeId); /* this function does two things: 1. add "(empty)" entry to an empty folder 2. remove an "(empty)" entry from a non empty folder (this state is possible after a drag&drop action) */ iter = feed_list_node_to_iter (nodeId); if (!iter) return; count = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (feedstore), iter); /* case 1 */ if (0 == count) { feed_list_node_add_empty_node (iter); return; } if (1 == count) return; /* else we could have case 2 */ feed_list_node_remove_empty_node (iter); }
void feed_list_node_add (nodePtr node) { gint position; GtkTreeIter *iter, *parentIter = NULL; debug2 (DEBUG_GUI, "adding node \"%s\" as child of parent=\"%s\"", node_get_title(node), (NULL != node->parent)?node_get_title(node->parent):"feed list root"); g_assert (NULL != node->parent); g_assert (NULL == feed_list_node_to_iter (node->id)); /* if parent is NULL we have the root folder and don't create a new row! */ iter = (GtkTreeIter *)g_new0 (GtkTreeIter, 1); /* if reduced feedlist, show flat treeview */ if (feedlist_reduced_unread) parentIter = NULL; else if (node->parent != feedlist_get_root ()) parentIter = feed_list_node_to_iter (node->parent->id); position = g_slist_index (node->parent->children, node); if (feedlist_reduced_unread || position < 0) gtk_tree_store_append (feedstore, iter, parentIter); else gtk_tree_store_insert (feedstore, iter, parentIter, position); gtk_tree_store_set (feedstore, iter, FS_PTR, node, -1); feed_list_node_add_iter (node->id, iter); feed_list_node_update (node->id); if (node->parent != feedlist_get_root ()) feed_list_node_check_if_folder_is_empty (node->parent->id); if (IS_FOLDER (node)) feed_list_node_check_if_folder_is_empty (node->id); }
gboolean feed_list_node_is_expanded (const gchar *nodeId) { GtkTreeIter *iter; gboolean expanded = FALSE; if (feedlist_reduced_unread) return FALSE; iter = feed_list_node_to_iter (nodeId); if (iter) { GtkTreeView *treeview = GTK_TREE_VIEW (liferea_shell_lookup ("feedlist")); GtkTreePath *path = gtk_tree_model_get_path (gtk_tree_view_get_model (treeview), iter); expanded = gtk_tree_view_row_expanded (treeview, path); gtk_tree_path_free (path); } return expanded; }
void feed_list_node_set_expansion (nodePtr folder, gboolean expanded) { GtkTreeIter *iter; GtkTreePath *path; GtkTreeView *treeview; if (feedlist_reduced_unread) return; iter = feed_list_node_to_iter (folder->id); if (!iter) return; treeview = GTK_TREE_VIEW (liferea_shell_lookup ("feedlist")); path = gtk_tree_model_get_path (gtk_tree_view_get_model (treeview), iter); if (expanded) gtk_tree_view_expand_row (treeview, path, FALSE); else gtk_tree_view_collapse_row (treeview, path); gtk_tree_path_free (path); }
void feed_list_node_remove_node (nodePtr node) { GtkTreeIter *iter; gboolean parentExpanded = FALSE; iter = feed_list_node_to_iter (node->id); if (!iter) return; /* must be tolerant because of DnD handling */ if (node->parent) parentExpanded = feed_list_node_is_expanded (node->parent->id); /* If the folder becomes empty, the folder would collapse */ gtk_tree_store_remove (feedstore, iter); g_hash_table_remove (flIterHash, node->id); if (node->parent) { feed_list_node_check_if_folder_is_empty (node->parent->id); if (parentExpanded) feed_list_node_set_expansion (node->parent, TRUE); feed_list_node_update (node->parent->id); } }
void feed_list_node_update (const gchar *nodeId) { GtkTreeIter *iter; gchar *label, *count = NULL; guint labeltype; nodePtr node; static gchar *countColor = NULL; node = node_from_id (nodeId); iter = feed_list_node_to_iter (nodeId); if (!iter) return; /* Initialize unread item color Pango CSS */ if (!countColor) { const gchar *bg = NULL, *fg = NULL; bg = render_get_theme_color ("FEEDLIST_UNREAD_BG"); fg = render_get_theme_color ("FEEDLIST_UNREAD_FG"); if (fg && bg) { countColor = g_strdup_printf ("foreground='#%s' background='#%s'", fg, bg); debug1 (DEBUG_HTML, "Feed list unread CSS: %s\n", countColor); } } labeltype = NODE_TYPE (node)->capabilities; labeltype &= (NODE_CAPABILITY_SHOW_UNREAD_COUNT | NODE_CAPABILITY_SHOW_ITEM_COUNT); if (node->unreadCount == 0 && (labeltype & NODE_CAPABILITY_SHOW_UNREAD_COUNT)) labeltype -= NODE_CAPABILITY_SHOW_UNREAD_COUNT; label = g_markup_escape_text (node_get_title (node), -1); switch (labeltype) { case NODE_CAPABILITY_SHOW_UNREAD_COUNT | NODE_CAPABILITY_SHOW_ITEM_COUNT: /* treat like show unread count */ case NODE_CAPABILITY_SHOW_UNREAD_COUNT: count = g_strdup_printf ("<span weight='bold' %s> %u </span>", countColor?countColor:"", node->unreadCount); break; case NODE_CAPABILITY_SHOW_ITEM_COUNT: count = g_strdup_printf ("<span weight='bold' %s> %u </span>", countColor?countColor:"", node->itemCount); break; default: break; } /* Extra message for search folder rebuilds */ if (IS_VFOLDER (node) && node->data) { if (((vfolderPtr)node->data)->reloading) { gchar *tmp = label; label = g_strdup_printf (_("%s\n<i>Rebuilding</i>"), label); g_free (tmp); } } gtk_tree_store_set (feedstore, iter, FS_LABEL, label, FS_UNREAD, node->unreadCount, FS_ICON, node->available?node_get_icon (node):icon_get (ICON_UNAVAILABLE), FS_COUNT, count, -1); g_free (label); if (node->parent) feed_list_node_update (node->parent->id); }
static gboolean ui_dnd_feed_drag_data_received (GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData *selection_data) { GtkTreeIter iter, iter2, parentIter; nodePtr node, oldParent, newParent; gboolean result, valid, added; gint oldPos, pos; result = old_feed_drag_data_received (drag_dest, dest, selection_data); if (result) { if (gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_dest), &iter, dest)) { gtk_tree_model_get (GTK_TREE_MODEL (drag_dest), &iter, FS_PTR, &node, -1); /* If we don't do anything, then because DnD is implemented by removal and re-insertion, and the removed node is selected, the treeview selects the next row after the removal, which is supremely irritating. But setting a selection at this point is pointless, because the treeview will reset it as soon as the DnD callback returns. Instead, we set the cursor, which controls where treeview resets the selection later. */ gtk_tree_view_set_cursor(GTK_TREE_VIEW (liferea_shell_lookup ("feedlist")), dest, NULL, FALSE); /* remove from old parents child list */ oldParent = node->parent; g_assert (oldParent); oldPos = g_slist_index (oldParent->children, node); oldParent->children = g_slist_remove (oldParent->children, node); node_update_counters (oldParent); if (0 == g_slist_length (oldParent->children)) feed_list_node_add_empty_node (feed_list_node_to_iter (oldParent->id)); /* and rebuild new parents child list */ if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (drag_dest), &parentIter, &iter)) { gtk_tree_model_get (GTK_TREE_MODEL (drag_dest), &parentIter, FS_PTR, &newParent, -1); } else { gtk_tree_model_get_iter_first (GTK_TREE_MODEL (drag_dest), &parentIter); newParent = feedlist_get_root (); } /* drop old list... */ debug3 (DEBUG_GUI, "old parent is %s (%d, position=%d)", oldParent->title, g_slist_length (oldParent->children), oldPos); debug2 (DEBUG_GUI, "new parent is %s (%d)", newParent->title, g_slist_length (newParent->children)); g_slist_free (newParent->children); newParent->children = NULL; node->parent = newParent; debug0 (DEBUG_GUI, "new newParent child list:"); /* and rebuild it from the tree model */ if (feedlist_get_root() != newParent) valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (drag_dest), &iter2, &parentIter); else valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (drag_dest), &iter2, NULL); pos = 0; added = FALSE; while (valid) { nodePtr child; gtk_tree_model_get (GTK_TREE_MODEL (drag_dest), &iter2, FS_PTR, &child, -1); if (child) { /* Well this is a bit complicated... If we move a feed inside a folder we need to skip the old insertion point (oldPos). This is easy if the feed is added behind this position. If it is dropped before the flag added is set once the new copy is encountered. The remaining copy is skipped automatically when the flag is set. */ /* check if this is a copy of the dragged node or the original itself */ if ((newParent == oldParent) && !strcmp(node->id, child->id)) { if ((pos == oldPos) || added) { /* it is the original */ debug2 (DEBUG_GUI, " -> %d: skipping old insertion point %s", pos, child->title); } else { /* it is a copy inserted before the original */ added = TRUE; debug2 (DEBUG_GUI, " -> %d: new insertion point of %s", pos, child->title); newParent->children = g_slist_append (newParent->children, child); } } else { /* all other nodes */ debug2 (DEBUG_GUI, " -> %d: adding %s", pos, child->title); newParent->children = g_slist_append (newParent->children, child); } } else { debug0 (DEBUG_GUI, " -> removing empty node"); /* remove possible existing "(empty)" node from newParent */ feed_list_node_remove_empty_node (&parentIter); } valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (drag_dest), &iter2); pos++; } db_node_update (node); node_update_counters (newParent); feedlist_schedule_save (); } } return result; }