* New version 2.20.11
[alpine.git] / alpine / mailindx.c
blob90ea22a69c76c70566b59ca2e3ee4d621d24ab8a
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailindx.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "mailindx.h"
21 #include "mailcmd.h"
22 #include "status.h"
23 #include "context.h"
24 #include "keymenu.h"
25 #include "alpine.h"
26 #include "help.h"
27 #include "radio.h"
28 #include "titlebar.h"
29 #include "../pith/flag.h"
30 #include "../pith/newmail.h"
31 #include "../pith/thread.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/icache.h"
35 #include "../pith/state.h"
36 #include "../pith/bitmap.h"
37 #include "../pith/news.h"
38 #include "../pith/strlst.h"
39 #include "../pith/sequence.h"
40 #include "../pith/sort.h"
41 #include "../pith/hist.h"
42 #include "../pith/busy.h"
43 #include "../pith/signal.h"
46 struct save_thrdinfo {
47 ICE_S *(*format_index_line)(INDEXDATA_S *);
48 void (*setup_header_widths)(MAILSTREAM *);
49 unsigned viewing_a_thread:1;
53 static OtherMenu what_keymenu = FirstMenu;
55 struct index_state *current_index_state = NULL;
59 * Internal prototypes
61 void index_index_screen(struct pine *);
62 void thread_index_screen(struct pine *);
63 int update_index(struct pine *, struct index_state *);
64 int index_scroll_up(long);
65 int index_scroll_down(long);
66 int index_scroll_to_pos(long);
67 long top_ent_calc(MAILSTREAM *, MSGNO_S *, long, long);
68 void reset_index_border(void);
69 void redraw_index_body(void);
70 int paint_index_line(ICE_S *, int, long, IndexColType, IndexColType, IndexColType,
71 struct entry_state *, int, int);
72 void pine_imap_envelope(MAILSTREAM *, unsigned long, ENVELOPE *);
73 void index_search(struct pine *, MAILSTREAM *, int, MSGNO_S *);
74 #ifdef _WINDOWS
75 int index_scroll_callback(int,long);
76 int index_gettext_callback(char *, size_t, void **, long *, int *);
77 void index_popup(IndexType style, MAILSTREAM *, MSGNO_S *, int);
78 char *pcpine_help_index(char *);
79 char *pcpine_help_index_simple(char *);
80 #endif
84 /*----------------------------------------------------------------------
87 ----*/
88 struct key_menu *
89 do_index_border(CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap,
90 IndexType style, int *which_keys, int flags)
92 struct key_menu *km = (style == ThreadIndex)
93 ? &thread_keymenu
94 : (ps_global->mail_stream != stream)
95 ? &simple_index_keymenu
96 : &index_keymenu;
98 if(flags & INDX_CLEAR)
99 ClearScreen();
101 if(flags & INDX_HEADER)
102 set_titlebar((style == ThreadIndex)
103 /* TRANSLATORS: these are some screen titles */
104 ? _("THREAD INDEX")
105 : (stream == ps_global->mail_stream)
106 ? (style == MsgIndex || style == MultiMsgIndex)
107 ? _("MESSAGE INDEX")
108 : _("ZOOMED MESSAGE INDEX")
109 : (!strcmp(folder, INTERRUPTED_MAIL))
110 ? _("COMPOSE: SELECT INTERRUPTED")
111 : (ps_global->VAR_FORM_FOLDER
112 && !strcmp(ps_global->VAR_FORM_FOLDER, folder))
113 ? _("COMPOSE: SELECT FORM LETTER")
114 : _("COMPOSE: SELECT POSTPONED"),
115 stream, cntxt, folder, msgmap, 1,
116 (style == ThreadIndex) ? ThrdIndex
117 : (THREADING()
118 && sp_viewing_a_thread(stream))
119 ? ThrdMsgNum
120 : MessageNumber,
121 0, 0, NULL);
123 if(flags & INDX_FOOTER) {
124 bitmap_t bitmap;
125 int cmd;
127 setbitmap(bitmap);
129 if(km == &index_keymenu){
130 if(THREADING() && sp_viewing_a_thread(stream)){
131 menu_init_binding(km, '<', MC_THRDINDX, "<",
132 N_("ThrdIndex"), BACK_KEY);
133 menu_add_binding(km, ',', MC_THRDINDX);
135 else{
136 menu_init_binding(km, '<', MC_FOLDERS, "<",
137 N_("FldrList"), BACK_KEY);
138 menu_add_binding(km, ',', MC_FOLDERS);
140 if(F_OFF(F_ENABLE_PIPE,ps_global))
141 clrbitn(VIEW_PIPE_KEY, bitmap); /* always clear for DOS */
142 if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
143 clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
144 if(F_OFF(F_ENABLE_BOUNCE,ps_global))
145 clrbitn(BOUNCE_KEY, bitmap);
146 if(F_OFF(F_ENABLE_FLAG,ps_global))
147 clrbitn(FLAG_KEY, bitmap);
148 if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
149 clrbitn(SELECT_KEY, bitmap);
150 clrbitn(APPLY_KEY, bitmap);
151 clrbitn(SELCUR_KEY, bitmap);
152 if(style != ZoomIndex)
153 clrbitn(ZOOM_KEY, bitmap);
157 if(style == MultiMsgIndex){
158 clrbitn(PREVM_KEY, bitmap);
159 clrbitn(NEXTM_KEY, bitmap);
163 if(km == &index_keymenu || km == &thread_keymenu){
164 if(IS_NEWS(stream)){
165 km->keys[EXCLUDE_KEY].label = N_("eXclude");
166 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_NONE);
168 else {
169 clrbitn(UNEXCLUDE_KEY, bitmap);
170 km->keys[EXCLUDE_KEY].label = N_("eXpunge");
171 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_EXPUNGE);
175 if(km != &simple_index_keymenu && !THRD_COLLAPSE_ENABLE())
176 clrbitn(COLLAPSE_KEY, bitmap);
178 menu_clear_binding(km, KEY_LEFT);
179 menu_clear_binding(km, KEY_RIGHT);
180 if(F_ON(F_ARROW_NAV, ps_global)){
181 if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
182 menu_add_binding(km, '<', cmd);
183 menu_add_binding(km, KEY_LEFT, cmd);
186 if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
187 menu_add_binding(km, '>', cmd);
188 menu_add_binding(km, KEY_RIGHT, cmd);
192 if(menu_binding_index(km, MC_JUMP) >= 0){
193 for(cmd = 0; cmd < 10; cmd++)
194 if(F_ON(F_ENABLE_JUMP, ps_global))
195 (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
196 else
197 (void) menu_clear_binding(km, '0' + cmd);
200 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
201 1-FOOTER_ROWS(ps_global), 0, what_keymenu);
202 what_keymenu = SameMenu;
203 if(which_keys)
204 *which_keys = km->which; /* pass back to caller */
207 return(km);
212 /*----------------------------------------------------------------------
213 Main loop executing commands for the mail index screen
215 Args: state -- the pine_state structure for next/prev screen pointers
216 and to pass to the index manager...
217 ----*/
219 void
220 mail_index_screen(struct pine *state)
222 if(!state->mail_stream) {
223 q_status_message(SM_ORDER, 0, 3, _("No folder is currently open"));
224 state->prev_screen = mail_index_screen;
225 state->next_screen = main_menu_screen;
226 return;
229 state->prev_screen = mail_index_screen;
230 state->next_screen = SCREEN_FUN_NULL;
232 if(THRD_AUTO_VIEW()
233 && sp_viewing_a_thread(state->mail_stream)
234 && state->view_skipped_index
235 && unview_thread(state, state->mail_stream, state->msgmap)){
236 state->next_screen = mail_index_screen;
237 state->view_skipped_index = 0;
238 state->mangled_screen = 1;
241 adjust_cur_to_visible(state->mail_stream, state->msgmap);
243 if(THRD_INDX())
244 thread_index_screen(state);
245 else
246 index_index_screen(state);
250 void
251 index_index_screen(struct pine *state)
253 dprint((1, "\n\n ---- MAIL INDEX ----\n"));
255 setup_for_index_index_screen();
257 index_lister(state, state->context_current, state->cur_folder,
258 state->mail_stream, state->msgmap);
262 void
263 thread_index_screen(struct pine *state)
265 dprint((1, "\n\n ---- THREAD INDEX ----\n"));
267 setup_for_thread_index_screen();
269 index_lister(state, state->context_current, state->cur_folder,
270 state->mail_stream, state->msgmap);
274 void *
275 stop_threading_temporarily(void)
277 struct save_thrdinfo *ti;
279 ps_global->turn_off_threading_temporarily = 1;
281 ti = (struct save_thrdinfo *) fs_get(sizeof(*ti));
282 ti->format_index_line = format_index_line;
283 ti->setup_header_widths = setup_header_widths;
284 ti->viewing_a_thread = sp_viewing_a_thread(ps_global->mail_stream);
286 setup_for_index_index_screen();
288 return((void *) ti);
292 void
293 restore_threading(void **p)
295 struct save_thrdinfo *ti;
297 ps_global->turn_off_threading_temporarily = 0;
299 if(p && *p){
300 ti = (struct save_thrdinfo *) (*p);
301 format_index_line = ti->format_index_line;
302 setup_header_widths = ti->setup_header_widths;
303 sp_set_viewing_a_thread(ps_global->mail_stream, ti->viewing_a_thread);
305 fs_give(p);
310 /*----------------------------------------------------------------------
311 Main loop executing commands for the mail index screen
313 Args: state -- pine_state structure for display flags and such
314 msgmap -- c-client/pine message number mapping struct
315 ----*/
318 index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap)
320 UCS ch;
321 int cmd, which_keys, force,
322 cur_row, cur_col, km_popped, paint_status;
323 static int old_day = -1;
324 long i, j, k, old_max_msgno;
325 long lflagged, old_lflagged = 0L;
326 char *utf8str;
327 IndexType style, old_style = MsgIndex;
328 struct index_state id;
329 struct key_menu *km = NULL;
332 dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
334 ch = 'x'; /* For displaying msg 1st time thru */
335 force = 0;
336 km_popped = 0;
337 state->mangled_screen = 1;
338 what_keymenu = FirstMenu;
339 old_max_msgno = mn_get_total(msgmap);
340 memset((void *)&id, 0, sizeof(struct index_state));
341 current_index_state = &id;
342 id.msgmap = msgmap;
343 if(msgmap->top != 0L)
344 id.msg_at_top = msgmap->top;
346 id.stream = stream;
347 set_need_format_setup(stream);
349 while (1) {
350 ps_global->user_says_cancel = 0;
352 if(km_popped){
353 km_popped--;
354 if(km_popped == 0){
355 clearfooter(state);
356 if(!state->mangled_body
357 && id.entry_state
358 && id.lines_per_page > 1){
359 id.entry_state[id.lines_per_page-2].id = 0;
360 id.entry_state[id.lines_per_page-1].id = 0;
362 else
363 state->mangled_body = 1;
367 /*------- Check for new mail -------*/
368 new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
369 force = 0; /* may not need to next time around */
372 * If the width of the message number field in the display changes
373 * we need to flush the cache and redraw. When the cache is cleared
374 * the widths are recalculated, taking into account the max msgno.
377 if(format_includes_msgno(stream) &&
378 ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
379 || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
380 || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
381 clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
382 state->mangled_body = 1;
385 old_max_msgno = mn_get_total(msgmap);
388 * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
389 * then when the day changes the date column will change. All of the
390 * Today's will become Yesterday's at midnight. So we have to
391 * clear the cache at midnight.
393 if(format_includes_smartdate(stream)){
394 char db[200];
395 struct date nnow;
397 rfc822_date(db);
398 parse_date(db, &nnow);
399 if(old_day != -1 && nnow.day != old_day){
400 clear_index_cache(stream, 0);
401 state->mangled_body = 1;
404 old_day = nnow.day;
407 if(streams_died())
408 state->mangled_header = 1;
410 if(state->mangled_screen){
411 state->mangled_header = 1;
412 state->mangled_body = 1;
413 state->mangled_footer = 1;
414 state->mangled_screen = 0;
418 * events may have occured that require us to shift from
419 * mode to another...
421 style = THRD_INDX()
422 ? ThreadIndex
423 : (any_lflagged(msgmap, MN_HIDE))
424 ? ZoomIndex
425 : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
426 lflagged = any_lflagged(msgmap, MN_HIDE);
427 if(style != old_style || lflagged < old_lflagged){
428 state->mangled_header = 1;
429 state->mangled_footer = 1;
430 old_style = style;
431 old_lflagged = lflagged;
432 if(!(style == ThreadIndex || old_style == ThreadIndex))
433 id.msg_at_top = 0L;
436 /*------------ Update the title bar -----------*/
437 if(state->mangled_header) {
438 km = do_index_border(cntxt, folder, stream, msgmap,
439 style, NULL, INDX_HEADER);
440 state->mangled_header = 0;
441 paint_status = 0;
443 else if(mn_get_total(msgmap) > 0) {
444 update_titlebar_message();
446 * If flags aren't available to update the status,
447 * defer it until after all the fetches associated
448 * with building index lines are done (no extra rtts!)...
450 paint_status = !update_titlebar_status();
453 current_index_state = &id;
455 /*------------ draw the index body ---------------*/
456 cur_row = update_index(state, &id);
457 if(F_OFF(F_SHOW_CURSOR, state)){
458 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
459 cur_col = 0;
461 else if(id.status_col >= 0)
462 cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
464 ps_global->redrawer = redraw_index_body;
466 if(paint_status)
467 (void) update_titlebar_status();
469 /*------------ draw the footer/key menus ---------------*/
470 if(state->mangled_footer) {
471 if(!state->painted_footer_on_startup){
472 if(km_popped){
473 FOOTER_ROWS(state) = 3;
474 clearfooter(state);
477 km = do_index_border(cntxt, folder, stream, msgmap, style,
478 &which_keys, INDX_FOOTER);
479 if(km_popped){
480 FOOTER_ROWS(state) = 1;
481 mark_keymenu_dirty();
485 state->mangled_footer = 0;
488 state->painted_body_on_startup = 0;
489 state->painted_footer_on_startup = 0;
491 /*-- Display any queued message (eg, new mail, command result --*/
492 if(km_popped){
493 FOOTER_ROWS(state) = 3;
494 mark_status_unknown();
497 display_message(ch);
498 if(km_popped){
499 FOOTER_ROWS(state) = 1;
500 mark_status_unknown();
503 if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
504 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
507 cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
508 MoveCursor(cur_row, cur_col);
510 /* Let read_command do the fflush(stdout) */
512 /*---------- Read command and validate it ----------------*/
513 #ifdef MOUSE
514 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
515 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
516 state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
517 state->ttyo->screen_cols);
518 #endif
519 #ifdef _WINDOWS
520 mswin_setscrollcallback (index_scroll_callback);
521 mswin_sethelptextcallback((stream == state->mail_stream)
522 ? pcpine_help_index
523 : pcpine_help_index_simple);
524 mswin_setviewinwindcallback(view_in_new_window);
525 #endif
526 ch = READ_COMMAND(&utf8str);
527 #ifdef MOUSE
528 clear_mfunc(mouse_in_content);
529 #endif
530 #ifdef _WINDOWS
531 mswin_setscrollcallback(NULL);
532 mswin_sethelptextcallback(NULL);
533 mswin_setviewinwindcallback(NULL);
534 #endif
536 cmd = menu_command(ch, km);
538 if(km_popped)
539 switch(cmd){
540 case MC_NONE :
541 case MC_OTHER :
542 case MC_RESIZE :
543 case MC_REPAINT :
544 km_popped++;
545 break;
547 default:
548 clearfooter(state);
549 break;
552 /*----------- Execute the command ------------------*/
553 switch(cmd){
555 /*---------- Roll keymenu ----------*/
556 case MC_OTHER :
557 if(F_OFF(F_USE_FK, ps_global))
558 warn_other_cmds();
560 what_keymenu = NextMenu;
561 state->mangled_footer = 1;
562 break;
565 /*---------- Scroll line up ----------*/
566 case MC_CHARUP :
567 (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
568 (style == MsgIndex
569 || style == MultiMsgIndex
570 || style == ZoomIndex)
571 ? MsgIndx
572 : (style == ThreadIndex)
573 ? ThrdIndx
574 : View,
575 &force);
576 if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
577 index_scroll_up(1L);
579 break;
582 /*---------- Scroll line down ----------*/
583 case MC_CHARDOWN :
585 * Special Page framing handling here. If we
586 * did something that should scroll-by-a-line, frame
587 * the page by hand here rather than leave it to the
588 * page-by-page framing in update_index()...
590 (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
591 (style == MsgIndex
592 || style == MultiMsgIndex
593 || style == ZoomIndex)
594 ? MsgIndx
595 : (style == ThreadIndex)
596 ? ThrdIndx
597 : View,
598 &force);
599 for(j = 0L, k = i = id.msg_at_top; ; i++){
600 if(!msgline_hidden(stream, msgmap, i, 0)){
601 k = i;
602 if(j++ >= id.lines_per_page)
603 break;
606 if(i >= mn_get_total(msgmap)){
607 k = 0L; /* don't scroll */
608 break;
612 if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
613 index_scroll_down(1L);
615 break;
618 /*---------- Scroll page up ----------*/
619 case MC_PAGEUP :
620 j = -1L;
621 for(k = i = id.msg_at_top; ; i--){
622 if(!msgline_hidden(stream, msgmap, i, 0)){
623 k = i;
624 if(++j >= id.lines_per_page){
625 if((id.msg_at_top = i) == 1L)
626 q_status_message(SM_ORDER, 0, 1, _("First Index page"));
628 break;
632 if(i <= 1L){
633 if((!THREADING() && mn_get_cur(msgmap) == 1L)
634 || (THREADING()
635 && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
636 stream,
638 FSF_SKIP_CHID)))
639 q_status_message(SM_ORDER, 0, 1,
640 _("Already at start of Index"));
642 break;
646 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
647 mn_set_cur(msgmap, k);
649 break;
652 /*---------- Scroll page forward ----------*/
653 case MC_PAGEDN :
654 j = -1L;
655 for(k = i = id.msg_at_top; ; i++){
656 if(!msgline_hidden(stream, msgmap, i, 0)){
657 k = i;
658 if(++j >= id.lines_per_page){
659 if(i+id.lines_per_page > mn_get_total(msgmap))
660 q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
662 id.msg_at_top = i;
663 break;
667 if(i >= mn_get_total(msgmap)){
668 if(mn_get_cur(msgmap) == k)
669 q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
671 break;
675 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
676 mn_set_cur(msgmap, k);
678 break;
681 /*---------- Scroll to first page ----------*/
682 case MC_HOMEKEY :
683 if((mn_get_total(msgmap) > 0L)
684 && (mn_total_cur(msgmap) <= 1L)){
685 long cur_msg = mn_get_cur(msgmap), selected;
687 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
688 do {
689 selected = cur_msg;
690 mn_dec_cur(stream, msgmap, MH_NONE);
691 cur_msg = mn_get_cur(msgmap);
693 while(selected != cur_msg);
695 else
696 cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
697 mn_set_cur(msgmap, cur_msg);
698 q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
700 break;
702 /*---------- Scroll to last page ----------*/
703 case MC_ENDKEY :
704 if((mn_get_total(msgmap) > 0L)
705 && (mn_total_cur(msgmap) <= 1L)){
706 long cur_msg = mn_get_cur(msgmap), selected;
708 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
709 do {
710 selected = cur_msg;
711 mn_inc_cur(stream, msgmap, MH_NONE);
712 cur_msg = mn_get_cur(msgmap);
714 while(selected != cur_msg);
716 else
717 cur_msg = mn_get_total(msgmap);
718 mn_set_cur(msgmap, cur_msg);
719 q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
721 break;
723 /*---------- Search (where is command) ----------*/
724 case MC_WHEREIS :
725 index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
726 state->mangled_footer = 1;
727 break;
730 /*-------------- jump command -------------*/
731 /* NOTE: preempt the process_cmd() version because
732 * we need to get at the number..
734 case MC_JUMP :
735 j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
736 (style == ThreadIndex) ? ThrdIndx : MsgIndx);
737 if(j > 0L){
738 if(style == ThreadIndex){
739 PINETHRD_S *thrd;
741 thrd = find_thread_by_number(stream, msgmap, j, NULL);
743 if(thrd && thrd->rawno)
744 mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
746 else{
747 /* jump to message */
748 if(mn_total_cur(msgmap) > 1L){
749 mn_reset_cur(msgmap, j);
751 else{
752 mn_set_cur(msgmap, j);
756 id.msg_at_top = 0L;
759 state->mangled_footer = 1;
760 break;
763 case MC_VIEW_ENTRY : /* only happens in thread index */
766 * If the feature F_THRD_AUTO_VIEW is turned on and there
767 * is only one message in the thread, then we skip the index
768 * view of the thread and go straight to the message view.
770 view_a_thread:
771 if(THRD_AUTO_VIEW() && style == ThreadIndex){
772 PINETHRD_S *thrd;
774 thrd = fetch_thread(stream,
775 mn_m2raw(msgmap, mn_get_cur(msgmap)));
776 if(thrd
777 && (count_lflags_in_thread(stream, thrd,
778 msgmap, MN_NONE) == 1)){
779 if(view_thread(state, stream, msgmap, 1)){
780 state->view_skipped_index = 1;
781 cmd = MC_VIEW_TEXT;
782 goto do_the_default;
787 if(view_thread(state, stream, msgmap, 1)){
788 ps_global->next_screen = mail_index_screen;
789 ps_global->redrawer = NULL;
790 current_index_state = NULL;
791 if(id.entry_state)
792 fs_give((void **)&(id.entry_state));
794 return(0);
797 break;
800 case MC_THRDINDX :
801 msgmap->top = msgmap->top_after_thrd;
802 if(unview_thread(state, stream, msgmap)){
803 state->next_screen = mail_index_screen;
804 state->view_skipped_index = 0;
805 state->mangled_screen = 1;
806 ps_global->redrawer = NULL;
807 current_index_state = NULL;
808 if(id.entry_state)
809 fs_give((void **)&(id.entry_state));
811 return(0);
814 break;
817 #ifdef MOUSE
818 case MC_MOUSE:
820 MOUSEPRESS mp;
821 int new_cur;
823 mouse_get_last (NULL, &mp);
824 mp.row -= 2;
826 for(i = id.msg_at_top;
827 mp.row >= 0 && i <= mn_get_total(msgmap);
828 i++)
829 if(!msgline_hidden(stream, msgmap, i, 0)){
830 mp.row--;
831 new_cur = i;
834 if(mn_get_total(msgmap) && mp.row < 0){
835 switch(mp.button){
836 case M_BUTTON_LEFT :
837 if(mn_total_cur(msgmap) == 1L)
838 mn_set_cur(msgmap, new_cur);
840 if(mp.flags & M_KEY_CONTROL){
841 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
842 (void) select_by_current(state, msgmap, MsgIndx);
845 else if(!(mp.flags & M_KEY_SHIFT)){
846 if (THREADING()
847 && mp.col >= 0
848 && mp.col == id.plus_col
849 && style != ThreadIndex){
850 collapse_or_expand(state, stream, msgmap,
851 mn_get_cur(msgmap));
853 else if (mp.doubleclick){
854 if(mp.button == M_BUTTON_LEFT){
855 if(stream == state->mail_stream){
856 if(THRD_INDX()){
857 cmd = MC_VIEW_ENTRY;
858 goto view_a_thread;
860 else{
861 cmd = MC_VIEW_TEXT;
862 goto do_the_default;
866 ps_global->redrawer = NULL;
867 current_index_state = NULL;
868 if(id.entry_state)
869 fs_give((void **)&(id.entry_state));
871 return(0);
876 break;
878 case M_BUTTON_MIDDLE:
879 break;
881 case M_BUTTON_RIGHT :
882 #ifdef _WINDOWS
883 if (!mp.doubleclick){
884 if(mn_total_cur(msgmap) == 1L)
885 mn_set_cur(msgmap, new_cur);
887 cur_row = update_index(state, &id);
889 index_popup(style, stream, msgmap, TRUE);
891 #endif
892 break;
895 else{
896 switch(mp.button){
897 case M_BUTTON_LEFT :
898 break;
900 case M_BUTTON_MIDDLE :
901 break;
903 case M_BUTTON_RIGHT :
904 #ifdef _WINDOWS
905 index_popup(style, stream, msgmap, FALSE);
906 #endif
907 break;
912 break;
913 #endif /* MOUSE */
915 /*---------- Resize ----------*/
916 case MC_RESIZE:
918 * If we were smarter we could do the
919 * IC_CLEAR_WIDTHS_DONE trick here. The problem is
920 * that entire columns of the format can go away or
921 * appear because the width gets smaller or larger,
922 * so in that case we need to re-do. If we could tell
923 * when that happened or not we could set the flag
924 * selectively.
926 clear_index_cache(stream, 0);
927 reset_index_border();
928 break;
930 case MC_QUOTA:
931 cmd_quota(state);
933 /*---------- Redraw ----------*/
934 case MC_REPAINT :
935 force = 1; /* check for new mail! */
936 reset_index_border();
937 break;
940 /*---------- No op command ----------*/
941 case MC_NONE :
942 break; /* no op check for new mail */
945 /*--------- keystroke not bound to command --------*/
946 case MC_CHARRIGHT :
947 case MC_CHARLEFT :
948 case MC_GOTOBOL :
949 case MC_GOTOEOL :
950 case MC_UNKNOWN :
951 if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
952 q_status_message(SM_ORDER, 0, 1, "Already in Index");
953 else
954 bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
956 break;
959 case MC_COLLAPSE :
960 thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
961 break;
963 case MC_DELETE :
964 case MC_UNDELETE :
965 case MC_REPLY :
966 case MC_FORWARD :
967 case MC_TAKE :
968 case MC_SAVE :
969 case MC_EXPORT :
970 case MC_BOUNCE :
971 case MC_PIPE :
972 case MC_FLAG :
973 case MC_SELCUR :
974 { int collapsed = 0;
975 unsigned long rawno;
976 PINETHRD_S *thrd = NULL;
978 if(THREADING()){
979 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
980 if(rawno)
981 thrd = fetch_thread(stream, rawno);
983 collapsed = thrd && thrd->next
984 && get_lflag(stream, NULL, rawno, MN_COLL);
987 if(collapsed){
988 thread_command(state, stream, msgmap,
989 ch, -FOOTER_ROWS(state));
990 /* increment current */
991 if(cmd == MC_DELETE){
992 advance_cur_after_delete(state, stream, msgmap,
993 (style == MsgIndex
994 || style == MultiMsgIndex
995 || style == ZoomIndex)
996 ? MsgIndx
997 : (style == ThreadIndex)
998 ? ThrdIndx
999 : View);
1001 else if((cmd == MC_SELCUR
1002 && (state->ugly_consider_advancing_bit
1003 || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
1004 || (state->ugly_consider_advancing_bit
1005 && cmd == MC_SAVE
1006 && F_ON(F_SAVE_ADVANCES, state))){
1007 mn_inc_cur(stream, msgmap, MH_NONE);
1010 else
1011 goto do_the_default;
1014 break;
1017 case MC_UTF8:
1018 bogus_utf8_command(utf8str, NULL);
1019 break;
1022 /*---------- First HELP command with menu hidden ----------*/
1023 case MC_HELP :
1024 if(FOOTER_ROWS(state) == 1 && km_popped == 0){
1025 km_popped = 2;
1026 mark_status_unknown();
1027 mark_keymenu_dirty();
1028 state->mangled_footer = 1;
1029 break;
1031 /* else fall thru to normal default */
1034 /*---------- Default -- all other command ----------*/
1035 default:
1036 do_the_default:
1037 if(stream == state->mail_stream){
1038 msgmap->top = id.msg_at_top;
1039 process_cmd(state, stream, msgmap, cmd,
1040 (style == MsgIndex
1041 || style == MultiMsgIndex
1042 || style == ZoomIndex)
1043 ? MsgIndx
1044 : (style == ThreadIndex)
1045 ? ThrdIndx
1046 : View,
1047 &force);
1048 if(state->next_screen != SCREEN_FUN_NULL){
1049 ps_global->redrawer = NULL;
1050 current_index_state = NULL;
1051 if(id.entry_state)
1052 fs_give((void **)&(id.entry_state));
1054 return(0);
1056 else{
1057 if(stream != state->mail_stream){
1059 * Must have had an failed open. repair our
1060 * pointers...
1062 id.stream = stream = state->mail_stream;
1063 id.msgmap = msgmap = state->msgmap;
1066 current_index_state = &id;
1068 if(cmd == MC_ZOOM && THRD_INDX())
1069 id.msg_at_top = 0L;
1072 else{ /* special processing */
1073 switch(cmd){
1074 case MC_HELP :
1075 helper(h_simple_index,
1076 (!strcmp(folder, INTERRUPTED_MAIL))
1077 ? _("HELP FOR SELECTING INTERRUPTED MSG")
1078 : _("HELP FOR SELECTING POSTPONED MSG"),
1079 HLPD_SIMPLE);
1080 state->mangled_screen = 1;
1081 break;
1083 case MC_DELETE : /* delete */
1084 dprint((3, "Special delete: msg %s\n",
1085 long2string(mn_get_cur(msgmap))));
1087 long raw, t;
1088 int del = 0;
1089 MESSAGECACHE *mc;
1091 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1092 if(raw > 0L && stream
1093 && raw <= stream->nmsgs
1094 && (mc = mail_elt(stream, raw))
1095 && !mc->deleted){
1096 if((t = mn_get_cur(msgmap)) > 0L)
1097 clear_index_cache_ent(stream, t, 0);
1099 mail_setflag(stream,long2string(raw),"\\DELETED");
1100 update_titlebar_status();
1101 del++;
1104 q_status_message1(SM_ORDER, 0, 1,
1105 del ? _("Message %s deleted") : _("Message %s already deleted"),
1106 long2string(mn_get_cur(msgmap)));
1109 break;
1111 case MC_UNDELETE : /* UNdelete */
1112 dprint((3, "Special UNdelete: msg %s\n",
1113 long2string(mn_get_cur(msgmap))));
1115 long raw, t;
1116 int del = 0;
1117 MESSAGECACHE *mc;
1119 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1120 if(raw > 0L && stream
1121 && raw <= stream->nmsgs
1122 && (mc = mail_elt(stream, raw))
1123 && mc->deleted){
1124 if((t = mn_get_cur(msgmap)) > 0L)
1125 clear_index_cache_ent(stream, t, 0);
1127 mail_clearflag(stream, long2string(raw),
1128 "\\DELETED");
1129 update_titlebar_status();
1130 del++;
1133 q_status_message1(SM_ORDER, 0, 1,
1134 del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
1135 long2string(mn_get_cur(msgmap)));
1138 break;
1140 case MC_EXIT : /* exit */
1141 ps_global->redrawer = NULL;
1142 current_index_state = NULL;
1143 if(id.entry_state)
1144 fs_give((void **)&(id.entry_state));
1146 return(1);
1148 case MC_SELECT : /* select */
1149 ps_global->redrawer = NULL;
1150 current_index_state = NULL;
1151 if(id.entry_state)
1152 fs_give((void **)&(id.entry_state));
1154 return(0);
1156 case MC_PREVITEM : /* previous */
1157 mn_dec_cur(stream, msgmap, MH_NONE);
1158 break;
1160 case MC_NEXTITEM : /* next */
1161 mn_inc_cur(stream, msgmap, MH_NONE);
1162 break;
1164 default :
1165 bogus_command(ch, NULL);
1166 break;
1169 } /* The big switch */
1170 } /* the BIG while loop! */
1175 /*----------------------------------------------------------------------
1176 Manage index body painting
1178 Args: state - pine struct containing selected message data
1179 index_state - struct describing what's currently displayed
1181 Returns: screen row number of first highlighted message
1183 The idea is pretty simple. Maintain an array of index line id's that
1184 are displayed and their hilited state. Decide what's to be displayed
1185 and update the screen appropriately. All index screen painting
1186 is done here. Pretty simple, huh?
1187 ----*/
1189 update_index(struct pine *state, struct index_state *screen)
1191 int i, retval = -1, row, already_fetched = 0;
1192 long n, visible;
1193 PINETHRD_S *thrd = NULL;
1194 int we_cancel = 0;
1196 dprint((7, "--update_index--\n"));
1198 if(!screen)
1199 return(-1);
1201 #ifdef _WINDOWS
1202 mswin_beginupdate();
1203 #endif
1205 /*---- reset the works if necessary ----*/
1206 if(state->mangled_body){
1207 ClearBody();
1208 if(screen->entry_state){
1209 fs_give((void **)&(screen->entry_state));
1210 screen->lines_per_page = 0;
1214 state->mangled_body = 0;
1216 /*---- make sure we have a place to write state ----*/
1217 if(screen->lines_per_page
1218 != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1219 - HEADER_ROWS(state))){
1220 i = screen->lines_per_page;
1221 screen->lines_per_page
1222 = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1223 - HEADER_ROWS(state));
1224 if(!i){
1225 size_t len = screen->lines_per_page * sizeof(struct entry_state);
1226 screen->entry_state = (struct entry_state *) fs_get(len);
1228 else
1229 fs_resize((void **)&(screen->entry_state),
1230 (size_t)screen->lines_per_page);
1232 for(; i < screen->lines_per_page; i++) /* init new entries */
1233 memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
1236 /*---- figure out the first message on the display ----*/
1237 if(screen->msg_at_top < 1L
1238 || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
1239 screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
1240 screen->msg_at_top,
1241 screen->lines_per_page);
1243 else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1244 long i, j, k;
1246 /* scroll back a page at a time until current is displayed */
1247 while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1248 for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
1249 i > 0L && j > 0L;
1250 j--)
1251 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1252 k = j;
1253 i--;
1256 if(i == screen->lines_per_page)
1257 break; /* can't scroll back ? */
1258 else
1259 screen->msg_at_top = k;
1262 else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
1263 + screen->lines_per_page){
1264 long i, j, k;
1266 while(1){
1267 for(i = screen->lines_per_page, j = k = screen->msg_at_top;
1268 j <= mn_get_total(screen->msgmap) && i > 0L;
1269 j++)
1270 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1271 k = j;
1272 i--;
1273 if(mn_get_cur(screen->msgmap) <= k)
1274 break;
1277 if(mn_get_cur(screen->msgmap) <= k)
1278 break;
1279 else{
1280 /* set msg_at_top to next displayed message */
1281 for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
1282 if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
1283 k = i;
1284 break;
1287 screen->msg_at_top = k;
1292 #ifdef _WINDOWS
1294 * Set scroll range and position. Note that message numbers start at 1
1295 * while scroll position starts at 0.
1298 if(THREADING() && sp_viewing_a_thread(screen->stream)
1299 && mn_get_total(screen->msgmap) > 1L){
1300 long x = 0L, range = 0L, lowest_numbered_msg;
1303 * We know that all visible messages in the thread are marked
1304 * with MN_CHID2.
1306 thrd = fetch_thread(screen->stream,
1307 mn_m2raw(screen->msgmap,
1308 mn_get_cur(screen->msgmap)));
1309 if(thrd && thrd->top && thrd->top != thrd->rawno)
1310 thrd = fetch_thread(screen->stream, thrd->top);
1312 if(thrd){
1313 if(mn_get_revsort(screen->msgmap)){
1314 n = mn_raw2m(screen->msgmap, thrd->rawno);
1315 while(n > 1L && get_lflag(screen->stream, screen->msgmap,
1316 n-1L, MN_CHID2))
1317 n--;
1319 lowest_numbered_msg = n;
1321 else
1322 lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
1325 if(thrd){
1326 n = lowest_numbered_msg;
1327 for(; n <= mn_get_total(screen->msgmap); n++){
1329 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1330 break;
1332 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
1333 range++;
1334 if(n < screen->msg_at_top)
1335 x++;
1340 scroll_setrange(screen->lines_per_page, range-1L);
1341 scroll_setpos(x);
1343 else if(THRD_INDX()){
1344 if(any_lflagged(screen->msgmap, MN_HIDE)){
1345 long x = 0L, range;
1347 range = screen->msgmap->visible_threads - 1L;
1348 scroll_setrange(screen->lines_per_page, range);
1349 if(range >= screen->lines_per_page){ /* else not needed */
1350 PINETHRD_S *topthrd;
1351 int thrddir;
1352 long xdir;
1354 /* find top of currently displayed top line */
1355 topthrd = fetch_thread(screen->stream,
1356 mn_m2raw(screen->msgmap,
1357 screen->msg_at_top));
1358 if(topthrd && topthrd->top != topthrd->rawno)
1359 topthrd = fetch_thread(screen->stream, topthrd->top);
1361 if(topthrd){
1363 * Split into two halves to speed up finding scroll pos.
1364 * It's tricky because the thread list always goes from
1365 * past to future but the thrdno's will be reversed if
1366 * the sort is reversed and of course the order on the
1367 * screen will be reversed.
1369 if((!mn_get_revsort(screen->msgmap)
1370 && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
1372 (mn_get_revsort(screen->msgmap)
1373 && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
1375 /* start with head of thread list */
1376 if(topthrd && topthrd->head)
1377 thrd = fetch_thread(screen->stream, topthrd->head);
1378 else
1379 thrd = NULL;
1381 thrddir = 1;
1383 else{
1384 long tailrawno;
1387 * Start with tail thread and work back.
1389 if(mn_get_revsort(screen->msgmap))
1390 tailrawno = mn_m2raw(screen->msgmap, 1L);
1391 else
1392 tailrawno = mn_m2raw(screen->msgmap,
1393 mn_get_total(screen->msgmap));
1395 thrd = fetch_thread(screen->stream, tailrawno);
1396 if(thrd && thrd->top && thrd->top != thrd->rawno)
1397 thrd = fetch_thread(screen->stream, thrd->top);
1399 thrddir = -1;
1403 * x is the scroll position. We try to use the fewest
1404 * number of steps to find it, so we start with either
1405 * the beginning or the end.
1407 if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
1408 x = 0L;
1409 xdir = 1L;
1411 else{
1412 x = range;
1413 xdir = -1L;
1416 while(thrd && thrd != topthrd){
1417 if(!msgline_hidden(screen->stream, screen->msgmap,
1418 mn_raw2m(screen->msgmap,thrd->rawno),
1420 x += xdir;
1422 if(thrddir > 0 && thrd->nextthd)
1423 thrd = fetch_thread(screen->stream, thrd->nextthd);
1424 else if(thrddir < 0 && thrd->prevthd)
1425 thrd = fetch_thread(screen->stream, thrd->prevthd);
1426 else
1427 thrd = NULL;
1431 scroll_setpos(x);
1434 else{
1436 * This works for forward or reverse sort because the thrdno's
1437 * will have been reversed.
1439 thrd = fetch_thread(screen->stream,
1440 mn_m2raw(screen->msgmap, screen->msg_at_top));
1441 if(thrd){
1442 scroll_setrange(screen->lines_per_page,
1443 screen->msgmap->max_thrdno - 1L);
1444 scroll_setpos(thrd->thrdno - 1L);
1448 else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
1449 long x, range;
1451 range = mn_get_total(screen->msgmap) - n - 1L;
1452 scroll_setrange(screen->lines_per_page, range);
1454 if(range >= screen->lines_per_page){ /* else not needed */
1455 if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
1456 for(n = 1, x = 0; n != screen->msg_at_top; n++)
1457 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1458 x++;
1460 else{
1461 for(n = mn_get_total(screen->msgmap), x = range;
1462 n != screen->msg_at_top; n--)
1463 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1464 x--;
1467 scroll_setpos(x);
1470 else{
1471 scroll_setrange(screen->lines_per_page,
1472 mn_get_total(screen->msgmap) - 1L);
1473 scroll_setpos(screen->msg_at_top - 1L);
1475 #endif
1478 * Set up c-client call back to tell us about IMAP envelope arrivals
1479 * Can't do it (easily) if single lines on the screen need information
1480 * about more than a single message before they can be drawn.
1482 if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
1483 && !(THREADING() && (sp_viewing_a_thread(screen->stream)
1484 || ps_global->thread_disp_style == THREAD_MUTTLIKE
1485 || any_lflagged(screen->msgmap, MN_COLL))))
1486 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
1488 if(THRD_INDX())
1489 visible = screen->msgmap->visible_threads;
1490 else if(THREADING() && sp_viewing_a_thread(screen->stream)){
1492 * We know that all visible messages in the thread are marked
1493 * with MN_CHID2.
1495 for(visible = 0L, n = screen->msg_at_top;
1496 visible < (int) screen->lines_per_page
1497 && n <= mn_get_total(screen->msgmap); n++){
1499 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1500 break;
1502 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1503 visible++;
1506 else
1507 visible = mn_get_total(screen->msgmap)
1508 - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
1510 /*---- march thru display lines, painting whatever is needed ----*/
1511 for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
1512 if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
1513 if(screen->entry_state[i].id != LINE_HASH_N){
1514 screen->entry_state[i].hilite = 0;
1515 screen->entry_state[i].bolded = 0;
1516 screen->entry_state[i].msgno = 0L;
1517 screen->entry_state[i].id = LINE_HASH_N;
1518 ClearLine(HEADER_ROWS(state) + i);
1521 else{
1522 ICE_S *ice;
1525 * This changes status_col as a side effect so it has to be
1526 * executed before next line.
1528 ice = build_header_line(state, screen->stream, screen->msgmap,
1529 n, &already_fetched);
1530 if(visible > 0L)
1531 visible--;
1533 if(THRD_INDX()){
1534 unsigned long rawno;
1536 rawno = mn_m2raw(screen->msgmap, n);
1537 if(rawno)
1538 thrd = fetch_thread(screen->stream, rawno);
1541 row = paint_index_line(ice, i,
1542 (THRD_INDX() && thrd) ? thrd->thrdno : n,
1543 screen->status_fld, screen->plus_fld,
1544 screen->arrow_fld, &screen->entry_state[i],
1545 mn_is_cur(screen->msgmap, n),
1546 THRD_INDX()
1547 ? (count_lflags_in_thread(screen->stream,
1548 thrd,
1549 screen->msgmap,
1550 MN_SLCT) > 0)
1551 : get_lflag(screen->stream, screen->msgmap,
1552 n, MN_SLCT));
1553 fflush(stdout);
1554 if(row && retval < 0)
1555 retval = row;
1558 /*--- increment n ---*/
1559 while((visible == -1L || visible > 0L)
1560 && ++n <= mn_get_total(screen->msgmap)
1561 && msgline_hidden(screen->stream, screen->msgmap, n, 0))
1565 if(we_cancel)
1566 cancel_busy_cue(-1);
1568 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
1570 #ifdef _WINDOWS
1571 mswin_endupdate();
1572 #endif
1573 fflush(stdout);
1574 dprint((7, "--update_index done\n"));
1575 return(retval);
1580 /*----------------------------------------------------------------------
1581 Create a string summarizing the message header for index on screen
1583 Args: stream -- mail stream to fetch envelope info from
1584 msgmap -- message number to pine sort mapping
1585 msgno -- Message number to create line for
1587 Result: returns a malloced string
1588 saves string in a cache for next call for same header
1589 ----*/
1590 ICE_S *
1591 build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
1593 return(build_header_work(state, stream, msgmap, msgno,
1594 current_index_state->msg_at_top,
1595 current_index_state->lines_per_page,
1596 already_fetched));
1600 /*----------------------------------------------------------------------
1601 Paint the given index line
1604 Args: ice -- index cache entry
1605 line -- index line number on screen, starting at 0 for first
1606 visible line, 1, 2, ...
1607 msgno -- for painting the message number field
1608 sfld -- field type of the status field, which is
1609 where we'll put the X for selected if necessary
1610 pfld -- field type where the thread indicator symbol goes
1611 afld -- field type of column which corresponds to the
1612 index-format ARROW token
1613 entry -- cache used to help us decide whether or not we need to
1614 redraw the index line or if we can just leave it alone because
1615 we know it is already correct
1616 cur -- is this the current message?
1617 sel -- is this message in the selected set?
1619 Returns: screen row number if this is current message, else 0
1620 ----*/
1622 paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
1623 IndexColType pfld, IndexColType afld, struct entry_state *entry,
1624 int cur, int sel)
1626 COLOR_PAIR *lastc = NULL, *base_color = NULL;
1627 ICE_S *ice;
1628 IFIELD_S *ifield, *previfield = NULL;
1629 IELEM_S *ielem;
1630 int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
1631 int draw_whole_line = 0, draw_partial_line = 0;
1632 int n = MAX_SCREEN_COLS*6;
1633 char draw[MAX_SCREEN_COLS*6+1], *p;
1635 ice = (THRD_INDX() && argice) ? argice->tice : argice;
1637 /* This better not happen! */
1638 if(!ice){
1639 q_status_message3(SM_ORDER | SM_DING, 5, 5,
1640 "NULL ice in paint_index_line: %s, msgno=%s line=%s",
1641 THRD_INDX() ? "THRD_INDX" : "reg index",
1642 comatose(msgno), comatose(line));
1643 dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
1644 THRD_INDX() ? "THRD_INDX" : "reg index",
1645 msgno, line));
1646 return 0;
1649 if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
1650 entry->id = 0L;
1651 entry->msgno = 0L;
1652 draw_whole_line = 1;
1654 else if((cur != entry->hilite) || (sel != entry->bolded)
1655 || (ice->plus != entry->plus)){
1656 draw_partial_line = 1;
1659 if(draw_whole_line || draw_partial_line){
1661 if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
1663 memset(draw, 0, sizeof(draw));
1664 p = draw;
1666 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1668 /* space between fields */
1669 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
1670 *p++ = ' ';
1672 /* message number string is generated on the fly */
1673 if(ifield->ctype == iMessNo){
1674 ielem = ifield->ielem;
1675 if(ielem && ielem->datalen >= ifield->width){
1676 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1677 ielem->data[ifield->width] = '\0';
1678 ielem->data[ielem->datalen] = '\0';
1682 if(ifield->ctype == sfld){
1683 ielem = ifield->ielem;
1684 if(ielem && ielem->datalen > 0){
1685 if(draw_partial_line)
1686 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1688 if(ielem->datalen == 1){
1689 save_schar1 = ielem->data[0];
1690 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1691 if(draw_partial_line)
1692 Writechar(ielem->data[0], 0);
1694 if(ielem->next && ielem->next->datalen){
1695 save_schar2 = ielem->next->data[0];
1696 if(cur)
1697 ielem->next->data[0] = '>';
1699 if(draw_partial_line)
1700 Writechar(ielem->next->data[0], 0);
1703 else if(ielem->datalen > 1){
1704 save_schar1 = ielem->data[0];
1705 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1706 if(draw_partial_line)
1707 Writechar(ielem->data[0], 0);
1709 save_schar2 = ielem->data[1];
1710 if(cur){
1711 ielem->data[1] = '>';
1712 if(draw_partial_line)
1713 Writechar(ielem->data[1], 0);
1718 else if(ifield->ctype == afld){
1720 if(draw_partial_line){
1721 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1722 for(i = 0; i < ifield->width-1; i++)
1723 Writechar(cur ? '-' : ' ', 0);
1725 Writechar(cur ? '>' : ' ', 0);
1728 ielem = ifield->ielem;
1729 if(ielem && ielem->datalen >= ifield->width){
1730 for(i = 0; i < ifield->width-1; i++)
1731 ielem->data[i] = cur ? '-' : ' ';
1733 ielem->data[i] = cur ? '>' : ' ';
1736 else if(ifield->ctype == pfld){
1737 ielem = ifield->ielem;
1738 if(ielem && ielem->datalen > 0){
1739 save_pchar = ielem->data[0];
1740 ielem->data[0] = ice->plus;
1742 if(draw_partial_line){
1743 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1744 Writechar(ielem->data[0], 0);
1745 Writechar(' ', 0);
1750 for(ielem = ifield->ielem;
1751 ielem && ielem->print_format && p-draw < n;
1752 ielem = ielem->next){
1753 char *src;
1754 size_t bytes_added;
1756 src = ielem->data;
1757 bytes_added = utf8_pad_to_width(p, src,
1758 ((n+1)-(p-draw)) * sizeof(char),
1759 ielem->wid, ifield->leftadj);
1760 p += bytes_added;
1763 draw[n] = '\0';
1765 if(ifield->ctype == sfld){
1766 ielem = ifield->ielem;
1767 if(ielem && ielem->datalen > 0){
1768 if(ielem->datalen == 1){
1769 ielem->data[0] = save_schar1;
1770 if(ielem->next && ielem->next->datalen)
1771 ielem->next->data[0] = save_schar2;
1773 else if(ielem->datalen > 1){
1774 ielem->data[0] = save_schar1;
1775 ielem->data[1] = save_schar2;
1779 else if(ifield->ctype == afld){
1780 ielem = ifield->ielem;
1781 if(ielem && ielem->datalen >= ifield->width)
1782 for(i = 0; i < ifield->width; i++)
1783 ielem->data[i] = ' ';
1785 else if(ifield->ctype == pfld){
1786 ielem = ifield->ielem;
1787 if(ielem && ielem->datalen > 0)
1788 ielem->data[0] = save_pchar;
1791 previfield = ifield;
1794 *p = '\0';
1796 if(draw_whole_line)
1797 PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
1799 else{
1800 int uc, ac, do_arrow;
1801 int i, drew_X = 0;
1802 int inverse_hack = 0, need_inverse_hack = 0;
1803 int doing_bold = 0;
1805 /* so we can restore current color at the end */
1806 if((uc=pico_usingcolor()) != 0)
1807 lastc = pico_get_cur_color();
1810 * There are two possible "arrow" cursors. One is the one that
1811 * you get when you are at a slow speed or you turn that slow
1812 * speed one on. It is drawn as part of the status column.
1813 * That one is the one associated with the variable "ac".
1814 * It is always the base_color or the inverse of the base_color.
1816 * The other "arrow" cursor is the one you get by including the
1817 * ARROW token in the index-format. It may be configured to
1818 * be colored.
1820 * The arrow cursors have two special properties that make
1821 * them different from other sections or fields.
1822 * First, the arrow cursors only show up on the current line.
1823 * Second, the arrow cursors are drawn with generated data, not
1824 * data that is present in the passed in data.
1827 /* ac is for the old integrated arrow cursor */
1828 ac = F_ON(F_FORCE_ARROW,ps_global);
1830 /* do_arrow is for the ARROW token in index-format */
1831 do_arrow = (afld != iNothing);
1833 MoveCursor(HEADER_ROWS(ps_global) + line, 0);
1835 /* find the base color for the whole line */
1836 if(cur && !ac && !do_arrow){
1838 * This stanza handles the current line marking in the
1839 * regular, non-arrow-cursor case.
1843 * If the current line has a linecolor, apply the
1844 * appropriate reverse transformation to show it is current.
1846 if(uc && ice->linecolor && ice->linecolor->fg[0]
1847 && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
1848 base_color = apply_rev_color(ice->linecolor,
1849 ps_global->index_color_style);
1851 (void)pico_set_colorp(base_color, PSC_NONE);
1853 else{
1854 inverse_hack++;
1855 if(uc){
1856 COLOR_PAIR *rev;
1858 if((rev = pico_get_rev_color()) != NULL){
1859 base_color = new_color_pair(rev->fg, rev->bg);
1860 (void)pico_set_colorp(base_color, PSC_NONE);
1862 else
1863 base_color = lastc;
1867 else if(uc && ice->linecolor && ice->linecolor->fg[0]
1868 && ice->linecolor->bg[0]
1869 && pico_is_good_colorpair(ice->linecolor)){
1870 (void)pico_set_colorp(ice->linecolor, PSC_NONE);
1871 base_color = ice->linecolor;
1873 else
1874 base_color = lastc;
1876 memset(draw, 0, sizeof(draw));
1877 p = draw;
1879 doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
1881 /* draw each field */
1882 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1884 drew_X = 0;
1887 * Fix up the data for some special cases.
1890 /* message number string is generated on the fly */
1891 if(ifield->ctype == iMessNo){
1892 ielem = ifield->ielem;
1893 if(ielem && ielem->datalen >= ifield->width){
1894 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1895 ielem->data[ifield->width] = '\0';
1896 ielem->data[ielem->datalen] = '\0';
1900 if(ifield->ctype == sfld){
1901 ielem = ifield->ielem;
1902 if(ielem && ielem->datalen > 0){
1903 if(ielem->datalen == 1){
1904 save_schar1 = ielem->data[0];
1905 if(sel && !doing_bold){
1906 ielem->data[0] = 'X';
1907 drew_X++;
1909 else if(ac && cur && ielem->data[0] == ' ')
1910 ielem->data[0] = '-';
1912 if(ielem->next && ielem->next->datalen){
1913 save_schar2 = ielem->next->data[0];
1914 if(ac && cur && ielem->next->data[0] != '\0')
1915 ielem->next->data[0] = '>';
1918 else if(ielem->datalen > 1){
1919 if(sel && !doing_bold){
1920 ielem->data[0] = 'X';
1921 drew_X++;
1923 else if(ac && cur && ielem->data[0] == ' ')
1924 ielem->data[0] = '-';
1926 save_schar2 = ielem->data[1];
1927 if(ac && cur && ielem->data[1] != '\0')
1928 ielem->data[1] = '>';
1932 else if(ifield->ctype == afld && do_arrow && cur){
1934 ielem = ifield->ielem;
1935 if(ielem && ielem->datalen >= ifield->width){
1936 for(i = 0; i < ifield->width-1; i++)
1937 ielem->data[i] = cur ? '-' : ' ';
1939 ielem->data[i] = '>';
1942 else if(ifield->ctype == pfld){
1943 ielem = ifield->ielem;
1944 if(ielem && ielem->datalen > 0){
1945 save_pchar = ielem->data[0];
1946 ielem->data[0] = ice->plus;
1950 /* space between fields */
1951 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
1952 if(inverse_hack)
1953 StartInverse();
1955 Write_to_screen(" ");
1956 if(inverse_hack)
1957 EndInverse();
1960 for(ielem = ifield->ielem; ielem; ielem = ielem->next){
1961 char *src;
1963 src = ielem->data;
1964 utf8_pad_to_width(draw, src, (n+1) * sizeof(char),
1965 ielem->wid, ifield->leftadj);
1966 draw[n] = '\0';
1969 * Switch to color for ielem.
1970 * But don't switch if we drew an X in this column,
1971 * because that overwrites the colored thing, and don't
1972 * switch if this is the ARROW field and this is not
1973 * the current message. ARROW field is only colored for
1974 * the current message.
1975 * And don't switch if current line and type eTypeCol.
1977 if(ielem->color && pico_is_good_colorpair(ielem->color)
1978 && !(do_arrow && ifield->ctype == afld && !cur)
1979 && (!drew_X || ielem != ifield->ielem)
1980 && !(cur && ielem->type == eTypeCol)){
1981 need_inverse_hack = 0;
1982 (void) pico_set_colorp(ielem->color, PSC_NORM);
1984 else
1985 need_inverse_hack = 1;
1987 if(need_inverse_hack && inverse_hack)
1988 StartInverse();
1990 Write_to_screen(draw);
1991 if(need_inverse_hack && inverse_hack)
1992 EndInverse();
1994 (void) pico_set_colorp(base_color, PSC_NORM);
1998 * Restore the data for the special cases.
2001 if(ifield->ctype == sfld){
2002 ielem = ifield->ielem;
2003 if(ielem && ielem->datalen > 0){
2004 if(ielem->datalen == 1){
2005 ielem->data[0] = save_schar1;
2006 if(ielem->next && ielem->next->datalen)
2007 ielem->next->data[0] = save_schar2;
2009 else if(ielem->datalen > 1){
2010 ielem->data[0] = save_schar1;
2011 ielem->data[1] = save_schar2;
2015 else if(ifield->ctype == afld){
2016 ielem = ifield->ielem;
2017 if(ielem && ielem->datalen >= ifield->width)
2018 for(i = 0; i < ifield->width; i++)
2019 ielem->data[i] = ' ';
2021 else if(ifield->ctype == pfld){
2022 ielem = ifield->ielem;
2023 if(ielem && ielem->datalen > 0)
2024 ielem->data[0] = save_pchar;
2027 previfield = ifield;
2030 if(doing_bold)
2031 EndBold();
2033 if(base_color && base_color != lastc && base_color != ice->linecolor)
2034 free_color_pair(&base_color);
2036 if(lastc){
2037 (void)pico_set_colorp(lastc, PSC_NORM);
2038 free_color_pair(&lastc);
2043 entry->hilite = cur;
2044 entry->bolded = sel;
2045 entry->msgno = msgno;
2046 entry->plus = ice->plus;
2047 entry->id = ice->id;
2049 if(!ice->color_lookup_done && pico_usingcolor())
2050 entry->id = 0;
2052 return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
2056 * setup_index_state - hooked onto pith_opt_save_index_state to setup
2057 * current_index_state after setup_{index,thread}_header_widths
2059 void
2060 setup_index_state(int threaded)
2062 if(current_index_state){
2063 if(threaded){
2064 current_index_state->status_col = 0;
2065 current_index_state->status_fld = iStatus;
2066 current_index_state->plus_fld = iNothing;
2067 current_index_state->arrow_fld = iNothing;
2068 } else {
2069 INDEX_COL_S *cdesc, *prevcdesc = NULL;
2070 IndexColType sfld, altfld, plusfld, arrowfld;
2071 int width, fld, col, pluscol, scol, altcol;
2073 col = 0;
2074 scol = -1;
2075 sfld = iNothing;
2076 altcol = -1;
2077 altfld = iNothing;
2078 /* figure out which field is status field */
2079 for(cdesc = ps_global->index_disp_format, fld = 0;
2080 cdesc->ctype != iNothing;
2081 cdesc++){
2082 width = cdesc->width;
2083 if(width == 0)
2084 continue;
2086 /* space between columns */
2087 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2088 col++;
2090 if(cdesc->ctype == iStatus){
2091 scol = col;
2092 sfld = cdesc->ctype;
2093 break;
2096 if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
2097 scol = col;
2098 sfld = cdesc->ctype;
2099 break;
2102 if(cdesc->ctype == iMessNo){
2103 altcol = col;
2104 altfld = cdesc->ctype;
2107 col += width;
2108 fld++;
2109 prevcdesc = cdesc;
2112 if(sfld == iNothing){
2113 if(altcol == -1){
2114 scol = 0;
2116 else{
2117 scol = altcol;
2118 sfld = altfld;
2123 current_index_state->status_col = scol;
2124 current_index_state->status_fld = sfld;
2126 col = 0;
2127 plusfld = iNothing;
2128 pluscol = -1;
2129 prevcdesc = NULL;
2130 /* figure out which column to use for threading '+' */
2131 if(THREADING()
2132 && ps_global->thread_disp_style != THREAD_NONE
2133 && ps_global->VAR_THREAD_MORE_CHAR[0]
2134 && ps_global->VAR_THREAD_EXP_CHAR[0])
2135 for(cdesc = ps_global->index_disp_format, fld = 0;
2136 cdesc->ctype != iNothing;
2137 cdesc++){
2138 width = cdesc->width;
2139 if(width == 0)
2140 continue;
2142 /* space between columns */
2143 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2144 col++;
2146 if((cdesc->ctype == iSubject
2147 || cdesc->ctype == iSubjectText
2148 || cdesc->ctype == iSubjKey
2149 || cdesc->ctype == iSubjKeyText
2150 || cdesc->ctype == iSubjKeyInit
2151 || cdesc->ctype == iSubjKeyInitText)
2152 && (ps_global->thread_disp_style == THREAD_STRUCT
2153 || ps_global->thread_disp_style == THREAD_MUTTLIKE
2154 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
2155 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
2156 plusfld = cdesc->ctype;
2157 pluscol = col;
2158 break;
2161 if((cdesc->ctype == iFrom
2162 || cdesc->ctype == iFromToNotNews
2163 || cdesc->ctype == iFromTo
2164 || cdesc->ctype == iAddress
2165 || cdesc->ctype == iMailbox)
2166 && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
2167 || ps_global->thread_disp_style == THREAD_INDENT_FROM2
2168 || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
2169 plusfld = cdesc->ctype;
2170 pluscol = col;
2171 break;
2174 col += width;
2175 fld++;
2176 prevcdesc = cdesc;
2179 current_index_state->plus_fld = plusfld;
2180 current_index_state->plus_col = pluscol;
2182 arrowfld = iNothing;
2183 /* figure out which field is arrow field, if any */
2184 for(cdesc = ps_global->index_disp_format, fld = 0;
2185 cdesc->ctype != iNothing;
2186 cdesc++){
2187 width = cdesc->width;
2188 if(width == 0)
2189 continue;
2191 if(cdesc->ctype == iArrow){
2192 arrowfld = cdesc->ctype;
2193 break;
2196 fld++;
2199 current_index_state->arrow_fld = arrowfld;
2206 * insert_condensed_thread_cue - used on pith hook to add decoration to
2207 * subject or from text to show condensed thread info
2210 condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
2211 char **fieldstr, size_t *strsize, int width, int collapsed)
2213 if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
2215 * WARNING!
2216 * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
2217 * and VAR_THREAD_EXP_CHAR[0] are ascii.
2218 * Could do something similar to the conversions done with keyword
2219 * initials in key_str.
2221 if(ice)
2222 ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
2223 : (thd && thd->next)
2224 ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
2226 if(strsize && *strsize > 0 && width != 0){
2227 *(*fieldstr)++ = ' ';
2228 (*strsize)--;
2229 if(width > 0)
2230 width--;
2233 if(strsize && *strsize > 0 && width != 0){
2234 *(*fieldstr)++ = ' ';
2235 (*strsize)--;
2236 if(width > 0)
2237 width--;
2241 return(width);
2246 truncate_subj_and_from_strings(void)
2248 return 1;
2253 * paint_index_hline - paint index line given what we got
2255 void
2256 paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
2258 PINETHRD_S *thrd;
2261 * Trust only what we get back that isn't bogus since
2262 * we were prevented from doing any fetches and such...
2264 if((ps_global->redrawer == redraw_index_body
2265 || ps_global->prev_screen == mail_index_screen)
2266 && current_index_state
2267 && current_index_state->stream == stream
2268 && !ps_global->msgmap->hilited){
2269 int line;
2272 * This test isn't right if there are hidden lines. The line will
2273 * fail the test because it seems like it is past the end of the
2274 * screen but since the hidden lines don't take up space the line
2275 * might actually be on the screen. Don't know that it is worth
2276 * it to fix this, though, since you may have to file through
2277 * many hidden lines before finding the visible ones. I'm not sure
2278 * if the logic inside the if is correct when we do pass the
2279 * top-level test. Leave it for now. Hubert - 2002-06-28
2281 if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
2282 && line < current_index_state->lines_per_page){
2283 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
2284 long n;
2285 long zoomhide, collapsehide;
2287 zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
2288 collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
2291 * Line is visible if it is selected and not hidden due to
2292 * thread collapse, or if there is no zooming happening and
2293 * it is not hidden due to thread collapse.
2295 for(line = 0, n = current_index_state->msg_at_top;
2296 n != msgno;
2297 n++)
2298 if((zoomhide
2299 && get_lflag(stream, current_index_state->msgmap,
2300 n, MN_SLCT)
2301 && (!collapsehide
2302 || !get_lflag(stream, current_index_state->msgmap, n,
2303 MN_CHID)))
2305 (!zoomhide
2306 && !get_lflag(stream, current_index_state->msgmap,
2307 n, MN_CHID)))
2308 line++;
2311 thrd = NULL;
2312 if(THRD_INDX()){
2313 unsigned long rawno;
2315 rawno = mn_m2raw(current_index_state->msgmap, msgno);
2316 if(rawno)
2317 thrd = fetch_thread(stream, rawno);
2320 paint_index_line(ice, line,
2321 (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
2322 current_index_state->status_fld,
2323 current_index_state->plus_fld,
2324 current_index_state->arrow_fld,
2325 &current_index_state->entry_state[line],
2326 mn_is_cur(current_index_state->msgmap, msgno),
2327 THRD_INDX()
2328 ? (count_lflags_in_thread(stream, thrd,
2329 current_index_state->msgmap,
2330 MN_SLCT) > 0)
2331 : get_lflag(stream, current_index_state->msgmap,
2332 msgno, MN_SLCT));
2333 fflush(stdout);
2342 * pine_imap_env -- C-client's telling us an envelope just arrived
2343 * from the server. Use it if we can...
2345 void
2346 pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
2348 MESSAGECACHE *mc;
2350 dprint((7, "imap_env(%ld)\n", rawno));
2351 if(stream && !sp_mail_box_changed(stream)
2352 && stream == ps_global->mail_stream
2353 && rawno > 0L && rawno <= stream->nmsgs
2354 && (mc = mail_elt(stream,rawno))
2355 && mc->valid
2356 && mc->rfc822_size
2357 && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
2358 INDEXDATA_S idata;
2359 ICE_S *ice;
2361 memset(&idata, 0, sizeof(INDEXDATA_S));
2362 idata.no_fetch = 1;
2363 idata.size = mc->rfc822_size;
2364 idata.rawno = rawno;
2365 idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
2366 idata.stream = stream;
2368 index_data_env(&idata, env);
2371 * Look for resent-to already in MAILCACHE data
2373 if(mc->private.msg.header.text.data){
2374 STRINGLIST *lines;
2375 SIZEDTEXT szt;
2376 static char *linelist[] = {"resent-to" , NULL};
2378 if(mail_match_lines(lines = new_strlst(linelist),
2379 mc->private.msg.lines, 0L)){
2380 idata.valid_resent_to = 1;
2381 memset(&szt, 0, sizeof(SIZEDTEXT));
2382 textcpy(&szt, &mc->private.msg.header.text);
2383 mail_filter((char *) szt.data, szt.size, lines, 0L);
2384 idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
2385 if(szt.data)
2386 fs_give((void **) &szt.data);
2389 free_strlst(&lines);
2392 ice = (*format_index_line)(&idata);
2393 if(idata.bogus)
2394 clear_ice(&ice);
2395 else
2396 paint_index_hline(stream, idata.msgno, ice);
2401 /*----------------------------------------------------------------------
2402 Scroll to specified postion.
2405 Args: pos - position to scroll to.
2407 Returns: TRUE - did the scroll operation.
2408 FALSE - was not able to do the scroll operation.
2409 ----*/
2411 index_scroll_to_pos (long int pos)
2413 static short bad_timing = 0;
2414 long i, j, k;
2416 if(bad_timing)
2417 return (FALSE);
2420 * Put the requested line at the top of the screen...
2424 * Starting at msg 'pos' find next visible message.
2426 for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
2427 if(!msgline_hidden(current_index_state->stream,
2428 current_index_state->msgmap, i, 0)){
2429 current_index_state->msg_at_top = i;
2430 break;
2435 * If single selection, move selected message to be on the screen.
2437 if (mn_total_cur(current_index_state->msgmap) == 1L) {
2438 if (current_index_state->msg_at_top >
2439 mn_get_cur (current_index_state->msgmap)) {
2440 /* Selection was above screen, move to top of screen. */
2441 mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
2443 else {
2444 /* Scan through the screen. If selection found, leave where is.
2445 * Otherwise, move to end of screen */
2446 for( i = current_index_state->msg_at_top,
2447 j = current_index_state->lines_per_page;
2448 i != mn_get_cur(current_index_state->msgmap) &&
2449 i <= mn_get_total(current_index_state->msgmap) &&
2450 j > 0L;
2451 i++) {
2452 if(!msgline_hidden(current_index_state->stream,
2453 current_index_state->msgmap, i, 0)){
2454 j--;
2455 k = i;
2458 if(j <= 0L)
2459 /* Move to end of screen. */
2460 mn_set_cur(current_index_state->msgmap, k);
2464 bad_timing = 0;
2465 return (TRUE);
2470 /*----------------------------------------------------------------------
2471 Adjust the index display state down a line
2473 Args: scroll_count -- number of lines to scroll
2475 Returns: TRUE - did the scroll operation.
2476 FALSE - was not able to do the scroll operation.
2477 ----*/
2479 index_scroll_down(long int scroll_count)
2481 static short bad_timing = 0;
2482 long i, j, k;
2483 long cur, total;
2485 if(bad_timing)
2486 return (FALSE);
2488 bad_timing = 1;
2491 j = -1L;
2492 total = mn_get_total (current_index_state->msgmap);
2493 for(k = i = current_index_state->msg_at_top; ; i++){
2495 /* Only examine non-hidden messages. */
2496 if(!msgline_hidden(current_index_state->stream,
2497 current_index_state->msgmap, i, 0)){
2498 /* Remember this message */
2499 k = i;
2500 /* Increment count of lines. */
2501 if (++j >= scroll_count) {
2502 /* Counted enough lines, stop. */
2503 current_index_state->msg_at_top = k;
2504 break;
2508 /* If at last message, stop. */
2509 if (i >= total){
2510 current_index_state->msg_at_top = k;
2511 break;
2516 * If not multiple selection, see if selected message visable. if not
2517 * set it to last visable message.
2519 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2520 j = 0L;
2521 cur = mn_get_cur (current_index_state->msgmap);
2522 for (i = current_index_state->msg_at_top; i <= total; ++i) {
2523 if(!msgline_hidden(current_index_state->stream,
2524 current_index_state->msgmap, i, 0)){
2525 if (++j >= current_index_state->lines_per_page) {
2526 break;
2528 if (i == cur)
2529 break;
2532 if (i != cur)
2533 mn_set_cur(current_index_state->msgmap,
2534 current_index_state->msg_at_top);
2537 bad_timing = 0;
2538 return (TRUE);
2543 /*----------------------------------------------------------------------
2544 Adjust the index display state up a line
2546 Args: scroll_count -- number of lines to scroll
2548 Returns: TRUE - did the scroll operation.
2549 FALSE - was not able to do the scroll operation.
2551 ----*/
2553 index_scroll_up(long int scroll_count)
2555 static short bad_timing = 0;
2556 long i, j, k;
2557 long cur;
2559 if(bad_timing)
2560 return(FALSE);
2562 bad_timing = 1;
2564 j = -1L;
2565 for(k = i = current_index_state->msg_at_top; ; i--){
2567 /* Only examine non-hidden messages. */
2568 if(!msgline_hidden(current_index_state->stream,
2569 current_index_state->msgmap, i, 0)){
2570 /* Remember this message */
2571 k = i;
2572 /* Increment count of lines. */
2573 if (++j >= scroll_count) {
2574 /* Counted enough lines, stop. */
2575 current_index_state->msg_at_top = k;
2576 break;
2580 /* If at first message, stop */
2581 if (i <= 1L){
2582 current_index_state->msg_at_top = k;
2583 break;
2589 * If not multiple selection, see if selected message visable. if not
2590 * set it to last visable message.
2592 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2593 j = 0L;
2594 cur = mn_get_cur (current_index_state->msgmap);
2595 for ( i = current_index_state->msg_at_top;
2596 i <= mn_get_total(current_index_state->msgmap);
2597 ++i) {
2598 if(!msgline_hidden(current_index_state->stream,
2599 current_index_state->msgmap, i, 0)){
2600 if (++j >= current_index_state->lines_per_page) {
2601 k = i;
2602 break;
2604 if (i == cur)
2605 break;
2608 if (i != cur)
2609 mn_set_cur(current_index_state->msgmap, k);
2613 bad_timing = 0;
2614 return (TRUE);
2619 /*----------------------------------------------------------------------
2620 Calculate the message number that should be at the top of the display
2622 Args: current - the current message number
2623 lines_per_page - the number of lines for the body of the index only
2625 Returns: -1 if the current message is -1
2626 the message entry for the first message at the top of the screen.
2628 When paging in the index it is always on even page boundies, and the
2629 current message is always on the page thus the top of the page is
2630 completely determined by the current message and the number of lines
2631 on the page.
2632 ----*/
2633 long
2634 top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
2636 long current, hidden, lastn;
2637 long n, m = 0L, t = 1L;
2639 current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
2641 if(current < 0L)
2642 return(-1);
2644 if(lines_per_page == 0L)
2645 return(current);
2647 if(THRD_INDX_ENABLED()){
2648 long rawno;
2649 PINETHRD_S *thrd = NULL;
2651 rawno = mn_m2raw(msgs, mn_get_cur(msgs));
2652 if(rawno)
2653 thrd = fetch_thread(stream, rawno);
2655 if(THRD_INDX()){
2657 if(any_lflagged(msgs, MN_HIDE)){
2658 PINETHRD_S *is_current_thrd;
2660 is_current_thrd = thrd;
2661 if(is_current_thrd){
2662 if(mn_get_revsort(msgs)){
2663 /* start with top of tail of thread list */
2664 thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
2665 if(thrd && thrd->top && thrd->top != thrd->rawno)
2666 thrd = fetch_thread(stream, thrd->top);
2668 else{
2669 /* start with head of thread list */
2670 thrd = fetch_head_thread(stream);
2673 t = 1L;
2674 m = 0L;
2675 if(thrd)
2676 n = mn_raw2m(msgs, thrd->rawno);
2678 while(thrd){
2679 if(!msgline_hidden(stream, msgs, n, 0)
2680 && (++m % lines_per_page) == 1L)
2681 t = n;
2683 if(thrd == is_current_thrd)
2684 break;
2686 if(mn_get_revsort(msgs) && thrd->prevthd)
2687 thrd = fetch_thread(stream, thrd->prevthd);
2688 else if(!mn_get_revsort(msgs) && thrd->nextthd)
2689 thrd = fetch_thread(stream, thrd->nextthd);
2690 else
2691 thrd = NULL;
2693 if(thrd)
2694 n = mn_raw2m(msgs, thrd->rawno);
2698 else{
2699 if(thrd){
2700 n = thrd->thrdno;
2701 m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
2702 n = thrd->rawno;
2704 * We want to find the m'th thread and the
2705 * message number that goes with that. We just have
2706 * to back up from where we are to get there.
2707 * If we have a reverse sort backing up is going
2708 * forward through the thread.
2710 while(thrd && m < thrd->thrdno){
2711 n = thrd->rawno;
2712 if(mn_get_revsort(msgs) && thrd->nextthd)
2713 thrd = fetch_thread(stream, thrd->nextthd);
2714 else if(!mn_get_revsort(msgs) && thrd->prevthd)
2715 thrd = fetch_thread(stream, thrd->prevthd);
2716 else
2717 thrd = NULL;
2720 if(thrd)
2721 n = thrd->rawno;
2723 t = mn_raw2m(msgs, n);
2727 else{ /* viewing a thread */
2729 lastn = mn_get_total(msgs);
2730 t = 1L;
2732 /* get top of thread */
2733 if(thrd && thrd->top && thrd->top != thrd->rawno)
2734 thrd = fetch_thread(stream, thrd->top);
2736 if(thrd){
2737 if(mn_get_revsort(msgs))
2738 lastn = mn_raw2m(msgs, thrd->rawno);
2739 else
2740 t = mn_raw2m(msgs, thrd->rawno);
2743 n = 0L;
2745 /* n is the end of this thread */
2746 while(thrd){
2747 n = mn_raw2m(msgs, thrd->rawno);
2748 if(thrd->branch)
2749 thrd = fetch_thread(stream, thrd->branch);
2750 else if(thrd->next)
2751 thrd = fetch_thread(stream, thrd->next);
2752 else
2753 thrd = NULL;
2756 if(n){
2757 if(mn_get_revsort(msgs))
2758 t = n;
2759 else
2760 lastn = n;
2763 for(m = 0L, n = t; n <= MIN(current, lastn); n++)
2764 if(!msgline_hidden(stream, msgs, n, 0)
2765 && (++m % lines_per_page) == 1L)
2766 t = n;
2769 return(t);
2771 else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
2773 if(current < mn_get_total(msgs) / 2){
2774 t = 1L;
2775 m = 0L;
2776 for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
2777 if(!msgline_hidden(stream, msgs, n, 0)
2778 && (++m % lines_per_page) == 1L)
2779 t = n;
2781 else{
2782 t = current+1L;
2783 m = mn_get_total(msgs)-hidden+1L;
2784 for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
2785 if(!msgline_hidden(stream, msgs, n, 0)
2786 && (--m % lines_per_page) == 1L)
2787 t = n;
2789 if(t > current)
2790 t = 1L;
2793 return(t);
2795 else
2796 return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
2800 /*----------------------------------------------------------------------
2801 Clear various bits that make up a healthy display
2803 ----*/
2804 void
2805 reset_index_border(void)
2807 mark_status_dirty();
2808 mark_keymenu_dirty();
2809 mark_titlebar_dirty();
2810 ps_global->mangled_screen = 1; /* signal FULL repaint */
2814 /*----------------------------------------------------------------------
2815 This redraws the body of the index screen, taking into
2816 account any change in the size of the screen. All the state needed to
2817 repaint is in the static variables so this can be called from
2818 anywhere.
2819 ----*/
2820 void
2821 redraw_index_body(void)
2823 int agg;
2825 if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
2826 restore_selected(current_index_state->msgmap);
2828 ps_global->mangled_body = 1;
2830 (void) update_index(ps_global, current_index_state);
2831 if(agg)
2832 pseudo_selected(current_index_state->stream, current_index_state->msgmap);
2836 /*----------------------------------------------------------------------
2837 Give hint about Other command being optional. Some people get the idea
2838 that it is required to use the commands on the 2nd and 3rd keymenus.
2840 Args: none
2842 Result: message may be printed to status line
2843 ----*/
2844 void
2845 warn_other_cmds(void)
2847 static int other_cmds = 0;
2849 other_cmds++;
2850 if(((ps_global->first_time_user || ps_global->show_new_version) &&
2851 other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
2852 q_status_message(SM_ASYNC, 0, 9,
2853 _("Remember the \"O\" command is always optional"));
2857 void
2858 thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
2859 UCS preloadkeystroke, int q_line)
2861 PINETHRD_S *thrd = NULL;
2862 unsigned long rawno, save_branch;
2863 int we_cancel = 0;
2864 int flags = AC_FROM_THREAD;
2866 if(!(stream && msgmap))
2867 return;
2869 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2870 if(rawno)
2871 thrd = fetch_thread(stream, rawno);
2873 if(!thrd)
2874 return;
2876 save_branch = thrd->branch;
2877 thrd->branch = 0L; /* branch is a sibling, not part of thread */
2879 if(!preloadkeystroke){
2880 if(!THRD_INDX()){
2881 if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
2882 flags |= AC_EXPN;
2883 else
2884 flags |= AC_COLL;
2887 if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
2888 == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
2889 flags |= AC_UNSEL;
2892 we_cancel = busy_cue(NULL, NULL, 1);
2894 /* save the SLCT flags in STMP for restoring at the bottom */
2895 copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
2897 /* clear the values from the SLCT flags */
2898 set_lflags(stream, msgmap, MN_SLCT, 0);
2900 /* set SLCT for thrd on down */
2901 set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
2902 thrd->branch = save_branch;
2904 if(we_cancel)
2905 cancel_busy_cue(0);
2907 (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
2908 q_line);
2910 /* restore the original flags */
2911 copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
2913 if(any_lflagged(msgmap, MN_HIDE) > 0L){
2914 /* if nothing left selected, unhide all */
2915 if(any_lflagged(msgmap, MN_SLCT) == 0L){
2916 (void) unzoom_index(ps_global, stream, msgmap);
2917 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
2918 q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
2921 /* if current is hidden, adjust */
2922 adjust_cur_to_visible(stream, msgmap);
2927 /*----------------------------------------------------------------------
2928 Search the message headers as displayed in index
2930 Args: command_line -- The screen line to prompt on
2931 msg -- The current message number to start searching at
2932 max_msg -- The largest message number in the current folder
2934 The headers are searched exactly as they are displayed on the screen. The
2935 search will wrap around to the beginning if not string is not found right
2936 away.
2937 ----*/
2938 void
2939 index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
2941 int rc, select_all = 0, flags, prefetch, we_turned_on = 0;
2942 long i, sorted_msg, selected = 0L;
2943 char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
2944 char buf[MAX_SCREEN_COLS+1], *p;
2945 HelpType help;
2946 char search_string[MAX_SEARCH+1];
2947 ICE_S *ice, *ic;
2948 static HISTORY_S *history = NULL;
2949 static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
2950 {ctrl('Y'), 10, "^Y", N_("First Msg")},
2951 {ctrl('V'), 11, "^V", N_("Last Msg")},
2952 {KEY_UP, 30, "", ""},
2953 {KEY_DOWN, 31, "", ""},
2954 {-1, 0, NULL, NULL} };
2955 #define KU_IS (3) /* index of KEY_UP */
2956 #define PREFETCH_THIS_MANY_LINES (50)
2958 init_hist(&history, HISTSIZE);
2959 search_string[0] = '\0';
2960 if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
2961 strncpy(search_string, p, sizeof(search_string));
2962 search_string[sizeof(search_string)-1] = '\0';
2965 dprint((4, "\n - search headers - \n"));
2967 if(!any_messages(msgmap, NULL, "to search")){
2968 return;
2970 else if(mn_total_cur(msgmap) > 1L){
2971 q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
2972 comatose(mn_total_cur(msgmap)));
2973 return;
2975 else
2976 sorted_msg = mn_get_cur(msgmap);
2978 help = NO_HELP;
2979 new_string[0] = '\0';
2981 while(1) {
2982 snprintf(prompt, sizeof(prompt), _("Word to search for [%s] : "), search_string);
2984 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
2985 header_search_key[0].ch = ctrl('X');
2986 header_search_key[0].rval = 12;
2987 header_search_key[0].name = "^X";
2988 header_search_key[0].label = N_("Select Matches");
2990 else{
2991 header_search_key[0].ch = header_search_key[0].rval = 0;
2992 header_search_key[0].name = header_search_key[0].label = NULL;
2996 * 2 is really 1 because there will be one real entry and
2997 * one entry of "" because of the get_prev_hist above.
2999 if(items_in_hist(history) > 2){
3000 header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
3001 header_search_key[KU_IS].label = HISTORY_KEYLABEL;
3002 header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
3003 header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
3005 else{
3006 header_search_key[KU_IS].name = "";
3007 header_search_key[KU_IS].label = "";
3008 header_search_key[KU_IS+1].name = "";
3009 header_search_key[KU_IS+1].label = "";
3012 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
3014 rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
3015 prompt, header_search_key, help, &flags);
3017 if(rc == 3) {
3018 help = (help != NO_HELP) ? NO_HELP :
3019 F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
3020 : h_os_index_whereis;
3021 continue;
3023 else if(rc == 10){
3024 q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
3025 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3027 selected = sorted_msg;
3028 mn_dec_cur(stream, msgmap, MH_NONE);
3029 sorted_msg = mn_get_cur(msgmap);
3031 while(selected != sorted_msg);
3033 else
3034 sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
3036 mn_set_cur(msgmap, sorted_msg);
3037 return;
3039 else if(rc == 11){
3040 q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
3041 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3043 selected = sorted_msg;
3044 mn_inc_cur(stream, msgmap, MH_NONE);
3045 sorted_msg = mn_get_cur(msgmap);
3047 while(selected != sorted_msg);
3049 else
3050 sorted_msg = mn_get_total(msgmap);
3052 mn_set_cur(msgmap, sorted_msg);
3053 return;
3055 else if(rc == 12){
3056 select_all = 1;
3057 break;
3059 else if(rc == 30){
3060 if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
3061 strncpy(new_string, p, sizeof(new_string));
3062 new_string[sizeof(new_string)-1] = '\0';
3064 else
3065 Writechar(BELL, 0);
3067 continue;
3069 else if(rc == 31){
3070 if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
3071 strncpy(new_string, p, sizeof(new_string));
3072 new_string[sizeof(new_string)-1] = '\0';
3074 else
3075 Writechar(BELL, 0);
3077 continue;
3080 if(rc != 4){ /* 4 is redraw */
3081 save_hist(history, new_string, 0, NULL);
3082 break;
3086 if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
3087 cmd_cancelled(_("Search"));
3088 return;
3091 if(new_string[0] == '\0'){
3092 strncpy(new_string, search_string, sizeof(new_string));
3093 new_string[sizeof(new_string)-1] = '\0';
3096 strncpy(search_string, new_string, sizeof(search_string));
3097 search_string[sizeof(search_string)-1] = '\0';
3099 we_turned_on = intr_handling_on();
3101 prefetch = 0;
3102 for(i = sorted_msg + ((select_all)?0:1);
3103 i <= mn_get_total(msgmap) && !ps_global->intr_pending;
3104 i++){
3105 if(msgline_hidden(stream, msgmap, i, 0))
3106 continue;
3108 if(prefetch <= 0)
3109 prefetch = PREFETCH_THIS_MANY_LINES;
3111 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3113 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3115 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3116 search_string)){
3117 selected++;
3118 if(select_all)
3119 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3120 else
3121 break;
3125 prefetch = 0;
3126 if(i > mn_get_total(msgmap)){
3127 for(i = 1; i < sorted_msg && !ps_global->intr_pending; i++){
3128 if(msgline_hidden(stream, msgmap, i, 0))
3129 continue;
3131 if(prefetch <= 0)
3132 prefetch = PREFETCH_THIS_MANY_LINES;
3134 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3136 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3138 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3139 search_string)){
3140 selected++;
3141 if(select_all)
3142 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3143 else
3144 break;
3149 /* search current line */
3150 if(!select_all && !selected){
3151 i = sorted_msg;
3152 if(!msgline_hidden(stream, msgmap, i, 0)){
3154 ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
3156 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3158 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3159 search_string)){
3160 selected++;
3165 if(ps_global->intr_pending){
3166 q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
3167 select_all ? _(" Selected set may be incomplete."):"");
3169 else if(select_all){
3170 if(selected
3171 && any_lflagged(msgmap, MN_SLCT) > 0L
3172 && !any_lflagged(msgmap, MN_HIDE)
3173 && F_ON(F_AUTO_ZOOM, state))
3174 (void) zoom_index(state, stream, msgmap, MN_SLCT);
3176 q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
3177 long2string(selected));
3179 else if(selected){
3180 q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
3181 (i < sorted_msg) ? _(". Search wrapped to beginning") :
3182 (i == sorted_msg) ? _(". Current line contains only match") : "");
3183 mn_set_cur(msgmap, i);
3185 else
3186 q_status_message(SM_ORDER, 0, 3, _("Word not found"));
3188 if(we_turned_on)
3189 intr_handling_off();
3194 * Original idea from Stephen Casner <casner@acm.org>.
3196 * Apply the appropriate reverse color transformation to the given
3197 * color pair and return a new color pair. The caller should free the
3198 * color pair.
3201 COLOR_PAIR *
3202 apply_rev_color(COLOR_PAIR *cp, int style)
3204 COLOR_PAIR *rc = pico_get_rev_color();
3206 if(rc){
3207 if(style == IND_COL_REV){
3208 /* just use Reverse color regardless */
3209 return(new_color_pair(rc->fg, rc->bg));
3211 else if(style == IND_COL_FG){
3213 * If changing to Rev fg is readable and different
3214 * from what it already is, do it.
3216 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
3217 return(new_color_pair(rc->fg, cp->bg));
3219 else if(style == IND_COL_BG){
3221 * If changing to Rev bg is readable and different
3222 * from what it already is, do it.
3224 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
3225 return(new_color_pair(cp->fg, rc->bg));
3227 else if(style == IND_COL_FG_NOAMBIG){
3229 * If changing to Rev fg is readable, different
3230 * from what it already is, and not the same as
3231 * the Rev color, do it.
3233 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
3234 strcmp(rc->bg, cp->bg))
3235 return(new_color_pair(rc->fg, cp->bg));
3237 else if(style == IND_COL_BG_NOAMBIG){
3239 * If changing to Rev bg is readable, different
3240 * from what it already is, and not the same as
3241 * the Rev color, do it.
3243 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
3244 strcmp(rc->fg, cp->fg))
3245 return(new_color_pair(cp->fg, rc->bg));
3249 /* come here for IND_COL_FLIP and for the cases which fail the tests */
3250 return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
3255 #ifdef _WINDOWS
3257 /*----------------------------------------------------------------------
3258 Callback to get the text of the current message. Used to display
3259 a message in an alternate window.
3261 Args: cmd - what type of scroll operation.
3262 text - filled with pointer to text.
3263 l - length of text.
3264 style - Returns style of text. Can be:
3265 GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
3266 lines
3267 GETTEXT_LINES - Is a pointer to NULL terminated array of
3268 char *. Each entry points to a line of
3269 text.
3271 this implementation always returns GETTEXT_TEXT.
3273 Returns: TRUE - did the scroll operation.
3274 FALSE - was not able to do the scroll operation.
3275 ----*/
3277 index_scroll_callback (cmd, scroll_pos)
3278 int cmd;
3279 long scroll_pos;
3281 int paint = TRUE;
3283 switch (cmd) {
3284 case MSWIN_KEY_SCROLLUPLINE:
3285 paint = index_scroll_up (scroll_pos);
3286 break;
3288 case MSWIN_KEY_SCROLLDOWNLINE:
3289 paint = index_scroll_down (scroll_pos);
3290 break;
3292 case MSWIN_KEY_SCROLLUPPAGE:
3293 paint = index_scroll_up (current_index_state->lines_per_page);
3294 break;
3296 case MSWIN_KEY_SCROLLDOWNPAGE:
3297 paint = index_scroll_down (current_index_state->lines_per_page);
3298 break;
3300 case MSWIN_KEY_SCROLLTO:
3301 /* Normalize msgno in zoomed case */
3302 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
3303 long n, x;
3305 for(n = 1L, x = 0;
3306 x < scroll_pos && n < mn_get_total(ps_global->msgmap);
3307 n++)
3308 if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
3309 n, 0))
3310 x++;
3312 scroll_pos = n - 1; /* list-position --> message number */
3315 paint = index_scroll_to_pos (scroll_pos + 1);
3316 break;
3319 if(paint){
3320 mswin_beginupdate();
3321 update_titlebar_message();
3322 update_titlebar_status();
3323 redraw_index_body();
3324 mswin_endupdate();
3327 return(paint);
3331 /*----------------------------------------------------------------------
3332 MSWin scroll callback to get the text of the current message
3334 Args: title - title for new window
3335 text -
3336 l -
3337 style -
3339 Returns: TRUE - got the requested text
3340 FALSE - was not able to get the requested text
3341 ----*/
3343 index_gettext_callback(title, titlelen, text, l, style)
3344 char *title;
3345 size_t titlelen;
3346 void **text;
3347 long *l;
3348 int *style;
3350 int rv = 0;
3351 ENVELOPE *env;
3352 BODY *body;
3353 STORE_S *so;
3354 gf_io_t pc;
3356 if(mn_get_total(ps_global->msgmap) > 0L
3357 && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
3358 gf_set_so_writec(&pc, so);
3360 if((env = pine_mail_fetchstructure(ps_global->mail_stream,
3361 mn_m2raw(ps_global->msgmap,
3362 mn_get_cur(ps_global->msgmap)),
3363 &body))
3364 && format_message(mn_m2raw(ps_global->msgmap,
3365 mn_get_cur(ps_global->msgmap)),
3366 env, body, NULL, FM_NEW_MESS, pc)){
3367 snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
3368 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
3369 mn_get_cur(ps_global->msgmap),
3370 mn_get_total(ps_global->msgmap));
3371 title[titlelen-1] = '\0';
3372 *text = so_text(so);
3373 *l = strlen((char *)so_text(so));
3374 *style = GETTEXT_TEXT;
3376 /* free alloc'd so, but preserve the text passed back to caller */
3377 so->txt = (void *) NULL;
3378 rv = 1;
3381 gf_clear_so_writec(so);
3382 so_give(&so);
3385 return(rv);
3393 index_sort_callback(set, order)
3394 int set;
3395 long order;
3397 int i = 0;
3399 if(set){
3400 sort_folder(ps_global->mail_stream, ps_global->msgmap,
3401 order & 0x000000ff,
3402 (order & 0x00000100) != 0, SRT_VRB);
3403 mswin_beginupdate();
3404 update_titlebar_message();
3405 update_titlebar_status();
3406 redraw_index_body();
3407 mswin_endupdate();
3408 flush_status_messages(1);
3410 else{
3411 i = (int) mn_get_sort(ps_global->msgmap);
3412 if(mn_get_revsort(ps_global->msgmap))
3413 i |= 0x0100;
3416 return(i);
3423 void
3424 index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
3426 int n = 0;
3427 int view_in_new_wind_index = -1;
3428 long rawno;
3429 MESSAGECACHE *mc;
3430 MPopup view_index_popup[32];
3431 struct key_menu *km = (style == ThreadIndex)
3432 ? &thread_keymenu
3433 : (ps_global->mail_stream != stream)
3434 ? &simple_index_keymenu
3435 : &index_keymenu;
3438 * Loosely follow the logic in do_index_border to figure
3439 * out which commands to show.
3442 if(full){
3443 if(km != &simple_index_keymenu){
3444 view_index_popup[n].type = tQueue;
3445 view_index_popup[n].label.string = (km == &thread_keymenu)
3446 ? "&View Thread" : "&View";
3447 view_index_popup[n].label.style = lNormal;
3448 view_index_popup[n++].data.val = 'V';
3451 if(km == &index_keymenu){
3452 view_in_new_wind_index = n;
3453 view_index_popup[n].type = tIndex;
3454 view_index_popup[n].label.style = lNormal;
3455 view_index_popup[n++].label.string = "View in New Window";
3458 if(km != &simple_index_keymenu)
3459 view_index_popup[n++].type = tSeparator;
3461 if(km == &thread_keymenu){
3462 view_index_popup[n].type = tQueue;
3463 view_index_popup[n].label.string = "&Delete Thread";
3464 view_index_popup[n].label.style = lNormal;
3465 view_index_popup[n++].data.val = 'D';
3467 view_index_popup[n].type = tQueue;
3468 view_index_popup[n].label.string = "&UnDelete Thread";
3469 view_index_popup[n].label.style = lNormal;
3470 view_index_popup[n++].data.val = 'U';
3472 else{
3473 /* Make "delete/undelete" item sensitive */
3474 mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
3475 && stream && rawno <= stream->nmsgs)
3476 ? mail_elt(stream, rawno) : NULL;
3477 view_index_popup[n].type = tQueue;
3478 view_index_popup[n].label.style = lNormal;
3479 if(mc && mc->deleted){
3480 view_index_popup[n].label.string = "&Undelete";
3481 view_index_popup[n++].data.val = 'U';
3483 else{
3484 view_index_popup[n].label.string = "&Delete";
3485 view_index_popup[n++].data.val = 'D';
3489 if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
3490 view_index_popup[n].type = tSubMenu;
3491 view_index_popup[n].label.string = "Flag";
3492 view_index_popup[n++].data.submenu = flag_submenu(mc);
3495 if(km == &simple_index_keymenu){
3496 view_index_popup[n].type = tQueue;
3497 view_index_popup[n].label.style = lNormal;
3498 view_index_popup[n].label.string = "&Select";
3499 view_index_popup[n++].data.val = 'S';
3501 else{
3502 view_index_popup[n].type = tQueue;
3503 view_index_popup[n].label.style = lNormal;
3504 view_index_popup[n].label.string = (km == &thread_keymenu)
3505 ? "&Save Thread" : "&Save";
3506 view_index_popup[n++].data.val = 'S';
3508 view_index_popup[n].type = tQueue;
3509 view_index_popup[n].label.style = lNormal;
3510 view_index_popup[n].label.string = (km == &thread_keymenu)
3511 ? "&Export Thread" : "&Export";
3512 view_index_popup[n++].data.val = 'E';
3514 view_index_popup[n].type = tQueue;
3515 view_index_popup[n].label.style = lNormal;
3516 view_index_popup[n].label.string = "Print";
3517 view_index_popup[n++].data.val = '%';
3519 view_index_popup[n].type = tQueue;
3520 view_index_popup[n].label.style = lNormal;
3521 view_index_popup[n].label.string = (km == &thread_keymenu)
3522 ? "&Reply To Thread" : "&Reply";
3523 view_index_popup[n++].data.val = 'R';
3525 view_index_popup[n].type = tQueue;
3526 view_index_popup[n].label.style = lNormal;
3527 view_index_popup[n].label.string = (km == &thread_keymenu)
3528 ? "&Forward Thread" : "&Forward";
3529 view_index_popup[n++].data.val = 'F';
3531 if(F_ON(F_ENABLE_BOUNCE, ps_global)){
3532 view_index_popup[n].type = tQueue;
3533 view_index_popup[n].label.style = lNormal;
3534 view_index_popup[n].label.string = (km == &thread_keymenu)
3535 ? "&Bounce Thread" : "&Bounce";
3536 view_index_popup[n++].data.val = 'B';
3539 view_index_popup[n].type = tQueue;
3540 view_index_popup[n].label.style = lNormal;
3541 view_index_popup[n].label.string = "&Take Addresses";
3542 view_index_popup[n++].data.val = 'T';
3544 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3545 view_index_popup[n].type = tQueue;
3546 view_index_popup[n].label.style = lNormal;
3547 view_index_popup[n].label.string = "[Un]Select Current";
3548 view_index_popup[n++].data.val = ':';
3552 view_index_popup[n].type = tQueue;
3553 view_index_popup[n].label.style = lNormal;
3554 view_index_popup[n].label.string = "&WhereIs";
3555 view_index_popup[n++].data.val = 'W';
3557 view_index_popup[n++].type = tSeparator;
3560 if(km == &simple_index_keymenu){
3561 view_index_popup[n].type = tQueue;
3562 view_index_popup[n].label.style = lNormal;
3563 view_index_popup[n].label.string = "&Exit Select";
3564 view_index_popup[n++].data.val = 'E';
3566 else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
3567 view_index_popup[n].type = tQueue;
3568 view_index_popup[n].label.style = lNormal;
3569 view_index_popup[n].label.string = "Thread Index";
3570 view_index_popup[n++].data.val = '<';
3572 else{
3573 view_index_popup[n].type = tQueue;
3574 view_index_popup[n].label.style = lNormal;
3575 view_index_popup[n].label.string = "Folder &List";
3576 view_index_popup[n++].data.val = '<';
3579 view_index_popup[n].type = tQueue;
3580 view_index_popup[n].label.style = lNormal;
3581 view_index_popup[n].label.string = "&Main Menu";
3582 view_index_popup[n++].data.val = 'M';
3584 view_index_popup[n].type = tTail;
3586 if(mswin_popup(view_index_popup) == view_in_new_wind_index
3587 && view_in_new_wind_index >= 0)
3588 view_in_new_window();
3592 char *
3593 pcpine_help_index(title)
3594 char *title;
3597 * Title is size 256 in pico. Put in args.
3599 if(title)
3600 strncpy(title, "Alpine MESSAGE INDEX Help", 256);
3602 return(pcpine_help(h_mail_index));
3605 char *
3606 pcpine_help_index_simple(title)
3607 char *title;
3610 * Title is size 256 in pico. Put in args.
3612 if(title)
3613 strncpy(title, "Alpine SELECT MESSAGE Help", 256);
3615 return(pcpine_help(h_simple_index));
3619 #include "../pico/osdep/mswin_tw.h"
3622 void
3623 view_in_new_window(void)
3625 char title[GETTEXT_TITLELEN+1];
3626 void *text;
3627 long len;
3628 int format;
3629 MSWIN_TEXTWINDOW *mswin_tw = NULL;
3631 /* Launch text in alt window. */
3632 if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
3633 if(format == GETTEXT_TEXT)
3634 mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
3635 NULL, MSWIN_DT_USEALTWINDOW);
3636 else if(format == GETTEXT_LINES)
3637 mswin_tw = mswin_displaytext(title, NULL, 0, text,
3638 NULL, MSWIN_DT_USEALTWINDOW);
3640 if(mswin_tw != NULL)
3641 mswin_set_readonly(mswin_tw, FALSE);
3645 #endif /* _WINDOWS */