1 /**
2  * @file item_state.c   item state controller
3  *
4  * Copyright (C) 2007-2014 Lars Windolf <lars.windolf@gmx.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 
21 #include "db.h"
22 #include "debug.h"
23 #include "feedlist.h"
24 #include "item.h"
25 #include "item_state.h"
26 #include "itemset.h"
27 #include "itemlist.h"
28 #include "node.h"
29 #include "vfolder.h"
30 #include "fl_sources/node_source.h"
31 
32 static void
item_state_set_recount_flag(nodePtr node)33 item_state_set_recount_flag (nodePtr node)
34 {
35 	node->needsRecount = TRUE;
36 }
37 
38 void
item_set_flag_state(itemPtr item,gboolean newState)39 item_set_flag_state (itemPtr item, gboolean newState)
40 {
41 	if (newState == item->flagStatus)
42 		return;
43 
44 	node_source_item_set_flag (node_from_id (item->nodeId), item, newState);
45 }
46 
47 void
item_flag_state_changed(itemPtr item,gboolean newState)48 item_flag_state_changed (itemPtr item, gboolean newState)
49 {
50 	/* 1. set value in memory */
51 	item->flagStatus = newState;
52 
53 	/* 2. save state to DB */
54 	db_item_state_update (item);
55 
56 	/* 3. update vfolder counters */
57 	vfolder_foreach (node_update_counters);
58 
59 	/* 4. update item list GUI state */
60 	itemlist_update_item (item);
61 
62 	/* no duplicate state propagation to avoid copies
63 	   in the "Important" search folder */
64 }
65 
66 void
item_set_read_state(itemPtr item,gboolean newState)67 item_set_read_state (itemPtr item, gboolean newState)
68 {
69 	/* Read and update state are coupled insofar as they
70 	   are changed by the same user actions. So we do something
71 	   here if either the read state has changed or the
72 	   updated flag is set (which is always just reset). */
73 
74 	if (newState == item->readStatus && !item->updateStatus)
75 		return;
76 
77 	node_source_item_mark_read (node_from_id (item->nodeId), item, newState);
78 }
79 
80 void
item_read_state_changed(itemPtr item,gboolean newState)81 item_read_state_changed (itemPtr item, gboolean newState)
82 {
83 	nodePtr node;
84 
85 	debug_start_measurement (DEBUG_GUI);
86 
87 	/* 1. set values in memory */
88 	item->readStatus = newState;
89 	item->updateStatus = FALSE;
90 
91 	/* 2. apply to DB */
92 	db_item_state_update (item);
93 
94 	/* 3. propagate to vfolders */
95 	vfolder_foreach (node_update_counters);
96 
97 	/* 4. update item list GUI state */
98 	itemlist_update_item (item);
99 
100 	/* 5. updated feed list unread counters */
101 	node = node_from_id (item->nodeId);
102 	node_update_counters (node);
103 
104 	/* 6. duplicate state propagation */
105 	if (item->validGuid) {
106 		GSList *duplicates, *iter;
107 
108 		duplicates = iter = db_item_get_duplicates (item->sourceId);
109 		while (iter) {
110 			itemPtr duplicate = item_load (GPOINTER_TO_UINT (iter->data));
111 
112 			/* The check on node_from_id() is an evil workaround
113 			   to handle "lost" items in the DB that have no
114 			   associated node in the feed list. This should be
115 			   fixed by having the feed list in the DB too, so
116 			   we can clean up correctly after crashes. */
117 			if (duplicate && duplicate->id != item->id && node_from_id (duplicate->nodeId)) {
118 				item_set_read_state (duplicate, newState);
119 			}
120 			if (duplicate) item_unload (duplicate);
121 			iter = g_slist_next (iter);
122 		}
123 		g_slist_free (duplicates);
124 	}
125 
126 	debug_end_measurement (DEBUG_GUI, "set read status");
127 }
128 
129 /**
130  * In difference to all the other item state handling methods
131  * item_state_set_all_read does not immediately apply the
132  * changes to the GUI because it is usually called recursively
133  * and would be to slow. Instead the node structure flag for
134  * recounting is set. By calling feedlist_update() afterwards
135  * those recounts are executed and applied to the GUI.
136  */
137 void
itemset_mark_read(nodePtr node)138 itemset_mark_read (nodePtr node)
139 {
140 	itemSetPtr	itemSet;
141 
142 	itemSet = node_get_itemset (node);
143 	GList *iter = itemSet->ids;
144 	while (iter) {
145 		gulong id = GPOINTER_TO_UINT (iter->data);
146 		itemPtr item = item_load (id);
147 		if (item) {
148 			if (!item->readStatus) {
149 				nodePtr node = node_from_id (item->nodeId);
150 				if (node) {
151 					item_state_set_recount_flag (node);
152 					node_source_item_mark_read (node, item, TRUE);
153 				}
154 
155 				debug_start_measurement (DEBUG_GUI);
156 
157 				GSList *duplicates = db_item_get_duplicate_nodes (item->sourceId);
158 				GSList *duplicate = duplicates;
159 				while (duplicate) {
160 					gchar *nodeId = (gchar *)duplicate->data;
161 					nodePtr affectedNode = node_from_id (nodeId);
162 					if (affectedNode)
163 						item_state_set_recount_flag (affectedNode);
164 					g_free (nodeId);
165 					duplicate = g_slist_next (duplicate);
166 				}
167 				g_slist_free(duplicates);
168 
169 				debug_end_measurement (DEBUG_GUI, "mark read of duplicates");
170 			}
171 			item_unload (item);
172 		}
173 		iter = g_list_next (iter);
174 	}
175 
176 	// FIXME: why not call itemset_free (itemSet); here? Crashes!
177 }
178 
179 void
item_state_set_all_popup(const gchar * nodeId)180 item_state_set_all_popup (const gchar *nodeId)
181 {
182 	db_itemset_mark_all_popup (nodeId);
183 
184 	/* No GUI updating necessary... */
185 }
186