* Reimplementation of the code that allows the .pinerc file to be a
[alpine.git] / alpine / mailindx.c
blob013a68aaf0bf571261ec0cd2c90adb896cbec1c4
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-2015 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 char *utf8str;
326 IndexType style, old_style = MsgIndex;
327 struct index_state id;
328 struct key_menu *km = NULL;
331 dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
333 ch = 'x'; /* For displaying msg 1st time thru */
334 force = 0;
335 km_popped = 0;
336 state->mangled_screen = 1;
337 what_keymenu = FirstMenu;
338 old_max_msgno = mn_get_total(msgmap);
339 memset((void *)&id, 0, sizeof(struct index_state));
340 current_index_state = &id;
341 id.msgmap = msgmap;
342 if(msgmap->top != 0L)
343 id.msg_at_top = msgmap->top;
345 id.stream = stream;
346 set_need_format_setup(stream);
348 while (1) {
349 ps_global->user_says_cancel = 0;
351 if(km_popped){
352 km_popped--;
353 if(km_popped == 0){
354 clearfooter(state);
355 if(!state->mangled_body
356 && id.entry_state
357 && id.lines_per_page > 1){
358 id.entry_state[id.lines_per_page-2].id = 0;
359 id.entry_state[id.lines_per_page-1].id = 0;
361 else
362 state->mangled_body = 1;
366 /*------- Check for new mail -------*/
367 new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
368 force = 0; /* may not need to next time around */
371 * If the width of the message number field in the display changes
372 * we need to flush the cache and redraw. When the cache is cleared
373 * the widths are recalculated, taking into account the max msgno.
376 if(format_includes_msgno(stream) &&
377 ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
378 || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
379 || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
380 clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
381 state->mangled_body = 1;
384 old_max_msgno = mn_get_total(msgmap);
387 * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
388 * then when the day changes the date column will change. All of the
389 * Today's will become Yesterday's at midnight. So we have to
390 * clear the cache at midnight.
392 if(format_includes_smartdate(stream)){
393 char db[200];
394 struct date nnow;
396 rfc822_date(db);
397 parse_date(db, &nnow);
398 if(old_day != -1 && nnow.day != old_day){
399 clear_index_cache(stream, 0);
400 state->mangled_body = 1;
403 old_day = nnow.day;
406 if(streams_died())
407 state->mangled_header = 1;
409 if(state->mangled_screen){
410 state->mangled_header = 1;
411 state->mangled_body = 1;
412 state->mangled_footer = 1;
413 state->mangled_screen = 0;
417 * events may have occured that require us to shift from
418 * mode to another...
420 style = THRD_INDX()
421 ? ThreadIndex
422 : (any_lflagged(msgmap, MN_HIDE))
423 ? ZoomIndex
424 : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
425 if(style != old_style || style == ZoomIndex){
426 state->mangled_header = 1;
427 state->mangled_footer = 1;
428 old_style = style;
429 if(!(style == ThreadIndex || old_style == ThreadIndex))
430 id.msg_at_top = 0L;
433 /*------------ Update the title bar -----------*/
434 if(state->mangled_header) {
435 km = do_index_border(cntxt, folder, stream, msgmap,
436 style, NULL, INDX_HEADER);
437 state->mangled_header = 0;
438 paint_status = 0;
440 else if(mn_get_total(msgmap) > 0) {
441 update_titlebar_message();
443 * If flags aren't available to update the status,
444 * defer it until after all the fetches associated
445 * with building index lines are done (no extra rtts!)...
447 paint_status = !update_titlebar_status();
450 current_index_state = &id;
452 /*------------ draw the index body ---------------*/
453 cur_row = update_index(state, &id);
454 if(F_OFF(F_SHOW_CURSOR, state)){
455 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
456 cur_col = 0;
458 else if(id.status_col >= 0)
459 cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
461 ps_global->redrawer = redraw_index_body;
463 if(paint_status)
464 (void) update_titlebar_status();
466 /*------------ draw the footer/key menus ---------------*/
467 if(state->mangled_footer) {
468 if(!state->painted_footer_on_startup){
469 if(km_popped){
470 FOOTER_ROWS(state) = 3;
471 clearfooter(state);
474 km = do_index_border(cntxt, folder, stream, msgmap, style,
475 &which_keys, INDX_FOOTER);
476 if(km_popped){
477 FOOTER_ROWS(state) = 1;
478 mark_keymenu_dirty();
482 state->mangled_footer = 0;
485 state->painted_body_on_startup = 0;
486 state->painted_footer_on_startup = 0;
488 /*-- Display any queued message (eg, new mail, command result --*/
489 if(km_popped){
490 FOOTER_ROWS(state) = 3;
491 mark_status_unknown();
494 display_message(ch);
495 if(km_popped){
496 FOOTER_ROWS(state) = 1;
497 mark_status_unknown();
500 if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
501 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
504 cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
505 MoveCursor(cur_row, cur_col);
507 /* Let read_command do the fflush(stdout) */
509 /*---------- Read command and validate it ----------------*/
510 #ifdef MOUSE
511 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
512 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
513 state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
514 state->ttyo->screen_cols);
515 #endif
516 #ifdef _WINDOWS
517 mswin_setscrollcallback (index_scroll_callback);
518 mswin_sethelptextcallback((stream == state->mail_stream)
519 ? pcpine_help_index
520 : pcpine_help_index_simple);
521 mswin_setviewinwindcallback(view_in_new_window);
522 #endif
523 ch = READ_COMMAND(&utf8str);
524 #ifdef MOUSE
525 clear_mfunc(mouse_in_content);
526 #endif
527 #ifdef _WINDOWS
528 mswin_setscrollcallback(NULL);
529 mswin_sethelptextcallback(NULL);
530 mswin_setviewinwindcallback(NULL);
531 #endif
533 cmd = menu_command(ch, km);
535 if(km_popped)
536 switch(cmd){
537 case MC_NONE :
538 case MC_OTHER :
539 case MC_RESIZE :
540 case MC_REPAINT :
541 km_popped++;
542 break;
544 default:
545 clearfooter(state);
546 break;
549 /*----------- Execute the command ------------------*/
550 switch(cmd){
552 /*---------- Roll keymenu ----------*/
553 case MC_OTHER :
554 if(F_OFF(F_USE_FK, ps_global))
555 warn_other_cmds();
557 what_keymenu = NextMenu;
558 state->mangled_footer = 1;
559 break;
562 /*---------- Scroll line up ----------*/
563 case MC_CHARUP :
564 (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
565 (style == MsgIndex
566 || style == MultiMsgIndex
567 || style == ZoomIndex)
568 ? MsgIndx
569 : (style == ThreadIndex)
570 ? ThrdIndx
571 : View,
572 &force);
573 if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
574 index_scroll_up(1L);
576 break;
579 /*---------- Scroll line down ----------*/
580 case MC_CHARDOWN :
582 * Special Page framing handling here. If we
583 * did something that should scroll-by-a-line, frame
584 * the page by hand here rather than leave it to the
585 * page-by-page framing in update_index()...
587 (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
588 (style == MsgIndex
589 || style == MultiMsgIndex
590 || style == ZoomIndex)
591 ? MsgIndx
592 : (style == ThreadIndex)
593 ? ThrdIndx
594 : View,
595 &force);
596 for(j = 0L, k = i = id.msg_at_top; ; i++){
597 if(!msgline_hidden(stream, msgmap, i, 0)){
598 k = i;
599 if(j++ >= id.lines_per_page)
600 break;
603 if(i >= mn_get_total(msgmap)){
604 k = 0L; /* don't scroll */
605 break;
609 if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
610 index_scroll_down(1L);
612 break;
615 /*---------- Scroll page up ----------*/
616 case MC_PAGEUP :
617 j = -1L;
618 for(k = i = id.msg_at_top; ; i--){
619 if(!msgline_hidden(stream, msgmap, i, 0)){
620 k = i;
621 if(++j >= id.lines_per_page){
622 if((id.msg_at_top = i) == 1L)
623 q_status_message(SM_ORDER, 0, 1, _("First Index page"));
625 break;
629 if(i <= 1L){
630 if((!THREADING() && mn_get_cur(msgmap) == 1L)
631 || (THREADING()
632 && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
633 stream,
635 FSF_SKIP_CHID)))
636 q_status_message(SM_ORDER, 0, 1,
637 _("Already at start of Index"));
639 break;
643 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
644 mn_set_cur(msgmap, k);
646 break;
649 /*---------- Scroll page forward ----------*/
650 case MC_PAGEDN :
651 j = -1L;
652 for(k = i = id.msg_at_top; ; i++){
653 if(!msgline_hidden(stream, msgmap, i, 0)){
654 k = i;
655 if(++j >= id.lines_per_page){
656 if(i+id.lines_per_page > mn_get_total(msgmap))
657 q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
659 id.msg_at_top = i;
660 break;
664 if(i >= mn_get_total(msgmap)){
665 if(mn_get_cur(msgmap) == k)
666 q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
668 break;
672 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
673 mn_set_cur(msgmap, k);
675 break;
678 /*---------- Scroll to first page ----------*/
679 case MC_HOMEKEY :
680 if((mn_get_total(msgmap) > 0L)
681 && (mn_total_cur(msgmap) <= 1L)){
682 long cur_msg = mn_get_cur(msgmap), selected;
684 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
685 do {
686 selected = cur_msg;
687 mn_dec_cur(stream, msgmap, MH_NONE);
688 cur_msg = mn_get_cur(msgmap);
690 while(selected != cur_msg);
692 else
693 cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
694 mn_set_cur(msgmap, cur_msg);
695 q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
697 break;
699 /*---------- Scroll to last page ----------*/
700 case MC_ENDKEY :
701 if((mn_get_total(msgmap) > 0L)
702 && (mn_total_cur(msgmap) <= 1L)){
703 long cur_msg = mn_get_cur(msgmap), selected;
705 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
706 do {
707 selected = cur_msg;
708 mn_inc_cur(stream, msgmap, MH_NONE);
709 cur_msg = mn_get_cur(msgmap);
711 while(selected != cur_msg);
713 else
714 cur_msg = mn_get_total(msgmap);
715 mn_set_cur(msgmap, cur_msg);
716 q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
718 break;
720 /*---------- Search (where is command) ----------*/
721 case MC_WHEREIS :
722 index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
723 state->mangled_footer = 1;
724 break;
727 /*-------------- jump command -------------*/
728 /* NOTE: preempt the process_cmd() version because
729 * we need to get at the number..
731 case MC_JUMP :
732 j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
733 (style == ThreadIndex) ? ThrdIndx : MsgIndx);
734 if(j > 0L){
735 if(style == ThreadIndex){
736 PINETHRD_S *thrd;
738 thrd = find_thread_by_number(stream, msgmap, j, NULL);
740 if(thrd && thrd->rawno)
741 mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
743 else{
744 /* jump to message */
745 if(mn_total_cur(msgmap) > 1L){
746 mn_reset_cur(msgmap, j);
748 else{
749 mn_set_cur(msgmap, j);
753 id.msg_at_top = 0L;
756 state->mangled_footer = 1;
757 break;
760 case MC_VIEW_ENTRY : /* only happens in thread index */
763 * If the feature F_THRD_AUTO_VIEW is turned on and there
764 * is only one message in the thread, then we skip the index
765 * view of the thread and go straight to the message view.
767 view_a_thread:
768 if(THRD_AUTO_VIEW() && style == ThreadIndex){
769 PINETHRD_S *thrd;
771 thrd = fetch_thread(stream,
772 mn_m2raw(msgmap, mn_get_cur(msgmap)));
773 if(thrd
774 && (count_lflags_in_thread(stream, thrd,
775 msgmap, MN_NONE) == 1)){
776 if(view_thread(state, stream, msgmap, 1)){
777 state->view_skipped_index = 1;
778 cmd = MC_VIEW_TEXT;
779 goto do_the_default;
784 if(view_thread(state, stream, msgmap, 1)){
785 ps_global->next_screen = mail_index_screen;
786 ps_global->redrawer = NULL;
787 current_index_state = NULL;
788 if(id.entry_state)
789 fs_give((void **)&(id.entry_state));
791 return(0);
794 break;
797 case MC_THRDINDX :
798 msgmap->top = msgmap->top_after_thrd;
799 if(unview_thread(state, stream, msgmap)){
800 state->next_screen = mail_index_screen;
801 state->view_skipped_index = 0;
802 state->mangled_screen = 1;
803 ps_global->redrawer = NULL;
804 current_index_state = NULL;
805 if(id.entry_state)
806 fs_give((void **)&(id.entry_state));
808 return(0);
811 break;
814 #ifdef MOUSE
815 case MC_MOUSE:
817 MOUSEPRESS mp;
818 int new_cur;
820 mouse_get_last (NULL, &mp);
821 mp.row -= 2;
823 for(i = id.msg_at_top;
824 mp.row >= 0 && i <= mn_get_total(msgmap);
825 i++)
826 if(!msgline_hidden(stream, msgmap, i, 0)){
827 mp.row--;
828 new_cur = i;
831 if(mn_get_total(msgmap) && mp.row < 0){
832 switch(mp.button){
833 case M_BUTTON_LEFT :
834 if(mn_total_cur(msgmap) == 1L)
835 mn_set_cur(msgmap, new_cur);
837 if(mp.flags & M_KEY_CONTROL){
838 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
839 (void) select_by_current(state, msgmap, MsgIndx);
842 else if(!(mp.flags & M_KEY_SHIFT)){
843 if (THREADING()
844 && mp.col >= 0
845 && mp.col == id.plus_col
846 && style != ThreadIndex){
847 collapse_or_expand(state, stream, msgmap,
848 mn_get_cur(msgmap));
850 else if (mp.doubleclick){
851 if(mp.button == M_BUTTON_LEFT){
852 if(stream == state->mail_stream){
853 if(THRD_INDX()){
854 cmd = MC_VIEW_ENTRY;
855 goto view_a_thread;
857 else{
858 cmd = MC_VIEW_TEXT;
859 goto do_the_default;
863 ps_global->redrawer = NULL;
864 current_index_state = NULL;
865 if(id.entry_state)
866 fs_give((void **)&(id.entry_state));
868 return(0);
873 break;
875 case M_BUTTON_MIDDLE:
876 break;
878 case M_BUTTON_RIGHT :
879 #ifdef _WINDOWS
880 if (!mp.doubleclick){
881 if(mn_total_cur(msgmap) == 1L)
882 mn_set_cur(msgmap, new_cur);
884 cur_row = update_index(state, &id);
886 index_popup(style, stream, msgmap, TRUE);
888 #endif
889 break;
892 else{
893 switch(mp.button){
894 case M_BUTTON_LEFT :
895 break;
897 case M_BUTTON_MIDDLE :
898 break;
900 case M_BUTTON_RIGHT :
901 #ifdef _WINDOWS
902 index_popup(style, stream, msgmap, FALSE);
903 #endif
904 break;
909 break;
910 #endif /* MOUSE */
912 /*---------- Resize ----------*/
913 case MC_RESIZE:
915 * If we were smarter we could do the
916 * IC_CLEAR_WIDTHS_DONE trick here. The problem is
917 * that entire columns of the format can go away or
918 * appear because the width gets smaller or larger,
919 * so in that case we need to re-do. If we could tell
920 * when that happened or not we could set the flag
921 * selectively.
923 clear_index_cache(stream, 0);
924 reset_index_border();
925 break;
927 case MC_QUOTA:
928 cmd_quota(state);
930 /*---------- Redraw ----------*/
931 case MC_REPAINT :
932 force = 1; /* check for new mail! */
933 reset_index_border();
934 break;
937 /*---------- No op command ----------*/
938 case MC_NONE :
939 break; /* no op check for new mail */
942 /*--------- keystroke not bound to command --------*/
943 case MC_CHARRIGHT :
944 case MC_CHARLEFT :
945 case MC_GOTOBOL :
946 case MC_GOTOEOL :
947 case MC_UNKNOWN :
948 if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
949 q_status_message(SM_ORDER, 0, 1, "Already in Index");
950 else
951 bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
953 break;
956 case MC_COLLAPSE :
957 thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
958 break;
960 case MC_DELETE :
961 case MC_UNDELETE :
962 case MC_REPLY :
963 case MC_FORWARD :
964 case MC_TAKE :
965 case MC_SAVE :
966 case MC_EXPORT :
967 case MC_BOUNCE :
968 case MC_PIPE :
969 case MC_FLAG :
970 case MC_SELCUR :
971 { int collapsed = 0;
972 unsigned long rawno;
973 PINETHRD_S *thrd = NULL;
975 if(THREADING()){
976 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
977 if(rawno)
978 thrd = fetch_thread(stream, rawno);
980 collapsed = thrd && thrd->next
981 && get_lflag(stream, NULL, rawno, MN_COLL);
984 if(collapsed){
985 thread_command(state, stream, msgmap,
986 ch, -FOOTER_ROWS(state));
987 /* increment current */
988 if(cmd == MC_DELETE){
989 advance_cur_after_delete(state, stream, msgmap,
990 (style == MsgIndex
991 || style == MultiMsgIndex
992 || style == ZoomIndex)
993 ? MsgIndx
994 : (style == ThreadIndex)
995 ? ThrdIndx
996 : View);
998 else if((cmd == MC_SELCUR
999 && (state->ugly_consider_advancing_bit
1000 || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
1001 || (state->ugly_consider_advancing_bit
1002 && cmd == MC_SAVE
1003 && F_ON(F_SAVE_ADVANCES, state))){
1004 mn_inc_cur(stream, msgmap, MH_NONE);
1007 else
1008 goto do_the_default;
1011 break;
1014 case MC_UTF8:
1015 bogus_utf8_command(utf8str, NULL);
1016 break;
1019 /*---------- First HELP command with menu hidden ----------*/
1020 case MC_HELP :
1021 if(FOOTER_ROWS(state) == 1 && km_popped == 0){
1022 km_popped = 2;
1023 mark_status_unknown();
1024 mark_keymenu_dirty();
1025 state->mangled_footer = 1;
1026 break;
1028 /* else fall thru to normal default */
1031 /*---------- Default -- all other command ----------*/
1032 default:
1033 do_the_default:
1034 if(stream == state->mail_stream){
1035 msgmap->top = id.msg_at_top;
1036 process_cmd(state, stream, msgmap, cmd,
1037 (style == MsgIndex
1038 || style == MultiMsgIndex
1039 || style == ZoomIndex)
1040 ? MsgIndx
1041 : (style == ThreadIndex)
1042 ? ThrdIndx
1043 : View,
1044 &force);
1045 if(state->next_screen != SCREEN_FUN_NULL){
1046 ps_global->redrawer = NULL;
1047 current_index_state = NULL;
1048 if(id.entry_state)
1049 fs_give((void **)&(id.entry_state));
1051 return(0);
1053 else{
1054 if(stream != state->mail_stream){
1056 * Must have had an failed open. repair our
1057 * pointers...
1059 id.stream = stream = state->mail_stream;
1060 id.msgmap = msgmap = state->msgmap;
1063 current_index_state = &id;
1065 if(cmd == MC_ZOOM && THRD_INDX())
1066 id.msg_at_top = 0L;
1069 else{ /* special processing */
1070 switch(cmd){
1071 case MC_HELP :
1072 helper(h_simple_index,
1073 (!strcmp(folder, INTERRUPTED_MAIL))
1074 ? _("HELP FOR SELECTING INTERRUPTED MSG")
1075 : _("HELP FOR SELECTING POSTPONED MSG"),
1076 HLPD_SIMPLE);
1077 state->mangled_screen = 1;
1078 break;
1080 case MC_DELETE : /* delete */
1081 dprint((3, "Special delete: msg %s\n",
1082 long2string(mn_get_cur(msgmap))));
1084 long raw, t;
1085 int del = 0;
1086 MESSAGECACHE *mc;
1088 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1089 if(raw > 0L && stream
1090 && raw <= stream->nmsgs
1091 && (mc = mail_elt(stream, raw))
1092 && !mc->deleted){
1093 if((t = mn_get_cur(msgmap)) > 0L)
1094 clear_index_cache_ent(stream, t, 0);
1096 mail_setflag(stream,long2string(raw),"\\DELETED");
1097 update_titlebar_status();
1098 del++;
1101 q_status_message1(SM_ORDER, 0, 1,
1102 del ? _("Message %s deleted") : _("Message %s already deleted"),
1103 long2string(mn_get_cur(msgmap)));
1106 break;
1108 case MC_UNDELETE : /* UNdelete */
1109 dprint((3, "Special UNdelete: msg %s\n",
1110 long2string(mn_get_cur(msgmap))));
1112 long raw, t;
1113 int del = 0;
1114 MESSAGECACHE *mc;
1116 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1117 if(raw > 0L && stream
1118 && raw <= stream->nmsgs
1119 && (mc = mail_elt(stream, raw))
1120 && mc->deleted){
1121 if((t = mn_get_cur(msgmap)) > 0L)
1122 clear_index_cache_ent(stream, t, 0);
1124 mail_clearflag(stream, long2string(raw),
1125 "\\DELETED");
1126 update_titlebar_status();
1127 del++;
1130 q_status_message1(SM_ORDER, 0, 1,
1131 del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
1132 long2string(mn_get_cur(msgmap)));
1135 break;
1137 case MC_EXIT : /* exit */
1138 ps_global->redrawer = NULL;
1139 current_index_state = NULL;
1140 if(id.entry_state)
1141 fs_give((void **)&(id.entry_state));
1143 return(1);
1145 case MC_SELECT : /* select */
1146 ps_global->redrawer = NULL;
1147 current_index_state = NULL;
1148 if(id.entry_state)
1149 fs_give((void **)&(id.entry_state));
1151 return(0);
1153 case MC_PREVITEM : /* previous */
1154 mn_dec_cur(stream, msgmap, MH_NONE);
1155 break;
1157 case MC_NEXTITEM : /* next */
1158 mn_inc_cur(stream, msgmap, MH_NONE);
1159 break;
1161 default :
1162 bogus_command(ch, NULL);
1163 break;
1166 } /* The big switch */
1167 } /* the BIG while loop! */
1172 /*----------------------------------------------------------------------
1173 Manage index body painting
1175 Args: state - pine struct containing selected message data
1176 index_state - struct describing what's currently displayed
1178 Returns: screen row number of first highlighted message
1180 The idea is pretty simple. Maintain an array of index line id's that
1181 are displayed and their hilited state. Decide what's to be displayed
1182 and update the screen appropriately. All index screen painting
1183 is done here. Pretty simple, huh?
1184 ----*/
1186 update_index(struct pine *state, struct index_state *screen)
1188 int i, retval = -1, row, already_fetched = 0;
1189 long n, visible;
1190 PINETHRD_S *thrd = NULL;
1191 int we_cancel = 0;
1193 dprint((7, "--update_index--\n"));
1195 if(!screen)
1196 return(-1);
1198 #ifdef _WINDOWS
1199 mswin_beginupdate();
1200 #endif
1202 /*---- reset the works if necessary ----*/
1203 if(state->mangled_body){
1204 ClearBody();
1205 if(screen->entry_state){
1206 fs_give((void **)&(screen->entry_state));
1207 screen->lines_per_page = 0;
1211 state->mangled_body = 0;
1213 /*---- make sure we have a place to write state ----*/
1214 if(screen->lines_per_page
1215 != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1216 - HEADER_ROWS(state))){
1217 i = screen->lines_per_page;
1218 screen->lines_per_page
1219 = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1220 - HEADER_ROWS(state));
1221 if(!i){
1222 size_t len = screen->lines_per_page * sizeof(struct entry_state);
1223 screen->entry_state = (struct entry_state *) fs_get(len);
1225 else
1226 fs_resize((void **)&(screen->entry_state),
1227 (size_t)screen->lines_per_page);
1229 for(; i < screen->lines_per_page; i++) /* init new entries */
1230 memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
1233 /*---- figure out the first message on the display ----*/
1234 if(screen->msg_at_top < 1L
1235 || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
1236 screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
1237 screen->msg_at_top,
1238 screen->lines_per_page);
1240 else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1241 long i, j, k;
1243 /* scroll back a page at a time until current is displayed */
1244 while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1245 for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
1246 i > 0L && j > 0L;
1247 j--)
1248 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1249 k = j;
1250 i--;
1253 if(i == screen->lines_per_page)
1254 break; /* can't scroll back ? */
1255 else
1256 screen->msg_at_top = k;
1259 else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
1260 + screen->lines_per_page){
1261 long i, j, k;
1263 while(1){
1264 for(i = screen->lines_per_page, j = k = screen->msg_at_top;
1265 j <= mn_get_total(screen->msgmap) && i > 0L;
1266 j++)
1267 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1268 k = j;
1269 i--;
1270 if(mn_get_cur(screen->msgmap) <= k)
1271 break;
1274 if(mn_get_cur(screen->msgmap) <= k)
1275 break;
1276 else{
1277 /* set msg_at_top to next displayed message */
1278 for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
1279 if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
1280 k = i;
1281 break;
1284 screen->msg_at_top = k;
1289 #ifdef _WINDOWS
1291 * Set scroll range and position. Note that message numbers start at 1
1292 * while scroll position starts at 0.
1295 if(THREADING() && sp_viewing_a_thread(screen->stream)
1296 && mn_get_total(screen->msgmap) > 1L){
1297 long x = 0L, range = 0L, lowest_numbered_msg;
1300 * We know that all visible messages in the thread are marked
1301 * with MN_CHID2.
1303 thrd = fetch_thread(screen->stream,
1304 mn_m2raw(screen->msgmap,
1305 mn_get_cur(screen->msgmap)));
1306 if(thrd && thrd->top && thrd->top != thrd->rawno)
1307 thrd = fetch_thread(screen->stream, thrd->top);
1309 if(thrd){
1310 if(mn_get_revsort(screen->msgmap)){
1311 n = mn_raw2m(screen->msgmap, thrd->rawno);
1312 while(n > 1L && get_lflag(screen->stream, screen->msgmap,
1313 n-1L, MN_CHID2))
1314 n--;
1316 lowest_numbered_msg = n;
1318 else
1319 lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
1322 if(thrd){
1323 n = lowest_numbered_msg;
1324 for(; n <= mn_get_total(screen->msgmap); n++){
1326 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1327 break;
1329 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
1330 range++;
1331 if(n < screen->msg_at_top)
1332 x++;
1337 scroll_setrange(screen->lines_per_page, range-1L);
1338 scroll_setpos(x);
1340 else if(THRD_INDX()){
1341 if(any_lflagged(screen->msgmap, MN_HIDE)){
1342 long x = 0L, range;
1344 range = screen->msgmap->visible_threads - 1L;
1345 scroll_setrange(screen->lines_per_page, range);
1346 if(range >= screen->lines_per_page){ /* else not needed */
1347 PINETHRD_S *topthrd;
1348 int thrddir;
1349 long xdir;
1351 /* find top of currently displayed top line */
1352 topthrd = fetch_thread(screen->stream,
1353 mn_m2raw(screen->msgmap,
1354 screen->msg_at_top));
1355 if(topthrd && topthrd->top != topthrd->rawno)
1356 topthrd = fetch_thread(screen->stream, topthrd->top);
1358 if(topthrd){
1360 * Split into two halves to speed up finding scroll pos.
1361 * It's tricky because the thread list always goes from
1362 * past to future but the thrdno's will be reversed if
1363 * the sort is reversed and of course the order on the
1364 * screen will be reversed.
1366 if((!mn_get_revsort(screen->msgmap)
1367 && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
1369 (mn_get_revsort(screen->msgmap)
1370 && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
1372 /* start with head of thread list */
1373 if(topthrd && topthrd->head)
1374 thrd = fetch_thread(screen->stream, topthrd->head);
1375 else
1376 thrd = NULL;
1378 thrddir = 1;
1380 else{
1381 long tailrawno;
1384 * Start with tail thread and work back.
1386 if(mn_get_revsort(screen->msgmap))
1387 tailrawno = mn_m2raw(screen->msgmap, 1L);
1388 else
1389 tailrawno = mn_m2raw(screen->msgmap,
1390 mn_get_total(screen->msgmap));
1392 thrd = fetch_thread(screen->stream, tailrawno);
1393 if(thrd && thrd->top && thrd->top != thrd->rawno)
1394 thrd = fetch_thread(screen->stream, thrd->top);
1396 thrddir = -1;
1400 * x is the scroll position. We try to use the fewest
1401 * number of steps to find it, so we start with either
1402 * the beginning or the end.
1404 if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
1405 x = 0L;
1406 xdir = 1L;
1408 else{
1409 x = range;
1410 xdir = -1L;
1413 while(thrd && thrd != topthrd){
1414 if(!msgline_hidden(screen->stream, screen->msgmap,
1415 mn_raw2m(screen->msgmap,thrd->rawno),
1417 x += xdir;
1419 if(thrddir > 0 && thrd->nextthd)
1420 thrd = fetch_thread(screen->stream, thrd->nextthd);
1421 else if(thrddir < 0 && thrd->prevthd)
1422 thrd = fetch_thread(screen->stream, thrd->prevthd);
1423 else
1424 thrd = NULL;
1428 scroll_setpos(x);
1431 else{
1433 * This works for forward or reverse sort because the thrdno's
1434 * will have been reversed.
1436 thrd = fetch_thread(screen->stream,
1437 mn_m2raw(screen->msgmap, screen->msg_at_top));
1438 if(thrd){
1439 scroll_setrange(screen->lines_per_page,
1440 screen->msgmap->max_thrdno - 1L);
1441 scroll_setpos(thrd->thrdno - 1L);
1445 else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
1446 long x, range;
1448 range = mn_get_total(screen->msgmap) - n - 1L;
1449 scroll_setrange(screen->lines_per_page, range);
1451 if(range >= screen->lines_per_page){ /* else not needed */
1452 if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
1453 for(n = 1, x = 0; n != screen->msg_at_top; n++)
1454 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1455 x++;
1457 else{
1458 for(n = mn_get_total(screen->msgmap), x = range;
1459 n != screen->msg_at_top; n--)
1460 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1461 x--;
1464 scroll_setpos(x);
1467 else{
1468 scroll_setrange(screen->lines_per_page,
1469 mn_get_total(screen->msgmap) - 1L);
1470 scroll_setpos(screen->msg_at_top - 1L);
1472 #endif
1475 * Set up c-client call back to tell us about IMAP envelope arrivals
1476 * Can't do it (easily) if single lines on the screen need information
1477 * about more than a single message before they can be drawn.
1479 if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
1480 && !(THREADING() && (sp_viewing_a_thread(screen->stream)
1481 || ps_global->thread_disp_style == THREAD_MUTTLIKE
1482 || any_lflagged(screen->msgmap, MN_COLL))))
1483 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
1485 if(THRD_INDX())
1486 visible = screen->msgmap->visible_threads;
1487 else if(THREADING() && sp_viewing_a_thread(screen->stream)){
1489 * We know that all visible messages in the thread are marked
1490 * with MN_CHID2.
1492 for(visible = 0L, n = screen->msg_at_top;
1493 visible < (int) screen->lines_per_page
1494 && n <= mn_get_total(screen->msgmap); n++){
1496 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1497 break;
1499 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1500 visible++;
1503 else
1504 visible = mn_get_total(screen->msgmap)
1505 - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
1507 /*---- march thru display lines, painting whatever is needed ----*/
1508 for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
1509 if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
1510 if(screen->entry_state[i].id != LINE_HASH_N){
1511 screen->entry_state[i].hilite = 0;
1512 screen->entry_state[i].bolded = 0;
1513 screen->entry_state[i].msgno = 0L;
1514 screen->entry_state[i].id = LINE_HASH_N;
1515 ClearLine(HEADER_ROWS(state) + i);
1518 else{
1519 ICE_S *ice;
1522 * This changes status_col as a side effect so it has to be
1523 * executed before next line.
1525 ice = build_header_line(state, screen->stream, screen->msgmap,
1526 n, &already_fetched);
1527 if(visible > 0L)
1528 visible--;
1530 if(THRD_INDX()){
1531 unsigned long rawno;
1533 rawno = mn_m2raw(screen->msgmap, n);
1534 if(rawno)
1535 thrd = fetch_thread(screen->stream, rawno);
1538 row = paint_index_line(ice, i,
1539 (THRD_INDX() && thrd) ? thrd->thrdno : n,
1540 screen->status_fld, screen->plus_fld,
1541 screen->arrow_fld, &screen->entry_state[i],
1542 mn_is_cur(screen->msgmap, n),
1543 THRD_INDX()
1544 ? (count_lflags_in_thread(screen->stream,
1545 thrd,
1546 screen->msgmap,
1547 MN_SLCT) > 0)
1548 : get_lflag(screen->stream, screen->msgmap,
1549 n, MN_SLCT));
1550 fflush(stdout);
1551 if(row && retval < 0)
1552 retval = row;
1555 /*--- increment n ---*/
1556 while((visible == -1L || visible > 0L)
1557 && ++n <= mn_get_total(screen->msgmap)
1558 && msgline_hidden(screen->stream, screen->msgmap, n, 0))
1562 if(we_cancel)
1563 cancel_busy_cue(-1);
1565 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
1567 #ifdef _WINDOWS
1568 mswin_endupdate();
1569 #endif
1570 fflush(stdout);
1571 dprint((7, "--update_index done\n"));
1572 return(retval);
1577 /*----------------------------------------------------------------------
1578 Create a string summarizing the message header for index on screen
1580 Args: stream -- mail stream to fetch envelope info from
1581 msgmap -- message number to pine sort mapping
1582 msgno -- Message number to create line for
1584 Result: returns a malloced string
1585 saves string in a cache for next call for same header
1586 ----*/
1587 ICE_S *
1588 build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
1590 return(build_header_work(state, stream, msgmap, msgno,
1591 current_index_state->msg_at_top,
1592 current_index_state->lines_per_page,
1593 already_fetched));
1597 /*----------------------------------------------------------------------
1598 Paint the given index line
1601 Args: ice -- index cache entry
1602 line -- index line number on screen, starting at 0 for first
1603 visible line, 1, 2, ...
1604 msgno -- for painting the message number field
1605 sfld -- field type of the status field, which is
1606 where we'll put the X for selected if necessary
1607 pfld -- field type where the thread indicator symbol goes
1608 afld -- field type of column which corresponds to the
1609 index-format ARROW token
1610 entry -- cache used to help us decide whether or not we need to
1611 redraw the index line or if we can just leave it alone because
1612 we know it is already correct
1613 cur -- is this the current message?
1614 sel -- is this message in the selected set?
1616 Returns: screen row number if this is current message, else 0
1617 ----*/
1619 paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
1620 IndexColType pfld, IndexColType afld, struct entry_state *entry,
1621 int cur, int sel)
1623 COLOR_PAIR *lastc = NULL, *base_color = NULL;
1624 ICE_S *ice;
1625 IFIELD_S *ifield, *previfield = NULL;
1626 IELEM_S *ielem;
1627 int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
1628 int draw_whole_line = 0, draw_partial_line = 0;
1629 int n = MAX_SCREEN_COLS*6;
1630 char draw[MAX_SCREEN_COLS*6+1], *p;
1632 ice = (THRD_INDX() && argice) ? argice->tice : argice;
1634 /* This better not happen! */
1635 if(!ice){
1636 q_status_message3(SM_ORDER | SM_DING, 5, 5,
1637 "NULL ice in paint_index_line: %s, msgno=%s line=%s",
1638 THRD_INDX() ? "THRD_INDX" : "reg index",
1639 comatose(msgno), comatose(line));
1640 dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
1641 THRD_INDX() ? "THRD_INDX" : "reg index",
1642 msgno, line));
1643 return 0;
1646 if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
1647 entry->id = 0L;
1648 entry->msgno = 0L;
1649 draw_whole_line = 1;
1651 else if((cur != entry->hilite) || (sel != entry->bolded)
1652 || (ice->plus != entry->plus)){
1653 draw_partial_line = 1;
1656 if(draw_whole_line || draw_partial_line){
1658 if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
1660 memset(draw, 0, sizeof(draw));
1661 p = draw;
1663 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1665 /* space between fields */
1666 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
1667 *p++ = ' ';
1669 /* message number string is generated on the fly */
1670 if(ifield->ctype == iMessNo){
1671 ielem = ifield->ielem;
1672 if(ielem && ielem->datalen >= ifield->width){
1673 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1674 ielem->data[ifield->width] = '\0';
1675 ielem->data[ielem->datalen] = '\0';
1679 if(ifield->ctype == sfld){
1680 ielem = ifield->ielem;
1681 if(ielem && ielem->datalen > 0){
1682 if(draw_partial_line)
1683 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1685 if(ielem->datalen == 1){
1686 save_schar1 = ielem->data[0];
1687 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1688 if(draw_partial_line)
1689 Writechar(ielem->data[0], 0);
1691 if(ielem->next && ielem->next->datalen){
1692 save_schar2 = ielem->next->data[0];
1693 if(cur)
1694 ielem->next->data[0] = '>';
1696 if(draw_partial_line)
1697 Writechar(ielem->next->data[0], 0);
1700 else if(ielem->datalen > 1){
1701 save_schar1 = ielem->data[0];
1702 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1703 if(draw_partial_line)
1704 Writechar(ielem->data[0], 0);
1706 save_schar2 = ielem->data[1];
1707 if(cur){
1708 ielem->data[1] = '>';
1709 if(draw_partial_line)
1710 Writechar(ielem->data[1], 0);
1715 else if(ifield->ctype == afld){
1717 if(draw_partial_line){
1718 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1719 for(i = 0; i < ifield->width-1; i++)
1720 Writechar(cur ? '-' : ' ', 0);
1722 Writechar(cur ? '>' : ' ', 0);
1725 ielem = ifield->ielem;
1726 if(ielem && ielem->datalen >= ifield->width){
1727 for(i = 0; i < ifield->width-1; i++)
1728 ielem->data[i] = cur ? '-' : ' ';
1730 ielem->data[i] = cur ? '>' : ' ';
1733 else if(ifield->ctype == pfld){
1734 ielem = ifield->ielem;
1735 if(ielem && ielem->datalen > 0){
1736 save_pchar = ielem->data[0];
1737 ielem->data[0] = ice->plus;
1739 if(draw_partial_line){
1740 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1741 Writechar(ielem->data[0], 0);
1742 Writechar(' ', 0);
1747 for(ielem = ifield->ielem;
1748 ielem && ielem->print_format && p-draw < n;
1749 ielem = ielem->next){
1750 char *src;
1751 size_t bytes_added;
1753 src = ielem->data;
1754 bytes_added = utf8_pad_to_width(p, src,
1755 ((n+1)-(p-draw)) * sizeof(char),
1756 ielem->wid, ifield->leftadj);
1757 p += bytes_added;
1760 draw[n] = '\0';
1762 if(ifield->ctype == sfld){
1763 ielem = ifield->ielem;
1764 if(ielem && ielem->datalen > 0){
1765 if(ielem->datalen == 1){
1766 ielem->data[0] = save_schar1;
1767 if(ielem->next && ielem->next->datalen)
1768 ielem->next->data[0] = save_schar2;
1770 else if(ielem->datalen > 1){
1771 ielem->data[0] = save_schar1;
1772 ielem->data[1] = save_schar2;
1776 else if(ifield->ctype == afld){
1777 ielem = ifield->ielem;
1778 if(ielem && ielem->datalen >= ifield->width)
1779 for(i = 0; i < ifield->width; i++)
1780 ielem->data[i] = ' ';
1782 else if(ifield->ctype == pfld){
1783 ielem = ifield->ielem;
1784 if(ielem && ielem->datalen > 0)
1785 ielem->data[0] = save_pchar;
1788 previfield = ifield;
1791 *p = '\0';
1793 if(draw_whole_line)
1794 PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
1796 else{
1797 int uc, ac, do_arrow;
1798 int i, drew_X = 0;
1799 int inverse_hack = 0, need_inverse_hack = 0;
1800 int doing_bold = 0;
1802 /* so we can restore current color at the end */
1803 if((uc=pico_usingcolor()) != 0)
1804 lastc = pico_get_cur_color();
1807 * There are two possible "arrow" cursors. One is the one that
1808 * you get when you are at a slow speed or you turn that slow
1809 * speed one on. It is drawn as part of the status column.
1810 * That one is the one associated with the variable "ac".
1811 * It is always the base_color or the inverse of the base_color.
1813 * The other "arrow" cursor is the one you get by including the
1814 * ARROW token in the index-format. It may be configured to
1815 * be colored.
1817 * The arrow cursors have two special properties that make
1818 * them different from other sections or fields.
1819 * First, the arrow cursors only show up on the current line.
1820 * Second, the arrow cursors are drawn with generated data, not
1821 * data that is present in the passed in data.
1824 /* ac is for the old integrated arrow cursor */
1825 ac = F_ON(F_FORCE_ARROW,ps_global);
1827 /* do_arrow is for the ARROW token in index-format */
1828 do_arrow = (afld != iNothing);
1830 MoveCursor(HEADER_ROWS(ps_global) + line, 0);
1832 /* find the base color for the whole line */
1833 if(cur && !ac && !do_arrow){
1835 * This stanza handles the current line marking in the
1836 * regular, non-arrow-cursor case.
1840 * If the current line has a linecolor, apply the
1841 * appropriate reverse transformation to show it is current.
1843 if(uc && ice->linecolor && ice->linecolor->fg[0]
1844 && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
1845 base_color = apply_rev_color(ice->linecolor,
1846 ps_global->index_color_style);
1848 (void)pico_set_colorp(base_color, PSC_NONE);
1850 else{
1851 inverse_hack++;
1852 if(uc){
1853 COLOR_PAIR *rev;
1855 if((rev = pico_get_rev_color()) != NULL){
1856 base_color = new_color_pair(rev->fg, rev->bg);
1857 (void)pico_set_colorp(base_color, PSC_NONE);
1859 else
1860 base_color = lastc;
1864 else if(uc && ice->linecolor && ice->linecolor->fg[0]
1865 && ice->linecolor->bg[0]
1866 && pico_is_good_colorpair(ice->linecolor)){
1867 (void)pico_set_colorp(ice->linecolor, PSC_NONE);
1868 base_color = ice->linecolor;
1870 else
1871 base_color = lastc;
1873 memset(draw, 0, sizeof(draw));
1874 p = draw;
1876 doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
1878 /* draw each field */
1879 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1881 drew_X = 0;
1884 * Fix up the data for some special cases.
1887 /* message number string is generated on the fly */
1888 if(ifield->ctype == iMessNo){
1889 ielem = ifield->ielem;
1890 if(ielem && ielem->datalen >= ifield->width){
1891 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1892 ielem->data[ifield->width] = '\0';
1893 ielem->data[ielem->datalen] = '\0';
1897 if(ifield->ctype == sfld){
1898 ielem = ifield->ielem;
1899 if(ielem && ielem->datalen > 0){
1900 if(ielem->datalen == 1){
1901 save_schar1 = ielem->data[0];
1902 if(sel && !doing_bold){
1903 ielem->data[0] = 'X';
1904 drew_X++;
1906 else if(ac && cur && ielem->data[0] == ' ')
1907 ielem->data[0] = '-';
1909 if(ielem->next && ielem->next->datalen){
1910 save_schar2 = ielem->next->data[0];
1911 if(ac && cur && ielem->next->data[0] != '\0')
1912 ielem->next->data[0] = '>';
1915 else if(ielem->datalen > 1){
1916 if(sel && !doing_bold){
1917 ielem->data[0] = 'X';
1918 drew_X++;
1920 else if(ac && cur && ielem->data[0] == ' ')
1921 ielem->data[0] = '-';
1923 save_schar2 = ielem->data[1];
1924 if(ac && cur && ielem->data[1] != '\0')
1925 ielem->data[1] = '>';
1929 else if(ifield->ctype == afld && do_arrow && cur){
1931 ielem = ifield->ielem;
1932 if(ielem && ielem->datalen >= ifield->width){
1933 for(i = 0; i < ifield->width-1; i++)
1934 ielem->data[i] = cur ? '-' : ' ';
1936 ielem->data[i] = '>';
1939 else if(ifield->ctype == pfld){
1940 ielem = ifield->ielem;
1941 if(ielem && ielem->datalen > 0){
1942 save_pchar = ielem->data[0];
1943 ielem->data[0] = ice->plus;
1947 /* space between fields */
1948 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
1949 if(inverse_hack)
1950 StartInverse();
1952 Write_to_screen(" ");
1953 if(inverse_hack)
1954 EndInverse();
1957 for(ielem = ifield->ielem; ielem; ielem = ielem->next){
1958 char *src;
1960 src = ielem->data;
1961 utf8_pad_to_width(draw, src, (n+1) * sizeof(char),
1962 ielem->wid, ifield->leftadj);
1963 draw[n] = '\0';
1966 * Switch to color for ielem.
1967 * But don't switch if we drew an X in this column,
1968 * because that overwrites the colored thing, and don't
1969 * switch if this is the ARROW field and this is not
1970 * the current message. ARROW field is only colored for
1971 * the current message.
1972 * And don't switch if current line and type eTypeCol.
1974 if(ielem->color && pico_is_good_colorpair(ielem->color)
1975 && !(do_arrow && ifield->ctype == afld && !cur)
1976 && (!drew_X || ielem != ifield->ielem)
1977 && !(cur && ielem->type == eTypeCol)){
1978 need_inverse_hack = 0;
1979 (void) pico_set_colorp(ielem->color, PSC_NORM);
1981 else
1982 need_inverse_hack = 1;
1984 if(need_inverse_hack && inverse_hack)
1985 StartInverse();
1987 Write_to_screen(draw);
1988 if(need_inverse_hack && inverse_hack)
1989 EndInverse();
1991 (void) pico_set_colorp(base_color, PSC_NORM);
1995 * Restore the data for the special cases.
1998 if(ifield->ctype == sfld){
1999 ielem = ifield->ielem;
2000 if(ielem && ielem->datalen > 0){
2001 if(ielem->datalen == 1){
2002 ielem->data[0] = save_schar1;
2003 if(ielem->next && ielem->next->datalen)
2004 ielem->next->data[0] = save_schar2;
2006 else if(ielem->datalen > 1){
2007 ielem->data[0] = save_schar1;
2008 ielem->data[1] = save_schar2;
2012 else if(ifield->ctype == afld){
2013 ielem = ifield->ielem;
2014 if(ielem && ielem->datalen >= ifield->width)
2015 for(i = 0; i < ifield->width; i++)
2016 ielem->data[i] = ' ';
2018 else if(ifield->ctype == pfld){
2019 ielem = ifield->ielem;
2020 if(ielem && ielem->datalen > 0)
2021 ielem->data[0] = save_pchar;
2024 previfield = ifield;
2027 if(doing_bold)
2028 EndBold();
2030 if(base_color && base_color != lastc && base_color != ice->linecolor)
2031 free_color_pair(&base_color);
2033 if(lastc){
2034 (void)pico_set_colorp(lastc, PSC_NORM);
2035 free_color_pair(&lastc);
2040 entry->hilite = cur;
2041 entry->bolded = sel;
2042 entry->msgno = msgno;
2043 entry->plus = ice->plus;
2044 entry->id = ice->id;
2046 if(!ice->color_lookup_done && pico_usingcolor())
2047 entry->id = 0;
2049 return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
2053 * setup_index_state - hooked onto pith_opt_save_index_state to setup
2054 * current_index_state after setup_{index,thread}_header_widths
2056 void
2057 setup_index_state(int threaded)
2059 if(current_index_state){
2060 if(threaded){
2061 current_index_state->status_col = 0;
2062 current_index_state->status_fld = iStatus;
2063 current_index_state->plus_fld = iNothing;
2064 current_index_state->arrow_fld = iNothing;
2065 } else {
2066 INDEX_COL_S *cdesc, *prevcdesc = NULL;
2067 IndexColType sfld, altfld, plusfld, arrowfld;
2068 int width, fld, col, pluscol, scol, altcol;
2070 col = 0;
2071 scol = -1;
2072 sfld = iNothing;
2073 altcol = -1;
2074 altfld = iNothing;
2075 /* figure out which field is status field */
2076 for(cdesc = ps_global->index_disp_format, fld = 0;
2077 cdesc->ctype != iNothing;
2078 cdesc++){
2079 width = cdesc->width;
2080 if(width == 0)
2081 continue;
2083 /* space between columns */
2084 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2085 col++;
2087 if(cdesc->ctype == iStatus){
2088 scol = col;
2089 sfld = cdesc->ctype;
2090 break;
2093 if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
2094 scol = col;
2095 sfld = cdesc->ctype;
2096 break;
2099 if(cdesc->ctype == iMessNo){
2100 altcol = col;
2101 altfld = cdesc->ctype;
2104 col += width;
2105 fld++;
2106 prevcdesc = cdesc;
2109 if(sfld == iNothing){
2110 if(altcol == -1){
2111 scol = 0;
2113 else{
2114 scol = altcol;
2115 sfld = altfld;
2120 current_index_state->status_col = scol;
2121 current_index_state->status_fld = sfld;
2123 col = 0;
2124 plusfld = iNothing;
2125 pluscol = -1;
2126 prevcdesc = NULL;
2127 /* figure out which column to use for threading '+' */
2128 if(THREADING()
2129 && ps_global->thread_disp_style != THREAD_NONE
2130 && ps_global->VAR_THREAD_MORE_CHAR[0]
2131 && ps_global->VAR_THREAD_EXP_CHAR[0])
2132 for(cdesc = ps_global->index_disp_format, fld = 0;
2133 cdesc->ctype != iNothing;
2134 cdesc++){
2135 width = cdesc->width;
2136 if(width == 0)
2137 continue;
2139 /* space between columns */
2140 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2141 col++;
2143 if((cdesc->ctype == iSubject
2144 || cdesc->ctype == iSubjectText
2145 || cdesc->ctype == iSubjKey
2146 || cdesc->ctype == iSubjKeyText
2147 || cdesc->ctype == iSubjKeyInit
2148 || cdesc->ctype == iSubjKeyInitText)
2149 && (ps_global->thread_disp_style == THREAD_STRUCT
2150 || ps_global->thread_disp_style == THREAD_MUTTLIKE
2151 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
2152 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
2153 plusfld = cdesc->ctype;
2154 pluscol = col;
2155 break;
2158 if((cdesc->ctype == iFrom
2159 || cdesc->ctype == iFromToNotNews
2160 || cdesc->ctype == iFromTo
2161 || cdesc->ctype == iAddress
2162 || cdesc->ctype == iMailbox)
2163 && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
2164 || ps_global->thread_disp_style == THREAD_INDENT_FROM2
2165 || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
2166 plusfld = cdesc->ctype;
2167 pluscol = col;
2168 break;
2171 col += width;
2172 fld++;
2173 prevcdesc = cdesc;
2176 current_index_state->plus_fld = plusfld;
2177 current_index_state->plus_col = pluscol;
2179 arrowfld = iNothing;
2180 /* figure out which field is arrow field, if any */
2181 for(cdesc = ps_global->index_disp_format, fld = 0;
2182 cdesc->ctype != iNothing;
2183 cdesc++){
2184 width = cdesc->width;
2185 if(width == 0)
2186 continue;
2188 if(cdesc->ctype == iArrow){
2189 arrowfld = cdesc->ctype;
2190 break;
2193 fld++;
2196 current_index_state->arrow_fld = arrowfld;
2203 * insert_condensed_thread_cue - used on pith hook to add decoration to
2204 * subject or from text to show condensed thread info
2207 condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
2208 char **fieldstr, size_t *strsize, int width, int collapsed)
2210 if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
2212 * WARNING!
2213 * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
2214 * and VAR_THREAD_EXP_CHAR[0] are ascii.
2215 * Could do something similar to the conversions done with keyword
2216 * initials in key_str.
2218 if(ice)
2219 ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
2220 : (thd && thd->next)
2221 ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
2223 if(strsize && *strsize > 0 && width != 0){
2224 *(*fieldstr)++ = ' ';
2225 (*strsize)--;
2226 if(width > 0)
2227 width--;
2230 if(strsize && *strsize > 0 && width != 0){
2231 *(*fieldstr)++ = ' ';
2232 (*strsize)--;
2233 if(width > 0)
2234 width--;
2238 return(width);
2243 truncate_subj_and_from_strings(void)
2245 return 1;
2250 * paint_index_hline - paint index line given what we got
2252 void
2253 paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
2255 PINETHRD_S *thrd;
2258 * Trust only what we get back that isn't bogus since
2259 * we were prevented from doing any fetches and such...
2261 if((ps_global->redrawer == redraw_index_body
2262 || ps_global->prev_screen == mail_index_screen)
2263 && current_index_state
2264 && current_index_state->stream == stream
2265 && !ps_global->msgmap->hilited){
2266 int line;
2269 * This test isn't right if there are hidden lines. The line will
2270 * fail the test because it seems like it is past the end of the
2271 * screen but since the hidden lines don't take up space the line
2272 * might actually be on the screen. Don't know that it is worth
2273 * it to fix this, though, since you may have to file through
2274 * many hidden lines before finding the visible ones. I'm not sure
2275 * if the logic inside the if is correct when we do pass the
2276 * top-level test. Leave it for now. Hubert - 2002-06-28
2278 if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
2279 && line < current_index_state->lines_per_page){
2280 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
2281 long n;
2282 long zoomhide, collapsehide;
2284 zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
2285 collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
2288 * Line is visible if it is selected and not hidden due to
2289 * thread collapse, or if there is no zooming happening and
2290 * it is not hidden due to thread collapse.
2292 for(line = 0, n = current_index_state->msg_at_top;
2293 n != msgno;
2294 n++)
2295 if((zoomhide
2296 && get_lflag(stream, current_index_state->msgmap,
2297 n, MN_SLCT)
2298 && (!collapsehide
2299 || !get_lflag(stream, current_index_state->msgmap, n,
2300 MN_CHID)))
2302 (!zoomhide
2303 && !get_lflag(stream, current_index_state->msgmap,
2304 n, MN_CHID)))
2305 line++;
2308 thrd = NULL;
2309 if(THRD_INDX()){
2310 unsigned long rawno;
2312 rawno = mn_m2raw(current_index_state->msgmap, msgno);
2313 if(rawno)
2314 thrd = fetch_thread(stream, rawno);
2317 paint_index_line(ice, line,
2318 (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
2319 current_index_state->status_fld,
2320 current_index_state->plus_fld,
2321 current_index_state->arrow_fld,
2322 &current_index_state->entry_state[line],
2323 mn_is_cur(current_index_state->msgmap, msgno),
2324 THRD_INDX()
2325 ? (count_lflags_in_thread(stream, thrd,
2326 current_index_state->msgmap,
2327 MN_SLCT) > 0)
2328 : get_lflag(stream, current_index_state->msgmap,
2329 msgno, MN_SLCT));
2330 fflush(stdout);
2339 * pine_imap_env -- C-client's telling us an envelope just arrived
2340 * from the server. Use it if we can...
2342 void
2343 pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
2345 MESSAGECACHE *mc;
2347 dprint((7, "imap_env(%ld)\n", rawno));
2348 if(stream && !sp_mail_box_changed(stream)
2349 && stream == ps_global->mail_stream
2350 && rawno > 0L && rawno <= stream->nmsgs
2351 && (mc = mail_elt(stream,rawno))
2352 && mc->valid
2353 && mc->rfc822_size
2354 && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
2355 INDEXDATA_S idata;
2356 ICE_S *ice;
2358 memset(&idata, 0, sizeof(INDEXDATA_S));
2359 idata.no_fetch = 1;
2360 idata.size = mc->rfc822_size;
2361 idata.rawno = rawno;
2362 idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
2363 idata.stream = stream;
2365 index_data_env(&idata, env);
2368 * Look for resent-to already in MAILCACHE data
2370 if(mc->private.msg.header.text.data){
2371 STRINGLIST *lines;
2372 SIZEDTEXT szt;
2373 static char *linelist[] = {"resent-to" , NULL};
2375 if(mail_match_lines(lines = new_strlst(linelist),
2376 mc->private.msg.lines, 0L)){
2377 idata.valid_resent_to = 1;
2378 memset(&szt, 0, sizeof(SIZEDTEXT));
2379 textcpy(&szt, &mc->private.msg.header.text);
2380 mail_filter((char *) szt.data, szt.size, lines, 0L);
2381 idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
2382 if(szt.data)
2383 fs_give((void **) &szt.data);
2386 free_strlst(&lines);
2389 ice = (*format_index_line)(&idata);
2390 if(idata.bogus)
2391 clear_ice(&ice);
2392 else
2393 paint_index_hline(stream, idata.msgno, ice);
2398 /*----------------------------------------------------------------------
2399 Scroll to specified postion.
2402 Args: pos - position to scroll to.
2404 Returns: TRUE - did the scroll operation.
2405 FALSE - was not able to do the scroll operation.
2406 ----*/
2408 index_scroll_to_pos (long int pos)
2410 static short bad_timing = 0;
2411 long i, j, k;
2413 if(bad_timing)
2414 return (FALSE);
2417 * Put the requested line at the top of the screen...
2421 * Starting at msg 'pos' find next visible message.
2423 for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
2424 if(!msgline_hidden(current_index_state->stream,
2425 current_index_state->msgmap, i, 0)){
2426 current_index_state->msg_at_top = i;
2427 break;
2432 * If single selection, move selected message to be on the screen.
2434 if (mn_total_cur(current_index_state->msgmap) == 1L) {
2435 if (current_index_state->msg_at_top >
2436 mn_get_cur (current_index_state->msgmap)) {
2437 /* Selection was above screen, move to top of screen. */
2438 mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
2440 else {
2441 /* Scan through the screen. If selection found, leave where is.
2442 * Otherwise, move to end of screen */
2443 for( i = current_index_state->msg_at_top,
2444 j = current_index_state->lines_per_page;
2445 i != mn_get_cur(current_index_state->msgmap) &&
2446 i <= mn_get_total(current_index_state->msgmap) &&
2447 j > 0L;
2448 i++) {
2449 if(!msgline_hidden(current_index_state->stream,
2450 current_index_state->msgmap, i, 0)){
2451 j--;
2452 k = i;
2455 if(j <= 0L)
2456 /* Move to end of screen. */
2457 mn_set_cur(current_index_state->msgmap, k);
2461 bad_timing = 0;
2462 return (TRUE);
2467 /*----------------------------------------------------------------------
2468 Adjust the index display state down a line
2470 Args: scroll_count -- number of lines to scroll
2472 Returns: TRUE - did the scroll operation.
2473 FALSE - was not able to do the scroll operation.
2474 ----*/
2476 index_scroll_down(long int scroll_count)
2478 static short bad_timing = 0;
2479 long i, j, k;
2480 long cur, total;
2482 if(bad_timing)
2483 return (FALSE);
2485 bad_timing = 1;
2488 j = -1L;
2489 total = mn_get_total (current_index_state->msgmap);
2490 for(k = i = current_index_state->msg_at_top; ; i++){
2492 /* Only examine non-hidden messages. */
2493 if(!msgline_hidden(current_index_state->stream,
2494 current_index_state->msgmap, i, 0)){
2495 /* Remember this message */
2496 k = i;
2497 /* Increment count of lines. */
2498 if (++j >= scroll_count) {
2499 /* Counted enough lines, stop. */
2500 current_index_state->msg_at_top = k;
2501 break;
2505 /* If at last message, stop. */
2506 if (i >= total){
2507 current_index_state->msg_at_top = k;
2508 break;
2513 * If not multiple selection, see if selected message visable. if not
2514 * set it to last visable message.
2516 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2517 j = 0L;
2518 cur = mn_get_cur (current_index_state->msgmap);
2519 for (i = current_index_state->msg_at_top; i <= total; ++i) {
2520 if(!msgline_hidden(current_index_state->stream,
2521 current_index_state->msgmap, i, 0)){
2522 if (++j >= current_index_state->lines_per_page) {
2523 break;
2525 if (i == cur)
2526 break;
2529 if (i != cur)
2530 mn_set_cur(current_index_state->msgmap,
2531 current_index_state->msg_at_top);
2534 bad_timing = 0;
2535 return (TRUE);
2540 /*----------------------------------------------------------------------
2541 Adjust the index display state up a line
2543 Args: scroll_count -- number of lines to scroll
2545 Returns: TRUE - did the scroll operation.
2546 FALSE - was not able to do the scroll operation.
2548 ----*/
2550 index_scroll_up(long int scroll_count)
2552 static short bad_timing = 0;
2553 long i, j, k;
2554 long cur;
2556 if(bad_timing)
2557 return(FALSE);
2559 bad_timing = 1;
2561 j = -1L;
2562 for(k = i = current_index_state->msg_at_top; ; i--){
2564 /* Only examine non-hidden messages. */
2565 if(!msgline_hidden(current_index_state->stream,
2566 current_index_state->msgmap, i, 0)){
2567 /* Remember this message */
2568 k = i;
2569 /* Increment count of lines. */
2570 if (++j >= scroll_count) {
2571 /* Counted enough lines, stop. */
2572 current_index_state->msg_at_top = k;
2573 break;
2577 /* If at first message, stop */
2578 if (i <= 1L){
2579 current_index_state->msg_at_top = k;
2580 break;
2586 * If not multiple selection, see if selected message visable. if not
2587 * set it to last visable message.
2589 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2590 j = 0L;
2591 cur = mn_get_cur (current_index_state->msgmap);
2592 for ( i = current_index_state->msg_at_top;
2593 i <= mn_get_total(current_index_state->msgmap);
2594 ++i) {
2595 if(!msgline_hidden(current_index_state->stream,
2596 current_index_state->msgmap, i, 0)){
2597 if (++j >= current_index_state->lines_per_page) {
2598 k = i;
2599 break;
2601 if (i == cur)
2602 break;
2605 if (i != cur)
2606 mn_set_cur(current_index_state->msgmap, k);
2610 bad_timing = 0;
2611 return (TRUE);
2616 /*----------------------------------------------------------------------
2617 Calculate the message number that should be at the top of the display
2619 Args: current - the current message number
2620 lines_per_page - the number of lines for the body of the index only
2622 Returns: -1 if the current message is -1
2623 the message entry for the first message at the top of the screen.
2625 When paging in the index it is always on even page boundies, and the
2626 current message is always on the page thus the top of the page is
2627 completely determined by the current message and the number of lines
2628 on the page.
2629 ----*/
2630 long
2631 top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
2633 long current, hidden, lastn;
2634 long n, m = 0L, t = 1L;
2636 current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
2638 if(current < 0L)
2639 return(-1);
2641 if(lines_per_page == 0L)
2642 return(current);
2644 if(THRD_INDX_ENABLED()){
2645 long rawno;
2646 PINETHRD_S *thrd = NULL;
2648 rawno = mn_m2raw(msgs, mn_get_cur(msgs));
2649 if(rawno)
2650 thrd = fetch_thread(stream, rawno);
2652 if(THRD_INDX()){
2654 if(any_lflagged(msgs, MN_HIDE)){
2655 PINETHRD_S *is_current_thrd;
2657 is_current_thrd = thrd;
2658 if(is_current_thrd){
2659 if(mn_get_revsort(msgs)){
2660 /* start with top of tail of thread list */
2661 thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
2662 if(thrd && thrd->top && thrd->top != thrd->rawno)
2663 thrd = fetch_thread(stream, thrd->top);
2665 else{
2666 /* start with head of thread list */
2667 thrd = fetch_head_thread(stream);
2670 t = 1L;
2671 m = 0L;
2672 if(thrd)
2673 n = mn_raw2m(msgs, thrd->rawno);
2675 while(thrd){
2676 if(!msgline_hidden(stream, msgs, n, 0)
2677 && (++m % lines_per_page) == 1L)
2678 t = n;
2680 if(thrd == is_current_thrd)
2681 break;
2683 if(mn_get_revsort(msgs) && thrd->prevthd)
2684 thrd = fetch_thread(stream, thrd->prevthd);
2685 else if(!mn_get_revsort(msgs) && thrd->nextthd)
2686 thrd = fetch_thread(stream, thrd->nextthd);
2687 else
2688 thrd = NULL;
2690 if(thrd)
2691 n = mn_raw2m(msgs, thrd->rawno);
2695 else{
2696 if(thrd){
2697 n = thrd->thrdno;
2698 m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
2699 n = thrd->rawno;
2701 * We want to find the m'th thread and the
2702 * message number that goes with that. We just have
2703 * to back up from where we are to get there.
2704 * If we have a reverse sort backing up is going
2705 * forward through the thread.
2707 while(thrd && m < thrd->thrdno){
2708 n = thrd->rawno;
2709 if(mn_get_revsort(msgs) && thrd->nextthd)
2710 thrd = fetch_thread(stream, thrd->nextthd);
2711 else if(!mn_get_revsort(msgs) && thrd->prevthd)
2712 thrd = fetch_thread(stream, thrd->prevthd);
2713 else
2714 thrd = NULL;
2717 if(thrd)
2718 n = thrd->rawno;
2720 t = mn_raw2m(msgs, n);
2724 else{ /* viewing a thread */
2726 lastn = mn_get_total(msgs);
2727 t = 1L;
2729 /* get top of thread */
2730 if(thrd && thrd->top && thrd->top != thrd->rawno)
2731 thrd = fetch_thread(stream, thrd->top);
2733 if(thrd){
2734 if(mn_get_revsort(msgs))
2735 lastn = mn_raw2m(msgs, thrd->rawno);
2736 else
2737 t = mn_raw2m(msgs, thrd->rawno);
2740 n = 0L;
2742 /* n is the end of this thread */
2743 while(thrd){
2744 n = mn_raw2m(msgs, thrd->rawno);
2745 if(thrd->branch)
2746 thrd = fetch_thread(stream, thrd->branch);
2747 else if(thrd->next)
2748 thrd = fetch_thread(stream, thrd->next);
2749 else
2750 thrd = NULL;
2753 if(n){
2754 if(mn_get_revsort(msgs))
2755 t = n;
2756 else
2757 lastn = n;
2760 for(m = 0L, n = t; n <= MIN(current, lastn); n++)
2761 if(!msgline_hidden(stream, msgs, n, 0)
2762 && (++m % lines_per_page) == 1L)
2763 t = n;
2766 return(t);
2768 else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
2770 if(current < mn_get_total(msgs) / 2){
2771 t = 1L;
2772 m = 0L;
2773 for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
2774 if(!msgline_hidden(stream, msgs, n, 0)
2775 && (++m % lines_per_page) == 1L)
2776 t = n;
2778 else{
2779 t = current+1L;
2780 m = mn_get_total(msgs)-hidden+1L;
2781 for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
2782 if(!msgline_hidden(stream, msgs, n, 0)
2783 && (--m % lines_per_page) == 1L)
2784 t = n;
2786 if(t > current)
2787 t = 1L;
2790 return(t);
2792 else
2793 return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
2797 /*----------------------------------------------------------------------
2798 Clear various bits that make up a healthy display
2800 ----*/
2801 void
2802 reset_index_border(void)
2804 mark_status_dirty();
2805 mark_keymenu_dirty();
2806 mark_titlebar_dirty();
2807 ps_global->mangled_screen = 1; /* signal FULL repaint */
2811 /*----------------------------------------------------------------------
2812 This redraws the body of the index screen, taking into
2813 account any change in the size of the screen. All the state needed to
2814 repaint is in the static variables so this can be called from
2815 anywhere.
2816 ----*/
2817 void
2818 redraw_index_body(void)
2820 int agg;
2822 if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
2823 restore_selected(current_index_state->msgmap);
2825 ps_global->mangled_body = 1;
2827 (void) update_index(ps_global, current_index_state);
2828 if(agg)
2829 pseudo_selected(current_index_state->stream, current_index_state->msgmap);
2833 /*----------------------------------------------------------------------
2834 Give hint about Other command being optional. Some people get the idea
2835 that it is required to use the commands on the 2nd and 3rd keymenus.
2837 Args: none
2839 Result: message may be printed to status line
2840 ----*/
2841 void
2842 warn_other_cmds(void)
2844 static int other_cmds = 0;
2846 other_cmds++;
2847 if(((ps_global->first_time_user || ps_global->show_new_version) &&
2848 other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
2849 q_status_message(SM_ASYNC, 0, 9,
2850 _("Remember the \"O\" command is always optional"));
2854 void
2855 thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
2856 UCS preloadkeystroke, int q_line)
2858 PINETHRD_S *thrd = NULL;
2859 unsigned long rawno, save_branch;
2860 int we_cancel = 0;
2861 int flags = AC_FROM_THREAD;
2863 if(!(stream && msgmap))
2864 return;
2866 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2867 if(rawno)
2868 thrd = fetch_thread(stream, rawno);
2870 if(!thrd)
2871 return;
2873 save_branch = thrd->branch;
2874 thrd->branch = 0L; /* branch is a sibling, not part of thread */
2876 if(!preloadkeystroke){
2877 if(!THRD_INDX()){
2878 if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
2879 flags |= AC_EXPN;
2880 else
2881 flags |= AC_COLL;
2884 if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
2885 == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
2886 flags |= AC_UNSEL;
2889 we_cancel = busy_cue(NULL, NULL, 1);
2891 /* save the SLCT flags in STMP for restoring at the bottom */
2892 copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
2894 /* clear the values from the SLCT flags */
2895 set_lflags(stream, msgmap, MN_SLCT, 0);
2897 /* set SLCT for thrd on down */
2898 set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
2899 thrd->branch = save_branch;
2901 if(we_cancel)
2902 cancel_busy_cue(0);
2904 (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
2905 q_line);
2907 /* restore the original flags */
2908 copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
2910 if(any_lflagged(msgmap, MN_HIDE) > 0L){
2911 /* if nothing left selected, unhide all */
2912 if(any_lflagged(msgmap, MN_SLCT) == 0L){
2913 (void) unzoom_index(ps_global, stream, msgmap);
2914 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
2915 q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
2918 /* if current is hidden, adjust */
2919 adjust_cur_to_visible(stream, msgmap);
2924 /*----------------------------------------------------------------------
2925 Search the message headers as displayed in index
2927 Args: command_line -- The screen line to prompt on
2928 msg -- The current message number to start searching at
2929 max_msg -- The largest message number in the current folder
2931 The headers are searched exactly as they are displayed on the screen. The
2932 search will wrap around to the beginning if not string is not found right
2933 away.
2934 ----*/
2935 void
2936 index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
2938 int rc, select_all = 0, flags, prefetch, we_turned_on = 0;
2939 long i, sorted_msg, selected = 0L;
2940 char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
2941 char buf[MAX_SCREEN_COLS+1], *p;
2942 HelpType help;
2943 char search_string[MAX_SEARCH+1];
2944 ICE_S *ice, *ic;
2945 static HISTORY_S *history = NULL;
2946 static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
2947 {ctrl('Y'), 10, "^Y", N_("First Msg")},
2948 {ctrl('V'), 11, "^V", N_("Last Msg")},
2949 {KEY_UP, 30, "", ""},
2950 {KEY_DOWN, 31, "", ""},
2951 {-1, 0, NULL, NULL} };
2952 #define KU_IS (3) /* index of KEY_UP */
2953 #define PREFETCH_THIS_MANY_LINES (50)
2955 init_hist(&history, HISTSIZE);
2956 search_string[0] = '\0';
2957 if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
2958 strncpy(search_string, p, sizeof(search_string));
2959 search_string[sizeof(search_string)-1] = '\0';
2962 dprint((4, "\n - search headers - \n"));
2964 if(!any_messages(msgmap, NULL, "to search")){
2965 return;
2967 else if(mn_total_cur(msgmap) > 1L){
2968 q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
2969 comatose(mn_total_cur(msgmap)));
2970 return;
2972 else
2973 sorted_msg = mn_get_cur(msgmap);
2975 help = NO_HELP;
2976 new_string[0] = '\0';
2978 while(1) {
2979 snprintf(prompt, sizeof(prompt), _("Word to search for [%s] : "), search_string);
2981 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
2982 header_search_key[0].ch = ctrl('X');
2983 header_search_key[0].rval = 12;
2984 header_search_key[0].name = "^X";
2985 header_search_key[0].label = N_("Select Matches");
2987 else{
2988 header_search_key[0].ch = header_search_key[0].rval = 0;
2989 header_search_key[0].name = header_search_key[0].label = NULL;
2993 * 2 is really 1 because there will be one real entry and
2994 * one entry of "" because of the get_prev_hist above.
2996 if(items_in_hist(history) > 2){
2997 header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
2998 header_search_key[KU_IS].label = HISTORY_KEYLABEL;
2999 header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
3000 header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
3002 else{
3003 header_search_key[KU_IS].name = "";
3004 header_search_key[KU_IS].label = "";
3005 header_search_key[KU_IS+1].name = "";
3006 header_search_key[KU_IS+1].label = "";
3009 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
3011 rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
3012 prompt, header_search_key, help, &flags);
3014 if(rc == 3) {
3015 help = (help != NO_HELP) ? NO_HELP :
3016 F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
3017 : h_os_index_whereis;
3018 continue;
3020 else if(rc == 10){
3021 q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
3022 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3024 selected = sorted_msg;
3025 mn_dec_cur(stream, msgmap, MH_NONE);
3026 sorted_msg = mn_get_cur(msgmap);
3028 while(selected != sorted_msg);
3030 else
3031 sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
3033 mn_set_cur(msgmap, sorted_msg);
3034 return;
3036 else if(rc == 11){
3037 q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
3038 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3040 selected = sorted_msg;
3041 mn_inc_cur(stream, msgmap, MH_NONE);
3042 sorted_msg = mn_get_cur(msgmap);
3044 while(selected != sorted_msg);
3046 else
3047 sorted_msg = mn_get_total(msgmap);
3049 mn_set_cur(msgmap, sorted_msg);
3050 return;
3052 else if(rc == 12){
3053 select_all = 1;
3054 break;
3056 else if(rc == 30){
3057 if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
3058 strncpy(new_string, p, sizeof(new_string));
3059 new_string[sizeof(new_string)-1] = '\0';
3061 else
3062 Writechar(BELL, 0);
3064 continue;
3066 else if(rc == 31){
3067 if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
3068 strncpy(new_string, p, sizeof(new_string));
3069 new_string[sizeof(new_string)-1] = '\0';
3071 else
3072 Writechar(BELL, 0);
3074 continue;
3077 if(rc != 4){ /* 4 is redraw */
3078 save_hist(history, new_string, 0, NULL);
3079 break;
3083 if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
3084 cmd_cancelled(_("Search"));
3085 return;
3088 if(new_string[0] == '\0'){
3089 strncpy(new_string, search_string, sizeof(new_string));
3090 new_string[sizeof(new_string)-1] = '\0';
3093 strncpy(search_string, new_string, sizeof(search_string));
3094 search_string[sizeof(search_string)-1] = '\0';
3096 we_turned_on = intr_handling_on();
3098 prefetch = 0;
3099 for(i = sorted_msg + ((select_all)?0:1);
3100 i <= mn_get_total(msgmap) && !ps_global->intr_pending;
3101 i++){
3102 if(msgline_hidden(stream, msgmap, i, 0))
3103 continue;
3105 if(prefetch <= 0)
3106 prefetch = PREFETCH_THIS_MANY_LINES;
3108 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3110 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3112 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3113 search_string)){
3114 selected++;
3115 if(select_all)
3116 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3117 else
3118 break;
3122 prefetch = 0;
3123 if(i > mn_get_total(msgmap)){
3124 for(i = 1; i < sorted_msg && !ps_global->intr_pending; i++){
3125 if(msgline_hidden(stream, msgmap, i, 0))
3126 continue;
3128 if(prefetch <= 0)
3129 prefetch = PREFETCH_THIS_MANY_LINES;
3131 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3133 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3135 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3136 search_string)){
3137 selected++;
3138 if(select_all)
3139 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3140 else
3141 break;
3146 /* search current line */
3147 if(!select_all && !selected){
3148 i = sorted_msg;
3149 if(!msgline_hidden(stream, msgmap, i, 0)){
3151 ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
3153 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3155 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3156 search_string)){
3157 selected++;
3162 if(ps_global->intr_pending){
3163 q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
3164 select_all ? _(" Selected set may be incomplete."):"");
3166 else if(select_all){
3167 if(selected
3168 && any_lflagged(msgmap, MN_SLCT) > 0L
3169 && !any_lflagged(msgmap, MN_HIDE)
3170 && F_ON(F_AUTO_ZOOM, state))
3171 (void) zoom_index(state, stream, msgmap, MN_SLCT);
3173 q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
3174 long2string(selected));
3176 else if(selected){
3177 q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
3178 (i < sorted_msg) ? _(". Search wrapped to beginning") :
3179 (i == sorted_msg) ? _(". Current line contains only match") : "");
3180 mn_set_cur(msgmap, i);
3182 else
3183 q_status_message(SM_ORDER, 0, 3, _("Word not found"));
3185 if(we_turned_on)
3186 intr_handling_off();
3191 * Original idea from Stephen Casner <casner@acm.org>.
3193 * Apply the appropriate reverse color transformation to the given
3194 * color pair and return a new color pair. The caller should free the
3195 * color pair.
3198 COLOR_PAIR *
3199 apply_rev_color(COLOR_PAIR *cp, int style)
3201 COLOR_PAIR *rc = pico_get_rev_color();
3203 if(rc){
3204 if(style == IND_COL_REV){
3205 /* just use Reverse color regardless */
3206 return(new_color_pair(rc->fg, rc->bg));
3208 else if(style == IND_COL_FG){
3210 * If changing to Rev fg is readable and different
3211 * from what it already is, do it.
3213 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
3214 return(new_color_pair(rc->fg, cp->bg));
3216 else if(style == IND_COL_BG){
3218 * If changing to Rev bg is readable and different
3219 * from what it already is, do it.
3221 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
3222 return(new_color_pair(cp->fg, rc->bg));
3224 else if(style == IND_COL_FG_NOAMBIG){
3226 * If changing to Rev fg is readable, different
3227 * from what it already is, and not the same as
3228 * the Rev color, do it.
3230 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
3231 strcmp(rc->bg, cp->bg))
3232 return(new_color_pair(rc->fg, cp->bg));
3234 else if(style == IND_COL_BG_NOAMBIG){
3236 * If changing to Rev bg is readable, different
3237 * from what it already is, and not the same as
3238 * the Rev color, do it.
3240 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
3241 strcmp(rc->fg, cp->fg))
3242 return(new_color_pair(cp->fg, rc->bg));
3246 /* come here for IND_COL_FLIP and for the cases which fail the tests */
3247 return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
3252 #ifdef _WINDOWS
3254 /*----------------------------------------------------------------------
3255 Callback to get the text of the current message. Used to display
3256 a message in an alternate window.
3258 Args: cmd - what type of scroll operation.
3259 text - filled with pointer to text.
3260 l - length of text.
3261 style - Returns style of text. Can be:
3262 GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
3263 lines
3264 GETTEXT_LINES - Is a pointer to NULL terminated array of
3265 char *. Each entry points to a line of
3266 text.
3268 this implementation always returns GETTEXT_TEXT.
3270 Returns: TRUE - did the scroll operation.
3271 FALSE - was not able to do the scroll operation.
3272 ----*/
3274 index_scroll_callback (cmd, scroll_pos)
3275 int cmd;
3276 long scroll_pos;
3278 int paint = TRUE;
3280 switch (cmd) {
3281 case MSWIN_KEY_SCROLLUPLINE:
3282 paint = index_scroll_up (scroll_pos);
3283 break;
3285 case MSWIN_KEY_SCROLLDOWNLINE:
3286 paint = index_scroll_down (scroll_pos);
3287 break;
3289 case MSWIN_KEY_SCROLLUPPAGE:
3290 paint = index_scroll_up (current_index_state->lines_per_page);
3291 break;
3293 case MSWIN_KEY_SCROLLDOWNPAGE:
3294 paint = index_scroll_down (current_index_state->lines_per_page);
3295 break;
3297 case MSWIN_KEY_SCROLLTO:
3298 /* Normalize msgno in zoomed case */
3299 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
3300 long n, x;
3302 for(n = 1L, x = 0;
3303 x < scroll_pos && n < mn_get_total(ps_global->msgmap);
3304 n++)
3305 if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
3306 n, 0))
3307 x++;
3309 scroll_pos = n - 1; /* list-position --> message number */
3312 paint = index_scroll_to_pos (scroll_pos + 1);
3313 break;
3316 if(paint){
3317 mswin_beginupdate();
3318 update_titlebar_message();
3319 update_titlebar_status();
3320 redraw_index_body();
3321 mswin_endupdate();
3324 return(paint);
3328 /*----------------------------------------------------------------------
3329 MSWin scroll callback to get the text of the current message
3331 Args: title - title for new window
3332 text -
3333 l -
3334 style -
3336 Returns: TRUE - got the requested text
3337 FALSE - was not able to get the requested text
3338 ----*/
3340 index_gettext_callback(title, titlelen, text, l, style)
3341 char *title;
3342 size_t titlelen;
3343 void **text;
3344 long *l;
3345 int *style;
3347 int rv = 0;
3348 ENVELOPE *env;
3349 BODY *body;
3350 STORE_S *so;
3351 gf_io_t pc;
3353 if(mn_get_total(ps_global->msgmap) > 0L
3354 && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
3355 gf_set_so_writec(&pc, so);
3357 if((env = pine_mail_fetchstructure(ps_global->mail_stream,
3358 mn_m2raw(ps_global->msgmap,
3359 mn_get_cur(ps_global->msgmap)),
3360 &body))
3361 && format_message(mn_m2raw(ps_global->msgmap,
3362 mn_get_cur(ps_global->msgmap)),
3363 env, body, NULL, FM_NEW_MESS, pc)){
3364 snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
3365 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
3366 mn_get_cur(ps_global->msgmap),
3367 mn_get_total(ps_global->msgmap));
3368 title[titlelen-1] = '\0';
3369 *text = so_text(so);
3370 *l = strlen((char *)so_text(so));
3371 *style = GETTEXT_TEXT;
3373 /* free alloc'd so, but preserve the text passed back to caller */
3374 so->txt = (void *) NULL;
3375 rv = 1;
3378 gf_clear_so_writec(so);
3379 so_give(&so);
3382 return(rv);
3390 index_sort_callback(set, order)
3391 int set;
3392 long order;
3394 int i = 0;
3396 if(set){
3397 sort_folder(ps_global->mail_stream, ps_global->msgmap,
3398 order & 0x000000ff,
3399 (order & 0x00000100) != 0, SRT_VRB);
3400 mswin_beginupdate();
3401 update_titlebar_message();
3402 update_titlebar_status();
3403 redraw_index_body();
3404 mswin_endupdate();
3405 flush_status_messages(1);
3407 else{
3408 i = (int) mn_get_sort(ps_global->msgmap);
3409 if(mn_get_revsort(ps_global->msgmap))
3410 i |= 0x0100;
3413 return(i);
3420 void
3421 index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
3423 int n = 0;
3424 int view_in_new_wind_index = -1;
3425 long rawno;
3426 MESSAGECACHE *mc;
3427 MPopup view_index_popup[32];
3428 struct key_menu *km = (style == ThreadIndex)
3429 ? &thread_keymenu
3430 : (ps_global->mail_stream != stream)
3431 ? &simple_index_keymenu
3432 : &index_keymenu;
3435 * Loosely follow the logic in do_index_border to figure
3436 * out which commands to show.
3439 if(full){
3440 if(km != &simple_index_keymenu){
3441 view_index_popup[n].type = tQueue;
3442 view_index_popup[n].label.string = (km == &thread_keymenu)
3443 ? "&View Thread" : "&View";
3444 view_index_popup[n].label.style = lNormal;
3445 view_index_popup[n++].data.val = 'V';
3448 if(km == &index_keymenu){
3449 view_in_new_wind_index = n;
3450 view_index_popup[n].type = tIndex;
3451 view_index_popup[n].label.style = lNormal;
3452 view_index_popup[n++].label.string = "View in New Window";
3455 if(km != &simple_index_keymenu)
3456 view_index_popup[n++].type = tSeparator;
3458 if(km == &thread_keymenu){
3459 view_index_popup[n].type = tQueue;
3460 view_index_popup[n].label.string = "&Delete Thread";
3461 view_index_popup[n].label.style = lNormal;
3462 view_index_popup[n++].data.val = 'D';
3464 view_index_popup[n].type = tQueue;
3465 view_index_popup[n].label.string = "&UnDelete Thread";
3466 view_index_popup[n].label.style = lNormal;
3467 view_index_popup[n++].data.val = 'U';
3469 else{
3470 /* Make "delete/undelete" item sensitive */
3471 mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
3472 && stream && rawno <= stream->nmsgs)
3473 ? mail_elt(stream, rawno) : NULL;
3474 view_index_popup[n].type = tQueue;
3475 view_index_popup[n].label.style = lNormal;
3476 if(mc && mc->deleted){
3477 view_index_popup[n].label.string = "&Undelete";
3478 view_index_popup[n++].data.val = 'U';
3480 else{
3481 view_index_popup[n].label.string = "&Delete";
3482 view_index_popup[n++].data.val = 'D';
3486 if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
3487 view_index_popup[n].type = tSubMenu;
3488 view_index_popup[n].label.string = "Flag";
3489 view_index_popup[n++].data.submenu = flag_submenu(mc);
3492 if(km == &simple_index_keymenu){
3493 view_index_popup[n].type = tQueue;
3494 view_index_popup[n].label.style = lNormal;
3495 view_index_popup[n].label.string = "&Select";
3496 view_index_popup[n++].data.val = 'S';
3498 else{
3499 view_index_popup[n].type = tQueue;
3500 view_index_popup[n].label.style = lNormal;
3501 view_index_popup[n].label.string = (km == &thread_keymenu)
3502 ? "&Save Thread" : "&Save";
3503 view_index_popup[n++].data.val = 'S';
3505 view_index_popup[n].type = tQueue;
3506 view_index_popup[n].label.style = lNormal;
3507 view_index_popup[n].label.string = (km == &thread_keymenu)
3508 ? "&Export Thread" : "&Export";
3509 view_index_popup[n++].data.val = 'E';
3511 view_index_popup[n].type = tQueue;
3512 view_index_popup[n].label.style = lNormal;
3513 view_index_popup[n].label.string = "Print";
3514 view_index_popup[n++].data.val = '%';
3516 view_index_popup[n].type = tQueue;
3517 view_index_popup[n].label.style = lNormal;
3518 view_index_popup[n].label.string = (km == &thread_keymenu)
3519 ? "&Reply To Thread" : "&Reply";
3520 view_index_popup[n++].data.val = 'R';
3522 view_index_popup[n].type = tQueue;
3523 view_index_popup[n].label.style = lNormal;
3524 view_index_popup[n].label.string = (km == &thread_keymenu)
3525 ? "&Forward Thread" : "&Forward";
3526 view_index_popup[n++].data.val = 'F';
3528 if(F_ON(F_ENABLE_BOUNCE, ps_global)){
3529 view_index_popup[n].type = tQueue;
3530 view_index_popup[n].label.style = lNormal;
3531 view_index_popup[n].label.string = (km == &thread_keymenu)
3532 ? "&Bounce Thread" : "&Bounce";
3533 view_index_popup[n++].data.val = 'B';
3536 view_index_popup[n].type = tQueue;
3537 view_index_popup[n].label.style = lNormal;
3538 view_index_popup[n].label.string = "&Take Addresses";
3539 view_index_popup[n++].data.val = 'T';
3541 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3542 view_index_popup[n].type = tQueue;
3543 view_index_popup[n].label.style = lNormal;
3544 view_index_popup[n].label.string = "[Un]Select Current";
3545 view_index_popup[n++].data.val = ':';
3549 view_index_popup[n].type = tQueue;
3550 view_index_popup[n].label.style = lNormal;
3551 view_index_popup[n].label.string = "&WhereIs";
3552 view_index_popup[n++].data.val = 'W';
3554 view_index_popup[n++].type = tSeparator;
3557 if(km == &simple_index_keymenu){
3558 view_index_popup[n].type = tQueue;
3559 view_index_popup[n].label.style = lNormal;
3560 view_index_popup[n].label.string = "&Exit Select";
3561 view_index_popup[n++].data.val = 'E';
3563 else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
3564 view_index_popup[n].type = tQueue;
3565 view_index_popup[n].label.style = lNormal;
3566 view_index_popup[n].label.string = "Thread Index";
3567 view_index_popup[n++].data.val = '<';
3569 else{
3570 view_index_popup[n].type = tQueue;
3571 view_index_popup[n].label.style = lNormal;
3572 view_index_popup[n].label.string = "Folder &List";
3573 view_index_popup[n++].data.val = '<';
3576 view_index_popup[n].type = tQueue;
3577 view_index_popup[n].label.style = lNormal;
3578 view_index_popup[n].label.string = "&Main Menu";
3579 view_index_popup[n++].data.val = 'M';
3581 view_index_popup[n].type = tTail;
3583 if(mswin_popup(view_index_popup) == view_in_new_wind_index
3584 && view_in_new_wind_index >= 0)
3585 view_in_new_window();
3589 char *
3590 pcpine_help_index(title)
3591 char *title;
3594 * Title is size 256 in pico. Put in args.
3596 if(title)
3597 strncpy(title, "Alpine MESSAGE INDEX Help", 256);
3599 return(pcpine_help(h_mail_index));
3602 char *
3603 pcpine_help_index_simple(title)
3604 char *title;
3607 * Title is size 256 in pico. Put in args.
3609 if(title)
3610 strncpy(title, "Alpine SELECT MESSAGE Help", 256);
3612 return(pcpine_help(h_simple_index));
3616 #include "../pico/osdep/mswin_tw.h"
3619 void
3620 view_in_new_window(void)
3622 char title[GETTEXT_TITLELEN+1];
3623 void *text;
3624 long len;
3625 int format;
3626 MSWIN_TEXTWINDOW *mswin_tw = NULL;
3628 /* Launch text in alt window. */
3629 if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
3630 if(format == GETTEXT_TEXT)
3631 mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
3632 NULL, MSWIN_DT_USEALTWINDOW);
3633 else if(format == GETTEXT_LINES)
3634 mswin_tw = mswin_displaytext(title, NULL, 0, text,
3635 NULL, MSWIN_DT_USEALTWINDOW);
3637 if(mswin_tw != NULL)
3638 mswin_set_readonly(mswin_tw, FALSE);
3642 #endif /* _WINDOWS */