Rocco Rutte:
[apps/madmutt.git] / 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 "mutt.h"
16 #include "mutt_menu.h"
17 #include "mutt_curses.h"
18 #include "sidebar.h"
19 #include "buffy.h"
20 #include "keymap.h"
21
22 #include "lib/mem.h"
23 #include "lib/str.h"
24 #include "lib/intl.h"
25
26 #include <libgen.h>
27 #include <ctype.h>
28
29 static int TopBuffy = 0;
30 static int CurBuffy = 0;
31 static int known_lines = 0;
32 static short initialized = 0;
33 static int prev_show_value;
34 static short saveSidebarWidth;
35 static char *entry = 0;
36
37 /* computes how many digets a number has;
38  * FIXME move out to library?
39  */
40 static int quick_log10 (int n) {
41   int len = 0;
42
43   for (; n > 9; len++, n /= 10);
44   return (++len);
45 }
46
47 /* computes first entry to be shown */
48 void calc_boundaries (int menu)
49 {
50   int lines = 0;
51
52   if (list_empty(Incoming))
53     return;
54   /* correct known_lines if it has changed because of a window resize */
55   if (known_lines != LINES)
56     known_lines = LINES;
57   lines = LINES - 2 - (menu != MENU_PAGER || option (OPTSTATUSONTOP));
58   TopBuffy = CurBuffy - (CurBuffy % lines);
59   if (TopBuffy < 0)
60     TopBuffy = 0;
61 }
62
63 /* compresses hierarchy in folder names;
64  * FIXME move out to library?
65  */
66 static char *shortened_hierarchy (char *box)
67 {
68   int dots = 0;
69   char *last_dot;
70   int i, j;
71   char *new_box;
72
73   for (i = 0; i < mutt_strlen (box); ++i) {
74     if (box[i] == '.')
75       ++dots;
76     else if (isupper (box[i]))
77       return (safe_strdup (box));
78   }
79   last_dot = strrchr (box, '.');
80   if (last_dot) {
81     ++last_dot;
82     new_box = safe_malloc (mutt_strlen (last_dot) + 2 * dots + 1);
83     new_box[0] = box[0];
84     for (i = 1, j = 1; i < mutt_strlen (box); ++i) {
85       if (box[i] == '.') {
86         new_box[j++] = '.';
87         new_box[j] = 0;
88         if (&box[i + 1] != last_dot) {
89           new_box[j++] = box[i + 1];
90           new_box[j] = 0;
91         }
92         else {
93           strcat (&new_box[j], last_dot);
94           break;
95         }
96       }
97     }
98     return new_box;
99   }
100   return safe_strdup (box);
101 }
102
103 /* print single item
104  * FIXME this is completely fucked up right now
105  */
106 char *make_sidebar_entry (char *box, int size, int new, int flagged)
107 {
108   int i = 0, dlen, max, shortened = 0;
109   int offset;
110
111   if (SidebarWidth > COLS)
112     SidebarWidth = COLS;
113
114   dlen = mutt_strlen (SidebarDelim);
115   max = SidebarWidth - dlen - 1;
116
117   safe_realloc (&entry, SidebarWidth + 1);
118   entry[SidebarWidth] = 0;
119   for (; i < SidebarWidth; entry[i++] = ' ');
120 #if USE_IMAP
121   if (ImapHomeNamespace && mutt_strlen (ImapHomeNamespace) > 0) {
122     if (strncmp (box, ImapHomeNamespace, mutt_strlen (ImapHomeNamespace)) == 0
123         && strcmp (box, ImapHomeNamespace) != 0) {
124       box += mutt_strlen (ImapHomeNamespace) + 1;
125     }
126   }
127 #endif
128   max -= quick_log10 (size);
129   if (new)
130     max -= quick_log10 (new) + 2;
131   if (flagged > 0)
132     max -= quick_log10 (flagged) + 2;
133   if (option (OPTSHORTENHIERARCHY) && mutt_strlen (box) > max) {
134     box = shortened_hierarchy (box);
135     shortened = 1;
136   }
137   i = mutt_strlen (box);
138   strncpy (entry, box, i < SidebarWidth - dlen ? i : SidebarWidth - dlen);
139
140   if (new) {
141     if (flagged > 0) {
142       offset =
143         SidebarWidth - 5 - quick_log10 (size) - dlen - quick_log10 (new) -
144         quick_log10 (flagged);
145       if (offset < 0)
146         offset = 0;
147       snprintf (entry + offset, SidebarWidth - dlen - offset + 1,
148                 "% d(%d)[%d]", size, new, flagged);
149     }
150     else {
151       offset =
152         SidebarWidth - 3 - quick_log10 (size) - dlen - quick_log10 (new);
153       if (offset < 0)
154         offset = 0;
155       snprintf (entry + offset, SidebarWidth - dlen - offset + 1,
156                 "% d(%d)", size, new);
157     }
158   }
159   else {
160     if (flagged > 0) {
161       offset =
162         SidebarWidth - 3 - quick_log10 (size) - dlen - quick_log10 (flagged);
163       if (offset < 0)
164         offset = 0;
165       snprintf (entry + offset, SidebarWidth - dlen - offset + 1,
166                 "% d[%d]", size, flagged);
167     }
168     else {
169       offset = SidebarWidth - 1 - quick_log10 (size) - dlen;
170       if (offset < 0)
171         offset = 0;
172       snprintf (entry + offset, SidebarWidth - dlen - offset + 1,
173                 "% d", size);
174     }
175   }
176
177   if (option (OPTSHORTENHIERARCHY) && shortened) {
178     free (box);
179   }
180   return entry;
181 }
182
183 /* returns folder name of currently 
184  * selected folder for <sidebar-open>
185  */
186 const char* sidebar_get_current (void) {
187   if (list_empty(Incoming))
188     return (NULL);
189   return ((char*) ((BUFFY*) Incoming->data[CurBuffy])->path);
190 }
191
192 /* internally sets item to buf */
193 void sidebar_set_current (const char* buf) {
194   int i = buffy_lookup (buf);
195   if (i >= 0)
196     CurBuffy = i;
197 }
198
199 /* fix counters for a context
200  * FIXME since ctx must not be of our business, move it elsewhere
201  */
202 void sidebar_set_buffystats (CONTEXT* Context) {
203   int i = 0;
204   BUFFY* tmp = NULL;
205   if (!Context || list_empty(Incoming) || (i = buffy_lookup (Context->path)) < 0)
206     return;
207   tmp = (BUFFY*) Incoming->data[i];
208   tmp->new = Context->new;
209   tmp->msg_unread = Context->unread;
210   tmp->msgcount = Context->msgcount;
211   tmp->msg_flagged = Context->flagged;
212 }
213
214 /* actually draws something
215  * FIXME this needs some clue when to do it
216  * FIXME this is completely fucked up right now
217  */
218 int sidebar_draw (int menu)
219 {
220
221   int lines = option (OPTHELP) ? 1 : 0;
222   BUFFY *tmp;
223   int i = 0;
224   short delim_len = mutt_strlen (SidebarDelim);
225
226   /* initialize first time */
227   if (!initialized) {
228     prev_show_value = option (OPTMBOXPANE);
229     saveSidebarWidth = SidebarWidth;
230     if (!option (OPTMBOXPANE))
231       SidebarWidth = 0;
232     initialized = 1;
233   }
234
235   /* save or restore the value SidebarWidth */
236   if (prev_show_value != option (OPTMBOXPANE)) {
237     if (prev_show_value && !option (OPTMBOXPANE)) {
238       saveSidebarWidth = SidebarWidth;
239       SidebarWidth = 0;
240     }
241     else if (!prev_show_value && option (OPTMBOXPANE)) {
242       SidebarWidth = saveSidebarWidth;
243       /* after toggle: force recounting of all mail */
244       mutt_buffy_check (2);
245     }
246     prev_show_value = option (OPTMBOXPANE);
247   }
248
249   if (SidebarWidth > 0 && option (OPTMBOXPANE)
250       && mutt_strlen (SidebarDelim) >= SidebarWidth) {
251     mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar."));
252     sleep (2);
253     unset_option (OPTMBOXPANE);
254     return (0);
255   }
256
257   if (SidebarWidth == 0 || !option (OPTMBOXPANE))
258     return 0;
259
260   /* draw the divider */
261   SETCOLOR (MT_COLOR_SIDEBAR);
262   for (lines = 1;
263        lines < LINES - 1 - (menu != MENU_PAGER || option (OPTSTATUSONTOP));
264        lines++) {
265     move (lines, SidebarWidth - delim_len);
266     if (option (OPTASCIICHARS))
267       addstr (NONULL (SidebarDelim));
268     else if (!option (OPTASCIICHARS) && !mutt_strcmp (SidebarDelim, "|"))
269       addch (ACS_VLINE);
270     else if ((Charset_is_utf8) && !mutt_strcmp (SidebarDelim, "|"))
271       addstr ("\342\224\202");
272     else
273       addstr (NONULL (SidebarDelim));
274   }
275   SETCOLOR (MT_COLOR_NORMAL);
276
277   if (list_empty(Incoming))
278     return 0;
279
280   lines = option (OPTHELP) ? 1 : 0;     /* go back to the top */
281
282   calc_boundaries (menu);
283
284   for (i = TopBuffy; i < Incoming->length && lines < LINES - 1 - 
285        (menu != MENU_PAGER || option (OPTSTATUSONTOP)); i++) {
286     tmp = (BUFFY*) Incoming->data[i];
287     if (i == CurBuffy)
288       SETCOLOR (MT_COLOR_INDICATOR);
289     else if (tmp->msg_flagged > 0)
290       SETCOLOR (MT_COLOR_FLAGGED);
291     else if (tmp->msg_unread > 0)
292       SETCOLOR (MT_COLOR_NEW);
293     else
294       SETCOLOR (MT_COLOR_NORMAL);
295
296     move (lines, 0);
297     if (option (OPTSIDEBARNEWMAILONLY)) {
298       if (tmp->msg_unread > 0) {
299         if (Context && !mutt_strcmp (tmp->path, Context->path)) {
300           printw ("%.*s", SidebarWidth - delim_len,
301                   make_sidebar_entry (basename (tmp->path),
302                                       Context->msgcount, Context->unread,
303                                       Context->flagged));
304           tmp->msg_unread = Context->unread;
305           tmp->msgcount = Context->msgcount;
306           tmp->msg_flagged = Context->flagged;
307         }
308         else
309           printw ("%.*s", SidebarWidth - delim_len,
310                   make_sidebar_entry (basename (tmp->path),
311                                       tmp->msgcount, tmp->msg_unread,
312                                       tmp->msg_flagged));
313         lines++;
314       }
315     }
316     else {
317       if (Context && !strcmp (tmp->path, Context->path)) {
318         printw ("%.*s", SidebarWidth - delim_len,
319                 make_sidebar_entry (basename (tmp->path),
320                                     Context->msgcount, Context->unread,
321                                     Context->flagged));
322         tmp->msg_unread = Context->unread;
323         tmp->msgcount = Context->msgcount;
324         tmp->msg_flagged = Context->flagged;
325       }
326       else
327         printw ("%.*s", SidebarWidth - delim_len,
328                 make_sidebar_entry (basename (tmp->path),
329                                     tmp->msgcount, tmp->msg_unread,
330                                     tmp->msg_flagged));
331       lines++;
332     }
333   }
334   SETCOLOR (MT_COLOR_NORMAL);
335   for (; lines < LINES - 1 - (menu != MENU_PAGER || option (OPTSTATUSONTOP));
336        lines++) {
337     int i = 0;
338
339     move (lines, 0);
340     for (; i < SidebarWidth - delim_len; i++)
341       addch (' ');
342   }
343   return 0;
344 }
345
346 /* returns index of new item with new mail or -1 */
347 static int exist_next_new () {
348   int i = 0;
349   if (list_empty(Incoming))
350     return (-1);
351   i = CurBuffy;
352   while (i < Incoming->length)
353     if (((BUFFY*) Incoming->data[i++])->msg_unread)
354       return (i-1);
355   return (-1);
356 }
357
358 /* returns index of prev item with new mail or -1 */
359 static int exist_prev_new () {
360   int i = 0;
361   if (list_empty(Incoming))
362     return (-1);
363   i = CurBuffy;
364   while (i >= 0)
365     if (((BUFFY*) Incoming->data[i--])->msg_unread)
366       return (i+1);
367   return (-1);
368 }
369
370 void sidebar_scroll (int op, int menu) {
371   int i = 0;
372
373   if (!SidebarWidth || list_empty(Incoming))
374     return;
375
376   switch (op) {
377   case OP_SIDEBAR_NEXT:
378     if (!option (OPTSIDEBARNEWMAILONLY)) {
379       if (CurBuffy + 1 == Incoming->length) {
380         mutt_error (_("You are on the last mailbox."));
381         return;
382       }
383       CurBuffy++;
384       break;
385     }                           /* the fall-through is intentional */
386   case OP_SIDEBAR_NEXT_NEW:
387     if ((i = exist_next_new ()) < 0) {
388       mutt_error (_("No next mailboxes with new mail."));
389       return;
390     }
391     else
392       CurBuffy = i;
393     break;
394   case OP_SIDEBAR_PREV:
395     if (!option (OPTSIDEBARNEWMAILONLY)) {
396       if (CurBuffy == 0) {
397         mutt_error (_("You are on the first mailbox."));
398         return;
399       }
400       CurBuffy--;
401       break;
402     }                           /* the fall-through is intentional */
403   case OP_SIDEBAR_PREV_NEW:
404     if ((i = exist_prev_new ()) < 0) {
405       mutt_error (_("No previous mailbox with new mail."));
406       return;
407     }
408     else
409       CurBuffy = i;
410     break;
411
412   case OP_SIDEBAR_SCROLL_UP:
413     if (TopBuffy == 0) {
414       mutt_error (_("You are on the first mailbox."));
415       return;
416     }
417     CurBuffy -= known_lines;
418     if (CurBuffy < 0)
419       CurBuffy = 0;
420     break;
421   case OP_SIDEBAR_SCROLL_DOWN:
422     if (TopBuffy + known_lines >= Incoming->length) {
423       mutt_error (_("You are on the last mailbox."));
424       return;
425     }
426     CurBuffy += known_lines;
427     if (CurBuffy >= Incoming->length)
428       CurBuffy = Incoming->length;
429     break;
430   default:
431     return;
432   }
433   calc_boundaries (menu);
434   sidebar_draw (menu);
435 }