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