Пример #1
0
static gboolean
ui_dnd_feed_drop_possible (GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data)
{
	GtkTreeModel	*model = NULL;
	GtkTreePath	*src_path = NULL;
	GtkTreeIter	iter;
	nodePtr		sourceNode, targetNode;
	
	debug1 (DEBUG_GUI, "DnD check if feed dropping is possible (%d)", dest_path);
		   	
	if (!(old_feed_drop_possible) (drag_dest, dest_path, selection_data))
		return FALSE;

	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_dest), &iter, dest_path))
		return FALSE;

	/* Try to get an iterator, if we get none it means either feed list
	   root or an "Empty" node. Both cases are fine */
	gtk_tree_model_get (GTK_TREE_MODEL (drag_dest), &iter, FS_PTR, &targetNode, -1);
	if (!targetNode)
		return TRUE;

	/* If we got an iterator it's either a possible dropping
	   candidate (a folder or source node to drop into, or a
	   iterator to insert after). In any case we have to check
	   if it is a writeable node source. */

	/* Never drop into read-only subscription node sources */
	if (!(NODE_SOURCE_TYPE (targetNode)->capabilities & NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST))
		return FALSE;

	/* never drag folders into non-hierarchic node sources */
	if (!gtk_tree_get_row_drag_data (selection_data, &model, &src_path))
		return TRUE;

	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, src_path)) {
		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, FS_PTR, &sourceNode, -1);

		g_assert (sourceNode);

		/* Never drop into another node source as this arises to many problems
		   (e.g. remote sync, different subscription type, e.g. SF #2855990) */
		if (NODE_SOURCE_TYPE (targetNode) != NODE_SOURCE_TYPE (sourceNode))
			return FALSE;

		if (IS_FOLDER(sourceNode) && !(NODE_SOURCE_TYPE (targetNode)->capabilities & NODE_SOURCE_CAPABILITY_HIERARCHIC_FEEDLIST))
			return FALSE;
	}

	gtk_tree_path_free (src_path);

	return TRUE;
}
Пример #2
0
static void
feed_list_view_selection_changed_cb (GtkTreeSelection *selection, gpointer data)
{
	GtkTreeIter		iter;
	GtkTreeModel	*model;
	nodePtr			node;

	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
	 	gtk_tree_model_get (model, &iter, FS_PTR, &node, -1);

		debug1 (DEBUG_GUI, "feed list selection changed to \"%s\"", node_get_title (node));

		/* 1.) update feed list and item list states */
		g_signal_emit_by_name (FEED_LIST_VIEW (flv), "selection-changed", node->id);

		/* 2.) Refilter the GtkTreeView to get rid of nodes with 0 unread
		   messages when in reduced mode. */
		gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (flv->filter));

		if (node) {
			gboolean allowModify = (NODE_SOURCE_TYPE (node->source->root)->capabilities & NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST);
			liferea_shell_update_update_menu ((NODE_TYPE (node)->capabilities & NODE_CAPABILITY_UPDATE) ||
			                                  (NODE_TYPE (node)->capabilities & NODE_CAPABILITY_UPDATE_CHILDS));
			liferea_shell_update_feed_menu (allowModify, TRUE, allowModify);
		} else {
			liferea_shell_update_feed_menu (TRUE, FALSE, FALSE);
		}
	} else {
		/* If we cannot get the new selection we keep the old one
		   this happens when we're doing drag&drop for example. */
	}
}
Пример #3
0
gboolean
node_can_add_child_folder (nodePtr node)
{
	g_assert (node->source->root);

	if (!(NODE_TYPE (node->source->root)->capabilities & NODE_CAPABILITY_ADD_CHILDS))
		return FALSE;

	return (NODE_SOURCE_TYPE (node)->capabilities & NODE_SOURCE_CAPABILITY_ADD_FOLDER);
}
Пример #4
0
static gboolean
feedlist_schedule_save_cb (gpointer user_data)
{
	/* step 1: request each node to save its state */
	feedlist_foreach (node_save);

	/* step 2: request saving for the root node and thereby
	   forcing the root plugin to save the feed list structure */
	NODE_SOURCE_TYPE (ROOTNODE)->source_export (ROOTNODE);
	
	feedlist->priv->saveTimer = 0;
	
	return FALSE;
}
Пример #5
0
static gboolean
aol_source_opml_subscription_prepare_update_request (subscriptionPtr subscription, struct updateRequest *request)
{
	nodePtr node = subscription->node;
	
	g_assert(node->source);
	if (node->source->loginState == NODE_SOURCE_STATE_NONE) {
		debug0 (DEBUG_UPDATE, "AolSource: login");
		aol_source_login (node->data, 0) ;
		return FALSE;
	}
	debug1 (DEBUG_UPDATE, "updating AOL Reader subscription (node id %s)", node->id);
	
	update_request_set_source (request, NODE_SOURCE_TYPE (node)->api.subscription_list);
	update_request_set_auth_value (request, node->source->authToken);
	
	return TRUE;
}
Пример #6
0
/** decides whether a feed cannot be dragged or not */
static gboolean
ui_dnd_feed_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path)
{
	GtkTreeIter	iter;
	nodePtr		node;
	
	debug1 (DEBUG_GUI, "DnD check if feed dragging is possible (%d)", path);

	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), &iter, path)) {
		gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter, FS_PTR, &node, -1);
		
		/* never drag "empty" entries or nodes of read-only subscription lists*/
		if (!node || !(NODE_SOURCE_TYPE (node->parent)->capabilities & NODE_SOURCE_CAPABILITY_WRITABLE_FEEDLIST))
			return FALSE;
		
		return TRUE;
	} else {
		g_warning ("fatal error! could not resolve tree path!");
		return FALSE;
	}
}
Пример #7
0
guint
itemset_merge_items (itemSetPtr itemSet, GList *list, gboolean allowUpdates, gboolean markAsRead)
{
	GList	*iter, *droppedItems = NULL, *items = NULL;
	guint	i, max, length, toBeDropped, newCount = 0, flagCount = 0;
	nodePtr	node;

	debug_start_measurement (DEBUG_UPDATE);

	debug2 (DEBUG_UPDATE, "old item set %p of (node id=%s):", itemSet, itemSet->nodeId);

	/* 1. Preparation: determine effective maximum cache size

	   The problem here is that the configured maximum cache
	   size might not always be sufficient. We need to check
	   border use cases in the following. */

	length = g_list_length (list);
	max = itemset_get_max_item_count (itemSet);

	/* Preload all items for flag counting and later merging comparison */
	iter = itemSet->ids;
	while (iter) {
		itemPtr item = item_load (GPOINTER_TO_UINT (iter->data));
		if (item) {
			items = g_list_append (items, item);
			if (item->flagStatus)
				flagCount++;
		}
		iter = g_list_next (iter);
	}
	debug1(DEBUG_UPDATE, "current cache size: %d", g_list_length(itemSet->ids));
	debug1(DEBUG_UPDATE, "current cache limit: %d", max);
	debug1(DEBUG_UPDATE, "downloaded feed size: %d", g_list_length(list));
	debug1(DEBUG_UPDATE, "flag count: %d", flagCount);

	/* Case #1: Avoid having too many flagged items. We count the
	   flagged items and check if they are fewer than

	      <cache limit> - <downloaded items>

	   to ensure that all new items fit in a feed cache full of
	   flagged items.

	   This handling MUST NOT be invoked when the number of items
	   is larger then the cache size, otherwise we would never
	   remove any items for large feeds. */
	if ((length < max) && (max < length + flagCount)) {
		max = flagCount + length;
		debug2 (DEBUG_UPDATE, "too many flagged items -> increasing cache limit to %u (node id=%s)", max, itemSet->nodeId);
	}

	/* 2. Avoid cache wrapping (if feed size > cache size)

	   Truncate the new itemset if it is longer than
	   the maximum cache size which could cause items
	   to be dropped and added again on subsequent
	   merges with the same feed content */
	if (length > max) {
		debug2 (DEBUG_UPDATE, "item list too long (%u, max=%u) for merging!", length, max);

		/* reach max element */
		for(i = 0, iter = list; (i < max) && iter; ++i)
			iter = g_list_next (iter);

		/* and remove all following elements */
		while (iter) {
			itemPtr item = (itemPtr) iter->data;
			debug2 (DEBUG_UPDATE, "ignoring item nr %u (%s)...", ++i, item_get_title (item));
			item_unload (item);
			iter = g_list_next (iter);
			list = g_list_remove (list, item);
		}
	}

	/* 3. Merge received items to existing item set

	   Items are given in top to bottom display order.
	   Adding them in this order would mean to reverse
	   their order in the merged list, so merging needs
	   to be done bottom to top. During this step the
	   item list (items) may exceed the cache limit. */
	iter = g_list_last (list);
	while (iter) {
		itemPtr item = (itemPtr)iter->data;

		if (markAsRead)
			item->readStatus = TRUE;

		if (itemset_merge_item (itemSet, items, item, length, allowUpdates)) {
			newCount++;
			items = g_list_prepend (items, iter->data);
		}
		iter = g_list_previous (iter);
	}
	g_list_free (list);

	vfolder_foreach (node_update_counters);

	node = node_from_id (itemSet->nodeId);
	if (node && (NODE_SOURCE_TYPE (node)->capabilities & NODE_SOURCE_CAPABILITY_ITEM_STATE_SYNC))
		node_update_counters (node);

	debug1(DEBUG_UPDATE, "added %d new items", newCount);

	/* 4. Apply cache limit for effective item set size
	      and unload older items as necessary. In this step
	      it is important never to drop flagged items and
	      to drop the oldest items only. */

	if (g_list_length (items) > max)
		toBeDropped = g_list_length (items) - max;
	else
		toBeDropped = 0;

	debug3 (DEBUG_UPDATE, "%u new items, cache limit is %u -> dropping %u items", newCount, max, toBeDropped);
	items = g_list_sort (items, itemset_sort_by_date);
	iter = g_list_last (items);
	while (iter) {
		itemPtr item = (itemPtr) iter->data;
		if (toBeDropped > 0 && !item->flagStatus) {
			debug2 (DEBUG_UPDATE, "dropping item nr %u (%s)....", item->id, item_get_title (item));
			droppedItems = g_list_append (droppedItems, item);
			/* no unloading here, it's done in itemlist_remove_items() */
			toBeDropped--;
		} else {
			item_unload (item);
		}
		iter = g_list_previous (iter);
	}

	if (droppedItems) {
		itemlist_remove_items (itemSet, droppedItems);
		g_list_free (droppedItems);
	}

	/* 5. Sanity check to detect merging bugs */
	if (g_list_length (items) > itemset_get_max_item_count (itemSet) + flagCount)
		debug0 (DEBUG_CACHE, "Fatal: Item merging bug! Resulting item list is too long! Cache limit does not work. This is a severe program bug!");

	g_list_free (items);

	debug_end_measurement (DEBUG_UPDATE, "merge itemset");

	return newCount;
}
Пример #8
0
static gboolean
itemset_merge_item (itemSetPtr itemSet, GList *items, itemPtr item, gint maxChecks, gboolean allowUpdates)
{
	gboolean	allowStateChanges = FALSE;
	gboolean	merge;
	nodePtr		node;

	debug2 (DEBUG_UPDATE, "trying to merge \"%s\" to node id \"%s\"", item_get_title (item), itemSet->nodeId);

	g_assert (itemSet->nodeId);
	node = node_from_id (itemSet->nodeId);
	if (node)
		allowStateChanges = NODE_SOURCE_TYPE (node)->capabilities & NODE_SOURCE_CAPABILITY_ITEM_STATE_SYNC;

	/* first try to merge with existing item */
	merge = itemset_generic_merge_check (items, item, maxChecks, allowUpdates, allowStateChanges);

	/* if it is a new item add it to the item set */
	if (merge) {
		g_assert (!item->nodeId);
		g_assert (!item->id);
		item->nodeId = g_strdup (itemSet->nodeId);
		if (!item->parentNodeId)
			item->parentNodeId = g_strdup (itemSet->nodeId);

		/* step 1: write item to DB */
		db_item_update (item);

		/* step 2: add to itemset */
		itemSet->ids = g_list_prepend (itemSet->ids, GUINT_TO_POINTER (item->id));

		/* step 3: trigger async enrichment of item description */
		if (node && IS_FEED (node) && ((feedPtr)node->data)->html5Extract)
			feed_enrich_item (node->subscription, item);

		debug3 (DEBUG_UPDATE, "-> added \"%s\" (id=%d) to item set %p...", item_get_title (item), item->id, itemSet);

		/* step 4: duplicate detection, mark read if it is a duplicate */
		if (item->validGuid) {
			GSList	*iter, *duplicates;

			duplicates = iter = db_item_get_duplicates (item->sourceId);
			while (iter) {
				debug1 (DEBUG_UPDATE, "-> duplicate guid exists: #%lu", GPOINTER_TO_UINT (iter->data));
				iter = g_slist_next (iter);
			}

			if (g_slist_length (duplicates) > 1) {
				item->readStatus = TRUE;	/* no unread counting... */
				item->popupStatus = FALSE;	/* no notification... */
			}

			g_slist_free (duplicates);
		}

		/* step 5: Check item for new enclosures to download */
		if (node && (((feedPtr)node->data)->encAutoDownload)) {
			GSList *iter = metadata_list_get_values (item->metadata, "enclosure");
			while (iter) {
				enclosurePtr enc = enclosure_from_string (iter->data);
				debug1 (DEBUG_UPDATE, "download enclosure (%s)", (gchar *)iter->data);
				enclosure_download (NULL, enc->url, FALSE /* non interactive */);
				iter = g_slist_next (iter);
				enclosure_free (enc);
			}
		}
	} else {
		debug2 (DEBUG_UPDATE, "-> not adding \"%s\" to node id \"%s\"...", item_get_title (item), itemSet->nodeId);
		item_unload (item);
	}

	return merge;
}