* Add new hidden feature "Enable Delete Before Writing" that makes Alpine
[alpine.git] / alpine / mailindx.c
blob63f92772f385d9781da78c511c28b48746d1f3f6
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 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "mailindx.h"
21 #include "mailcmd.h"
22 #include "status.h"
23 #include "context.h"
24 #include "keymenu.h"
25 #include "alpine.h"
26 #include "help.h"
27 #include "radio.h"
28 #include "titlebar.h"
29 #include "../pith/flag.h"
30 #include "../pith/newmail.h"
31 #include "../pith/thread.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/icache.h"
35 #include "../pith/state.h"
36 #include "../pith/bitmap.h"
37 #include "../pith/news.h"
38 #include "../pith/strlst.h"
39 #include "../pith/sequence.h"
40 #include "../pith/sort.h"
41 #include "../pith/hist.h"
42 #include "../pith/busy.h"
43 #include "../pith/signal.h"
46 struct save_thrdinfo {
47 ICE_S *(*format_index_line)(INDEXDATA_S *);
48 void (*setup_header_widths)(MAILSTREAM *);
49 unsigned viewing_a_thread:1;
53 static OtherMenu what_keymenu = FirstMenu;
55 struct index_state *current_index_state = NULL;
59 * Internal prototypes
61 void index_index_screen(struct pine *);
62 void thread_index_screen(struct pine *);
63 int update_index(struct pine *, struct index_state *);
64 int index_scroll_up(long);
65 int index_scroll_down(long);
66 int index_scroll_to_pos(long);
67 long top_ent_calc(MAILSTREAM *, MSGNO_S *, long, long);
68 void reset_index_border(void);
69 void redraw_index_body(void);
70 int paint_index_line(ICE_S *, int, long, IndexColType, IndexColType, IndexColType,
71 struct entry_state *, int, int);
72 void pine_imap_envelope(MAILSTREAM *, unsigned long, ENVELOPE *);
73 void index_search(struct pine *, MAILSTREAM *, int, MSGNO_S *);
74 #ifdef _WINDOWS
75 int index_scroll_callback(int,long);
76 int index_gettext_callback(char *, size_t, void **, long *, int *);
77 void index_popup(IndexType style, MAILSTREAM *, MSGNO_S *, int);
78 char *pcpine_help_index(char *);
79 char *pcpine_help_index_simple(char *);
80 #endif
84 /*----------------------------------------------------------------------
87 ----*/
88 struct key_menu *
89 do_index_border(CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap,
90 IndexType style, int *which_keys, int flags)
92 struct key_menu *km = (style == ThreadIndex)
93 ? &thread_keymenu
94 : (ps_global->mail_stream != stream)
95 ? &simple_index_keymenu
96 : &index_keymenu;
98 if(flags & INDX_CLEAR)
99 ClearScreen();
101 if(flags & INDX_HEADER)
102 set_titlebar((style == ThreadIndex)
103 /* TRANSLATORS: these are some screen titles */
104 ? _("THREAD INDEX")
105 : (stream == ps_global->mail_stream)
106 ? (style == MsgIndex || style == MultiMsgIndex)
107 ? _("MESSAGE INDEX")
108 : _("ZOOMED MESSAGE INDEX")
109 : (!strcmp(folder, INTERRUPTED_MAIL))
110 ? _("COMPOSE: SELECT INTERRUPTED")
111 : (ps_global->VAR_FORM_FOLDER
112 && !strcmp(ps_global->VAR_FORM_FOLDER, folder))
113 ? _("COMPOSE: SELECT FORM LETTER")
114 : _("COMPOSE: SELECT POSTPONED"),
115 stream, cntxt, folder, msgmap, 1,
116 (style == ThreadIndex) ? ThrdIndex
117 : (THREADING()
118 && sp_viewing_a_thread(stream))
119 ? ThrdMsgNum
120 : MessageNumber,
121 0, 0, NULL);
123 if(flags & INDX_FOOTER) {
124 bitmap_t bitmap;
125 int cmd;
127 setbitmap(bitmap);
129 if(km == &index_keymenu){
130 if(THREADING() && sp_viewing_a_thread(stream)){
131 menu_init_binding(km, '<', MC_THRDINDX, "<",
132 N_("ThrdIndex"), BACK_KEY);
133 menu_add_binding(km, ',', MC_THRDINDX);
135 else{
136 menu_init_binding(km, '<', MC_FOLDERS, "<",
137 N_("FldrList"), BACK_KEY);
138 menu_add_binding(km, ',', MC_FOLDERS);
140 if(F_OFF(F_ENABLE_PIPE,ps_global))
141 clrbitn(VIEW_PIPE_KEY, bitmap); /* always clear for DOS */
142 if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
143 clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
144 if(F_OFF(F_ENABLE_BOUNCE,ps_global))
145 clrbitn(BOUNCE_KEY, bitmap);
146 if(F_OFF(F_ENABLE_FLAG,ps_global))
147 clrbitn(FLAG_KEY, bitmap);
148 if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
149 clrbitn(SELECT_KEY, bitmap);
150 clrbitn(APPLY_KEY, bitmap);
151 clrbitn(SELCUR_KEY, bitmap);
152 if(style != ZoomIndex)
153 clrbitn(ZOOM_KEY, bitmap);
157 if(style == MultiMsgIndex){
158 clrbitn(PREVM_KEY, bitmap);
159 clrbitn(NEXTM_KEY, bitmap);
163 if(km == &index_keymenu || km == &thread_keymenu){
164 if(IS_NEWS(stream)){
165 km->keys[EXCLUDE_KEY].label = N_("eXclude");
166 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_NONE);
168 else {
169 clrbitn(UNEXCLUDE_KEY, bitmap);
170 km->keys[EXCLUDE_KEY].label = N_("eXpunge");
171 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_EXPUNGE);
175 if(km != &simple_index_keymenu && !THRD_COLLAPSE_ENABLE())
176 clrbitn(COLLAPSE_KEY, bitmap);
178 menu_clear_binding(km, KEY_LEFT);
179 menu_clear_binding(km, KEY_RIGHT);
180 if(F_ON(F_ARROW_NAV, ps_global)){
181 if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
182 menu_add_binding(km, '<', cmd);
183 menu_add_binding(km, KEY_LEFT, cmd);
186 if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
187 menu_add_binding(km, '>', cmd);
188 menu_add_binding(km, KEY_RIGHT, cmd);
192 if(menu_binding_index(km, MC_JUMP) >= 0){
193 for(cmd = 0; cmd < 10; cmd++)
194 if(F_ON(F_ENABLE_JUMP, ps_global))
195 (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
196 else
197 (void) menu_clear_binding(km, '0' + cmd);
200 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
201 1-FOOTER_ROWS(ps_global), 0, what_keymenu);
202 what_keymenu = SameMenu;
203 if(which_keys)
204 *which_keys = km->which; /* pass back to caller */
207 return(km);
212 /*----------------------------------------------------------------------
213 Main loop executing commands for the mail index screen
215 Args: state -- the pine_state structure for next/prev screen pointers
216 and to pass to the index manager...
217 ----*/
219 void
220 mail_index_screen(struct pine *state)
222 if(!state->mail_stream) {
223 q_status_message(SM_ORDER, 0, 3, _("No folder is currently open"));
224 state->prev_screen = mail_index_screen;
225 state->next_screen = main_menu_screen;
226 return;
229 state->prev_screen = mail_index_screen;
230 state->next_screen = SCREEN_FUN_NULL;
232 if(THRD_AUTO_VIEW()
233 && sp_viewing_a_thread(state->mail_stream)
234 && state->view_skipped_index
235 && unview_thread(state, state->mail_stream, state->msgmap)){
236 state->next_screen = mail_index_screen;
237 state->view_skipped_index = 0;
238 state->mangled_screen = 1;
241 adjust_cur_to_visible(state->mail_stream, state->msgmap);
243 if(THRD_INDX())
244 thread_index_screen(state);
245 else
246 index_index_screen(state);
250 void
251 index_index_screen(struct pine *state)
253 dprint((1, "\n\n ---- MAIL INDEX ----\n"));
255 setup_for_index_index_screen();
257 index_lister(state, state->context_current, state->cur_folder,
258 state->mail_stream, state->msgmap);
262 void
263 thread_index_screen(struct pine *state)
265 dprint((1, "\n\n ---- THREAD INDEX ----\n"));
267 setup_for_thread_index_screen();
269 index_lister(state, state->context_current, state->cur_folder,
270 state->mail_stream, state->msgmap);
274 void *
275 stop_threading_temporarily(void)
277 struct save_thrdinfo *ti;
279 ps_global->turn_off_threading_temporarily = 1;
281 ti = (struct save_thrdinfo *) fs_get(sizeof(*ti));
282 ti->format_index_line = format_index_line;
283 ti->setup_header_widths = setup_header_widths;
284 ti->viewing_a_thread = sp_viewing_a_thread(ps_global->mail_stream);
286 setup_for_index_index_screen();
288 return((void *) ti);
292 void
293 restore_threading(void **p)
295 struct save_thrdinfo *ti;
297 ps_global->turn_off_threading_temporarily = 0;
299 if(p && *p){
300 ti = (struct save_thrdinfo *) (*p);
301 format_index_line = ti->format_index_line;
302 setup_header_widths = ti->setup_header_widths;
303 sp_set_viewing_a_thread(ps_global->mail_stream, ti->viewing_a_thread);
305 fs_give(p);
310 /*----------------------------------------------------------------------
311 Main loop executing commands for the mail index screen
313 Args: state -- pine_state structure for display flags and such
314 msgmap -- c-client/pine message number mapping struct
315 ----*/
318 index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap)
320 UCS ch;
321 int cmd, which_keys, force,
322 cur_row, cur_col, km_popped, paint_status;
323 static int old_day = -1;
324 long i, j, k, old_max_msgno;
325 long lflagged, old_lflagged = 0L;
326 char *utf8str;
327 IndexType style, old_style = MsgIndex;
328 struct index_state id;
329 struct key_menu *km = NULL;
332 dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
334 ch = 'x'; /* For displaying msg 1st time thru */
335 force = 0;
336 km_popped = 0;
337 state->mangled_screen = 1;
338 what_keymenu = FirstMenu;
339 old_max_msgno = mn_get_total(msgmap);
340 memset((void *)&id, 0, sizeof(struct index_state));
341 current_index_state = &id;
342 id.msgmap = msgmap;
343 if(msgmap->top != 0L)
344 id.msg_at_top = msgmap->top;
346 id.stream = stream;
347 set_need_format_setup(stream);
349 while (1) {
350 ps_global->user_says_cancel = 0;
352 if(km_popped){
353 km_popped--;
354 if(km_popped == 0){
355 clearfooter(state);
356 if(!state->mangled_body
357 && id.entry_state
358 && id.lines_per_page > 1){
359 id.entry_state[id.lines_per_page-2].id = 0;
360 id.entry_state[id.lines_per_page-1].id = 0;
362 else
363 state->mangled_body = 1;
367 /*------- Check for new mail -------*/
368 new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
369 force = 0; /* may not need to next time around */
372 * If the width of the message number field in the display changes
373 * we need to flush the cache and redraw. When the cache is cleared
374 * the widths are recalculated, taking into account the max msgno.
377 if(format_includes_msgno(stream) &&
378 ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
379 || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
380 || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
381 clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
382 state->mangled_body = 1;
385 old_max_msgno = mn_get_total(msgmap);
388 * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
389 * then when the day changes the date column will change. All of the
390 * Today's will become Yesterday's at midnight. So we have to
391 * clear the cache at midnight.
393 if(format_includes_smartdate(stream)){
394 char db[200];
395 struct date nnow;
397 rfc822_date(db);
398 parse_date(db, &nnow);
399 if(old_day != -1 && nnow.day != old_day){
400 clear_index_cache(stream, 0);
401 state->mangled_body = 1;
404 old_day = nnow.day;
407 if(streams_died())
408 state->mangled_header = 1;
410 if(state->mangled_screen){
411 state->mangled_header = 1;
412 state->mangled_body = 1;
413 state->mangled_footer = 1;
414 state->mangled_screen = 0;
418 * events may have occurred that require us to shift from
419 * mode to another...
421 style = THRD_INDX()
422 ? ThreadIndex
423 : (any_lflagged(msgmap, MN_HIDE))
424 ? ZoomIndex
425 : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
426 lflagged = any_lflagged(msgmap, MN_HIDE);
427 if(style != old_style || lflagged < old_lflagged){
428 state->mangled_header = 1;
429 state->mangled_footer = 1;
430 old_style = style;
431 old_lflagged = lflagged;
432 if(!(style == ThreadIndex || old_style == ThreadIndex))
433 id.msg_at_top = 0L;
436 /*------------ Update the title bar -----------*/
437 if(state->mangled_header) {
438 km = do_index_border(cntxt, folder, stream, msgmap,
439 style, NULL, INDX_HEADER);
440 state->mangled_header = 0;
441 paint_status = 0;
443 else if(mn_get_total(msgmap) > 0) {
444 update_titlebar_message();
446 * If flags aren't available to update the status,
447 * defer it until after all the fetches associated
448 * with building index lines are done (no extra rtts!)...
450 paint_status = !update_titlebar_status();
453 current_index_state = &id;
455 /*------------ draw the index body ---------------*/
456 cur_row = update_index(state, &id);
457 if(F_OFF(F_SHOW_CURSOR, state)){
458 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
459 cur_col = 0;
461 else if(id.status_col >= 0)
462 cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
464 ps_global->redrawer = redraw_index_body;
466 if(paint_status)
467 (void) update_titlebar_status();
469 /*------------ draw the footer/key menus ---------------*/
470 if(state->mangled_footer) {
471 if(!state->painted_footer_on_startup){
472 if(km_popped){
473 FOOTER_ROWS(state) = 3;
474 clearfooter(state);
477 km = do_index_border(cntxt, folder, stream, msgmap, style,
478 &which_keys, INDX_FOOTER);
479 if(km_popped){
480 FOOTER_ROWS(state) = 1;
481 mark_keymenu_dirty();
485 state->mangled_footer = 0;
488 state->painted_body_on_startup = 0;
489 state->painted_footer_on_startup = 0;
491 /*-- Display any queued message (eg, new mail, command result --*/
492 if(km_popped){
493 FOOTER_ROWS(state) = 3;
494 mark_status_unknown();
497 display_message(ch);
498 if(km_popped){
499 FOOTER_ROWS(state) = 1;
500 mark_status_unknown();
503 if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
504 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
507 cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
508 MoveCursor(cur_row, cur_col);
510 /* Let read_command do the fflush(stdout) */
512 /*---------- Read command and validate it ----------------*/
513 #ifdef MOUSE
514 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
515 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
516 state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
517 state->ttyo->screen_cols);
518 #endif
519 #ifdef _WINDOWS
520 mswin_setscrollcallback (index_scroll_callback);
521 mswin_sethelptextcallback((stream == state->mail_stream)
522 ? pcpine_help_index
523 : pcpine_help_index_simple);
524 mswin_setviewinwindcallback(view_in_new_window);
525 #endif
526 ch = READ_COMMAND(&utf8str);
527 #ifdef MOUSE
528 clear_mfunc(mouse_in_content);
529 #endif
530 #ifdef _WINDOWS
531 mswin_setscrollcallback(NULL);
532 mswin_sethelptextcallback(NULL);
533 mswin_setviewinwindcallback(NULL);
534 #endif
536 cmd = menu_command(ch, km);
538 if(km_popped)
539 switch(cmd){
540 case MC_NONE :
541 case MC_OTHER :
542 case MC_RESIZE :
543 case MC_REPAINT :
544 km_popped++;
545 break;
547 default:
548 clearfooter(state);
549 break;
552 /*----------- Execute the command ------------------*/
553 switch(cmd){
555 /*---------- Roll keymenu ----------*/
556 case MC_OTHER :
557 if(F_OFF(F_USE_FK, ps_global))
558 warn_other_cmds();
560 what_keymenu = NextMenu;
561 state->mangled_footer = 1;
562 break;
565 /*---------- Scroll line up ----------*/
566 case MC_CHARUP :
567 (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
568 (style == MsgIndex
569 || style == MultiMsgIndex
570 || style == ZoomIndex)
571 ? MsgIndx
572 : (style == ThreadIndex)
573 ? ThrdIndx
574 : View,
575 &force);
576 if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
577 index_scroll_up(1L);
579 break;
582 /*---------- Scroll line down ----------*/
583 case MC_CHARDOWN :
585 * Special Page framing handling here. If we
586 * did something that should scroll-by-a-line, frame
587 * the page by hand here rather than leave it to the
588 * page-by-page framing in update_index()...
590 (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
591 (style == MsgIndex
592 || style == MultiMsgIndex
593 || style == ZoomIndex)
594 ? MsgIndx
595 : (style == ThreadIndex)
596 ? ThrdIndx
597 : View,
598 &force);
599 for(j = 0L, k = i = id.msg_at_top; ; i++){
600 if(!msgline_hidden(stream, msgmap, i, 0)){
601 k = i;
602 if(j++ >= id.lines_per_page)
603 break;
606 if(i >= mn_get_total(msgmap)){
607 k = 0L; /* don't scroll */
608 break;
612 if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
613 index_scroll_down(1L);
615 break;
618 /*---------- Scroll page up ----------*/
619 case MC_PAGEUP :
620 j = -1L;
621 for(k = i = id.msg_at_top; ; i--){
622 if(!msgline_hidden(stream, msgmap, i, 0)){
623 k = i;
624 if(++j >= id.lines_per_page){
625 if((id.msg_at_top = i) == 1L)
626 q_status_message(SM_ORDER, 0, 1, _("First Index page"));
628 break;
632 if(i <= 1L){
633 if((!THREADING() && mn_get_cur(msgmap) == 1L)
634 || (THREADING()
635 && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
636 stream,
638 FSF_SKIP_CHID)))
639 q_status_message(SM_ORDER, 0, 1,
640 _("Already at start of Index"));
642 break;
646 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
647 mn_set_cur(msgmap, k);
649 break;
652 /*---------- Scroll page forward ----------*/
653 case MC_PAGEDN :
654 j = -1L;
655 for(k = i = id.msg_at_top; ; i++){
656 if(!msgline_hidden(stream, msgmap, i, 0)){
657 k = i;
658 if(++j >= id.lines_per_page){
659 if(i+id.lines_per_page > mn_get_total(msgmap))
660 q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
662 id.msg_at_top = i;
663 break;
667 if(i >= mn_get_total(msgmap)){
668 if(mn_get_cur(msgmap) == k)
669 q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
671 break;
675 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
676 mn_set_cur(msgmap, k);
678 break;
681 /*---------- Scroll to first page ----------*/
682 case MC_HOMEKEY :
683 if((mn_get_total(msgmap) > 0L)
684 && (mn_total_cur(msgmap) <= 1L)){
685 long cur_msg = mn_get_cur(msgmap), selected;
687 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
688 do {
689 selected = cur_msg;
690 mn_dec_cur(stream, msgmap, MH_NONE);
691 cur_msg = mn_get_cur(msgmap);
693 while(selected != cur_msg);
695 else
696 cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
697 mn_set_cur(msgmap, cur_msg);
698 q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
700 break;
702 /*---------- Scroll to last page ----------*/
703 case MC_ENDKEY :
704 if((mn_get_total(msgmap) > 0L)
705 && (mn_total_cur(msgmap) <= 1L)){
706 long cur_msg = mn_get_cur(msgmap), selected;
708 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
709 do {
710 selected = cur_msg;
711 mn_inc_cur(stream, msgmap, MH_NONE);
712 cur_msg = mn_get_cur(msgmap);
714 while(selected != cur_msg);
716 else
717 cur_msg = mn_get_total(msgmap);
718 mn_set_cur(msgmap, cur_msg);
719 q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
721 break;
723 /*---------- Search (where is command) ----------*/
724 case MC_WHEREIS :
725 index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
726 state->mangled_footer = 1;
727 break;
730 /*-------------- jump command -------------*/
731 /* NOTE: preempt the process_cmd() version because
732 * we need to get at the number..
734 case MC_JUMP :
735 j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
736 (style == ThreadIndex) ? ThrdIndx : MsgIndx);
737 if(j > 0L){
738 if(style == ThreadIndex){
739 PINETHRD_S *thrd;
741 thrd = find_thread_by_number(stream, msgmap, j, NULL);
743 if(thrd && thrd->rawno)
744 mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
746 else{
747 /* jump to message */
748 if(mn_total_cur(msgmap) > 1L){
749 mn_reset_cur(msgmap, j);
751 else{
752 mn_set_cur(msgmap, j);
756 id.msg_at_top = 0L;
759 state->mangled_footer = 1;
760 break;
763 case MC_VIEW_ENTRY : /* only happens in thread index */
766 * If the feature F_THRD_AUTO_VIEW is turned on and there
767 * is only one message in the thread, then we skip the index
768 * view of the thread and go straight to the message view.
770 view_a_thread:
771 if(THRD_AUTO_VIEW() && style == ThreadIndex){
772 PINETHRD_S *thrd;
774 thrd = fetch_thread(stream,
775 mn_m2raw(msgmap, mn_get_cur(msgmap)));
776 if(thrd
777 && (count_lflags_in_thread(stream, thrd,
778 msgmap, MN_NONE) == 1)){
779 if(view_thread(state, stream, msgmap, 1)){
780 state->view_skipped_index = 1;
781 cmd = MC_VIEW_TEXT;
782 goto do_the_default;
787 if(view_thread(state, stream, msgmap, 1)){
788 ps_global->next_screen = mail_index_screen;
789 ps_global->redrawer = NULL;
790 current_index_state = NULL;
791 if(id.entry_state)
792 fs_give((void **)&(id.entry_state));
794 return(0);
797 break;
800 case MC_THRDINDX :
801 if(any_lflagged(msgmap, MN_SLCT)){
802 PINETHRD_S *thrd, *topthrd;
803 for(i = 1L; i > 0L && i <= mn_get_total(msgmap);){
804 thrd = fetch_thread(stream, i);
805 if(thrd && thrd->top)
806 topthrd = fetch_thread(stream, thrd->top);
807 if(topthrd){
808 set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1);
809 set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0);
810 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0);
811 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1);
813 i = thrd->nextthd;
816 msgmap->top = msgmap->top_after_thrd;
817 if(unview_thread(state, stream, msgmap)){
818 state->next_screen = mail_index_screen;
819 state->view_skipped_index = 0;
820 state->mangled_screen = 1;
821 ps_global->redrawer = NULL;
822 current_index_state = NULL;
823 if(id.entry_state)
824 fs_give((void **)&(id.entry_state));
826 return(0);
829 break;
832 #ifdef MOUSE
833 case MC_MOUSE:
835 MOUSEPRESS mp;
836 int new_cur;
838 mouse_get_last (NULL, &mp);
839 mp.row -= 2;
841 for(i = id.msg_at_top;
842 mp.row >= 0 && i <= mn_get_total(msgmap);
843 i++)
844 if(!msgline_hidden(stream, msgmap, i, 0)){
845 mp.row--;
846 new_cur = i;
849 if(mn_get_total(msgmap) && mp.row < 0){
850 switch(mp.button){
851 case M_BUTTON_LEFT :
852 if(mn_total_cur(msgmap) == 1L)
853 mn_set_cur(msgmap, new_cur);
855 if(mp.flags & M_KEY_CONTROL){
856 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
857 (void) select_by_current(state, msgmap, MsgIndx);
860 else if(!(mp.flags & M_KEY_SHIFT)){
861 if (THREADING()
862 && mp.col >= 0
863 && mp.col == id.plus_col
864 && style != ThreadIndex){
865 collapse_or_expand(state, stream, msgmap,
866 mn_get_cur(msgmap));
868 else if (mp.doubleclick){
869 if(mp.button == M_BUTTON_LEFT){
870 if(stream == state->mail_stream){
871 if(THRD_INDX()){
872 cmd = MC_VIEW_ENTRY;
873 goto view_a_thread;
875 else{
876 cmd = MC_VIEW_TEXT;
877 goto do_the_default;
881 ps_global->redrawer = NULL;
882 current_index_state = NULL;
883 if(id.entry_state)
884 fs_give((void **)&(id.entry_state));
886 return(0);
891 break;
893 case M_BUTTON_MIDDLE:
894 break;
896 case M_BUTTON_RIGHT :
897 #ifdef _WINDOWS
898 if (!mp.doubleclick){
899 if(mn_total_cur(msgmap) == 1L)
900 mn_set_cur(msgmap, new_cur);
902 cur_row = update_index(state, &id);
904 index_popup(style, stream, msgmap, TRUE);
906 #endif
907 break;
910 else{
911 switch(mp.button){
912 case M_BUTTON_LEFT :
913 break;
915 case M_BUTTON_MIDDLE :
916 break;
918 case M_BUTTON_RIGHT :
919 #ifdef _WINDOWS
920 index_popup(style, stream, msgmap, FALSE);
921 #endif
922 break;
927 break;
928 #endif /* MOUSE */
930 /*---------- Resize ----------*/
931 case MC_RESIZE:
933 * If we were smarter we could do the
934 * IC_CLEAR_WIDTHS_DONE trick here. The problem is
935 * that entire columns of the format can go away or
936 * appear because the width gets smaller or larger,
937 * so in that case we need to re-do. If we could tell
938 * when that happened or not we could set the flag
939 * selectively.
941 clear_index_cache(stream, 0);
942 reset_index_border();
943 break;
945 case MC_QUOTA:
946 cmd_quota(state);
948 /*---------- Redraw ----------*/
949 case MC_REPAINT :
950 force = 1; /* check for new mail! */
951 reset_index_border();
952 break;
955 /*---------- No op command ----------*/
956 case MC_NONE :
957 break; /* no op check for new mail */
960 /*--------- keystroke not bound to command --------*/
961 case MC_CHARRIGHT :
962 case MC_CHARLEFT :
963 case MC_GOTOBOL :
964 case MC_GOTOEOL :
965 case MC_UNKNOWN :
966 if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
967 q_status_message(SM_ORDER, 0, 1, "Already in Index");
968 else
969 bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
971 break;
974 case MC_COLLAPSE :
975 thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
976 break;
978 case MC_DELETE :
979 case MC_UNDELETE :
980 case MC_REPLY :
981 case MC_FORWARD :
982 case MC_TAKE :
983 case MC_SAVE :
984 case MC_EXPORT :
985 case MC_BOUNCE :
986 case MC_PIPE :
987 case MC_FLAG :
988 case MC_SELCUR :
989 { int collapsed = 0;
990 unsigned long rawno;
991 PINETHRD_S *thrd = NULL;
993 if(THREADING()){
994 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
995 if(rawno)
996 thrd = fetch_thread(stream, rawno);
998 collapsed = thrd && thrd->next
999 && get_lflag(stream, NULL, rawno, MN_COLL);
1002 if(collapsed){
1003 thread_command(state, stream, msgmap,
1004 ch, -FOOTER_ROWS(state));
1005 /* increment current */
1006 if(cmd == MC_DELETE){
1007 advance_cur_after_delete(state, stream, msgmap,
1008 (style == MsgIndex
1009 || style == MultiMsgIndex
1010 || style == ZoomIndex)
1011 ? MsgIndx
1012 : (style == ThreadIndex)
1013 ? ThrdIndx
1014 : View);
1016 else if((cmd == MC_SELCUR
1017 && (state->ugly_consider_advancing_bit
1018 || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
1019 || (state->ugly_consider_advancing_bit
1020 && cmd == MC_SAVE
1021 && F_ON(F_SAVE_ADVANCES, state))){
1022 mn_inc_cur(stream, msgmap, MH_NONE);
1025 else
1026 goto do_the_default;
1029 break;
1032 case MC_UTF8:
1033 bogus_utf8_command(utf8str, NULL);
1034 break;
1037 /*---------- First HELP command with menu hidden ----------*/
1038 case MC_HELP :
1039 if(FOOTER_ROWS(state) == 1 && km_popped == 0){
1040 km_popped = 2;
1041 mark_status_unknown();
1042 mark_keymenu_dirty();
1043 state->mangled_footer = 1;
1044 break;
1046 /* else fall thru to normal default */
1049 /*---------- Default -- all other command ----------*/
1050 default:
1051 do_the_default:
1052 if(stream == state->mail_stream){
1053 msgmap->top = id.msg_at_top;
1054 process_cmd(state, stream, msgmap, cmd,
1055 (style == MsgIndex
1056 || style == MultiMsgIndex
1057 || style == ZoomIndex)
1058 ? MsgIndx
1059 : (style == ThreadIndex)
1060 ? ThrdIndx
1061 : View,
1062 &force);
1063 if(state->next_screen != SCREEN_FUN_NULL){
1064 ps_global->redrawer = NULL;
1065 current_index_state = NULL;
1066 if(id.entry_state)
1067 fs_give((void **)&(id.entry_state));
1069 return(0);
1071 else{
1072 if(stream != state->mail_stream){
1074 * Must have had an failed open. repair our
1075 * pointers...
1077 id.stream = stream = state->mail_stream;
1078 id.msgmap = msgmap = state->msgmap;
1081 current_index_state = &id;
1083 if(cmd == MC_ZOOM && THRD_INDX())
1084 id.msg_at_top = 0L;
1087 else{ /* special processing */
1088 switch(cmd){
1089 case MC_HELP :
1090 helper(h_simple_index,
1091 (!strcmp(folder, INTERRUPTED_MAIL))
1092 ? _("HELP FOR SELECTING INTERRUPTED MSG")
1093 : _("HELP FOR SELECTING POSTPONED MSG"),
1094 HLPD_SIMPLE);
1095 state->mangled_screen = 1;
1096 break;
1098 case MC_DELETE : /* delete */
1099 dprint((3, "Special delete: msg %s\n",
1100 long2string(mn_get_cur(msgmap))));
1102 long raw, t;
1103 int del = 0;
1104 MESSAGECACHE *mc;
1106 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1107 if(raw > 0L && stream
1108 && raw <= stream->nmsgs
1109 && (mc = mail_elt(stream, raw))
1110 && !mc->deleted){
1111 if((t = mn_get_cur(msgmap)) > 0L)
1112 clear_index_cache_ent(stream, t, 0);
1114 mail_setflag(stream,long2string(raw),"\\DELETED");
1115 update_titlebar_status();
1116 del++;
1119 q_status_message1(SM_ORDER, 0, 1,
1120 del ? _("Message %s deleted") : _("Message %s already deleted"),
1121 long2string(mn_get_cur(msgmap)));
1124 break;
1126 case MC_UNDELETE : /* UNdelete */
1127 dprint((3, "Special UNdelete: msg %s\n",
1128 long2string(mn_get_cur(msgmap))));
1130 long raw, t;
1131 int del = 0;
1132 MESSAGECACHE *mc;
1134 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1135 if(raw > 0L && stream
1136 && raw <= stream->nmsgs
1137 && (mc = mail_elt(stream, raw))
1138 && mc->deleted){
1139 if((t = mn_get_cur(msgmap)) > 0L)
1140 clear_index_cache_ent(stream, t, 0);
1142 mail_clearflag(stream, long2string(raw),
1143 "\\DELETED");
1144 update_titlebar_status();
1145 del++;
1148 q_status_message1(SM_ORDER, 0, 1,
1149 del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
1150 long2string(mn_get_cur(msgmap)));
1153 break;
1155 case MC_EXIT : /* exit */
1156 ps_global->redrawer = NULL;
1157 current_index_state = NULL;
1158 if(id.entry_state)
1159 fs_give((void **)&(id.entry_state));
1161 return(1);
1163 case MC_SELECT : /* select */
1164 ps_global->redrawer = NULL;
1165 current_index_state = NULL;
1166 if(id.entry_state)
1167 fs_give((void **)&(id.entry_state));
1169 return(0);
1171 case MC_PREVITEM : /* previous */
1172 mn_dec_cur(stream, msgmap, MH_NONE);
1173 break;
1175 case MC_NEXTITEM : /* next */
1176 mn_inc_cur(stream, msgmap, MH_NONE);
1177 break;
1179 default :
1180 bogus_command(ch, NULL);
1181 break;
1184 } /* The big switch */
1185 } /* the BIG while loop! */
1190 /*----------------------------------------------------------------------
1191 Manage index body painting
1193 Args: state - pine struct containing selected message data
1194 index_state - struct describing what's currently displayed
1196 Returns: screen row number of first highlighted message
1198 The idea is pretty simple. Maintain an array of index line id's that
1199 are displayed and their hilited state. Decide what's to be displayed
1200 and update the screen appropriately. All index screen painting
1201 is done here. Pretty simple, huh?
1202 ----*/
1204 update_index(struct pine *state, struct index_state *screen)
1206 int i, retval = -1, row, already_fetched = 0;
1207 long n, visible;
1208 PINETHRD_S *thrd = NULL;
1209 int we_cancel = 0;
1211 dprint((7, "--update_index--\n"));
1213 if(!screen)
1214 return(-1);
1216 #ifdef _WINDOWS
1217 mswin_beginupdate();
1218 #endif
1220 /*---- reset the works if necessary ----*/
1221 if(state->mangled_body){
1222 ClearBody();
1223 if(screen->entry_state){
1224 fs_give((void **)&(screen->entry_state));
1225 screen->lines_per_page = 0;
1229 state->mangled_body = 0;
1231 /*---- make sure we have a place to write state ----*/
1232 if(screen->lines_per_page
1233 != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1234 - HEADER_ROWS(state))){
1235 i = screen->lines_per_page;
1236 screen->lines_per_page
1237 = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1238 - HEADER_ROWS(state));
1239 if(!i){
1240 size_t len = screen->lines_per_page * sizeof(struct entry_state);
1241 screen->entry_state = (struct entry_state *) fs_get(len);
1243 else
1244 fs_resize((void **)&(screen->entry_state),
1245 (size_t)screen->lines_per_page);
1247 for(; i < screen->lines_per_page; i++) /* init new entries */
1248 memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
1251 /*---- figure out the first message on the display ----*/
1252 if(screen->msg_at_top < 1L
1253 || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
1254 screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
1255 screen->msg_at_top,
1256 screen->lines_per_page);
1258 else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1259 long i, j, k;
1261 /* scroll back a page at a time until current is displayed */
1262 while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1263 for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
1264 i > 0L && j > 0L;
1265 j--)
1266 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1267 k = j;
1268 i--;
1271 if(i == screen->lines_per_page)
1272 break; /* can't scroll back ? */
1273 else
1274 screen->msg_at_top = k;
1277 else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
1278 + screen->lines_per_page){
1279 long i, j, k;
1281 while(1){
1282 for(i = screen->lines_per_page, j = k = screen->msg_at_top;
1283 j <= mn_get_total(screen->msgmap) && i > 0L;
1284 j++)
1285 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1286 k = j;
1287 i--;
1288 if(mn_get_cur(screen->msgmap) <= k)
1289 break;
1292 if(mn_get_cur(screen->msgmap) <= k)
1293 break;
1294 else{
1295 /* set msg_at_top to next displayed message */
1296 for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
1297 if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
1298 k = i;
1299 break;
1302 screen->msg_at_top = k;
1307 #ifdef _WINDOWS
1309 * Set scroll range and position. Note that message numbers start at 1
1310 * while scroll position starts at 0.
1313 if(THREADING() && sp_viewing_a_thread(screen->stream)
1314 && mn_get_total(screen->msgmap) > 1L){
1315 long x = 0L, range = 0L, lowest_numbered_msg;
1318 * We know that all visible messages in the thread are marked
1319 * with MN_CHID2.
1321 thrd = fetch_thread(screen->stream,
1322 mn_m2raw(screen->msgmap,
1323 mn_get_cur(screen->msgmap)));
1324 if(thrd && thrd->top && thrd->top != thrd->rawno)
1325 thrd = fetch_thread(screen->stream, thrd->top);
1327 if(thrd){
1328 if(mn_get_revsort(screen->msgmap)){
1329 n = mn_raw2m(screen->msgmap, thrd->rawno);
1330 while(n > 1L && get_lflag(screen->stream, screen->msgmap,
1331 n-1L, MN_CHID2))
1332 n--;
1334 lowest_numbered_msg = n;
1336 else
1337 lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
1340 if(thrd){
1341 n = lowest_numbered_msg;
1342 for(; n <= mn_get_total(screen->msgmap); n++){
1344 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1345 break;
1347 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
1348 range++;
1349 if(n < screen->msg_at_top)
1350 x++;
1355 scroll_setrange(screen->lines_per_page, range-1L);
1356 scroll_setpos(x);
1358 else if(THRD_INDX()){
1359 if(any_lflagged(screen->msgmap, MN_HIDE)){
1360 long x = 0L, range;
1362 range = screen->msgmap->visible_threads - 1L;
1363 scroll_setrange(screen->lines_per_page, range);
1364 if(range >= screen->lines_per_page){ /* else not needed */
1365 PINETHRD_S *topthrd;
1366 int thrddir;
1367 long xdir;
1369 /* find top of currently displayed top line */
1370 topthrd = fetch_thread(screen->stream,
1371 mn_m2raw(screen->msgmap,
1372 screen->msg_at_top));
1373 if(topthrd && topthrd->top != topthrd->rawno)
1374 topthrd = fetch_thread(screen->stream, topthrd->top);
1376 if(topthrd){
1378 * Split into two halves to speed up finding scroll pos.
1379 * It's tricky because the thread list always goes from
1380 * past to future but the thrdno's will be reversed if
1381 * the sort is reversed and of course the order on the
1382 * screen will be reversed.
1384 if((!mn_get_revsort(screen->msgmap)
1385 && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
1387 (mn_get_revsort(screen->msgmap)
1388 && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
1390 /* start with head of thread list */
1391 if(topthrd && topthrd->head)
1392 thrd = fetch_thread(screen->stream, topthrd->head);
1393 else
1394 thrd = NULL;
1396 thrddir = 1;
1398 else{
1399 long tailrawno;
1402 * Start with tail thread and work back.
1404 if(mn_get_revsort(screen->msgmap))
1405 tailrawno = mn_m2raw(screen->msgmap, 1L);
1406 else
1407 tailrawno = mn_m2raw(screen->msgmap,
1408 mn_get_total(screen->msgmap));
1410 thrd = fetch_thread(screen->stream, tailrawno);
1411 if(thrd && thrd->top && thrd->top != thrd->rawno)
1412 thrd = fetch_thread(screen->stream, thrd->top);
1414 thrddir = -1;
1418 * x is the scroll position. We try to use the fewest
1419 * number of steps to find it, so we start with either
1420 * the beginning or the end.
1422 if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
1423 x = 0L;
1424 xdir = 1L;
1426 else{
1427 x = range;
1428 xdir = -1L;
1431 while(thrd && thrd != topthrd){
1432 if(!msgline_hidden(screen->stream, screen->msgmap,
1433 mn_raw2m(screen->msgmap,thrd->rawno),
1435 x += xdir;
1437 if(thrddir > 0 && thrd->nextthd)
1438 thrd = fetch_thread(screen->stream, thrd->nextthd);
1439 else if(thrddir < 0 && thrd->prevthd)
1440 thrd = fetch_thread(screen->stream, thrd->prevthd);
1441 else
1442 thrd = NULL;
1446 scroll_setpos(x);
1449 else{
1451 * This works for forward or reverse sort because the thrdno's
1452 * will have been reversed.
1454 thrd = fetch_thread(screen->stream,
1455 mn_m2raw(screen->msgmap, screen->msg_at_top));
1456 if(thrd){
1457 scroll_setrange(screen->lines_per_page,
1458 screen->msgmap->max_thrdno - 1L);
1459 scroll_setpos(thrd->thrdno - 1L);
1463 else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
1464 long x, range;
1466 range = mn_get_total(screen->msgmap) - n - 1L;
1467 scroll_setrange(screen->lines_per_page, range);
1469 if(range >= screen->lines_per_page){ /* else not needed */
1470 if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
1471 for(n = 1, x = 0; n != screen->msg_at_top; n++)
1472 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1473 x++;
1475 else{
1476 for(n = mn_get_total(screen->msgmap), x = range;
1477 n != screen->msg_at_top; n--)
1478 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1479 x--;
1482 scroll_setpos(x);
1485 else{
1486 scroll_setrange(screen->lines_per_page,
1487 mn_get_total(screen->msgmap) - 1L);
1488 scroll_setpos(screen->msg_at_top - 1L);
1490 #endif
1493 * Set up c-client call back to tell us about IMAP envelope arrivals
1494 * Can't do it (easily) if single lines on the screen need information
1495 * about more than a single message before they can be drawn.
1497 if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
1498 && !(THREADING() && (sp_viewing_a_thread(screen->stream)
1499 || ps_global->thread_disp_style == THREAD_MUTTLIKE
1500 || any_lflagged(screen->msgmap, MN_COLL))))
1501 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
1503 if(THRD_INDX())
1504 visible = screen->msgmap->visible_threads;
1505 else if(THREADING() && sp_viewing_a_thread(screen->stream)){
1507 * We know that all visible messages in the thread are marked
1508 * with MN_CHID2.
1510 for(visible = 0L, n = screen->msg_at_top;
1511 visible < (int) screen->lines_per_page
1512 && n <= mn_get_total(screen->msgmap); n++){
1514 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1515 break;
1517 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1518 visible++;
1521 else
1522 visible = mn_get_total(screen->msgmap)
1523 - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
1525 /*---- march thru display lines, painting whatever is needed ----*/
1526 for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
1527 if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
1528 if(screen->entry_state[i].id != LINE_HASH_N){
1529 screen->entry_state[i].hilite = 0;
1530 screen->entry_state[i].bolded = 0;
1531 screen->entry_state[i].msgno = 0L;
1532 screen->entry_state[i].id = LINE_HASH_N;
1533 ClearLine(HEADER_ROWS(state) + i);
1536 else{
1537 ICE_S *ice;
1540 * This changes status_col as a side effect so it has to be
1541 * executed before next line.
1543 ice = build_header_line(state, screen->stream, screen->msgmap,
1544 n, &already_fetched);
1545 if(visible > 0L)
1546 visible--;
1548 if(THRD_INDX()){
1549 unsigned long rawno;
1551 rawno = mn_m2raw(screen->msgmap, n);
1552 if(rawno)
1553 thrd = fetch_thread(screen->stream, rawno);
1556 row = paint_index_line(ice, i,
1557 (THRD_INDX() && thrd) ? thrd->thrdno : n,
1558 screen->status_fld, screen->plus_fld,
1559 screen->arrow_fld, &screen->entry_state[i],
1560 mn_is_cur(screen->msgmap, n),
1561 THRD_INDX()
1562 ? (count_lflags_in_thread(screen->stream,
1563 thrd,
1564 screen->msgmap,
1565 MN_SLCT) > 0)
1566 : get_lflag(screen->stream, screen->msgmap,
1567 n, MN_SLCT));
1568 fflush(stdout);
1569 if(row && retval < 0)
1570 retval = row;
1573 /*--- increment n ---*/
1574 while((visible == -1L || visible > 0L)
1575 && ++n <= mn_get_total(screen->msgmap)
1576 && msgline_hidden(screen->stream, screen->msgmap, n, 0))
1580 if(we_cancel)
1581 cancel_busy_cue(-1);
1583 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
1585 #ifdef _WINDOWS
1586 mswin_endupdate();
1587 #endif
1588 fflush(stdout);
1589 dprint((7, "--update_index done\n"));
1590 return(retval);
1595 /*----------------------------------------------------------------------
1596 Create a string summarizing the message header for index on screen
1598 Args: stream -- mail stream to fetch envelope info from
1599 msgmap -- message number to pine sort mapping
1600 msgno -- Message number to create line for
1602 Result: returns a malloced string
1603 saves string in a cache for next call for same header
1604 ----*/
1605 ICE_S *
1606 build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
1608 return(build_header_work(state, stream, msgmap, msgno,
1609 current_index_state->msg_at_top,
1610 current_index_state->lines_per_page,
1611 already_fetched));
1615 /*----------------------------------------------------------------------
1616 Paint the given index line
1619 Args: ice -- index cache entry
1620 line -- index line number on screen, starting at 0 for first
1621 visible line, 1, 2, ...
1622 msgno -- for painting the message number field
1623 sfld -- field type of the status field, which is
1624 where we'll put the X for selected if necessary
1625 pfld -- field type where the thread indicator symbol goes
1626 afld -- field type of column which corresponds to the
1627 index-format ARROW token
1628 entry -- cache used to help us decide whether or not we need to
1629 redraw the index line or if we can just leave it alone because
1630 we know it is already correct
1631 cur -- is this the current message?
1632 sel -- is this message in the selected set?
1634 Returns: screen row number if this is current message, else 0
1635 ----*/
1637 paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
1638 IndexColType pfld, IndexColType afld, struct entry_state *entry,
1639 int cur, int sel)
1641 COLOR_PAIR *lastc = NULL, *base_color = NULL;
1642 ICE_S *ice;
1643 IFIELD_S *ifield, *previfield = NULL;
1644 IELEM_S *ielem;
1645 int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
1646 int draw_whole_line = 0, draw_partial_line = 0;
1647 int n = MAX_SCREEN_COLS*6;
1648 char draw[MAX_SCREEN_COLS*6+1], *p;
1650 ice = (THRD_INDX() && argice) ? argice->tice : argice;
1652 /* This better not happen! */
1653 if(!ice){
1654 q_status_message3(SM_ORDER | SM_DING, 5, 5,
1655 "NULL ice in paint_index_line: %s, msgno=%s line=%s",
1656 THRD_INDX() ? "THRD_INDX" : "reg index",
1657 comatose(msgno), comatose(line));
1658 dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
1659 THRD_INDX() ? "THRD_INDX" : "reg index",
1660 msgno, line));
1661 return 0;
1664 if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
1665 entry->id = 0L;
1666 entry->msgno = 0L;
1667 draw_whole_line = 1;
1669 else if((cur != entry->hilite) || (sel != entry->bolded)
1670 || (ice->plus != entry->plus)){
1671 draw_partial_line = 1;
1674 if(draw_whole_line || draw_partial_line){
1676 if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
1678 memset(draw, 0, sizeof(draw));
1679 p = draw;
1681 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1683 /* space between fields */
1684 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
1685 *p++ = ' ';
1687 /* message number string is generated on the fly */
1688 if(ifield->ctype == iMessNo){
1689 ielem = ifield->ielem;
1690 if(ielem && ielem->datalen >= ifield->width){
1691 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1692 ielem->data[ifield->width] = '\0';
1693 ielem->data[ielem->datalen] = '\0';
1697 if(ifield->ctype == sfld){
1698 ielem = ifield->ielem;
1699 if(ielem && ielem->datalen > 0){
1700 if(draw_partial_line)
1701 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1703 if(ielem->datalen == 1){
1704 save_schar1 = ielem->data[0];
1705 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1706 if(draw_partial_line)
1707 Writechar(ielem->data[0], 0);
1709 if(ielem->next && ielem->next->datalen){
1710 save_schar2 = ielem->next->data[0];
1711 if(cur)
1712 ielem->next->data[0] = '>';
1714 if(draw_partial_line)
1715 Writechar(ielem->next->data[0], 0);
1718 else if(ielem->datalen > 1){
1719 save_schar1 = ielem->data[0];
1720 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1721 if(draw_partial_line)
1722 Writechar(ielem->data[0], 0);
1724 save_schar2 = ielem->data[1];
1725 if(cur){
1726 ielem->data[1] = '>';
1727 if(draw_partial_line)
1728 Writechar(ielem->data[1], 0);
1733 else if(ifield->ctype == afld){
1735 if(draw_partial_line){
1736 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1737 for(i = 0; i < ifield->width-1; i++)
1738 Writechar(cur ? '-' : ' ', 0);
1740 Writechar(cur ? '>' : ' ', 0);
1743 ielem = ifield->ielem;
1744 if(ielem && ielem->datalen >= ifield->width){
1745 for(i = 0; i < ifield->width-1; i++)
1746 ielem->data[i] = cur ? '-' : ' ';
1748 ielem->data[i] = cur ? '>' : ' ';
1751 else if(ifield->ctype == pfld){
1752 ielem = ifield->ielem;
1753 if(ielem && ielem->datalen > 0){
1754 save_pchar = ielem->data[0];
1755 ielem->data[0] = ice->plus;
1757 if(draw_partial_line){
1758 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1759 Writechar(ielem->data[0], 0);
1760 Writechar(' ', 0);
1765 for(ielem = ifield->ielem;
1766 ielem && ielem->print_format && p-draw < n;
1767 ielem = ielem->next){
1768 char *src;
1769 size_t bytes_added;
1771 src = ielem->data;
1772 bytes_added = utf8_pad_to_width(p, src,
1773 ((n+1)-(p-draw)) * sizeof(char),
1774 ielem->wid, ifield->leftadj);
1775 p += bytes_added;
1778 draw[n] = '\0';
1780 if(ifield->ctype == sfld){
1781 ielem = ifield->ielem;
1782 if(ielem && ielem->datalen > 0){
1783 if(ielem->datalen == 1){
1784 ielem->data[0] = save_schar1;
1785 if(ielem->next && ielem->next->datalen)
1786 ielem->next->data[0] = save_schar2;
1788 else if(ielem->datalen > 1){
1789 ielem->data[0] = save_schar1;
1790 ielem->data[1] = save_schar2;
1794 else if(ifield->ctype == afld){
1795 ielem = ifield->ielem;
1796 if(ielem && ielem->datalen >= ifield->width)
1797 for(i = 0; i < ifield->width; i++)
1798 ielem->data[i] = ' ';
1800 else if(ifield->ctype == pfld){
1801 ielem = ifield->ielem;
1802 if(ielem && ielem->datalen > 0)
1803 ielem->data[0] = save_pchar;
1806 previfield = ifield;
1809 *p = '\0';
1811 if(draw_whole_line)
1812 PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
1814 else{
1815 int uc, ac, do_arrow;
1816 int i, drew_X = 0;
1817 int inverse_hack = 0, need_inverse_hack = 0;
1818 int doing_bold = 0;
1820 /* so we can restore current color at the end */
1821 if((uc=pico_usingcolor()) != 0)
1822 lastc = pico_get_cur_color();
1825 * There are two possible "arrow" cursors. One is the one that
1826 * you get when you are at a slow speed or you turn that slow
1827 * speed one on. It is drawn as part of the status column.
1828 * That one is the one associated with the variable "ac".
1829 * It is always the base_color or the inverse of the base_color.
1831 * The other "arrow" cursor is the one you get by including the
1832 * ARROW token in the index-format. It may be configured to
1833 * be colored.
1835 * The arrow cursors have two special properties that make
1836 * them different from other sections or fields.
1837 * First, the arrow cursors only show up on the current line.
1838 * Second, the arrow cursors are drawn with generated data, not
1839 * data that is present in the passed in data.
1842 /* ac is for the old integrated arrow cursor */
1843 ac = F_ON(F_FORCE_ARROW,ps_global);
1845 /* do_arrow is for the ARROW token in index-format */
1846 do_arrow = (afld != iNothing);
1848 MoveCursor(HEADER_ROWS(ps_global) + line, 0);
1849 if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1850 CleartoEOLN();
1852 /* find the base color for the whole line */
1853 if(cur && !ac && !do_arrow){
1855 * This stanza handles the current line marking in the
1856 * regular, non-arrow-cursor case.
1860 * If the current line has a linecolor, apply the
1861 * appropriate reverse transformation to show it is current.
1863 if(uc && ice->linecolor && ice->linecolor->fg[0]
1864 && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
1865 base_color = apply_rev_color(ice->linecolor,
1866 ps_global->index_color_style);
1868 (void)pico_set_colorp(base_color, PSC_NONE);
1870 else{
1871 inverse_hack++;
1872 if(uc){
1873 COLOR_PAIR *rev;
1875 if((rev = pico_get_rev_color()) != NULL){
1876 base_color = new_color_pair(rev->fg, rev->bg);
1877 (void)pico_set_colorp(base_color, PSC_NONE);
1879 else
1880 base_color = lastc;
1884 else if(uc && ice->linecolor && ice->linecolor->fg[0]
1885 && ice->linecolor->bg[0]
1886 && pico_is_good_colorpair(ice->linecolor)){
1887 (void)pico_set_colorp(ice->linecolor, PSC_NONE);
1888 base_color = ice->linecolor;
1890 else
1891 base_color = lastc;
1893 memset(draw, 0, sizeof(draw));
1894 p = draw;
1896 doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
1898 /* draw each field */
1899 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1901 drew_X = 0;
1904 * Fix up the data for some special cases.
1907 /* message number string is generated on the fly */
1908 if(ifield->ctype == iMessNo){
1909 ielem = ifield->ielem;
1910 if(ielem && ielem->datalen >= ifield->width){
1911 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1912 ielem->data[ifield->width] = '\0';
1913 ielem->data[ielem->datalen] = '\0';
1917 if(ifield->ctype == sfld){
1918 ielem = ifield->ielem;
1919 if(ielem && ielem->datalen > 0){
1920 if(ielem->datalen == 1){
1921 save_schar1 = ielem->data[0];
1922 if(sel && !doing_bold){
1923 ielem->data[0] = 'X';
1924 drew_X++;
1926 else if(ac && cur && ielem->data[0] == ' ')
1927 ielem->data[0] = '-';
1929 if(ielem->next && ielem->next->datalen){
1930 save_schar2 = ielem->next->data[0];
1931 if(ac && cur && ielem->next->data[0] != '\0')
1932 ielem->next->data[0] = '>';
1935 else if(ielem->datalen > 1){
1936 if(sel && !doing_bold){
1937 ielem->data[0] = 'X';
1938 drew_X++;
1940 else if(ac && cur && ielem->data[0] == ' ')
1941 ielem->data[0] = '-';
1943 save_schar2 = ielem->data[1];
1944 if(ac && cur && ielem->data[1] != '\0')
1945 ielem->data[1] = '>';
1949 else if(ifield->ctype == afld && do_arrow && cur){
1951 ielem = ifield->ielem;
1952 if(ielem && ielem->datalen >= ifield->width){
1953 for(i = 0; i < ifield->width-1; i++)
1954 ielem->data[i] = cur ? '-' : ' ';
1956 ielem->data[i] = '>';
1959 else if(ifield->ctype == pfld){
1960 ielem = ifield->ielem;
1961 if(ielem && ielem->datalen > 0){
1962 save_pchar = ielem->data[0];
1963 ielem->data[0] = ice->plus;
1967 /* space between fields */
1968 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
1969 if(inverse_hack)
1970 StartInverse();
1972 Write_to_screen(" ");
1973 if(inverse_hack)
1974 EndInverse();
1977 for(ielem = ifield->ielem; ielem; ielem = ielem->next){
1978 char *src;
1980 src = ielem->data;
1981 utf8_pad_to_width(draw, src, (n+1) * sizeof(char),
1982 ielem->wid, ifield->leftadj);
1983 draw[n] = '\0';
1986 * Switch to color for ielem.
1987 * But don't switch if we drew an X in this column,
1988 * because that overwrites the colored thing, and don't
1989 * switch if this is the ARROW field and this is not
1990 * the current message. ARROW field is only colored for
1991 * the current message.
1992 * And don't switch if current line and type eTypeCol.
1994 if(ielem->color && pico_is_good_colorpair(ielem->color)
1995 && !(do_arrow && ifield->ctype == afld && !cur)
1996 && (!drew_X || ielem != ifield->ielem)
1997 && !(cur && ielem->type == eTypeCol)){
1998 need_inverse_hack = 0;
1999 (void) pico_set_colorp(ielem->color, PSC_NORM);
2001 else
2002 need_inverse_hack = 1;
2004 if(need_inverse_hack && inverse_hack)
2005 StartInverse();
2007 Write_to_screen(draw);
2008 if(need_inverse_hack && inverse_hack)
2009 EndInverse();
2011 (void) pico_set_colorp(base_color, PSC_NORM);
2015 * Restore the data for the special cases.
2018 if(ifield->ctype == sfld){
2019 ielem = ifield->ielem;
2020 if(ielem && ielem->datalen > 0){
2021 if(ielem->datalen == 1){
2022 ielem->data[0] = save_schar1;
2023 if(ielem->next && ielem->next->datalen)
2024 ielem->next->data[0] = save_schar2;
2026 else if(ielem->datalen > 1){
2027 ielem->data[0] = save_schar1;
2028 ielem->data[1] = save_schar2;
2032 else if(ifield->ctype == afld){
2033 ielem = ifield->ielem;
2034 if(ielem && ielem->datalen >= ifield->width)
2035 for(i = 0; i < ifield->width; i++)
2036 ielem->data[i] = ' ';
2038 else if(ifield->ctype == pfld){
2039 ielem = ifield->ielem;
2040 if(ielem && ielem->datalen > 0)
2041 ielem->data[0] = save_pchar;
2044 previfield = ifield;
2047 if(doing_bold)
2048 EndBold();
2050 if(base_color && base_color != lastc && base_color != ice->linecolor)
2051 free_color_pair(&base_color);
2053 if(lastc){
2054 (void)pico_set_colorp(lastc, PSC_NORM);
2055 free_color_pair(&lastc);
2060 entry->hilite = cur;
2061 entry->bolded = sel;
2062 entry->msgno = msgno;
2063 entry->plus = ice->plus;
2064 entry->id = ice->id;
2066 if(!ice->color_lookup_done && pico_usingcolor())
2067 entry->id = 0;
2069 return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
2073 * setup_index_state - hooked onto pith_opt_save_index_state to setup
2074 * current_index_state after setup_{index,thread}_header_widths
2076 void
2077 setup_index_state(int threaded)
2079 if(current_index_state){
2080 if(threaded){
2081 current_index_state->status_col = 0;
2082 current_index_state->status_fld = iStatus;
2083 current_index_state->plus_fld = iNothing;
2084 current_index_state->arrow_fld = iNothing;
2085 } else {
2086 INDEX_COL_S *cdesc, *prevcdesc = NULL;
2087 IndexColType sfld, altfld, plusfld, arrowfld;
2088 int width, fld, col, pluscol, scol, altcol;
2090 col = 0;
2091 scol = -1;
2092 sfld = iNothing;
2093 altcol = -1;
2094 altfld = iNothing;
2095 /* figure out which field is status field */
2096 for(cdesc = ps_global->index_disp_format, fld = 0;
2097 cdesc->ctype != iNothing;
2098 cdesc++){
2099 width = cdesc->width;
2100 if(width == 0)
2101 continue;
2103 /* space between columns */
2104 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2105 col++;
2107 if(cdesc->ctype == iStatus){
2108 scol = col;
2109 sfld = cdesc->ctype;
2110 break;
2113 if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
2114 scol = col;
2115 sfld = cdesc->ctype;
2116 break;
2119 if(cdesc->ctype == iMessNo){
2120 altcol = col;
2121 altfld = cdesc->ctype;
2124 col += width;
2125 fld++;
2126 prevcdesc = cdesc;
2129 if(sfld == iNothing){
2130 if(altcol == -1){
2131 scol = 0;
2133 else{
2134 scol = altcol;
2135 sfld = altfld;
2140 current_index_state->status_col = scol;
2141 current_index_state->status_fld = sfld;
2143 col = 0;
2144 plusfld = iNothing;
2145 pluscol = -1;
2146 prevcdesc = NULL;
2147 /* figure out which column to use for threading '+' */
2148 if(THREADING()
2149 && ps_global->thread_disp_style != THREAD_NONE
2150 && ps_global->VAR_THREAD_MORE_CHAR[0]
2151 && ps_global->VAR_THREAD_EXP_CHAR[0])
2152 for(cdesc = ps_global->index_disp_format, fld = 0;
2153 cdesc->ctype != iNothing;
2154 cdesc++){
2155 width = cdesc->width;
2156 if(width == 0)
2157 continue;
2159 /* space between columns */
2160 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2161 col++;
2163 if((cdesc->ctype == iSubject
2164 || cdesc->ctype == iShortSubject
2165 || cdesc->ctype == iSubjectText
2166 || cdesc->ctype == iSubjKey
2167 || cdesc->ctype == iSubjKeyText
2168 || cdesc->ctype == iSubjKeyInit
2169 || cdesc->ctype == iSubjKeyInitText)
2170 && (ps_global->thread_disp_style == THREAD_STRUCT
2171 || ps_global->thread_disp_style == THREAD_MUTTLIKE
2172 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
2173 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
2174 plusfld = cdesc->ctype;
2175 pluscol = col;
2176 break;
2179 if((cdesc->ctype == iFrom
2180 || cdesc->ctype == iFromToNotNews
2181 || cdesc->ctype == iFromTo
2182 || cdesc->ctype == iAddress
2183 || cdesc->ctype == iMailbox)
2184 && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
2185 || ps_global->thread_disp_style == THREAD_INDENT_FROM2
2186 || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
2187 plusfld = cdesc->ctype;
2188 pluscol = col;
2189 break;
2192 col += width;
2193 fld++;
2194 prevcdesc = cdesc;
2197 current_index_state->plus_fld = plusfld;
2198 current_index_state->plus_col = pluscol;
2200 arrowfld = iNothing;
2201 /* figure out which field is arrow field, if any */
2202 for(cdesc = ps_global->index_disp_format, fld = 0;
2203 cdesc->ctype != iNothing;
2204 cdesc++){
2205 width = cdesc->width;
2206 if(width == 0)
2207 continue;
2209 if(cdesc->ctype == iArrow){
2210 arrowfld = cdesc->ctype;
2211 break;
2214 fld++;
2217 current_index_state->arrow_fld = arrowfld;
2224 * insert_condensed_thread_cue - used on pith hook to add decoration to
2225 * subject or from text to show condensed thread info
2228 condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
2229 char **fieldstr, size_t *strsize, int width, int collapsed)
2231 if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
2233 * WARNING!
2234 * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
2235 * and VAR_THREAD_EXP_CHAR[0] are ascii.
2236 * Could do something similar to the conversions done with keyword
2237 * initials in key_str.
2239 if(ice)
2240 ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
2241 : (thd && thd->next)
2242 ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
2244 if(strsize && *strsize > 0 && width != 0){
2245 *(*fieldstr)++ = ' ';
2246 (*strsize)--;
2247 if(width > 0)
2248 width--;
2251 if(strsize && *strsize > 0 && width != 0){
2252 *(*fieldstr)++ = ' ';
2253 (*strsize)--;
2254 if(width > 0)
2255 width--;
2259 return(width);
2264 truncate_subj_and_from_strings(void)
2266 return 1;
2271 * paint_index_hline - paint index line given what we got
2273 void
2274 paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
2276 PINETHRD_S *thrd;
2279 * Trust only what we get back that isn't bogus since
2280 * we were prevented from doing any fetches and such...
2282 if((ps_global->redrawer == redraw_index_body
2283 || ps_global->prev_screen == mail_index_screen)
2284 && current_index_state
2285 && current_index_state->stream == stream
2286 && !ps_global->msgmap->hilited){
2287 int line;
2290 * This test isn't right if there are hidden lines. The line will
2291 * fail the test because it seems like it is past the end of the
2292 * screen but since the hidden lines don't take up space the line
2293 * might actually be on the screen. Don't know that it is worth
2294 * it to fix this, though, since you may have to file through
2295 * many hidden lines before finding the visible ones. I'm not sure
2296 * if the logic inside the if is correct when we do pass the
2297 * top-level test. Leave it for now. Hubert - 2002-06-28
2299 if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
2300 && line < current_index_state->lines_per_page){
2301 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
2302 long n;
2303 long zoomhide, collapsehide;
2305 zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
2306 collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
2309 * Line is visible if it is selected and not hidden due to
2310 * thread collapse, or if there is no zooming happening and
2311 * it is not hidden due to thread collapse.
2313 for(line = 0, n = current_index_state->msg_at_top;
2314 n != msgno;
2315 n++)
2316 if((zoomhide
2317 && get_lflag(stream, current_index_state->msgmap,
2318 n, MN_SLCT)
2319 && (!collapsehide
2320 || !get_lflag(stream, current_index_state->msgmap, n,
2321 MN_CHID)))
2323 (!zoomhide
2324 && !get_lflag(stream, current_index_state->msgmap,
2325 n, MN_CHID)))
2326 line++;
2329 thrd = NULL;
2330 if(THRD_INDX()){
2331 unsigned long rawno;
2333 rawno = mn_m2raw(current_index_state->msgmap, msgno);
2334 if(rawno)
2335 thrd = fetch_thread(stream, rawno);
2338 paint_index_line(ice, line,
2339 (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
2340 current_index_state->status_fld,
2341 current_index_state->plus_fld,
2342 current_index_state->arrow_fld,
2343 &current_index_state->entry_state[line],
2344 mn_is_cur(current_index_state->msgmap, msgno),
2345 THRD_INDX()
2346 ? (count_lflags_in_thread(stream, thrd,
2347 current_index_state->msgmap,
2348 MN_SLCT) > 0)
2349 : get_lflag(stream, current_index_state->msgmap,
2350 msgno, MN_SLCT));
2351 fflush(stdout);
2360 * pine_imap_env -- C-client's telling us an envelope just arrived
2361 * from the server. Use it if we can...
2363 void
2364 pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
2366 MESSAGECACHE *mc;
2368 dprint((7, "imap_env(%ld)\n", rawno));
2369 if(stream && !sp_mail_box_changed(stream)
2370 && stream == ps_global->mail_stream
2371 && rawno > 0L && rawno <= stream->nmsgs
2372 && (mc = mail_elt(stream,rawno))
2373 && mc->valid
2374 && mc->rfc822_size
2375 && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
2376 INDEXDATA_S idata;
2377 ICE_S *ice;
2379 memset(&idata, 0, sizeof(INDEXDATA_S));
2380 idata.no_fetch = 1;
2381 idata.size = mc->rfc822_size;
2382 idata.rawno = rawno;
2383 idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
2384 idata.stream = stream;
2386 index_data_env(&idata, env);
2389 * Look for resent-to already in MAILCACHE data
2391 if(mc->private.msg.header.text.data){
2392 STRINGLIST *lines;
2393 SIZEDTEXT szt;
2394 static char *linelist[] = {"resent-to" , NULL};
2396 if(mail_match_lines(lines = new_strlst(linelist),
2397 mc->private.msg.lines, 0L)){
2398 idata.valid_resent_to = 1;
2399 memset(&szt, 0, sizeof(SIZEDTEXT));
2400 textcpy(&szt, &mc->private.msg.header.text);
2401 mail_filter((char *) szt.data, szt.size, lines, 0L);
2402 idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
2403 if(szt.data)
2404 fs_give((void **) &szt.data);
2407 free_strlst(&lines);
2410 ice = (*format_index_line)(&idata);
2411 if(idata.bogus)
2412 clear_ice(&ice);
2413 else
2414 paint_index_hline(stream, idata.msgno, ice);
2419 /*----------------------------------------------------------------------
2420 Scroll to specified position.
2423 Args: pos - position to scroll to.
2425 Returns: TRUE - did the scroll operation.
2426 FALSE - was not able to do the scroll operation.
2427 ----*/
2429 index_scroll_to_pos (long int pos)
2431 static short bad_timing = 0;
2432 long i, j, k;
2434 if(bad_timing)
2435 return (FALSE);
2438 * Put the requested line at the top of the screen...
2442 * Starting at msg 'pos' find next visible message.
2444 for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
2445 if(!msgline_hidden(current_index_state->stream,
2446 current_index_state->msgmap, i, 0)){
2447 current_index_state->msg_at_top = i;
2448 break;
2453 * If single selection, move selected message to be on the screen.
2455 if (mn_total_cur(current_index_state->msgmap) == 1L) {
2456 if (current_index_state->msg_at_top >
2457 mn_get_cur (current_index_state->msgmap)) {
2458 /* Selection was above screen, move to top of screen. */
2459 mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
2461 else {
2462 /* Scan through the screen. If selection found, leave where is.
2463 * Otherwise, move to end of screen */
2464 for( i = current_index_state->msg_at_top,
2465 j = current_index_state->lines_per_page;
2466 i != mn_get_cur(current_index_state->msgmap) &&
2467 i <= mn_get_total(current_index_state->msgmap) &&
2468 j > 0L;
2469 i++) {
2470 if(!msgline_hidden(current_index_state->stream,
2471 current_index_state->msgmap, i, 0)){
2472 j--;
2473 k = i;
2476 if(j <= 0L)
2477 /* Move to end of screen. */
2478 mn_set_cur(current_index_state->msgmap, k);
2482 bad_timing = 0;
2483 return (TRUE);
2488 /*----------------------------------------------------------------------
2489 Adjust the index display state down a line
2491 Args: scroll_count -- number of lines to scroll
2493 Returns: TRUE - did the scroll operation.
2494 FALSE - was not able to do the scroll operation.
2495 ----*/
2497 index_scroll_down(long int scroll_count)
2499 static short bad_timing = 0;
2500 long i, j, k;
2501 long cur, total;
2503 if(bad_timing)
2504 return (FALSE);
2506 bad_timing = 1;
2509 j = -1L;
2510 total = mn_get_total (current_index_state->msgmap);
2511 for(k = i = current_index_state->msg_at_top; ; i++){
2513 /* Only examine non-hidden messages. */
2514 if(!msgline_hidden(current_index_state->stream,
2515 current_index_state->msgmap, i, 0)){
2516 /* Remember this message */
2517 k = i;
2518 /* Increment count of lines. */
2519 if (++j >= scroll_count) {
2520 /* Counted enough lines, stop. */
2521 current_index_state->msg_at_top = k;
2522 break;
2526 /* If at last message, stop. */
2527 if (i >= total){
2528 current_index_state->msg_at_top = k;
2529 break;
2534 * If not multiple selection, see if selected message visible. if not
2535 * set it to last visible message.
2537 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2538 j = 0L;
2539 cur = mn_get_cur (current_index_state->msgmap);
2540 for (i = current_index_state->msg_at_top; i <= total; ++i) {
2541 if(!msgline_hidden(current_index_state->stream,
2542 current_index_state->msgmap, i, 0)){
2543 if (++j >= current_index_state->lines_per_page) {
2544 break;
2546 if (i == cur)
2547 break;
2550 if (i != cur)
2551 mn_set_cur(current_index_state->msgmap,
2552 current_index_state->msg_at_top);
2555 bad_timing = 0;
2556 return (TRUE);
2561 /*----------------------------------------------------------------------
2562 Adjust the index display state up a line
2564 Args: scroll_count -- number of lines to scroll
2566 Returns: TRUE - did the scroll operation.
2567 FALSE - was not able to do the scroll operation.
2569 ----*/
2571 index_scroll_up(long int scroll_count)
2573 static short bad_timing = 0;
2574 long i, j, k;
2575 long cur;
2577 if(bad_timing)
2578 return(FALSE);
2580 bad_timing = 1;
2582 j = -1L;
2583 for(k = i = current_index_state->msg_at_top; ; i--){
2585 /* Only examine non-hidden messages. */
2586 if(!msgline_hidden(current_index_state->stream,
2587 current_index_state->msgmap, i, 0)){
2588 /* Remember this message */
2589 k = i;
2590 /* Increment count of lines. */
2591 if (++j >= scroll_count) {
2592 /* Counted enough lines, stop. */
2593 current_index_state->msg_at_top = k;
2594 break;
2598 /* If at first message, stop */
2599 if (i <= 1L){
2600 current_index_state->msg_at_top = k;
2601 break;
2607 * If not multiple selection, see if selected message visible. if not
2608 * set it to last visible message.
2610 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2611 j = 0L;
2612 cur = mn_get_cur (current_index_state->msgmap);
2613 for ( i = current_index_state->msg_at_top;
2614 i <= mn_get_total(current_index_state->msgmap);
2615 ++i) {
2616 if(!msgline_hidden(current_index_state->stream,
2617 current_index_state->msgmap, i, 0)){
2618 if (++j >= current_index_state->lines_per_page) {
2619 k = i;
2620 break;
2622 if (i == cur)
2623 break;
2626 if (i != cur)
2627 mn_set_cur(current_index_state->msgmap, k);
2631 bad_timing = 0;
2632 return (TRUE);
2637 /*----------------------------------------------------------------------
2638 Calculate the message number that should be at the top of the display
2640 Args: current - the current message number
2641 lines_per_page - the number of lines for the body of the index only
2643 Returns: -1 if the current message is -1
2644 the message entry for the first message at the top of the screen.
2646 When paging in the index it is always on even page boundies, and the
2647 current message is always on the page thus the top of the page is
2648 completely determined by the current message and the number of lines
2649 on the page.
2650 ----*/
2651 long
2652 top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
2654 long current, hidden, lastn;
2655 long n, m = 0L, t = 1L;
2657 current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
2659 if(current < 0L)
2660 return(-1);
2662 if(lines_per_page == 0L)
2663 return(current);
2665 if(THRD_INDX_ENABLED()){
2666 long rawno;
2667 PINETHRD_S *thrd = NULL;
2669 rawno = mn_m2raw(msgs, mn_get_cur(msgs));
2670 if(rawno)
2671 thrd = fetch_thread(stream, rawno);
2673 if(THRD_INDX()){
2675 if(any_lflagged(msgs, MN_HIDE)){
2676 PINETHRD_S *is_current_thrd;
2678 is_current_thrd = thrd;
2679 if(is_current_thrd){
2680 if(mn_get_revsort(msgs)){
2681 /* start with top of tail of thread list */
2682 thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
2683 if(thrd && thrd->top && thrd->top != thrd->rawno)
2684 thrd = fetch_thread(stream, thrd->top);
2686 else{
2687 /* start with head of thread list */
2688 thrd = fetch_head_thread(stream);
2691 t = 1L;
2692 m = 0L;
2693 if(thrd)
2694 n = mn_raw2m(msgs, thrd->rawno);
2696 while(thrd){
2697 if(!msgline_hidden(stream, msgs, n, 0)
2698 && (++m % lines_per_page) == 1L)
2699 t = n;
2701 if(thrd == is_current_thrd)
2702 break;
2704 if(mn_get_revsort(msgs) && thrd->prevthd)
2705 thrd = fetch_thread(stream, thrd->prevthd);
2706 else if(!mn_get_revsort(msgs) && thrd->nextthd)
2707 thrd = fetch_thread(stream, thrd->nextthd);
2708 else
2709 thrd = NULL;
2711 if(thrd)
2712 n = mn_raw2m(msgs, thrd->rawno);
2716 else{
2717 if(thrd){
2718 n = thrd->thrdno;
2719 m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
2720 n = thrd->rawno;
2722 * We want to find the m'th thread and the
2723 * message number that goes with that. We just have
2724 * to back up from where we are to get there.
2725 * If we have a reverse sort backing up is going
2726 * forward through the thread.
2728 while(thrd && m < thrd->thrdno){
2729 n = thrd->rawno;
2730 if(mn_get_revsort(msgs) && thrd->nextthd)
2731 thrd = fetch_thread(stream, thrd->nextthd);
2732 else if(!mn_get_revsort(msgs) && thrd->prevthd)
2733 thrd = fetch_thread(stream, thrd->prevthd);
2734 else
2735 thrd = NULL;
2738 if(thrd)
2739 n = thrd->rawno;
2741 t = mn_raw2m(msgs, n);
2745 else{ /* viewing a thread */
2747 lastn = mn_get_total(msgs);
2748 t = 1L;
2750 /* get top of thread */
2751 if(thrd && thrd->top && thrd->top != thrd->rawno)
2752 thrd = fetch_thread(stream, thrd->top);
2754 if(thrd){
2755 if(mn_get_revsort(msgs))
2756 lastn = mn_raw2m(msgs, thrd->rawno);
2757 else
2758 t = mn_raw2m(msgs, thrd->rawno);
2761 n = 0L;
2763 /* n is the end of this thread */
2764 while(thrd){
2765 n = mn_raw2m(msgs, thrd->rawno);
2766 if(thrd->branch)
2767 thrd = fetch_thread(stream, thrd->branch);
2768 else if(thrd->next)
2769 thrd = fetch_thread(stream, thrd->next);
2770 else
2771 thrd = NULL;
2774 if(n){
2775 if(mn_get_revsort(msgs))
2776 t = n;
2777 else
2778 lastn = n;
2781 for(m = 0L, n = t; n <= MIN(current, lastn); n++)
2782 if(!msgline_hidden(stream, msgs, n, 0)
2783 && (++m % lines_per_page) == 1L)
2784 t = n;
2787 return(t);
2789 else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
2791 if(current < mn_get_total(msgs) / 2){
2792 t = 1L;
2793 m = 0L;
2794 for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
2795 if(!msgline_hidden(stream, msgs, n, 0)
2796 && (++m % lines_per_page) == 1L)
2797 t = n;
2799 else{
2800 t = current+1L;
2801 m = mn_get_total(msgs)-hidden+1L;
2802 for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
2803 if(!msgline_hidden(stream, msgs, n, 0)
2804 && (--m % lines_per_page) == 1L)
2805 t = n;
2807 if(t > current)
2808 t = 1L;
2811 return(t);
2813 else
2814 return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
2818 /*----------------------------------------------------------------------
2819 Clear various bits that make up a healthy display
2821 ----*/
2822 void
2823 reset_index_border(void)
2825 mark_status_dirty();
2826 mark_keymenu_dirty();
2827 mark_titlebar_dirty();
2828 ps_global->mangled_screen = 1; /* signal FULL repaint */
2832 /*----------------------------------------------------------------------
2833 This redraws the body of the index screen, taking into
2834 account any change in the size of the screen. All the state needed to
2835 repaint is in the static variables so this can be called from
2836 anywhere.
2837 ----*/
2838 void
2839 redraw_index_body(void)
2841 int agg;
2843 if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
2844 restore_selected(current_index_state->msgmap);
2846 ps_global->mangled_body = 1;
2848 (void) update_index(ps_global, current_index_state);
2849 if(agg)
2850 pseudo_selected(current_index_state->stream, current_index_state->msgmap);
2854 /*----------------------------------------------------------------------
2855 Give hint about Other command being optional. Some people get the idea
2856 that it is required to use the commands on the 2nd and 3rd keymenus.
2858 Args: none
2860 Result: message may be printed to status line
2861 ----*/
2862 void
2863 warn_other_cmds(void)
2865 static int other_cmds = 0;
2867 other_cmds++;
2868 if(((ps_global->first_time_user || ps_global->show_new_version) &&
2869 other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
2870 q_status_message(SM_ASYNC, 0, 9,
2871 _("Remember the \"O\" command is always optional"));
2875 void
2876 thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
2877 UCS preloadkeystroke, int q_line)
2879 PINETHRD_S *thrd = NULL;
2880 unsigned long rawno, save_branch;
2881 int we_cancel = 0;
2882 int flags = AC_FROM_THREAD;
2884 if(!(stream && msgmap))
2885 return;
2887 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2888 if(rawno)
2889 thrd = fetch_thread(stream, rawno);
2891 if(!thrd)
2892 return;
2894 save_branch = thrd->branch;
2895 thrd->branch = 0L; /* branch is a sibling, not part of thread */
2897 if(!preloadkeystroke){
2898 if(!THRD_INDX()){
2899 if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
2900 flags |= AC_EXPN;
2901 else
2902 flags |= AC_COLL;
2905 if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
2906 == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
2907 flags |= AC_UNSEL;
2910 we_cancel = busy_cue(NULL, NULL, 1);
2912 /* save the SLCT flags in STMP for restoring at the bottom */
2913 copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
2915 /* clear the values from the SLCT flags */
2916 set_lflags(stream, msgmap, MN_SLCT, 0);
2918 /* set SLCT for thrd on down */
2919 set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
2920 thrd->branch = save_branch;
2922 if(we_cancel)
2923 cancel_busy_cue(0);
2925 (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
2926 q_line);
2928 /* restore the original flags */
2929 copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
2931 if(any_lflagged(msgmap, MN_HIDE) > 0L){
2932 /* if nothing left selected, unhide all */
2933 if(any_lflagged(msgmap, MN_SLCT) == 0L){
2934 (void) unzoom_index(ps_global, stream, msgmap);
2935 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
2936 q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
2939 /* if current is hidden, adjust */
2940 adjust_cur_to_visible(stream, msgmap);
2945 /*----------------------------------------------------------------------
2946 Search the message headers as displayed in index
2948 Args: command_line -- The screen line to prompt on
2949 msg -- The current message number to start searching at
2950 max_msg -- The largest message number in the current folder
2952 The headers are searched exactly as they are displayed on the screen. The
2953 search will wrap around to the beginning if not string is not found right
2954 away.
2955 ----*/
2956 void
2957 index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
2959 int rc, select_all = 0, flags, prefetch, searchbound, otherbound, offset = 0, we_turned_on = 0;
2960 long i, sorted_msg, selected = 0L;
2961 char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
2962 char buf[MAX_SCREEN_COLS+1], *p;
2963 HelpType help;
2964 char search_string[MAX_SEARCH+1];
2965 ICE_S *ice, *ic;
2966 static int direction = 1;
2967 static HISTORY_S *history = NULL;
2968 static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
2969 {ctrl('Y'), 10, "^Y", N_("First Msg")},
2970 {ctrl('V'), 11, "^V", N_("Last Msg")},
2971 {ctrl('W'), 20, "^W", ""},
2972 {0, 0, NULL, NULL },
2973 {KEY_UP, 30, "", ""},
2974 {KEY_DOWN, 31, "", ""},
2975 {-1, 0, NULL, NULL} };
2976 #define KD_IS (3) /* index of Direction key */
2977 #define KU_IS (5) /* index of KEY_UP */
2978 #define PREFETCH_THIS_MANY_LINES (50)
2980 init_hist(&history, HISTSIZE);
2981 search_string[0] = '\0';
2982 if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
2983 strncpy(search_string, p, sizeof(search_string));
2984 search_string[sizeof(search_string)-1] = '\0';
2987 dprint((4, "\n - search headers - \n"));
2989 if(!any_messages(msgmap, NULL, "to search")){
2990 return;
2992 else if(mn_total_cur(msgmap) > 1L){
2993 q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
2994 comatose(mn_total_cur(msgmap)));
2995 return;
2997 else
2998 sorted_msg = mn_get_cur(msgmap);
3000 help = NO_HELP;
3001 new_string[0] = '\0';
3003 while(1) {
3004 snprintf(prompt, sizeof(prompt), _("Word to search %sfor [%s] : "),
3005 direction == -1 ? _("(backwards) ") : "", search_string);
3007 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3008 header_search_key[0].ch = ctrl('X');
3009 header_search_key[0].rval = 12;
3010 header_search_key[0].name = "^X";
3011 header_search_key[0].label = N_("Select Matches");
3013 else{
3014 header_search_key[0].ch = header_search_key[0].rval = 0;
3015 header_search_key[0].name = header_search_key[0].label = NULL;
3019 * 2 is really 1 because there will be one real entry and
3020 * one entry of "" because of the get_prev_hist above.
3022 if(items_in_hist(history) > 2){
3023 header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
3024 header_search_key[KU_IS].label = HISTORY_KEYLABEL;
3025 header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
3026 header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
3028 else{
3029 header_search_key[KU_IS].name = "";
3030 header_search_key[KU_IS].label = "";
3031 header_search_key[KU_IS+1].name = "";
3032 header_search_key[KU_IS+1].label = "";
3035 header_search_key[KD_IS].label = direction == 1 ? _("Backward") : _("Forward");
3037 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
3039 rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
3040 prompt, header_search_key, help, &flags);
3042 if(rc == 3) {
3043 help = (help != NO_HELP) ? NO_HELP :
3044 F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
3045 : h_os_index_whereis;
3046 continue;
3048 else if(rc == 10){
3049 q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
3050 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3052 selected = sorted_msg;
3053 mn_dec_cur(stream, msgmap, MH_NONE);
3054 sorted_msg = mn_get_cur(msgmap);
3056 while(selected != sorted_msg);
3058 else
3059 sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
3061 mn_set_cur(msgmap, sorted_msg);
3062 return;
3064 else if(rc == 11){
3065 q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
3066 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3068 selected = sorted_msg;
3069 mn_inc_cur(stream, msgmap, MH_NONE);
3070 sorted_msg = mn_get_cur(msgmap);
3072 while(selected != sorted_msg);
3074 else
3075 sorted_msg = mn_get_total(msgmap);
3077 mn_set_cur(msgmap, sorted_msg);
3078 return;
3080 else if(rc == 12){
3081 select_all = 1;
3082 break;
3084 else if(rc == 30){
3085 if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
3086 strncpy(new_string, p, sizeof(new_string));
3087 new_string[sizeof(new_string)-1] = '\0';
3089 else
3090 Writechar(BELL, 0);
3092 continue;
3094 else if(rc == 31){
3095 if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
3096 strncpy(new_string, p, sizeof(new_string));
3097 new_string[sizeof(new_string)-1] = '\0';
3099 else
3100 Writechar(BELL, 0);
3102 continue;
3104 else if(rc == 20){
3105 direction *= -1;
3106 continue;
3109 if(rc != 4){ /* 4 is redraw */
3110 save_hist(history, new_string, 0, NULL);
3111 break;
3115 if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
3116 cmd_cancelled(_("Search"));
3117 return;
3120 if(new_string[0] == '\0'){
3121 strncpy(new_string, search_string, sizeof(new_string));
3122 new_string[sizeof(new_string)-1] = '\0';
3125 strncpy(search_string, new_string, sizeof(search_string));
3126 search_string[sizeof(search_string)-1] = '\0';
3128 we_turned_on = intr_handling_on();
3130 prefetch = 0;
3131 if (direction == -1) {
3132 searchbound = -1;
3133 otherbound = mn_get_total(msgmap);
3134 offset = 2;
3135 } else {
3136 searchbound = mn_get_total(msgmap);
3137 otherbound = 1;
3140 for(i = sorted_msg + ((select_all)?0:1) - offset;
3141 i * direction <= searchbound && !ps_global->intr_pending;
3142 i += direction){
3143 if(msgline_hidden(stream, msgmap, i, 0))
3144 continue;
3146 if(prefetch <= 0)
3147 prefetch = PREFETCH_THIS_MANY_LINES;
3149 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3151 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3153 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3154 search_string)){
3155 selected++;
3156 if(select_all)
3157 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3158 else
3159 break;
3163 prefetch = 0;
3164 if(i * direction > searchbound){
3165 for(i = otherbound;
3166 i * direction < sorted_msg * direction && !ps_global->intr_pending;
3167 i += direction ){
3169 if(msgline_hidden(stream, msgmap, i, 0))
3170 continue;
3172 if(prefetch <= 0)
3173 prefetch = PREFETCH_THIS_MANY_LINES;
3175 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3177 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3179 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3180 search_string)){
3181 selected++;
3182 if(select_all)
3183 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3184 else
3185 break;
3190 /* search current line */
3191 if(!select_all && !selected){
3192 i = sorted_msg;
3193 if(!msgline_hidden(stream, msgmap, i, 0)){
3195 ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
3197 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3199 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3200 search_string)){
3201 selected++;
3206 if(ps_global->intr_pending){
3207 q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
3208 select_all ? _(" Selected set may be incomplete."):"");
3210 else if(select_all){
3211 if(selected
3212 && any_lflagged(msgmap, MN_SLCT) > 0L
3213 && !any_lflagged(msgmap, MN_HIDE)
3214 && F_ON(F_AUTO_ZOOM, state))
3215 (void) zoom_index(state, stream, msgmap, MN_SLCT);
3217 q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
3218 long2string(selected));
3220 else if(selected){
3221 q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
3222 (i * direction < sorted_msg * direction) ? _(". Search wrapped to beginning") :
3223 (i == sorted_msg) ? _(". Current line contains only match") : "");
3224 mn_set_cur(msgmap, i);
3226 else
3227 q_status_message(SM_ORDER, 0, 3, _("Word not found"));
3229 if(we_turned_on)
3230 intr_handling_off();
3235 * Original idea from Stephen Casner <casner@acm.org>.
3237 * Apply the appropriate reverse color transformation to the given
3238 * color pair and return a new color pair. The caller should free the
3239 * color pair.
3242 COLOR_PAIR *
3243 apply_rev_color(COLOR_PAIR *cp, int style)
3245 COLOR_PAIR *rc = pico_get_rev_color();
3247 if(rc){
3248 if(style == IND_COL_REV){
3249 /* just use Reverse color regardless */
3250 return(new_color_pair(rc->fg, rc->bg));
3252 else if(style == IND_COL_FG){
3254 * If changing to Rev fg is readable and different
3255 * from what it already is, do it.
3257 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
3258 return(new_color_pair(rc->fg, cp->bg));
3260 else if(style == IND_COL_BG){
3262 * If changing to Rev bg is readable and different
3263 * from what it already is, do it.
3265 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
3266 return(new_color_pair(cp->fg, rc->bg));
3268 else if(style == IND_COL_FG_NOAMBIG){
3270 * If changing to Rev fg is readable, different
3271 * from what it already is, and not the same as
3272 * the Rev color, do it.
3274 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
3275 strcmp(rc->bg, cp->bg))
3276 return(new_color_pair(rc->fg, cp->bg));
3278 else if(style == IND_COL_BG_NOAMBIG){
3280 * If changing to Rev bg is readable, different
3281 * from what it already is, and not the same as
3282 * the Rev color, do it.
3284 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
3285 strcmp(rc->fg, cp->fg))
3286 return(new_color_pair(cp->fg, rc->bg));
3290 /* come here for IND_COL_FLIP and for the cases which fail the tests */
3291 return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
3296 #ifdef _WINDOWS
3298 /*----------------------------------------------------------------------
3299 Callback to get the text of the current message. Used to display
3300 a message in an alternate window.
3302 Args: cmd - what type of scroll operation.
3303 text - filled with pointer to text.
3304 l - length of text.
3305 style - Returns style of text. Can be:
3306 GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
3307 lines
3308 GETTEXT_LINES - Is a pointer to NULL terminated array of
3309 char *. Each entry points to a line of
3310 text.
3312 this implementation always returns GETTEXT_TEXT.
3314 Returns: TRUE - did the scroll operation.
3315 FALSE - was not able to do the scroll operation.
3316 ----*/
3318 index_scroll_callback (cmd, scroll_pos)
3319 int cmd;
3320 long scroll_pos;
3322 int paint = TRUE;
3324 switch (cmd) {
3325 case MSWIN_KEY_SCROLLUPLINE:
3326 paint = index_scroll_up (scroll_pos);
3327 break;
3329 case MSWIN_KEY_SCROLLDOWNLINE:
3330 paint = index_scroll_down (scroll_pos);
3331 break;
3333 case MSWIN_KEY_SCROLLUPPAGE:
3334 paint = index_scroll_up (current_index_state->lines_per_page);
3335 break;
3337 case MSWIN_KEY_SCROLLDOWNPAGE:
3338 paint = index_scroll_down (current_index_state->lines_per_page);
3339 break;
3341 case MSWIN_KEY_SCROLLTO:
3342 /* Normalize msgno in zoomed case */
3343 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
3344 long n, x;
3346 for(n = 1L, x = 0;
3347 x < scroll_pos && n < mn_get_total(ps_global->msgmap);
3348 n++)
3349 if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
3350 n, 0))
3351 x++;
3353 scroll_pos = n - 1; /* list-position --> message number */
3356 paint = index_scroll_to_pos (scroll_pos + 1);
3357 break;
3360 if(paint){
3361 mswin_beginupdate();
3362 update_titlebar_message();
3363 update_titlebar_status();
3364 redraw_index_body();
3365 mswin_endupdate();
3368 return(paint);
3372 /*----------------------------------------------------------------------
3373 MSWin scroll callback to get the text of the current message
3375 Args: title - title for new window
3376 text -
3377 l -
3378 style -
3380 Returns: TRUE - got the requested text
3381 FALSE - was not able to get the requested text
3382 ----*/
3384 index_gettext_callback(title, titlelen, text, l, style)
3385 char *title;
3386 size_t titlelen;
3387 void **text;
3388 long *l;
3389 int *style;
3391 int rv = 0;
3392 ENVELOPE *env;
3393 BODY *body;
3394 STORE_S *so;
3395 gf_io_t pc;
3397 if(mn_get_total(ps_global->msgmap) > 0L
3398 && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
3399 gf_set_so_writec(&pc, so);
3401 if((env = pine_mail_fetchstructure(ps_global->mail_stream,
3402 mn_m2raw(ps_global->msgmap,
3403 mn_get_cur(ps_global->msgmap)),
3404 &body))
3405 && format_message(mn_m2raw(ps_global->msgmap,
3406 mn_get_cur(ps_global->msgmap)),
3407 env, body, NULL, FM_NEW_MESS, pc)){
3408 snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
3409 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
3410 mn_get_cur(ps_global->msgmap),
3411 mn_get_total(ps_global->msgmap));
3412 title[titlelen-1] = '\0';
3413 *text = so_text(so);
3414 *l = strlen((char *)so_text(so));
3415 *style = GETTEXT_TEXT;
3417 /* free alloc'd so, but preserve the text passed back to caller */
3418 so->txt = (void *) NULL;
3419 rv = 1;
3422 gf_clear_so_writec(so);
3423 so_give(&so);
3426 return(rv);
3434 index_sort_callback(set, order)
3435 int set;
3436 long order;
3438 int i = 0;
3440 if(set){
3441 sort_folder(ps_global->mail_stream, ps_global->msgmap,
3442 order & 0x000000ff,
3443 (order & 0x00000100) != 0, SRT_VRB);
3444 mswin_beginupdate();
3445 update_titlebar_message();
3446 update_titlebar_status();
3447 redraw_index_body();
3448 mswin_endupdate();
3449 flush_status_messages(1);
3451 else{
3452 i = (int) mn_get_sort(ps_global->msgmap);
3453 if(mn_get_revsort(ps_global->msgmap))
3454 i |= 0x0100;
3457 return(i);
3464 void
3465 index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
3467 int n = 0;
3468 int view_in_new_wind_index = -1;
3469 long rawno;
3470 MESSAGECACHE *mc;
3471 MPopup view_index_popup[32];
3472 struct key_menu *km = (style == ThreadIndex)
3473 ? &thread_keymenu
3474 : (ps_global->mail_stream != stream)
3475 ? &simple_index_keymenu
3476 : &index_keymenu;
3479 * Loosely follow the logic in do_index_border to figure
3480 * out which commands to show.
3483 if(full){
3484 if(km != &simple_index_keymenu){
3485 view_index_popup[n].type = tQueue;
3486 view_index_popup[n].label.string = (km == &thread_keymenu)
3487 ? "&View Thread" : "&View";
3488 view_index_popup[n].label.style = lNormal;
3489 view_index_popup[n++].data.val = 'V';
3492 if(km == &index_keymenu){
3493 view_in_new_wind_index = n;
3494 view_index_popup[n].type = tIndex;
3495 view_index_popup[n].label.style = lNormal;
3496 view_index_popup[n++].label.string = "View in New Window";
3499 if(km != &simple_index_keymenu)
3500 view_index_popup[n++].type = tSeparator;
3502 if(km == &thread_keymenu){
3503 view_index_popup[n].type = tQueue;
3504 view_index_popup[n].label.string = "&Delete Thread";
3505 view_index_popup[n].label.style = lNormal;
3506 view_index_popup[n++].data.val = 'D';
3508 view_index_popup[n].type = tQueue;
3509 view_index_popup[n].label.string = "&UnDelete Thread";
3510 view_index_popup[n].label.style = lNormal;
3511 view_index_popup[n++].data.val = 'U';
3513 else{
3514 /* Make "delete/undelete" item sensitive */
3515 mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
3516 && stream && rawno <= stream->nmsgs)
3517 ? mail_elt(stream, rawno) : NULL;
3518 view_index_popup[n].type = tQueue;
3519 view_index_popup[n].label.style = lNormal;
3520 if(mc && mc->deleted){
3521 view_index_popup[n].label.string = "&Undelete";
3522 view_index_popup[n++].data.val = 'U';
3524 else{
3525 view_index_popup[n].label.string = "&Delete";
3526 view_index_popup[n++].data.val = 'D';
3530 if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
3531 view_index_popup[n].type = tSubMenu;
3532 view_index_popup[n].label.string = "Flag";
3533 view_index_popup[n++].data.submenu = flag_submenu(mc);
3536 if(km == &simple_index_keymenu){
3537 view_index_popup[n].type = tQueue;
3538 view_index_popup[n].label.style = lNormal;
3539 view_index_popup[n].label.string = "&Select";
3540 view_index_popup[n++].data.val = 'S';
3542 else{
3543 view_index_popup[n].type = tQueue;
3544 view_index_popup[n].label.style = lNormal;
3545 view_index_popup[n].label.string = (km == &thread_keymenu)
3546 ? "&Save Thread" : "&Save";
3547 view_index_popup[n++].data.val = 'S';
3549 view_index_popup[n].type = tQueue;
3550 view_index_popup[n].label.style = lNormal;
3551 view_index_popup[n].label.string = (km == &thread_keymenu)
3552 ? "&Export Thread" : "&Export";
3553 view_index_popup[n++].data.val = 'E';
3555 view_index_popup[n].type = tQueue;
3556 view_index_popup[n].label.style = lNormal;
3557 view_index_popup[n].label.string = "Print";
3558 view_index_popup[n++].data.val = '%';
3560 view_index_popup[n].type = tQueue;
3561 view_index_popup[n].label.style = lNormal;
3562 view_index_popup[n].label.string = (km == &thread_keymenu)
3563 ? "&Reply To Thread" : "&Reply";
3564 view_index_popup[n++].data.val = 'R';
3566 view_index_popup[n].type = tQueue;
3567 view_index_popup[n].label.style = lNormal;
3568 view_index_popup[n].label.string = (km == &thread_keymenu)
3569 ? "&Forward Thread" : "&Forward";
3570 view_index_popup[n++].data.val = 'F';
3572 if(F_ON(F_ENABLE_BOUNCE, ps_global)){
3573 view_index_popup[n].type = tQueue;
3574 view_index_popup[n].label.style = lNormal;
3575 view_index_popup[n].label.string = (km == &thread_keymenu)
3576 ? "&Bounce Thread" : "&Bounce";
3577 view_index_popup[n++].data.val = 'B';
3580 view_index_popup[n].type = tQueue;
3581 view_index_popup[n].label.style = lNormal;
3582 view_index_popup[n].label.string = "&Take Addresses";
3583 view_index_popup[n++].data.val = 'T';
3585 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3586 view_index_popup[n].type = tQueue;
3587 view_index_popup[n].label.style = lNormal;
3588 view_index_popup[n].label.string = "[Un]Select Current";
3589 view_index_popup[n++].data.val = ':';
3593 view_index_popup[n].type = tQueue;
3594 view_index_popup[n].label.style = lNormal;
3595 view_index_popup[n].label.string = "&WhereIs";
3596 view_index_popup[n++].data.val = 'W';
3598 view_index_popup[n++].type = tSeparator;
3601 if(km == &simple_index_keymenu){
3602 view_index_popup[n].type = tQueue;
3603 view_index_popup[n].label.style = lNormal;
3604 view_index_popup[n].label.string = "&Exit Select";
3605 view_index_popup[n++].data.val = 'E';
3607 else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
3608 view_index_popup[n].type = tQueue;
3609 view_index_popup[n].label.style = lNormal;
3610 view_index_popup[n].label.string = "Thread Index";
3611 view_index_popup[n++].data.val = '<';
3613 else{
3614 view_index_popup[n].type = tQueue;
3615 view_index_popup[n].label.style = lNormal;
3616 view_index_popup[n].label.string = "Folder &List";
3617 view_index_popup[n++].data.val = '<';
3620 view_index_popup[n].type = tQueue;
3621 view_index_popup[n].label.style = lNormal;
3622 view_index_popup[n].label.string = "&Main Menu";
3623 view_index_popup[n++].data.val = 'M';
3625 view_index_popup[n].type = tTail;
3627 if(mswin_popup(view_index_popup) == view_in_new_wind_index
3628 && view_in_new_wind_index >= 0)
3629 view_in_new_window();
3633 char *
3634 pcpine_help_index(title)
3635 char *title;
3638 * Title is size 256 in pico. Put in args.
3640 if(title)
3641 strncpy(title, "Alpine MESSAGE INDEX Help", 256);
3643 return(pcpine_help(h_mail_index));
3646 char *
3647 pcpine_help_index_simple(title)
3648 char *title;
3651 * Title is size 256 in pico. Put in args.
3653 if(title)
3654 strncpy(title, "Alpine SELECT MESSAGE Help", 256);
3656 return(pcpine_help(h_simple_index));
3660 #include "../pico/osdep/mswin_tw.h"
3663 void
3664 view_in_new_window(void)
3666 char title[GETTEXT_TITLELEN+1];
3667 void *text;
3668 long len;
3669 int format;
3670 MSWIN_TEXTWINDOW *mswin_tw = NULL;
3672 /* Launch text in alt window. */
3673 if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
3674 if(format == GETTEXT_TEXT)
3675 mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
3676 NULL, MSWIN_DT_USEALTWINDOW);
3677 else if(format == GETTEXT_LINES)
3678 mswin_tw = mswin_displaytext(title, NULL, 0, text,
3679 NULL, MSWIN_DT_USEALTWINDOW);
3681 if(mswin_tw != NULL)
3682 mswin_set_readonly(mswin_tw, FALSE);
3686 #endif /* _WINDOWS */