X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=cm.c;h=e1ed5029980d318d1b8f727456ab0a3339faf38b;hb=7dfcaf2ab0bd6438e5c77ea46cb67691770a4afc;hp=b9b2050266fced91e61fc8854426ee3cedd87fbc;hpb=ad83dd5c4803f74a7ba386323b8bfcad1d4ff0da;p=checkmail.git diff --git a/cm.c b/cm.c index b9b2050..e1ed502 100644 --- a/cm.c +++ b/cm.c @@ -86,7 +86,8 @@ struct mbox { int last_beep_new; int force_refresh; int snippet_is_new; - char snippet[256]; + char sender_snippet[128]; + char subject_snippet[128]; }; static clist mboxes; @@ -102,7 +103,7 @@ struct osd_opt_node { static clist osd_opts; static void redraw_line(int i); -static void rethink_display(void); +static void rethink_display(int notify); static void add_pattern(char *patt) @@ -252,26 +253,41 @@ mbox_visible_p(struct mbox *b) } static void -prepare_snippet(struct mbox *b, char *sender, char *subject) +prepare_snippets(struct mbox *b, char *sender, char *subject) { + char *pos, *term; + while (*sender == ' ' || *sender == '\t') sender++; while (*subject == ' ' || *subject == '\t') subject++; - char *pos = b->snippet; - char *term = b->snippet + sizeof(b->snippet) - 1; + pos = b->sender_snippet; + term = pos + sizeof(b->sender_snippet) - 1; if (sender[0] && (b->o.sender_mbox || b->o.sender_personal)) - { - add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal); - add_snippet(&pos, term, ": "); - } + add_addr_snippet(&pos, term, sender, b->o.sender_mbox, b->o.sender_personal); + else + *pos = 0; + + pos = b->subject_snippet; + term = pos + sizeof(b->subject_snippet) - 1; if (subject[0]) add_subject_snippet(&pos, term, subject); else add_snippet(&pos, term, "No subject"); } +static void +build_snippet(char *buf, char *term, struct mbox *b) +{ + if (b->sender_snippet[0]) + { + add_snippet(&buf, term, b->sender_snippet); + add_snippet(&buf, term, ": "); + } + add_snippet(&buf, term, b->subject_snippet); +} + static int mb_fd, mb_pos; static unsigned char mb_buf[4096], *mb_cc, *mb_end; @@ -492,7 +508,7 @@ scan_mbox(struct mbox *b, struct stat *st) if (new || (flagged && !b->snippet_is_new)) { b->snippet_is_new = new; - prepare_snippet(b, sender, subject); + prepare_snippets(b, sender, subject); } int ct = 1; @@ -517,9 +533,9 @@ scan_mbox(struct mbox *b, struct stat *st) } static void -scan(void) +scan(int notify) { - debug("Searching for mailboxes...\n"); + debug("Searching for mailboxes (notify=%d)...\n", notify); last_scan_time = time(NULL); CLIST_FOR_EACH(struct pattern_node *, p, patterns) { @@ -556,7 +572,7 @@ scan(void) } } - rethink_display(); + rethink_display(0); debug("Scanning mailboxes...\n"); CLIST_FOR_EACH(struct mbox *, b, mboxes) @@ -598,7 +614,7 @@ scan(void) debug("Scan finished\n"); last_scan_time = time(NULL); - rethink_display(); + rethink_display(notify); } #ifdef CONFIG_X11 @@ -608,8 +624,11 @@ scan(void) static Display *x11_dpy; static unsigned leds_care, leds_have, leds_want; -static unsigned osd_care, osd_have, osd_want; + static Atom osd_pty; +static unsigned osd_care; +#define OSD_MSG_SIZE 1024 +static char osd_last_msg[OSD_MSG_SIZE]; static void x11_init(void) @@ -649,7 +668,12 @@ x11_init(void) if (!n->key[0]) seen_msg = 1; if (!seen_msg) - add_osd_opt("=You have new mail"); + { + add_osd_opt("=%40f"); + add_osd_opt("=%40s"); + add_osd_opt("="); + add_osd_opt("=(and %m more)"); + } } leds_have = ~0U; @@ -676,51 +700,161 @@ sync_leds(void) } static void -sync_osd(void) +rethink_leds(void) +{ + if (!leds_care || !x11_dpy) + return; + + leds_want = 0; + CLIST_FOR_EACH(struct mbox *, b, mboxes) + if (b->o.led > 0 && b->new) + leds_want |= (1 << b->o.led); + sync_leds(); +} + +struct osd_params { + struct mbox *mbox; + int total_new; +}; + +static int +format_osd_string(char *dest, char *src, struct osd_params *par) { - if (!osd_want || !allow_osd) + char *stop = dest + OSD_MSG_SIZE - 1; + char numbuf[16]; + + while (*src && dest < stop) { - osd_have = 0; + if (*src == '%') + { + src++; + int size = 0; + while (*src >= '0' && *src <= '9') + size = 10*size + *src++ - '0'; + if (!size || size > stop-dest) + size = dest-stop; + + int spec = *src++; + if (!spec) + break; + + char *arg = numbuf; + switch (spec) + { + case 'f': + arg = par->mbox->sender_snippet; + break; + case 's': + arg = par->mbox->subject_snippet; + break; + case 'n': + snprintf(numbuf, sizeof(numbuf), "%d", par->total_new); + break; + case 'm': + if (par->total_new < 2) + return 0; + snprintf(numbuf, sizeof(numbuf), "%d", par->total_new - 1); + break; + case '%': + arg = "%"; + break; + default: + arg = "???"; + break; + } + + while (*arg && size) + { + *dest++ = *arg++; + size--; + } + } + else + *dest++ = *src++; + } + *dest = 0; + return 1; +} + +static void +format_osd(char *msg, struct osd_params *par) +{ + if (!par->mbox) + { + msg[0] = 0; return; } - if (osd_have) - return; - debug("OSD: Displaying\n"); - char msg[1024]; unsigned pos = 0; + unsigned have_text = 0; CLIST_FOR_EACH(struct osd_opt_node *, n, osd_opts) { - pos += snprintf(msg+pos, sizeof(msg)-pos-1, "%s:%s\n", n->key, n->val); - if (pos > sizeof(msg)-1) + char buf[OSD_MSG_SIZE]; + if (!format_osd_string(buf, n->val, par)) + continue; + if (!n->key[0] && buf[0]) + have_text = 1; + pos += snprintf(msg+pos, OSD_MSG_SIZE-pos-1, "%s:%s\n", n->key, buf); + if (pos > OSD_MSG_SIZE-1) { pos = sprintf(msg, "OSD message too long!\n"); break; } } - msg[pos++] = '\n'; - - XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) msg, pos); - XFlush(x11_dpy); + if (have_text) + msg[pos++] = '\n'; + else + pos = 0; + msg[pos] = 0; } static void -rethink_leds(void) +debug_osd_msg(char *msg) { - if (!x11_dpy) + if (!debug_mode) return; + fprintf(stderr, "OSD: <"); + while (*msg) + { + fputc((*msg != '\n' ? *msg : '|'), stderr); + msg++; + } + fprintf(stderr, ">\n"); +} - leds_want = 0; - osd_want = 0; +static void +rethink_osd(int notify) +{ + if (!osd_care || !x11_dpy || !allow_osd) + { + osd_last_msg[0] = 0; + return; + } + + struct osd_params p = { .mbox = NULL, .total_new = 0 }; CLIST_FOR_EACH(struct mbox *, b, mboxes) + if (b->o.osd > 0) + { + p.total_new += b->new; + if (b->new && (!p.mbox || p.mbox->o.priority < b->o.priority)) + p.mbox = b; + } + + char new_msg[OSD_MSG_SIZE]; + format_osd(new_msg, &p); + debug_osd_msg(new_msg); + if (strcmp(new_msg, osd_last_msg)) { - if (b->o.led > 0 && b->new) - leds_want |= (1 << b->o.led); - if (b->o.osd > 0 && b->new) - osd_want = 1; + strcpy(osd_last_msg, new_msg); + if (notify && new_msg[0]) + { + debug("OSD: Sending to daemon\n"); + XChangeProperty(x11_dpy, DefaultRootWindow(x11_dpy), osd_pty, XA_STRING, 8, PropModeAppend, (unsigned char *) new_msg, strlen(new_msg)); + XFlush(x11_dpy); + } + else + debug("OSD: No changes\n"); } - sync_leds(); - sync_osd(); } static void @@ -737,21 +871,26 @@ x11_cleanup(void) static void x11_init(void) { } static void rethink_leds(void) { } +static void rethink_osd(int notify UNUSED) { } static void x11_cleanup(void) { } #endif static int cursor_at, cursor_max; +static unsigned is_active, is_pos; // incremental search +static char is_buf[64]; + enum { M_IDLE, M_SCAN, M_NEW, M_FLAG, M_BAD, + M_INCSEARCH, M_MAX }; -static int attrs[2][2][M_MAX]; // active, hilite, status +static int attrs[3][2][M_MAX]; // active (2=incsearch), hilite, status static void redraw_line(int i) @@ -760,8 +899,10 @@ redraw_line(int i) if (i < cursor_max) { struct mbox *b = mbox_array[i]; - int cc = (cursor_at == i); + int cc = (cursor_at == i ? (is_active ? 2 : 1) : 0); int hi = b->o.highlight; + unsigned namepos = 0; + unsigned namelen = strlen(b->name); attrset(attrs[cc][hi][M_IDLE]); if (b->o.hotkey) @@ -770,11 +911,22 @@ redraw_line(int i) printw("> "); else printw(" "); + if (cc == 2) + { + attrset(attrs[cc][hi][M_INCSEARCH]); + for (namepos=0; namepos < is_pos && namepos < 20; namepos++) + addch(is_buf[namepos]); + } if (b->new) attrset(attrs[cc][hi][M_NEW]); else if (b->flagged && b->o.show_flagged) attrset(attrs[cc][hi][M_FLAG]); - printw("%-20s ", b->name); + else + attrset(attrs[cc][hi][M_IDLE]); + while (namepos < namelen) + addch(b->name[namepos++]); + while (namepos++ < 20) + addch(' '); if (b->scanning < 0) ; else if (b->scanning) @@ -817,20 +969,24 @@ redraw_line(int i) attrset(attrs[cc][0][M_FLAG]); /* We avoid the highlight intentionally */ snip = 1; } - if (snip && b->o.snippets && b->snippet[0]) + if (snip && b->o.snippets) { int xx, yy; getyx(stdscr, yy, xx); int remains = COLS-1-xx; - if (remains > 2) + + char snip[256]; + build_snippet(snip, snip + sizeof(snip) - 1, b); + + if (snip[0] && remains > 2) { #ifdef CONFIG_WIDE_CURSES - size_t len = strlen(b->snippet)+1; - wchar_t snip[len]; - mbstowcs(snip, b->snippet, len); - addnwstr(snip, remains); + size_t len = strlen(snip)+1; + wchar_t snip2[len]; + mbstowcs(snip2, snip, len); + addnwstr(snip2, remains); #else - printw("%-.*s", remains, b->snippet); + printw("%-.*s", remains, snip); #endif } } @@ -864,7 +1020,7 @@ redraw_all(void) } static void -rethink_display(void) +rethink_display(int notify) { int i = 0; int changed = 0; @@ -898,7 +1054,8 @@ rethink_display(void) refresh(); } rethink_leds(); - if (beeeep && allow_bells) + rethink_osd(notify); + if (beeeep && allow_bells && notify) beep(); } @@ -914,20 +1071,31 @@ term_init(void) curs_set(0); static const int attrs_mono[2][M_MAX] = { - [0] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_BOLD, [M_FLAG] = 0, [M_BAD] = A_DIM }, - [1] = { [M_IDLE] = 0, [M_SCAN] = A_BOLD, [M_NEW] = A_REVERSE | A_BOLD, [M_FLAG] = A_REVERSE, [M_BAD] = A_DIM }, + [0] = { [M_IDLE] = 0, + [M_SCAN] = A_BOLD, + [M_NEW] = A_BOLD, + [M_FLAG] = 0, + [M_BAD] = A_DIM, + [M_INCSEARCH] = A_REVERSE }, + [1] = { [M_IDLE] = 0, + [M_SCAN] = A_BOLD, + [M_NEW] = A_REVERSE | A_BOLD, + [M_FLAG] = A_REVERSE, + [M_BAD] = A_DIM, + [M_INCSEARCH] = A_REVERSE }, }; for (int i=0; i<2; i++) for (int j=0; j= 5) + if (COLOR_PAIRS >= 12) { init_pair(1, COLOR_YELLOW, COLOR_BLACK); init_pair(2, COLOR_RED, COLOR_BLACK); @@ -936,11 +1104,44 @@ term_init(void) init_pair(5, COLOR_RED, COLOR_BLUE); init_pair(6, COLOR_GREEN, COLOR_BLACK); init_pair(7, COLOR_GREEN, COLOR_BLUE); - static const int attrs_color[2][2][M_MAX] = { - [0][0] = { [M_IDLE] = 0, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1), [M_FLAG] = COLOR_PAIR(6), [M_BAD] = COLOR_PAIR(2) }, - [0][1] = { [M_IDLE] = A_BOLD, [M_SCAN] = COLOR_PAIR(1), [M_NEW] = COLOR_PAIR(1) | A_BOLD, [M_FLAG] = COLOR_PAIR(6) | A_BOLD, [M_BAD] = COLOR_PAIR(2) | A_BOLD }, - [1][0] = { [M_IDLE] = COLOR_PAIR(3), [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4), [M_FLAG] = COLOR_PAIR(7), [M_BAD] = COLOR_PAIR(5) }, - [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD, [M_SCAN] = COLOR_PAIR(4), [M_NEW] = COLOR_PAIR(4) | A_BOLD, [M_FLAG] = COLOR_PAIR(7) | A_BOLD, [M_BAD] = COLOR_PAIR(5) | A_BOLD }, + init_pair(8, COLOR_WHITE, COLOR_MAGENTA); + init_pair(9, COLOR_YELLOW, COLOR_MAGENTA); + init_pair(10, COLOR_GREEN, COLOR_MAGENTA); + init_pair(11, COLOR_RED, COLOR_MAGENTA); + init_pair(12, COLOR_BLACK, COLOR_YELLOW); + static const int attrs_color[3][2][M_MAX] = { + [0][0] = { [M_IDLE] = 0, + [M_SCAN] = COLOR_PAIR(1), + [M_NEW] = COLOR_PAIR(1), + [M_FLAG] = COLOR_PAIR(6), + [M_BAD] = COLOR_PAIR(2) }, + [0][1] = { [M_IDLE] = A_BOLD, + [M_SCAN] = COLOR_PAIR(1), + [M_NEW] = COLOR_PAIR(1) | A_BOLD, + [M_FLAG] = COLOR_PAIR(6) | A_BOLD, + [M_BAD] = COLOR_PAIR(2) | A_BOLD }, + [1][0] = { [M_IDLE] = COLOR_PAIR(3), + [M_SCAN] = COLOR_PAIR(4), + [M_NEW] = COLOR_PAIR(4), + [M_FLAG] = COLOR_PAIR(7), + [M_BAD] = COLOR_PAIR(5) }, + [1][1] = { [M_IDLE] = COLOR_PAIR(3) | A_BOLD, + [M_SCAN] = COLOR_PAIR(4), + [M_NEW] = COLOR_PAIR(4) | A_BOLD, + [M_FLAG] = COLOR_PAIR(7) | A_BOLD, + [M_BAD] = COLOR_PAIR(5) | A_BOLD }, + [2][0] = { [M_IDLE] = COLOR_PAIR(8), + [M_SCAN] = COLOR_PAIR(9), + [M_NEW] = COLOR_PAIR(9), + [M_FLAG] = COLOR_PAIR(10), + [M_BAD] = COLOR_PAIR(11), + [M_INCSEARCH] = COLOR_PAIR(12) | A_DIM }, + [2][1] = { [M_IDLE] = COLOR_PAIR(8) | A_BOLD, + [M_SCAN] = COLOR_PAIR(9), + [M_NEW] = COLOR_PAIR(9) | A_BOLD, + [M_FLAG] = COLOR_PAIR(10) | A_BOLD, + [M_BAD] = COLOR_PAIR(11) | A_BOLD, + [M_INCSEARCH] = COLOR_PAIR(12) }, }; memcpy(attrs, attrs_color, sizeof(attrs)); } @@ -964,10 +1165,10 @@ print_status(char *status) } static void -scan_and_redraw(void) +scan_and_redraw(int notify) { print_status("Busy..."); - scan(); + scan(notify); print_status(NULL); } @@ -1019,7 +1220,54 @@ mbox_run(struct mbox *b) redraw_all(); refresh(); b->force_refresh = 1; - scan_and_redraw(); + scan_and_redraw(0); +} + +static void +enter_incsearch(void) +{ + print_status("Incremental search..."); + is_active = 1; + is_pos = 0; + redraw_line(cursor_at); +} + +static int +handle_incsearch(int ch) +{ + if ((ch == KEY_BACKSPACE || ch == KEY_DC) && is_pos) + --is_pos; + else if (ch >= ' ' && ch <= '~') + { + if (is_pos < sizeof(is_buf) - 1) + is_buf[is_pos++] = ch; + } + else + { + print_status(NULL); + is_active = 0; + is_pos = 0; + redraw_line(cursor_at); + return 0; + } + + is_buf[is_pos] = 0; + for (int i=0; iname, is_buf, is_pos)) + { + if (i != cursor_at) + { + move_cursor(i); + return 1; + } + break; + } + } + + redraw_line(cursor_at); + return 1; } #define STR2(c) #c @@ -1169,7 +1417,7 @@ main(int argc, char **argv) charset_init(); term_init(); x11_init(); - scan_and_redraw(); + scan_and_redraw(0); next_active(0, 1); int should_exit = 0; @@ -1178,13 +1426,22 @@ restart: { time_t now = time(NULL); int remains = last_scan_time + check_interval - now; - if (remains <= 0 || force_refresh) - scan_and_redraw(); + if (force_refresh) + scan_and_redraw(0); + if (remains <= 0) + scan_and_redraw(1); else { remains *= 10; halfdelay((remains > 255) ? 255 : remains); int ch = getch(); + if (ch < 0) + continue; + if (is_active && handle_incsearch(ch)) + { + refresh(); + continue; + } for (int i=0; io.hotkey) { @@ -1251,11 +1508,14 @@ restart: allow_osd = 0; print_status("On-screen display is now disabled. Watch your step."); break; + case '/': + enter_incsearch(); + break; default: if (ch >= '0' && ch <= '9') { minimum_priority = ch - '0'; - scan_and_redraw(); + scan_and_redraw(0); } else debug("Pressed unknown key %d\n", ch);