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