workaround a stupid issue in how decoding is performed in mutt *sigh*
[apps/madmutt.git] / lib-ui / sidebar.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) ????-2004 Justin Hibbits <jrh29@po.cwru.edu>
4  * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com>
5  *
6  * Parts were written/modified by:
7  * Rocco Rutte <pdmef@cs.tu-berlin.de>
8  * Nico Golde <nico@ngolde.de>
9  *
10  * This file is part of mutt-ng, see http://www.muttng.org/.
11  * It's licensed under the GNU General Public License,
12  * please see the file GPL in the top level source directory.
13  */
14
15 #include <lib-ui/lib-ui.h>
16 #include <lib-ui/menu.h>
17
18 #include <libgen.h>
19
20 #include "mutt.h"
21 #include "charset.h"
22 #include "buffy.h"
23 #include "keymap.h"
24
25 static int CurBuffy = 0;
26
27 /* computes first entry to be shown */
28 static int calc_boundaries(void)
29 {
30     if (CurBuffy < 0 || CurBuffy >= Incoming.len)
31         CurBuffy = 0;
32
33     if (option(OPTSIDEBARNEWMAILONLY)) {
34         int n = 0;
35
36         for (int i = 0; i < CurBuffy; i++) {
37             n += Incoming.arr[i]->new > 0;
38         }
39
40         n %= (LINES - 3);
41         if (!n)
42             return CurBuffy;
43
44         for (int i = CurBuffy - 1; i >= 0; i--) {
45             if (Incoming.arr[i]->new > 0 && --n <= 0) {
46                 return i;
47             }
48         }
49
50         return 0;
51     } else {
52         return CurBuffy - (CurBuffy % (LINES - 3));
53     }
54 }
55
56 static void shortened_hierarchy(char *dst, char *hbox, int maxlen)
57 {
58     int dots = 0, last_dot = 0, len = m_strlen(hbox);
59
60     if (m_strisempty(SidebarBoundary)) {
61         memcpy(dst, hbox, MIN(len, maxlen));
62         return;
63     }
64
65     for (int i = 0; i < len; ++i) {
66         if (strchr(SidebarBoundary, hbox[i])) {
67             ++dots;
68             last_dot = i;
69         }
70     }
71
72     if (dots == 0) {
73         memcpy(dst, hbox, MIN(len, maxlen));
74         return;
75     }
76
77     dst[0] = hbox[0];
78     for (int i = 1, j = 1; i < len && j < maxlen - 1; ++i) {
79         if (!strchr(SidebarBoundary, hbox[i]))
80             continue;
81
82         if (len - i <= maxlen - j) {
83             memcpy(dst, hbox + i, len - i);
84             return;
85         }
86
87         if (i == last_dot) {
88             memcpy(dst, hbox + i, maxlen - j);
89             return;
90         }
91
92         dst[j++] = hbox[i];
93         dst[j++] = hbox[i + 1];
94     }
95 }
96
97 static const char *
98 sidebar_number_format(char* dest, ssize_t destlen,
99                       char op, const char* src, const char* fmt,
100                       const char* ifstr, const char* elstr,
101                       anytype data, format_flag flags)
102 {
103   char tmp[STRING];
104   BUFFY* b = Incoming.arr[data.i];
105   int opt = flags & M_FORMAT_OPTIONAL;
106   int c = Context && !m_strcmp(Context->path, b->path);
107
108   switch (op) {
109     /* deleted */
110     case 'd':
111       if (!opt) {
112         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
113         snprintf (dest, destlen, tmp, c ? Context->deleted : 0);
114       } else if ((c && Context->deleted == 0) || !c)
115         opt = 0;
116       break;
117     /* flagged */
118     case 'F':
119     case 'f':                   /* for compatibility */
120       if (!opt) {
121         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
122         snprintf (dest, destlen, tmp, c ? Context->flagged : b->msg_flagged);
123       } else if ((c && Context->flagged == 0) || (!c && b->msg_flagged == 0))
124         opt = 0;
125       break;
126     /* total */
127     case 'c':                   /* for compatibility */
128     case 'm':
129       if (!opt) {
130         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
131         snprintf (dest, destlen, tmp, c ? Context->msgcount : b->msgcount);
132       } else if ((c && Context->msgcount == 0) || (!c && b->msgcount == 0))
133         opt = 0;
134       break;
135     /* total shown, i.e. not hidden by limit */
136     case 'M':
137       if (!opt) {
138         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
139         snprintf (dest, destlen, tmp, c ? Context->vcount : 0);
140       } else if ((c && Context->vcount == 0) || !c)
141         opt = 0;
142       break;
143     /* new */
144     case 'n':
145       if (!opt) {
146         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
147         snprintf (dest, destlen, tmp, c ? Context->new : b->new);
148       } else if ((c && Context->new == 0) || (!c && b->new == 0))
149         opt = 0;
150       break;
151     /* unread */
152     case 'u':
153       if (!opt) {
154         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
155         snprintf (dest, destlen, tmp, c ? Context->unread : b->msg_unread);
156       } else if ((c && Context->unread == 0) || (!c && b->msg_unread == 0))
157         opt = 0;
158       break;
159     /* tagged */
160     case 't':
161       if (!opt) {
162         snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
163         snprintf (dest, destlen, tmp, c ? Context->tagged : 0);
164       } else if ((c && Context->tagged == 0) || !c)
165         opt = 0;
166       break;
167   }
168
169   if (flags & M_FORMAT_OPTIONAL)
170     m_strformat(dest, destlen, 0, opt ? ifstr : elstr,
171                 sidebar_number_format, data, flags);
172   return src;
173 }
174
175 /* print single item
176  * returns:
177  *      0       item was not printed ('cause of $sidebar_newmail_only)
178  *      1       item was printed
179  */
180 static int make_sidebar_entry(WINDOW *sw, char *sbox, int idx, ssize_t len)
181 {
182     int lencnt = 0;
183     char no[STRING], entry[STRING];
184     int l_m = m_strlen(Maildir);
185
186     if (option(OPTSIDEBARNEWMAILONLY) && sbox && Context && Context->path
187     &&  m_strcmp(Context->path, sbox) && Incoming.arr[idx]->new == 0
188     &&  idx != CurBuffy)
189         return 0;
190
191     m_strformat(no, sizeof(no), len, SidebarNumberFormat,
192                 sidebar_number_format, idx, 0);
193     lencnt = m_strlen(no);
194
195     if (l_m > 0 && m_strncmp(sbox, Maildir, l_m) == 0 && m_strlen(sbox) > l_m)
196     {
197         sbox += l_m;
198         if (Maildir[strlen(Maildir) - 1] != '/') {
199             sbox += 1;
200         }
201     } else {
202         sbox = basename(sbox);
203     }
204
205     snprintf(entry, sizeof(entry), "%*s", (int)len, no);
206     if (option(OPTSHORTENHIERARCHY) && m_strlen(sbox) > len - lencnt - 1) {
207         shortened_hierarchy(entry, sbox, len - lencnt - 1);
208     } else {
209         memcpy(entry, sbox, MIN(len - lencnt - 1, m_strlen(sbox)));
210     }
211     waddnstr(sw, entry, len);
212
213     return 1;
214 }
215
216 /* returns folder name of currently 
217  * selected folder for <sidebar-open>
218  */
219 const char *sidebar_get_current(void)
220 {
221     if (0 <= CurBuffy && CurBuffy < Incoming.len)
222         return Incoming.arr[CurBuffy]->path;
223     return NULL;
224 }
225
226 /* internally sets item to buf */
227 void sidebar_set_current(const char* buf)
228 {
229     int i = buffy_lookup(buf);
230     if (i >= 0) {
231         CurBuffy = i;
232     }
233 }
234
235 /* fix counters for a context
236  * FIXME since ctx must not be of our business, move it elsewhere
237  */
238 void sidebar_set_buffystats(CONTEXT *curContext)
239 {
240     int i = 0;
241     BUFFY *tmp;
242
243     if (!curContext || (i = buffy_lookup(curContext->path)) < 0)
244         return;
245     tmp = Incoming.arr[i];
246     tmp->new         = curContext->new;
247     tmp->msg_unread  = curContext->unread;
248     tmp->msgcount    = curContext->msgcount;
249     tmp->msg_flagged = curContext->flagged;
250 }
251
252 int sidebar_draw(void)
253 {
254     static short prev_show_value = -1;
255     int x, y, line;
256     WINDOW *sw;
257
258     /* initialize first time */
259     if (prev_show_value != option(OPTMBOXPANE)) {
260         if ((prev_show_value = option(OPTMBOXPANE))) {
261             /* after toggle: force recounting of all mail */
262             buffy_check(2);
263         }
264     }
265
266     sw = ui_layout_sidebar_w();
267     if (!sw)
268         return 0;
269     getmaxyx(sw, y, x);
270
271     SETCOLOR(sw, MT_COLOR_SIDEBAR);
272     mvwhline(sw, 0, 0, ACS_HLINE, x - 1);
273     mvwaddch(sw, 0, x - 1, ACS_TTEE);
274
275     line = 1;
276     for (int i = calc_boundaries(); i < Incoming.len && line < y - 1; i++) {
277         BUFFY *tmp = Incoming.arr[i];
278
279         if (i == CurBuffy)
280             SETCOLOR(sw, MT_COLOR_INDICATOR);
281         else if (tmp->new > 0)
282             SETCOLOR(sw, MT_COLOR_NEW);
283         else if (tmp->msg_flagged > 0)
284             SETCOLOR(sw, MT_COLOR_FLAGGED);
285         else
286             SETCOLOR(sw, MT_COLOR_NORMAL);
287
288         if (make_sidebar_entry(sw, tmp->path, i, x - 1)) {
289             SETCOLOR(sw, MT_COLOR_SIDEBAR);
290             waddch(sw, ACS_VLINE);
291             line++;
292         }
293     }
294
295     while (line < y - 1) {
296         SETCOLOR(sw, MT_COLOR_NORMAL);
297         whline(sw, ' ', x - 1);
298         SETCOLOR(sw, MT_COLOR_SIDEBAR);
299         mvwaddch(sw, line++, x - 1, ACS_VLINE);
300     }
301
302     SETCOLOR(sw, MT_COLOR_SIDEBAR);
303     mvwhline(sw, y - 1, 0, ACS_HLINE, x - 1);
304     mvwaddch(sw, y - 1, x - 1, ACS_BTEE);
305     return 0;
306 }
307
308 void sidebar_scroll(int op)
309 {
310     if (!Incoming.len)
311         return;
312
313     switch (op) {
314       case OP_SIDEBAR_NEXT:
315         if (!option(OPTSIDEBARNEWMAILONLY)) {
316             CurBuffy = (CurBuffy + 1) % Incoming.len;
317             break;
318         }
319         /* FALLTHROUGH */
320       case OP_SIDEBAR_NEXT_NEW:
321         for (int i = 1; i < Incoming.len; i++) {
322             if (Incoming.arr[(CurBuffy + i) % Incoming.len]->new > 0) {
323                 CurBuffy = (CurBuffy + i) % Incoming.len;
324                 break;
325             }
326         }
327         break;
328
329       case OP_SIDEBAR_PREV:
330         if (!option(OPTSIDEBARNEWMAILONLY)) {
331             if (!CurBuffy) {
332                 CurBuffy = Incoming.len;
333             }
334             CurBuffy--;
335             break;
336         }
337         /* FALLTHROUGH */
338       case OP_SIDEBAR_PREV_NEW:
339         for (int i = Incoming.len - 1; i > 0; i--) {
340             if (Incoming.arr[(CurBuffy + i) % Incoming.len]->new > 0) {
341                 CurBuffy = (CurBuffy + i) % Incoming.len;
342                 break;
343             }
344         }
345         break;
346
347       case OP_SIDEBAR_SCROLL_UP:
348         CurBuffy -= LINES - 3;
349         if (CurBuffy < 0)
350             CurBuffy = 0;
351         break;
352       case OP_SIDEBAR_SCROLL_DOWN:
353         CurBuffy += LINES - 3;
354         if (CurBuffy >= Incoming.len)
355             CurBuffy = Incoming.len - 1;
356         break;
357       default:
358         return;
359     }
360
361     sidebar_draw();
362 }