streamline headers
[apps/madmutt.git] / flags.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <lib-ui/lib-ui.h>
13 #include <lib-ui/menu.h>
14 #include <lib-mx/mx.h>
15
16 #include "mutt.h"
17 #include "sort.h"
18
19 #include <imap/imap_private.h>
20
21
22 void _mutt_set_flag (CONTEXT * ctx, HEADER * h, int flag, int bf, int upd_ctx)
23 {
24   int changed = h->changed;
25   int deleted = ctx->deleted;
26   int tagged = ctx->tagged;
27   int flagged = ctx->flagged;
28
29   if (ctx->readonly && flag != M_TAG)
30     return;                     /* don't modify anything if we are read-only */
31
32   switch (flag) {
33   case M_DELETE:
34
35     if (!mx_acl_check (ctx, ACL_DELETE))
36       return;
37
38     if (bf) {
39       if (!h->deleted && !ctx->readonly) {
40         h->deleted = 1;
41         if (upd_ctx)
42           ctx->deleted++;
43         /* deleted messages aren't treated as changed elsewhere so that the
44          * purge-on-sync option works correctly. This isn't applicable here */
45         if (ctx && ctx->magic == M_IMAP) {
46           h->changed = 1;
47           if (upd_ctx)
48             ctx->changed = 1;
49         }
50       }
51     }
52     else if (h->deleted) {
53       h->deleted = 0;
54       if (upd_ctx) {
55         ctx->deleted--;
56         if (h->appended)
57           ctx->appended--;
58       }
59       h->appended = 0;          /* when undeleting, also reset the appended flag */
60       /* see my comment above */
61       if (ctx->magic == M_IMAP) {
62         h->changed = 1;
63         if (upd_ctx)
64           ctx->changed = 1;
65       }
66       /* 
67        * If the user undeletes a message which is marked as
68        * "trash" in the maildir folder on disk, the folder has
69        * been changed, and is marked accordingly.  However, we do
70        * _not_ mark the message itself changed, because trashing
71        * is checked in specific code in the maildir folder
72        * driver. 
73        */
74       if (ctx->magic == M_MAILDIR && upd_ctx && h->trash)
75         ctx->changed = 1;
76     }
77     break;
78
79   case M_APPENDED:
80     if (bf) {
81       if (!h->appended) {
82         h->appended = 1;
83         if (upd_ctx)
84           ctx->appended++;
85       }
86     }
87     break;
88
89   case M_PURGED:
90     if (bf) {
91       if (!h->purged)
92         h->purged = 1;
93     }
94     else if (h->purged)
95       h->purged = 0;
96     break;
97
98   case M_NEW:
99
100     if (!mx_acl_check (ctx, ACL_SEEN))
101       return;
102
103     if (bf) {
104       if (h->read || h->old) {
105         h->old = 0;
106         if (upd_ctx)
107           ctx->new++;
108         if (h->read) {
109           h->read = 0;
110           if (upd_ctx)
111             ctx->unread++;
112         }
113         h->changed = 1;
114         if (upd_ctx)
115           ctx->changed = 1;
116       }
117     }
118     else if (!h->read) {
119       if (!h->old)
120         if (upd_ctx)
121           ctx->new--;
122       h->read = 1;
123       if (upd_ctx)
124         ctx->unread--;
125       h->changed = 1;
126       if (upd_ctx)
127         ctx->changed = 1;
128     }
129     break;
130
131   case M_OLD:
132
133     if (!mx_acl_check (ctx, ACL_SEEN))
134       return;
135
136     if (bf) {
137       if (!h->old) {
138         h->old = 1;
139         if (!h->read)
140           if (upd_ctx)
141             ctx->new--;
142         h->changed = 1;
143         if (upd_ctx)
144           ctx->changed = 1;
145       }
146     }
147     else if (h->old) {
148       h->old = 0;
149       if (!h->read)
150         if (upd_ctx)
151           ctx->new++;
152       h->changed = 1;
153       if (upd_ctx)
154         ctx->changed = 1;
155     }
156     break;
157
158   case M_READ:
159
160     if (!mx_acl_check (ctx, ACL_SEEN))
161       return;
162
163     if (bf) {
164       if (!h->read) {
165         h->read = 1;
166         if (upd_ctx)
167           ctx->unread--;
168         if (!h->old)
169           if (upd_ctx)
170             ctx->new--;
171         h->changed = 1;
172         if (upd_ctx)
173           ctx->changed = 1;
174       }
175     }
176     else if (h->read) {
177       h->read = 0;
178       if (upd_ctx)
179         ctx->unread++;
180       if (!h->old)
181         if (upd_ctx)
182           ctx->new++;
183       h->changed = 1;
184       if (upd_ctx)
185         ctx->changed = 1;
186     }
187     break;
188
189   case M_REPLIED:
190
191     if (!mx_acl_check (ctx, ACL_WRITE))
192       return;
193
194     if (bf) {
195       if (!h->replied) {
196         h->replied = 1;
197         if (!h->read) {
198           h->read = 1;
199           if (upd_ctx)
200             ctx->unread--;
201           if (!h->old)
202             if (upd_ctx)
203               ctx->new--;
204         }
205         h->changed = 1;
206         if (upd_ctx)
207           ctx->changed = 1;
208       }
209     }
210     else if (h->replied) {
211       h->replied = 0;
212       h->changed = 1;
213       if (upd_ctx)
214         ctx->changed = 1;
215     }
216     break;
217
218   case M_FLAG:
219
220     if (!mx_acl_check (ctx, ACL_WRITE))
221       return;
222
223     if (bf) {
224       if (!h->flagged) {
225         h->flagged = bf;
226         if (upd_ctx)
227           ctx->flagged++;
228         h->changed = 1;
229         if (upd_ctx)
230           ctx->changed = 1;
231       }
232     }
233     else if (h->flagged) {
234       h->flagged = 0;
235       if (upd_ctx)
236         ctx->flagged--;
237       h->changed = 1;
238       if (upd_ctx)
239         ctx->changed = 1;
240     }
241     break;
242
243   case M_TAG:
244     if (bf) {
245       if (!h->tagged) {
246         h->tagged = 1;
247         if (upd_ctx)
248           ctx->tagged++;
249       }
250     }
251     else if (h->tagged) {
252       h->tagged = 0;
253       if (upd_ctx)
254         ctx->tagged--;
255     }
256     break;
257   }
258
259   mutt_set_header_color (ctx, h);
260
261   /* if the message status has changed, we need to invalidate the cached
262    * search results so that any future search will match the current status
263    * of this message and not what it was at the time it was last searched.
264    */
265   if (h->searched && (changed != h->changed || deleted != ctx->deleted ||
266                       tagged != ctx->tagged || flagged != ctx->flagged))
267     h->searched = 0;
268   sidebar_draw ();
269 }
270
271 void mutt_tag_set_flag (int flag, int bf)
272 {
273   int j;
274
275   for (j = 0; j < Context->vcount; j++)
276     if (Context->hdrs[Context->v2r[j]]->tagged)
277       mutt_set_flag (Context, Context->hdrs[Context->v2r[j]], flag, bf);
278 }
279 int mutt_thread_set_flag (HEADER * hdr, int flag, int bf, int subthread)
280 {
281   THREAD *start, *cur = hdr->thread;
282
283   if ((Sort & SORT_MASK) != SORT_THREADS) {
284     mutt_error _("Threading is not enabled.");
285
286     return (-1);
287   }
288
289   if (!subthread)
290     while (cur->parent)
291       cur = cur->parent;
292   start = cur;
293
294   if (cur->message)
295     mutt_set_flag (Context, cur->message, flag, bf);
296
297   if ((cur = cur->child) == NULL)
298     return (0);
299
300   for (;;) {
301     if (cur->message)
302       mutt_set_flag (Context, cur->message, flag, bf);
303
304     if (cur->child)
305       cur = cur->child;
306     else if (cur->next)
307       cur = cur->next;
308     else {
309       while (!cur->next) {
310         cur = cur->parent;
311         if (cur == start)
312           return (0);
313       }
314       cur = cur->next;
315     }
316   }
317   /* not reached */
318 }
319
320 int mutt_change_flag(HEADER * h, int bf)
321 {
322     char buf[STRING];
323     const char letters[] = "dDnNoOrR*!";
324     static char const actions[] = {
325         ['d'] = M_DELETE,       ['D'] = M_DELETE,
326         ['n'] = M_NEW,          ['N'] = M_NEW,
327         ['o'] = M_OLD,          ['O'] = M_OLD,
328         ['r'] = M_REPLIED,      ['R'] = M_REPLIED,
329         ['*'] = M_TAG,
330         ['!'] = M_FLAG,
331     };
332
333     int c;
334
335     snprintf(buf, sizeof(buf), "%s? (D/N/O/r/*/!): ",
336              bf ? _("Set flag") : _("Clear flag"));
337     c = mutt_multi_choice(buf, letters);
338     if (c <= 0)
339         return -1;
340     c = letters[c - 1];
341     if (actions[c] == M_OLD) {
342         if (h) {
343             mutt_set_flag(Context, h, M_READ, !bf);
344         } else {
345             mutt_tag_set_flag(M_READ, !bf);
346         }
347     }
348
349     if (h) {
350         mutt_set_flag(Context, h, actions[c], bf);
351     } else {
352         mutt_tag_set_flag(actions[c], bf);
353     }
354
355     return 0;
356 }