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