1 /*
2 * Copyright (C) 1995, 1996 Karl-Johan Johnsson.
3 */
4
5 #include "global.h"
6 #include "thread.h"
7 #include "util.h"
8 #include "widgets.h"
9 #include "../Widgets/ArtTree.h"
10
11 static ARTICLE **tagged_articles = NULL;
12 static long no_tagged = 0;
13 static long no_alloc = 0;
14
add_tag(ARTICLE * art)15 static void add_tag(ARTICLE *art)
16 {
17 if (no_alloc < no_tagged + 3) {
18 long i = no_alloc;
19
20 no_alloc = 2 * (no_alloc + 1);
21 tagged_articles =
22 (ARTICLE **)XtRealloc((char *)tagged_articles,
23 no_alloc * sizeof(ARTICLE*));
24 while (i < no_alloc)
25 tagged_articles[i++] = NULL;
26 }
27
28 tagged_articles[no_tagged++] = art;
29 art->subject->has_tagged = True;
30 update_subj_entry(art->subject);
31 }
32
remove_tag(ARTICLE * art)33 static void remove_tag(ARTICLE *art)
34 {
35 long i, n = no_tagged;
36 int found_subj = False;
37
38 for (i = 0 ; i < n ; i++) {
39 if (tagged_articles[i] == art) {
40 while (i < n) {
41 tagged_articles[i] = tagged_articles[i + 1];
42 if (tagged_articles[i] &&
43 tagged_articles[i]->subject == art->subject)
44 found_subj = True;
45 i++;
46 }
47 no_tagged--;
48 art->subject->has_tagged = found_subj;
49 if (!found_subj)
50 update_subj_entry(art->subject);
51 return;
52 }
53
54 if (tagged_articles[i]->subject == art->subject)
55 found_subj = True;
56 }
57 }
58
is_tagged(ARTICLE * art)59 static int is_tagged(ARTICLE *art)
60 {
61 ARTICLE **tagged = tagged_articles;
62 long n = no_tagged;
63
64 if (art->subject->has_tagged)
65 while (n > 0) {
66 if (*tagged == art)
67 return True;
68 tagged++;
69 n--;
70 }
71
72 return False;
73 }
74
75 /*************************************************************************/
76
mark_tagged_articles(ARTICLE * thread)77 void mark_tagged_articles(ARTICLE *thread)
78 {
79 long i, n = no_tagged;
80
81 for (i = 0 ; i < n ; i++)
82 if (tagged_articles[i]->subject->thread == thread)
83 ArtTreeNodeSetOuter(main_widgets.arttree,
84 (ART_TREE_NODE *)tagged_articles[i], True);
85 }
86
clear_tagged_articles(void)87 void clear_tagged_articles(void)
88 {
89 SUBJECT *subj;
90 ARTICLE *thread;
91 long i;
92
93 switch (global.mode) {
94 case NewsModeThread: /* clear the tree */
95 if (global.curr_subj)
96 thread = global.curr_subj->thread;
97 else if (global.curr_art)
98 thread = global.curr_art->subject->thread;
99 else
100 thread = NULL;
101
102 for (i = 0 ; i < no_tagged ; i++)
103 if (tagged_articles[i]->subject->thread == thread)
104 ArtTreeNodeSetOuter(main_widgets.arttree,
105 (ART_TREE_NODE *)tagged_articles[i],
106 False);
107
108 /* fall through */
109 case NewsModeGroup: /* clear the list */
110 for (subj = get_subjects(main_thr) ; subj ; subj = subj->next) {
111 if (subj->has_tagged) {
112 subj->has_tagged = False;
113 update_subj_entry(subj);
114 }
115 }
116 break;
117 default:
118 break;
119 }
120
121 XtFree((char *)tagged_articles);
122 tagged_articles = NULL;
123 no_tagged = 0;
124 no_alloc = 0;
125 }
126
tag_hot_articles(void)127 void tag_hot_articles(void)
128 {
129 ARTICLE *art;
130
131 for (art = get_articles(main_thr) ; art ; art = art->next)
132 if (art->from && !art->read && art->pixmap != None)
133 add_tag(art);
134 }
135
untag_article(ARTICLE * art)136 void untag_article(ARTICLE *art)
137 {
138 remove_tag(art);
139 }
140
get_tagged_articles(void)141 ARTICLE **get_tagged_articles(void)
142 {
143 return tagged_articles;
144 }
145
no_tagged_articles(void)146 long no_tagged_articles(void)
147 {
148 return no_tagged;
149 }
150
arttree_tag_callback(Widget gw,XtPointer client_data,XtPointer call_data)151 void arttree_tag_callback(Widget gw,
152 XtPointer client_data,
153 XtPointer call_data)
154 {
155 ARTICLE *art = (ARTICLE *)call_data;
156
157 if (!art)
158 XBell(display, 0);
159 else if (!art->from)
160 set_message("Can't tag a fake article!", True);
161 else {
162 int tagged = ArtTreeNodeGetOuter(main_widgets.arttree,
163 (ART_TREE_NODE *)art);
164
165 if (tagged)
166 add_tag(art);
167 else
168 remove_tag(art);
169
170 if (no_tagged == 1)
171 set_message("1 tagged article.", False);
172 else {
173 char buffer[80];
174
175 sprintf(buffer, "%ld tagged articles.", no_tagged);
176 set_message(buffer, False);
177 }
178 }
179 }
180
action_tag_subject(Widget w,XEvent * event,String * params,Cardinal * no_params)181 void action_tag_subject(Widget w, XEvent *event,
182 String *params, Cardinal *no_params)
183 {
184 ARTICLE *art;
185 int only_unread = True;
186 char message[80];
187
188 if (global.busy || (global.mode != NewsModeGroup &&
189 global.mode != NewsModeThread))
190 return;
191
192 if (!global.curr_subj) {
193 set_message("No selected subject!", True);
194 return;
195 }
196
197 if (*no_params > 0)
198 only_unread = False;
199
200 for (art = global.curr_subj->thread ; art ;
201 art = next_in_thread_preorder(art)) {
202 if (art->from && !(only_unread && art->read) &&
203 !is_tagged(art) && art->subject == global.curr_subj) {
204 add_tag(art);
205 if (global.mode == NewsModeThread)
206 ArtTreeNodeSetOuter(main_widgets.arttree,
207 (ART_TREE_NODE *)art, True);
208 }
209 }
210
211 update_subj_entry(global.curr_subj);
212
213 sprintf(message, "%ld tagged articles", no_tagged);
214 set_message(message, False);
215 }
216
action_untag_subject(Widget w,XEvent * event,String * params,Cardinal * no_params)217 void action_untag_subject(Widget w, XEvent *event,
218 String *params, Cardinal *no_params)
219 {
220 ARTICLE *art;
221 char message[80];
222
223 if (global.busy || (global.mode != NewsModeGroup &&
224 global.mode != NewsModeThread))
225 return;
226
227 if (!global.curr_subj) {
228 set_message("No selected subject!", True);
229 return;
230 }
231
232 for (art = global.curr_subj->thread ; art ;
233 art = next_in_thread_preorder(art)) {
234 if (art->from && art->subject == global.curr_subj) {
235 remove_tag(art);
236 if (global.mode == NewsModeThread)
237 ArtTreeNodeSetOuter(main_widgets.arttree,
238 (ART_TREE_NODE *)art, False);
239 }
240 }
241
242 update_subj_entry(global.curr_subj);
243
244 sprintf(message, "%ld tagged articles", no_tagged);
245 set_message(message, False);
246 }
247
action_tag_thread(Widget w,XEvent * event,String * params,Cardinal * no_params)248 void action_tag_thread(Widget w, XEvent *event,
249 String *params, Cardinal *no_params)
250 {
251 ARTICLE *art;
252 SUBJECT *subj;
253 int only_unread = True;
254 char message[80];
255
256 if (global.busy || (global.mode != NewsModeGroup &&
257 global.mode != NewsModeThread))
258 return;
259
260 if (!global.curr_subj) {
261 set_message("No selected subject!", True);
262 return;
263 }
264
265 if (*no_params > 0)
266 only_unread = False;
267
268 for (art = global.curr_subj->thread ; art ;
269 art = next_in_thread_preorder(art)) {
270 if (art->from && !(only_unread && art->read) && !is_tagged(art)) {
271 add_tag(art);
272 if (global.mode == NewsModeThread)
273 ArtTreeNodeSetOuter(main_widgets.arttree,
274 (ART_TREE_NODE *)art, True);
275 }
276 }
277
278 subj = global.curr_subj;
279 art = subj->thread;
280 while (subj->prev && subj->prev->thread == art)
281 subj = subj->prev;
282
283 while (subj && subj->thread == art) {
284 update_subj_entry(subj);
285 subj = subj->next;
286 }
287
288 sprintf(message, "%ld tagged articles", no_tagged);
289 set_message(message, False);
290 }
291
action_untag_thread(Widget w,XEvent * event,String * params,Cardinal * no_params)292 void action_untag_thread(Widget w, XEvent *event,
293 String *params, Cardinal *no_params)
294 {
295 ARTICLE *art;
296 SUBJECT *subj = global.curr_subj;
297 char message[80];
298
299 if (global.busy || (global.mode != NewsModeGroup &&
300 global.mode != NewsModeThread))
301 return;
302
303 if (!subj) {
304 set_message("No selected subject!", True);
305 return;
306 }
307
308 for (art = subj->thread ; art ; art = next_in_thread_preorder(art)) {
309 if (art->from) {
310 remove_tag(art);
311 if (global.mode == NewsModeThread)
312 ArtTreeNodeSetOuter(main_widgets.arttree,
313 (ART_TREE_NODE *)art, False);
314 }
315 }
316
317 art = subj->thread;
318 while (subj->prev && subj->prev->thread == art)
319 subj = subj->prev;
320
321 while (subj && subj->thread == art) {
322 update_subj_entry(subj);
323 subj = subj->next;
324 }
325
326 sprintf(message, "%ld tagged articles", no_tagged);
327 set_message(message, False);
328 }
329
330 /*************************************************************************/
331
332 #define HIST_SIZE 32
333
334 static ARTICLE *history[HIST_SIZE];
335 static unsigned int h_end = 0;
336 static unsigned int h_size = 0;
337
clear_history(void)338 void clear_history(void)
339 {
340 h_end = h_size = 0;
341 }
342
history_pop(void)343 ARTICLE *history_pop(void)
344 {
345 if (h_size == 0)
346 return NULL;
347
348 h_size--;
349 h_end--;
350 h_end %= HIST_SIZE;
351
352 return history[h_end];
353 }
354
history_peek(void)355 ARTICLE *history_peek(void)
356 {
357 if (h_size == 0)
358 return NULL;
359
360 return history[(h_end - 1) % HIST_SIZE];
361 }
362
history_push(ARTICLE * art)363 void history_push(ARTICLE *art)
364 {
365 if (h_size < HIST_SIZE)
366 h_size++;
367
368 history[h_end++] = art;
369 h_end %= HIST_SIZE;
370 }
371