* Update configure script according to previous change to configure.ac
[alpine.git] / alpine / mailindx.c
blob70926d53f8a136a070dc7576dff2e45aa2c3815a
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "mailindx.h"
17 #include "mailcmd.h"
18 #include "status.h"
19 #include "context.h"
20 #include "keymenu.h"
21 #include "alpine.h"
22 #include "help.h"
23 #include "radio.h"
24 #include "titlebar.h"
25 #include "../pith/flag.h"
26 #include "../pith/newmail.h"
27 #include "../pith/thread.h"
28 #include "../pith/conf.h"
29 #include "../pith/msgno.h"
30 #include "../pith/icache.h"
31 #include "../pith/state.h"
32 #include "../pith/bitmap.h"
33 #include "../pith/news.h"
34 #include "../pith/strlst.h"
35 #include "../pith/sequence.h"
36 #include "../pith/sort.h"
37 #include "../pith/hist.h"
38 #include "../pith/busy.h"
39 #include "../pith/signal.h"
42 struct save_thrdinfo {
43 ICE_S *(*format_index_line)(INDEXDATA_S *);
44 void (*setup_header_widths)(MAILSTREAM *);
45 unsigned viewing_a_thread:1;
49 static OtherMenu what_keymenu = FirstMenu;
51 struct index_state *current_index_state = NULL;
55 * Internal prototypes
57 void index_index_screen(struct pine *);
58 void thread_index_screen(struct pine *);
59 int update_index(struct pine *, struct index_state *);
60 int index_scroll_up(long);
61 int index_scroll_down(long);
62 int index_scroll_to_pos(long);
63 long top_ent_calc(MAILSTREAM *, MSGNO_S *, long, long);
64 void reset_index_border(void);
65 void redraw_index_body(void);
66 int paint_index_line(ICE_S *, int, long, IndexColType, IndexColType, IndexColType,
67 struct entry_state *, int, int);
68 void pine_imap_envelope(MAILSTREAM *, unsigned long, ENVELOPE *);
69 void index_search(struct pine *, MAILSTREAM *, int, MSGNO_S *);
70 #ifdef _WINDOWS
71 int index_scroll_callback(int,long);
72 int index_gettext_callback(char *, size_t, void **, long *, int *);
73 void index_popup(IndexType style, MAILSTREAM *, MSGNO_S *, int);
74 char *pcpine_help_index(char *);
75 char *pcpine_help_index_simple(char *);
76 #endif
80 /*----------------------------------------------------------------------
83 ----*/
84 struct key_menu *
85 do_index_border(CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap,
86 IndexType style, int *which_keys, int flags)
88 struct key_menu *km = (style == ThreadIndex)
89 ? &thread_keymenu
90 : (ps_global->mail_stream != stream)
91 ? &simple_index_keymenu
92 : &index_keymenu;
94 if(flags & INDX_CLEAR)
95 ClearScreen();
97 if(flags & INDX_HEADER)
98 set_titlebar((style == ThreadIndex)
99 /* TRANSLATORS: these are some screen titles */
100 ? _("THREAD INDEX")
101 : (stream == ps_global->mail_stream)
102 ? (style == MsgIndex || style == MultiMsgIndex)
103 ? _("MESSAGE INDEX")
104 : _("ZOOMED MESSAGE INDEX")
105 : (!strcmp(folder, INTERRUPTED_MAIL))
106 ? _("COMPOSE: SELECT INTERRUPTED")
107 : (ps_global->VAR_FORM_FOLDER
108 && !strcmp(ps_global->VAR_FORM_FOLDER, folder))
109 ? _("COMPOSE: SELECT FORM LETTER")
110 : _("COMPOSE: SELECT POSTPONED"),
111 stream, cntxt, folder, msgmap, 1,
112 (style == ThreadIndex) ? ThrdIndex
113 : (THREADING()
114 && sp_viewing_a_thread(stream))
115 ? ThrdMsgNum
116 : MessageNumber,
117 0, 0, NULL);
119 if(flags & INDX_FOOTER) {
120 bitmap_t bitmap;
121 int cmd;
123 setbitmap(bitmap);
125 if(km == &index_keymenu){
126 if(THREADING() && sp_viewing_a_thread(stream)){
127 menu_init_binding(km, '<', MC_THRDINDX, "<",
128 N_("ThrdIndex"), BACK_KEY);
129 menu_add_binding(km, ',', MC_THRDINDX);
131 else{
132 menu_init_binding(km, '<', MC_FOLDERS, "<",
133 N_("FldrList"), BACK_KEY);
134 menu_add_binding(km, ',', MC_FOLDERS);
136 if(F_OFF(F_ENABLE_PIPE,ps_global))
137 clrbitn(VIEW_PIPE_KEY, bitmap); /* always clear for DOS */
138 if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
139 clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
140 if(F_OFF(F_ENABLE_BOUNCE,ps_global))
141 clrbitn(BOUNCE_KEY, bitmap);
142 if(F_OFF(F_ENABLE_FLAG,ps_global))
143 clrbitn(FLAG_KEY, bitmap);
144 if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
145 clrbitn(SELECT_KEY, bitmap);
146 clrbitn(APPLY_KEY, bitmap);
147 clrbitn(SELCUR_KEY, bitmap);
148 if(style != ZoomIndex)
149 clrbitn(ZOOM_KEY, bitmap);
153 if(style == MultiMsgIndex){
154 clrbitn(PREVM_KEY, bitmap);
155 clrbitn(NEXTM_KEY, bitmap);
159 if(km == &index_keymenu || km == &thread_keymenu){
160 if(IS_NEWS(stream)){
161 km->keys[EXCLUDE_KEY].label = N_("eXclude");
162 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_NONE);
164 else {
165 clrbitn(UNEXCLUDE_KEY, bitmap);
166 km->keys[EXCLUDE_KEY].label = N_("eXpunge");
167 KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_EXPUNGE);
171 if(km != &simple_index_keymenu && !THRD_COLLAPSE_ENABLE())
172 clrbitn(COLLAPSE_KEY, bitmap);
174 menu_clear_binding(km, KEY_LEFT);
175 menu_clear_binding(km, KEY_RIGHT);
176 if(F_ON(F_ARROW_NAV, ps_global)){
177 if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
178 menu_add_binding(km, '<', cmd);
179 menu_add_binding(km, KEY_LEFT, cmd);
182 if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
183 menu_add_binding(km, '>', cmd);
184 menu_add_binding(km, KEY_RIGHT, cmd);
188 if(menu_binding_index(km, MC_JUMP) >= 0){
189 for(cmd = 0; cmd < 10; cmd++)
190 if(F_ON(F_ENABLE_JUMP, ps_global))
191 (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
192 else
193 (void) menu_clear_binding(km, '0' + cmd);
196 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
197 1-FOOTER_ROWS(ps_global), 0, what_keymenu);
198 what_keymenu = SameMenu;
199 if(which_keys)
200 *which_keys = km->which; /* pass back to caller */
203 return(km);
208 /*----------------------------------------------------------------------
209 Main loop executing commands for the mail index screen
211 Args: state -- the pine_state structure for next/prev screen pointers
212 and to pass to the index manager...
213 ----*/
215 void
216 mail_index_screen(struct pine *state)
218 if(!state->mail_stream) {
219 q_status_message(SM_ORDER, 0, 3, _("No folder is currently open"));
220 state->prev_screen = mail_index_screen;
221 state->next_screen = main_menu_screen;
222 return;
225 state->prev_screen = mail_index_screen;
226 state->next_screen = SCREEN_FUN_NULL;
228 if(THRD_AUTO_VIEW()
229 && sp_viewing_a_thread(state->mail_stream)
230 && state->view_skipped_index
231 && unview_thread(state, state->mail_stream, state->msgmap)){
232 state->next_screen = mail_index_screen;
233 state->view_skipped_index = 0;
234 state->mangled_screen = 1;
237 adjust_cur_to_visible(state->mail_stream, state->msgmap);
239 if(THRD_INDX())
240 thread_index_screen(state);
241 else
242 index_index_screen(state);
246 void
247 index_index_screen(struct pine *state)
249 dprint((1, "\n\n ---- MAIL INDEX ----\n"));
251 setup_for_index_index_screen();
253 index_lister(state, state->context_current, state->cur_folder,
254 state->mail_stream, state->msgmap);
258 void
259 thread_index_screen(struct pine *state)
261 dprint((1, "\n\n ---- THREAD INDEX ----\n"));
263 setup_for_thread_index_screen();
265 index_lister(state, state->context_current, state->cur_folder,
266 state->mail_stream, state->msgmap);
270 void *
271 stop_threading_temporarily(void)
273 struct save_thrdinfo *ti;
275 ps_global->turn_off_threading_temporarily = 1;
277 ti = (struct save_thrdinfo *) fs_get(sizeof(*ti));
278 ti->format_index_line = format_index_line;
279 ti->setup_header_widths = setup_header_widths;
280 ti->viewing_a_thread = sp_viewing_a_thread(ps_global->mail_stream);
282 setup_for_index_index_screen();
284 return((void *) ti);
288 void
289 restore_threading(void **p)
291 struct save_thrdinfo *ti;
293 ps_global->turn_off_threading_temporarily = 0;
295 if(p && *p){
296 ti = (struct save_thrdinfo *) (*p);
297 format_index_line = ti->format_index_line;
298 setup_header_widths = ti->setup_header_widths;
299 sp_set_viewing_a_thread(ps_global->mail_stream, ti->viewing_a_thread);
301 fs_give(p);
306 /*----------------------------------------------------------------------
307 Main loop executing commands for the mail index screen
309 Args: state -- pine_state structure for display flags and such
310 msgmap -- c-client/pine message number mapping struct
311 ----*/
314 index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap)
316 UCS ch;
317 int cmd, which_keys, force,
318 cur_row, cur_col = 0, km_popped, paint_status = 0;
319 static int old_day = -1;
320 long i, j, k, old_max_msgno;
321 long lflagged, old_lflagged = 0L;
322 char *utf8str;
323 IndexType style, old_style = MsgIndex;
324 struct index_state id;
325 struct key_menu *km = NULL;
328 dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
330 ch = 'x'; /* For displaying msg 1st time thru */
331 force = 0;
332 km_popped = 0;
333 state->mangled_screen = 1;
334 what_keymenu = FirstMenu;
335 old_max_msgno = mn_get_total(msgmap);
336 memset((void *)&id, 0, sizeof(struct index_state));
337 current_index_state = &id;
338 id.msgmap = msgmap;
339 if(msgmap->top != 0L)
340 id.msg_at_top = msgmap->top;
342 id.stream = stream;
343 set_need_format_setup(stream);
345 while (1) {
346 ps_global->user_says_cancel = 0;
348 if(km_popped){
349 km_popped--;
350 if(km_popped == 0){
351 clearfooter(state);
352 if(!state->mangled_body
353 && id.entry_state
354 && id.lines_per_page > 1){
355 id.entry_state[id.lines_per_page-2].id = 0;
356 id.entry_state[id.lines_per_page-1].id = 0;
358 else
359 state->mangled_body = 1;
363 /*------- Check for new mail -------*/
364 new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
365 force = 0; /* may not need to next time around */
368 * If the width of the message number field in the display changes
369 * we need to flush the cache and redraw. When the cache is cleared
370 * the widths are recalculated, taking into account the max msgno.
373 if(format_includes_msgno(stream) &&
374 ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
375 || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
376 || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
377 clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
378 state->mangled_body = 1;
381 old_max_msgno = mn_get_total(msgmap);
384 * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
385 * then when the day changes the date column will change. All of the
386 * Today's will become Yesterday's at midnight. So we have to
387 * clear the cache at midnight.
389 if(format_includes_smartdate(stream)){
390 char db[200];
391 struct date nnow;
393 rfc822_date(db);
394 parse_date(db, &nnow);
395 if(old_day != -1 && nnow.day != old_day){
396 clear_index_cache(stream, 0);
397 state->mangled_body = 1;
400 old_day = nnow.day;
403 if(streams_died())
404 state->mangled_header = 1;
406 if(state->mangled_screen){
407 state->mangled_header = 1;
408 state->mangled_body = 1;
409 state->mangled_footer = 1;
410 state->mangled_screen = 0;
414 * events may have occurred that require us to shift from
415 * mode to another...
417 style = THRD_INDX()
418 ? ThreadIndex
419 : (any_lflagged(msgmap, MN_HIDE))
420 ? ZoomIndex
421 : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
422 lflagged = any_lflagged(msgmap, MN_HIDE);
423 if(style != old_style || lflagged < old_lflagged){
424 state->mangled_header = 1;
425 state->mangled_footer = 1;
426 old_style = style;
427 old_lflagged = lflagged;
428 if(!(style == ThreadIndex || old_style == ThreadIndex))
429 id.msg_at_top = 0L;
432 /*------------ Update the title bar -----------*/
433 if(state->mangled_header) {
434 km = do_index_border(cntxt, folder, stream, msgmap,
435 style, NULL, INDX_HEADER);
436 state->mangled_header = 0;
437 paint_status = 0;
439 else if(mn_get_total(msgmap) > 0) {
440 update_titlebar_message();
442 * If flags aren't available to update the status,
443 * defer it until after all the fetches associated
444 * with building index lines are done (no extra rtts!)...
446 paint_status = !update_titlebar_status();
449 current_index_state = &id;
451 /*------------ draw the index body ---------------*/
452 cur_row = update_index(state, &id);
453 if(F_OFF(F_SHOW_CURSOR, state)){
454 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
455 cur_col = 0;
457 else if(id.status_col >= 0)
458 cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
460 ps_global->redrawer = redraw_index_body;
462 if(paint_status)
463 (void) update_titlebar_status();
465 /*------------ draw the footer/key menus ---------------*/
466 if(state->mangled_footer) {
467 if(!state->painted_footer_on_startup){
468 if(km_popped){
469 FOOTER_ROWS(state) = 3;
470 clearfooter(state);
473 km = do_index_border(cntxt, folder, stream, msgmap, style,
474 &which_keys, INDX_FOOTER);
475 if(km_popped){
476 FOOTER_ROWS(state) = 1;
477 mark_keymenu_dirty();
481 state->mangled_footer = 0;
484 state->painted_body_on_startup = 0;
485 state->painted_footer_on_startup = 0;
487 /*-- Display any queued message (eg, new mail, command result --*/
488 if(km_popped){
489 FOOTER_ROWS(state) = 3;
490 mark_status_unknown();
493 display_message(ch);
494 if(km_popped){
495 FOOTER_ROWS(state) = 1;
496 mark_status_unknown();
499 if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
500 cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
503 cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
504 MoveCursor(cur_row, cur_col);
506 /* Let read_command do the fflush(stdout) */
508 /*---------- Read command and validate it ----------------*/
509 #ifdef MOUSE
510 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
511 register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
512 state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
513 state->ttyo->screen_cols);
514 #endif
515 #ifdef _WINDOWS
516 mswin_setscrollcallback (index_scroll_callback);
517 mswin_sethelptextcallback((stream == state->mail_stream)
518 ? pcpine_help_index
519 : pcpine_help_index_simple);
520 mswin_setviewinwindcallback(view_in_new_window);
521 #endif
522 ch = READ_COMMAND(&utf8str);
523 #ifdef MOUSE
524 clear_mfunc(mouse_in_content);
525 #endif
526 #ifdef _WINDOWS
527 mswin_setscrollcallback(NULL);
528 mswin_sethelptextcallback(NULL);
529 mswin_setviewinwindcallback(NULL);
530 #endif
532 cmd = menu_command(ch, km);
534 if(km_popped)
535 switch(cmd){
536 case MC_NONE :
537 case MC_OTHER :
538 case MC_RESIZE :
539 case MC_REPAINT :
540 km_popped++;
541 break;
543 default:
544 clearfooter(state);
545 break;
548 /*----------- Execute the command ------------------*/
549 switch(cmd){
551 /*---------- Roll keymenu ----------*/
552 case MC_OTHER :
553 if(F_OFF(F_USE_FK, ps_global))
554 warn_other_cmds();
556 what_keymenu = NextMenu;
557 state->mangled_footer = 1;
558 break;
561 /*---------- Scroll line up ----------*/
562 case MC_CHARUP :
563 (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
564 (style == MsgIndex
565 || style == MultiMsgIndex
566 || style == ZoomIndex)
567 ? MsgIndx
568 : (style == ThreadIndex)
569 ? ThrdIndx
570 : View,
571 &force);
572 if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
573 index_scroll_up(1L);
575 break;
578 /*---------- Scroll line down ----------*/
579 case MC_CHARDOWN :
581 * Special Page framing handling here. If we
582 * did something that should scroll-by-a-line, frame
583 * the page by hand here rather than leave it to the
584 * page-by-page framing in update_index()...
586 (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
587 (style == MsgIndex
588 || style == MultiMsgIndex
589 || style == ZoomIndex)
590 ? MsgIndx
591 : (style == ThreadIndex)
592 ? ThrdIndx
593 : View,
594 &force);
595 for(j = 0L, k = i = id.msg_at_top; ; i++){
596 if(!msgline_hidden(stream, msgmap, i, 0)){
597 k = i;
598 if(j++ >= id.lines_per_page)
599 break;
602 if(i >= mn_get_total(msgmap)){
603 k = 0L; /* don't scroll */
604 break;
608 if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
609 index_scroll_down(1L);
611 break;
614 /*---------- Scroll page up ----------*/
615 case MC_PAGEUP :
616 j = -1L;
617 for(k = i = id.msg_at_top; ; i--){
618 if(!msgline_hidden(stream, msgmap, i, 0)){
619 k = i;
620 if(++j >= id.lines_per_page){
621 if((id.msg_at_top = i) == 1L)
622 q_status_message(SM_ORDER, 0, 1, _("First Index page"));
624 break;
628 if(i <= 1L){
629 if((!THREADING() && mn_get_cur(msgmap) == 1L)
630 || (THREADING()
631 && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
632 stream,
634 FSF_SKIP_CHID)))
635 q_status_message(SM_ORDER, 0, 1,
636 _("Already at start of Index"));
638 break;
642 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
643 mn_set_cur(msgmap, k);
645 break;
648 /*---------- Scroll page forward ----------*/
649 case MC_PAGEDN :
650 j = -1L;
651 for(k = i = id.msg_at_top; ; i++){
652 if(!msgline_hidden(stream, msgmap, i, 0)){
653 k = i;
654 if(++j >= id.lines_per_page){
655 if(i+id.lines_per_page > mn_get_total(msgmap))
656 q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
658 id.msg_at_top = i;
659 break;
663 if(i >= mn_get_total(msgmap)){
664 if(mn_get_cur(msgmap) == k)
665 q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
667 break;
671 if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
672 mn_set_cur(msgmap, k);
674 break;
677 /*---------- Scroll to first page ----------*/
678 case MC_HOMEKEY :
679 if((mn_get_total(msgmap) > 0L)
680 && (mn_total_cur(msgmap) <= 1L)){
681 long cur_msg = mn_get_cur(msgmap), selected;
683 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
684 do {
685 selected = cur_msg;
686 mn_dec_cur(stream, msgmap, MH_NONE);
687 cur_msg = mn_get_cur(msgmap);
689 while(selected != cur_msg);
691 else
692 cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
693 mn_set_cur(msgmap, cur_msg);
694 q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
696 break;
698 /*---------- Scroll to last page ----------*/
699 case MC_ENDKEY :
700 if((mn_get_total(msgmap) > 0L)
701 && (mn_total_cur(msgmap) <= 1L)){
702 long cur_msg = mn_get_cur(msgmap), selected;
704 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
705 do {
706 selected = cur_msg;
707 mn_inc_cur(stream, msgmap, MH_NONE);
708 cur_msg = mn_get_cur(msgmap);
710 while(selected != cur_msg);
712 else
713 cur_msg = mn_get_total(msgmap);
714 mn_set_cur(msgmap, cur_msg);
715 q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
717 break;
719 /*---------- Search (where is command) ----------*/
720 case MC_WHEREIS :
721 index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
722 state->mangled_footer = 1;
723 break;
726 /*-------------- jump command -------------*/
727 /* NOTE: preempt the process_cmd() version because
728 * we need to get at the number..
730 case MC_JUMP :
731 j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
732 (style == ThreadIndex) ? ThrdIndx : MsgIndx);
733 if(j > 0L){
734 if(style == ThreadIndex){
735 PINETHRD_S *thrd;
737 thrd = find_thread_by_number(stream, msgmap, j, NULL);
739 if(thrd && thrd->rawno)
740 mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
742 else{
743 /* jump to message */
744 if(mn_total_cur(msgmap) > 1L){
745 mn_reset_cur(msgmap, j);
747 else{
748 mn_set_cur(msgmap, j);
752 id.msg_at_top = 0L;
755 state->mangled_footer = 1;
756 break;
759 case MC_VIEW_ENTRY : /* only happens in thread index */
762 * If the feature F_THRD_AUTO_VIEW is turned on and there
763 * is only one message in the thread, then we skip the index
764 * view of the thread and go straight to the message view.
766 view_a_thread:
767 if(THRD_AUTO_VIEW() && style == ThreadIndex){
768 PINETHRD_S *thrd;
770 thrd = fetch_thread(stream,
771 mn_m2raw(msgmap, mn_get_cur(msgmap)));
772 if(thrd
773 && (count_lflags_in_thread(stream, thrd,
774 msgmap, MN_NONE) == 1)){
775 if(view_thread(state, stream, msgmap, 1)){
776 state->view_skipped_index = 1;
777 cmd = MC_VIEW_TEXT;
778 goto do_the_default;
783 if(view_thread(state, stream, msgmap, 1)){
784 ps_global->next_screen = mail_index_screen;
785 ps_global->redrawer = NULL;
786 current_index_state = NULL;
787 if(id.entry_state)
788 fs_give((void **)&(id.entry_state));
790 return(0);
793 break;
796 case MC_THRDINDX :
797 if(any_lflagged(msgmap, MN_SLCT)){
798 PINETHRD_S *thrd, *topthrd;
799 for(i = 1L; i > 0L && i <= mn_get_total(msgmap);){
800 thrd = fetch_thread(stream, i);
801 if(thrd && thrd->top)
802 topthrd = fetch_thread(stream, thrd->top);
803 else
804 topthrd = NULL;
805 if(topthrd){
806 set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1);
807 set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0);
808 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0);
809 set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1);
811 i = thrd->nextthd;
814 msgmap->top = msgmap->top_after_thrd;
815 if(unview_thread(state, stream, msgmap)){
816 state->next_screen = mail_index_screen;
817 state->view_skipped_index = 0;
818 state->mangled_screen = 1;
819 ps_global->redrawer = NULL;
820 current_index_state = NULL;
821 if(id.entry_state)
822 fs_give((void **)&(id.entry_state));
824 return(0);
827 break;
830 #ifdef MOUSE
831 case MC_MOUSE:
833 MOUSEPRESS mp;
834 int new_cur = 0;
836 mouse_get_last (NULL, &mp);
837 mp.row -= 2;
839 for(i = id.msg_at_top;
840 mp.row >= 0 && i <= mn_get_total(msgmap);
841 i++)
842 if(!msgline_hidden(stream, msgmap, i, 0)){
843 mp.row--;
844 new_cur = i;
847 if(mn_get_total(msgmap) && mp.row < 0){
848 switch(mp.button){
849 case M_BUTTON_LEFT :
850 if(mn_total_cur(msgmap) == 1L)
851 mn_set_cur(msgmap, new_cur);
853 if(mp.flags & M_KEY_CONTROL){
854 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
855 (void) select_by_current(state, msgmap, MsgIndx);
858 else if(!(mp.flags & M_KEY_SHIFT)){
859 if (THREADING()
860 && mp.col >= 0
861 && mp.col == id.plus_col
862 && style != ThreadIndex){
863 collapse_or_expand(state, stream, msgmap,
864 mn_get_cur(msgmap));
866 else if (mp.doubleclick){
867 if(mp.button == M_BUTTON_LEFT){
868 if(stream == state->mail_stream){
869 if(THRD_INDX()){
870 cmd = MC_VIEW_ENTRY;
871 goto view_a_thread;
873 else{
874 cmd = MC_VIEW_TEXT;
875 goto do_the_default;
879 ps_global->redrawer = NULL;
880 current_index_state = NULL;
881 if(id.entry_state)
882 fs_give((void **)&(id.entry_state));
884 return(0);
889 break;
891 case M_BUTTON_MIDDLE:
892 break;
894 case M_BUTTON_RIGHT :
895 #ifdef _WINDOWS
896 if (!mp.doubleclick){
897 if(mn_total_cur(msgmap) == 1L)
898 mn_set_cur(msgmap, new_cur);
900 cur_row = update_index(state, &id);
902 index_popup(style, stream, msgmap, TRUE);
904 #endif
905 break;
908 else{
909 switch(mp.button){
910 case M_BUTTON_LEFT :
911 break;
913 case M_BUTTON_MIDDLE :
914 break;
916 case M_BUTTON_RIGHT :
917 #ifdef _WINDOWS
918 index_popup(style, stream, msgmap, FALSE);
919 #endif
920 break;
925 break;
926 #endif /* MOUSE */
928 /*---------- Resize ----------*/
929 case MC_RESIZE:
931 * If we were smarter we could do the
932 * IC_CLEAR_WIDTHS_DONE trick here. The problem is
933 * that entire columns of the format can go away or
934 * appear because the width gets smaller or larger,
935 * so in that case we need to re-do. If we could tell
936 * when that happened or not we could set the flag
937 * selectively.
939 clear_index_cache(stream, 0);
940 reset_index_border();
941 break;
943 case MC_QUOTA:
944 cmd_quota(state);
946 /*---------- Redraw ----------*/
947 case MC_REPAINT :
948 force = 1; /* check for new mail! */
949 reset_index_border();
950 break;
953 /*---------- No op command ----------*/
954 case MC_NONE :
955 break; /* no op check for new mail */
958 /*--------- keystroke not bound to command --------*/
959 case MC_CHARRIGHT :
960 case MC_CHARLEFT :
961 case MC_GOTOBOL :
962 case MC_GOTOEOL :
963 case MC_UNKNOWN :
964 if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
965 q_status_message(SM_ORDER, 0, 1, "Already in Index");
966 else
967 bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
969 break;
972 case MC_COLLAPSE :
973 thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
974 break;
976 case MC_DELETE :
977 case MC_UNDELETE :
978 case MC_REPLY :
979 case MC_FORWARD :
980 case MC_TAKE :
981 case MC_SAVE :
982 case MC_EXPORT :
983 case MC_BOUNCE :
984 case MC_PIPE :
985 case MC_FLAG :
986 case MC_SELCUR :
987 { int collapsed = 0;
988 unsigned long rawno;
989 PINETHRD_S *thrd = NULL;
991 if(THREADING()){
992 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
993 if(rawno)
994 thrd = fetch_thread(stream, rawno);
996 collapsed = thrd && thrd->next
997 && get_lflag(stream, NULL, rawno, MN_COLL);
1000 if(collapsed){
1001 thread_command(state, stream, msgmap,
1002 ch, -FOOTER_ROWS(state));
1003 /* increment current */
1004 if(cmd == MC_DELETE){
1005 advance_cur_after_delete(state, stream, msgmap,
1006 (style == MsgIndex
1007 || style == MultiMsgIndex
1008 || style == ZoomIndex)
1009 ? MsgIndx
1010 : (style == ThreadIndex)
1011 ? ThrdIndx
1012 : View);
1014 else if((cmd == MC_SELCUR
1015 && (state->ugly_consider_advancing_bit
1016 || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
1017 || (state->ugly_consider_advancing_bit
1018 && cmd == MC_SAVE
1019 && F_ON(F_SAVE_ADVANCES, state))){
1020 mn_inc_cur(stream, msgmap, MH_NONE);
1023 else
1024 goto do_the_default;
1027 break;
1030 case MC_UTF8:
1031 bogus_utf8_command(utf8str, NULL);
1032 break;
1035 /*---------- First HELP command with menu hidden ----------*/
1036 case MC_HELP :
1037 if(FOOTER_ROWS(state) == 1 && km_popped == 0){
1038 km_popped = 2;
1039 mark_status_unknown();
1040 mark_keymenu_dirty();
1041 state->mangled_footer = 1;
1042 break;
1044 /* else fall thru to normal default */
1047 /*---------- Default -- all other command ----------*/
1048 default:
1049 do_the_default:
1050 if(stream == state->mail_stream){
1051 msgmap->top = id.msg_at_top;
1052 process_cmd(state, stream, msgmap, cmd,
1053 (style == MsgIndex
1054 || style == MultiMsgIndex
1055 || style == ZoomIndex)
1056 ? MsgIndx
1057 : (style == ThreadIndex)
1058 ? ThrdIndx
1059 : View,
1060 &force);
1061 if(state->next_screen != SCREEN_FUN_NULL){
1062 ps_global->redrawer = NULL;
1063 current_index_state = NULL;
1064 if(id.entry_state)
1065 fs_give((void **)&(id.entry_state));
1067 return(0);
1069 else{
1070 if(stream != state->mail_stream){
1072 * Must have had an failed open. repair our
1073 * pointers...
1075 id.stream = stream = state->mail_stream;
1076 id.msgmap = msgmap = state->msgmap;
1079 current_index_state = &id;
1081 if(cmd == MC_ZOOM && THRD_INDX())
1082 id.msg_at_top = 0L;
1085 else{ /* special processing */
1086 switch(cmd){
1087 case MC_HELP :
1088 helper(h_simple_index,
1089 (!strcmp(folder, INTERRUPTED_MAIL))
1090 ? _("HELP FOR SELECTING INTERRUPTED MSG")
1091 : _("HELP FOR SELECTING POSTPONED MSG"),
1092 HLPD_SIMPLE);
1093 state->mangled_screen = 1;
1094 break;
1096 case MC_DELETE : /* delete */
1097 dprint((3, "Special delete: msg %s\n",
1098 long2string(mn_get_cur(msgmap))));
1100 long raw, t;
1101 int del = 0;
1102 MESSAGECACHE *mc;
1104 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1105 if(raw > 0L && stream
1106 && raw <= stream->nmsgs
1107 && (mc = mail_elt(stream, raw))
1108 && !mc->deleted){
1109 if((t = mn_get_cur(msgmap)) > 0L)
1110 clear_index_cache_ent(stream, t, 0);
1112 mail_setflag(stream,long2string(raw),"\\DELETED");
1113 update_titlebar_status();
1114 del++;
1117 q_status_message1(SM_ORDER, 0, 1,
1118 del ? _("Message %s deleted") : _("Message %s already deleted"),
1119 long2string(mn_get_cur(msgmap)));
1122 break;
1124 case MC_UNDELETE : /* UNdelete */
1125 dprint((3, "Special UNdelete: msg %s\n",
1126 long2string(mn_get_cur(msgmap))));
1128 long raw, t;
1129 int del = 0;
1130 MESSAGECACHE *mc;
1132 raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
1133 if(raw > 0L && stream
1134 && raw <= stream->nmsgs
1135 && (mc = mail_elt(stream, raw))
1136 && mc->deleted){
1137 if((t = mn_get_cur(msgmap)) > 0L)
1138 clear_index_cache_ent(stream, t, 0);
1140 mail_clearflag(stream, long2string(raw),
1141 "\\DELETED");
1142 update_titlebar_status();
1143 del++;
1146 q_status_message1(SM_ORDER, 0, 1,
1147 del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
1148 long2string(mn_get_cur(msgmap)));
1151 break;
1153 case MC_EXIT : /* exit */
1154 ps_global->redrawer = NULL;
1155 current_index_state = NULL;
1156 if(id.entry_state)
1157 fs_give((void **)&(id.entry_state));
1159 return(1);
1161 case MC_SELECT : /* select */
1162 ps_global->redrawer = NULL;
1163 current_index_state = NULL;
1164 if(id.entry_state)
1165 fs_give((void **)&(id.entry_state));
1167 return(0);
1169 case MC_PREVITEM : /* previous */
1170 mn_dec_cur(stream, msgmap, MH_NONE);
1171 break;
1173 case MC_NEXTITEM : /* next */
1174 mn_inc_cur(stream, msgmap, MH_NONE);
1175 break;
1177 default :
1178 bogus_command(ch, NULL);
1179 break;
1182 } /* The big switch */
1183 } /* the BIG while loop! */
1188 /*----------------------------------------------------------------------
1189 Manage index body painting
1191 Args: state - pine struct containing selected message data
1192 index_state - struct describing what's currently displayed
1194 Returns: screen row number of first highlighted message
1196 The idea is pretty simple. Maintain an array of index line id's that
1197 are displayed and their hilited state. Decide what's to be displayed
1198 and update the screen appropriately. All index screen painting
1199 is done here. Pretty simple, huh?
1200 ----*/
1202 update_index(struct pine *state, struct index_state *screen)
1204 int i, retval = -1, row, already_fetched = 0;
1205 long n, visible;
1206 PINETHRD_S *thrd = NULL;
1207 int we_cancel = 0;
1209 dprint((7, "--update_index--\n"));
1211 if(!screen)
1212 return(-1);
1214 #ifdef _WINDOWS
1215 mswin_beginupdate();
1216 #endif
1218 /*---- reset the works if necessary ----*/
1219 if(state->mangled_body){
1220 ClearBody();
1221 if(screen->entry_state){
1222 fs_give((void **)&(screen->entry_state));
1223 screen->lines_per_page = 0;
1227 state->mangled_body = 0;
1229 /*---- make sure we have a place to write state ----*/
1230 if(screen->lines_per_page
1231 != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1232 - HEADER_ROWS(state))){
1233 i = screen->lines_per_page;
1234 screen->lines_per_page
1235 = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
1236 - HEADER_ROWS(state));
1237 if(!i){
1238 size_t len = screen->lines_per_page * sizeof(struct entry_state);
1239 screen->entry_state = (struct entry_state *) fs_get(len);
1241 else
1242 fs_resize((void **)&(screen->entry_state),
1243 (size_t)screen->lines_per_page);
1245 for(; i < screen->lines_per_page; i++) /* init new entries */
1246 memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
1249 /*---- figure out the first message on the display ----*/
1250 if(screen->msg_at_top < 1L
1251 || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
1252 screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
1253 screen->msg_at_top,
1254 screen->lines_per_page);
1256 else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1257 long i, j, k;
1259 /* scroll back a page at a time until current is displayed */
1260 while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
1261 for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
1262 i > 0L && j > 0L;
1263 j--)
1264 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1265 k = j;
1266 i--;
1269 if(i == screen->lines_per_page)
1270 break; /* can't scroll back ? */
1271 else
1272 screen->msg_at_top = k;
1275 else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
1276 + screen->lines_per_page){
1277 long i, j, k;
1279 while(1){
1280 for(i = screen->lines_per_page, j = k = screen->msg_at_top;
1281 j <= mn_get_total(screen->msgmap) && i > 0L;
1282 j++)
1283 if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
1284 k = j;
1285 i--;
1286 if(mn_get_cur(screen->msgmap) <= k)
1287 break;
1290 if(mn_get_cur(screen->msgmap) <= k)
1291 break;
1292 else{
1293 /* set msg_at_top to next displayed message */
1294 for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
1295 if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
1296 k = i;
1297 break;
1300 screen->msg_at_top = k;
1305 #ifdef _WINDOWS
1307 * Set scroll range and position. Note that message numbers start at 1
1308 * while scroll position starts at 0.
1311 if(THREADING() && sp_viewing_a_thread(screen->stream)
1312 && mn_get_total(screen->msgmap) > 1L){
1313 long x = 0L, range = 0L, lowest_numbered_msg;
1316 * We know that all visible messages in the thread are marked
1317 * with MN_CHID2.
1319 thrd = fetch_thread(screen->stream,
1320 mn_m2raw(screen->msgmap,
1321 mn_get_cur(screen->msgmap)));
1322 if(thrd && thrd->top && thrd->top != thrd->rawno)
1323 thrd = fetch_thread(screen->stream, thrd->top);
1325 if(thrd){
1326 if(mn_get_revsort(screen->msgmap)){
1327 n = mn_raw2m(screen->msgmap, thrd->rawno);
1328 while(n > 1L && get_lflag(screen->stream, screen->msgmap,
1329 n-1L, MN_CHID2))
1330 n--;
1332 lowest_numbered_msg = n;
1334 else
1335 lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
1338 if(thrd){
1339 n = lowest_numbered_msg;
1340 for(; n <= mn_get_total(screen->msgmap); n++){
1342 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1343 break;
1345 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
1346 range++;
1347 if(n < screen->msg_at_top)
1348 x++;
1353 scroll_setrange(screen->lines_per_page, range-1L);
1354 scroll_setpos(x);
1356 else if(THRD_INDX()){
1357 if(any_lflagged(screen->msgmap, MN_HIDE)){
1358 long x = 0L, range;
1360 range = screen->msgmap->visible_threads - 1L;
1361 scroll_setrange(screen->lines_per_page, range);
1362 if(range >= screen->lines_per_page){ /* else not needed */
1363 PINETHRD_S *topthrd;
1364 int thrddir;
1365 long xdir;
1367 /* find top of currently displayed top line */
1368 topthrd = fetch_thread(screen->stream,
1369 mn_m2raw(screen->msgmap,
1370 screen->msg_at_top));
1371 if(topthrd && topthrd->top != topthrd->rawno)
1372 topthrd = fetch_thread(screen->stream, topthrd->top);
1374 if(topthrd){
1376 * Split into two halves to speed up finding scroll pos.
1377 * It's tricky because the thread list always goes from
1378 * past to future but the thrdno's will be reversed if
1379 * the sort is reversed and of course the order on the
1380 * screen will be reversed.
1382 if((!mn_get_revsort(screen->msgmap)
1383 && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
1385 (mn_get_revsort(screen->msgmap)
1386 && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
1388 /* start with head of thread list */
1389 if(topthrd && topthrd->head)
1390 thrd = fetch_thread(screen->stream, topthrd->head);
1391 else
1392 thrd = NULL;
1394 thrddir = 1;
1396 else{
1397 long tailrawno;
1400 * Start with tail thread and work back.
1402 if(mn_get_revsort(screen->msgmap))
1403 tailrawno = mn_m2raw(screen->msgmap, 1L);
1404 else
1405 tailrawno = mn_m2raw(screen->msgmap,
1406 mn_get_total(screen->msgmap));
1408 thrd = fetch_thread(screen->stream, tailrawno);
1409 if(thrd && thrd->top && thrd->top != thrd->rawno)
1410 thrd = fetch_thread(screen->stream, thrd->top);
1412 thrddir = -1;
1416 * x is the scroll position. We try to use the fewest
1417 * number of steps to find it, so we start with either
1418 * the beginning or the end.
1420 if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
1421 x = 0L;
1422 xdir = 1L;
1424 else{
1425 x = range;
1426 xdir = -1L;
1429 while(thrd && thrd != topthrd){
1430 if(!msgline_hidden(screen->stream, screen->msgmap,
1431 mn_raw2m(screen->msgmap,thrd->rawno),
1433 x += xdir;
1435 if(thrddir > 0 && thrd->nextthd)
1436 thrd = fetch_thread(screen->stream, thrd->nextthd);
1437 else if(thrddir < 0 && thrd->prevthd)
1438 thrd = fetch_thread(screen->stream, thrd->prevthd);
1439 else
1440 thrd = NULL;
1444 scroll_setpos(x);
1447 else{
1449 * This works for forward or reverse sort because the thrdno's
1450 * will have been reversed.
1452 thrd = fetch_thread(screen->stream,
1453 mn_m2raw(screen->msgmap, screen->msg_at_top));
1454 if(thrd){
1455 scroll_setrange(screen->lines_per_page,
1456 screen->msgmap->max_thrdno - 1L);
1457 scroll_setpos(thrd->thrdno - 1L);
1461 else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
1462 long x, range;
1464 range = mn_get_total(screen->msgmap) - n - 1L;
1465 scroll_setrange(screen->lines_per_page, range);
1467 if(range >= screen->lines_per_page){ /* else not needed */
1468 if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
1469 for(n = 1, x = 0; n != screen->msg_at_top; n++)
1470 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1471 x++;
1473 else{
1474 for(n = mn_get_total(screen->msgmap), x = range;
1475 n != screen->msg_at_top; n--)
1476 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1477 x--;
1480 scroll_setpos(x);
1483 else{
1484 scroll_setrange(screen->lines_per_page,
1485 mn_get_total(screen->msgmap) - 1L);
1486 scroll_setpos(screen->msg_at_top - 1L);
1488 #endif
1491 * Set up c-client call back to tell us about IMAP envelope arrivals
1492 * Can't do it (easily) if single lines on the screen need information
1493 * about more than a single message before they can be drawn.
1495 if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
1496 && !(THREADING() && (sp_viewing_a_thread(screen->stream)
1497 || ps_global->thread_disp_style == THREAD_MUTTLIKE
1498 || any_lflagged(screen->msgmap, MN_COLL))))
1499 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
1501 if(THRD_INDX())
1502 visible = screen->msgmap->visible_threads;
1503 else if(THREADING() && sp_viewing_a_thread(screen->stream)){
1505 * We know that all visible messages in the thread are marked
1506 * with MN_CHID2.
1508 for(visible = 0L, n = screen->msg_at_top;
1509 visible < (int) screen->lines_per_page
1510 && n <= mn_get_total(screen->msgmap); n++){
1512 if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
1513 break;
1515 if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
1516 visible++;
1519 else
1520 visible = mn_get_total(screen->msgmap)
1521 - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
1523 /*---- march thru display lines, painting whatever is needed ----*/
1524 for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
1525 if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
1526 if(screen->entry_state[i].id != LINE_HASH_N){
1527 screen->entry_state[i].hilite = 0;
1528 screen->entry_state[i].bolded = 0;
1529 screen->entry_state[i].msgno = 0L;
1530 screen->entry_state[i].id = LINE_HASH_N;
1531 ClearLine(HEADER_ROWS(state) + i);
1534 else{
1535 ICE_S *ice;
1538 * This changes status_col as a side effect so it has to be
1539 * executed before next line.
1541 ice = build_header_line(state, screen->stream, screen->msgmap,
1542 n, &already_fetched);
1543 if(visible > 0L)
1544 visible--;
1546 if(THRD_INDX()){
1547 unsigned long rawno;
1549 rawno = mn_m2raw(screen->msgmap, n);
1550 if(rawno)
1551 thrd = fetch_thread(screen->stream, rawno);
1554 row = paint_index_line(ice, i,
1555 (THRD_INDX() && thrd) ? thrd->thrdno : n,
1556 screen->status_fld, screen->plus_fld,
1557 screen->arrow_fld, &screen->entry_state[i],
1558 mn_is_cur(screen->msgmap, n),
1559 THRD_INDX()
1560 ? (count_lflags_in_thread(screen->stream,
1561 thrd,
1562 screen->msgmap,
1563 MN_SLCT) > 0)
1564 : get_lflag(screen->stream, screen->msgmap,
1565 n, MN_SLCT));
1566 fflush(stdout);
1567 if(row && retval < 0)
1568 retval = row;
1571 /*--- increment n ---*/
1572 while((visible == -1L || visible > 0L)
1573 && ++n <= mn_get_total(screen->msgmap)
1574 && msgline_hidden(screen->stream, screen->msgmap, n, 0))
1578 if(we_cancel)
1579 cancel_busy_cue(-1);
1581 mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
1583 #ifdef _WINDOWS
1584 mswin_endupdate();
1585 #endif
1586 fflush(stdout);
1587 dprint((7, "--update_index done\n"));
1588 return(retval);
1593 /*----------------------------------------------------------------------
1594 Create a string summarizing the message header for index on screen
1596 Args: stream -- mail stream to fetch envelope info from
1597 msgmap -- message number to pine sort mapping
1598 msgno -- Message number to create line for
1600 Result: returns a malloced string
1601 saves string in a cache for next call for same header
1602 ----*/
1603 ICE_S *
1604 build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
1606 return(build_header_work(state, stream, msgmap, msgno,
1607 current_index_state->msg_at_top,
1608 current_index_state->lines_per_page,
1609 already_fetched));
1613 /*----------------------------------------------------------------------
1614 Paint the given index line
1617 Args: ice -- index cache entry
1618 line -- index line number on screen, starting at 0 for first
1619 visible line, 1, 2, ...
1620 msgno -- for painting the message number field
1621 sfld -- field type of the status field, which is
1622 where we'll put the X for selected if necessary
1623 pfld -- field type where the thread indicator symbol goes
1624 afld -- field type of column which corresponds to the
1625 index-format ARROW token
1626 entry -- cache used to help us decide whether or not we need to
1627 redraw the index line or if we can just leave it alone because
1628 we know it is already correct
1629 cur -- is this the current message?
1630 sel -- is this message in the selected set?
1632 Returns: screen row number if this is current message, else 0
1633 ----*/
1635 paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
1636 IndexColType pfld, IndexColType afld, struct entry_state *entry,
1637 int cur, int sel)
1639 COLOR_PAIR *lastc = NULL, *base_color = NULL;
1640 ICE_S *ice;
1641 IFIELD_S *ifield, *previfield = NULL;
1642 IELEM_S *ielem;
1643 int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
1644 int draw_whole_line = 0, draw_partial_line = 0;
1645 int n = MAX_SCREEN_COLS*6;
1646 char draw[MAX_SCREEN_COLS*6+1], *p;
1648 ice = (THRD_INDX() && argice) ? argice->tice : argice;
1650 /* This better not happen! */
1651 if(!ice){
1652 q_status_message3(SM_ORDER | SM_DING, 5, 5,
1653 "NULL ice in paint_index_line: %s, msgno=%s line=%s",
1654 THRD_INDX() ? "THRD_INDX" : "reg index",
1655 comatose(msgno), comatose(line));
1656 dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
1657 THRD_INDX() ? "THRD_INDX" : "reg index",
1658 msgno, line));
1659 return 0;
1662 if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
1663 entry->id = 0L;
1664 entry->msgno = 0L;
1665 draw_whole_line = 1;
1667 else if((cur != entry->hilite) || (sel != entry->bolded)
1668 || (ice->plus != entry->plus)){
1669 draw_partial_line = 1;
1672 if(draw_whole_line || draw_partial_line){
1674 if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
1676 memset(draw, 0, sizeof(draw));
1677 p = draw;
1679 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1681 /* space between fields */
1682 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
1683 *p++ = ' ';
1685 /* message number string is generated on the fly */
1686 if(ifield->ctype == iMessNo){
1687 ielem = ifield->ielem;
1688 if(ielem && ielem->datalen >= ifield->width){
1689 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1690 ielem->data[ifield->width] = '\0';
1691 ielem->data[ielem->datalen] = '\0';
1695 if(ifield->ctype == sfld){
1696 ielem = ifield->ielem;
1697 if(ielem && ielem->datalen > 0){
1698 if(draw_partial_line)
1699 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1701 if(ielem->datalen == 1){
1702 save_schar1 = ielem->data[0];
1703 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1704 if(draw_partial_line)
1705 Writechar(ielem->data[0], 0);
1707 if(ielem->next && ielem->next->datalen){
1708 save_schar2 = ielem->next->data[0];
1709 if(cur)
1710 ielem->next->data[0] = '>';
1712 if(draw_partial_line)
1713 Writechar(ielem->next->data[0], 0);
1716 else if(ielem->datalen > 1){
1717 save_schar1 = ielem->data[0];
1718 ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
1719 if(draw_partial_line)
1720 Writechar(ielem->data[0], 0);
1722 save_schar2 = ielem->data[1];
1723 if(cur){
1724 ielem->data[1] = '>';
1725 if(draw_partial_line)
1726 Writechar(ielem->data[1], 0);
1731 else if(ifield->ctype == afld){
1733 if(draw_partial_line){
1734 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1735 for(i = 0; i < ifield->width-1; i++)
1736 Writechar(cur ? '-' : ' ', 0);
1738 Writechar(cur ? '>' : ' ', 0);
1741 ielem = ifield->ielem;
1742 if(ielem && ielem->datalen >= ifield->width){
1743 for(i = 0; i < ifield->width-1; i++)
1744 ielem->data[i] = cur ? '-' : ' ';
1746 ielem->data[i] = cur ? '>' : ' ';
1749 else if(ifield->ctype == pfld){
1750 ielem = ifield->ielem;
1751 if(ielem && ielem->datalen > 0){
1752 save_pchar = ielem->data[0];
1753 ielem->data[0] = ice->plus;
1755 if(draw_partial_line){
1756 MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
1757 Writechar(ielem->data[0], 0);
1758 Writechar(' ', 0);
1763 for(ielem = ifield->ielem;
1764 ielem && ielem->print_format && p-draw < n;
1765 ielem = ielem->next){
1766 char *src;
1767 size_t bytes_added;
1769 src = ielem->data;
1770 bytes_added = utf8_pad_to_width(p, src,
1771 ((n+1)-(p-draw)) * sizeof(char),
1772 ielem->wid, ifield->leftadj);
1773 p += bytes_added;
1776 draw[n] = '\0';
1778 if(ifield->ctype == sfld){
1779 ielem = ifield->ielem;
1780 if(ielem && ielem->datalen > 0){
1781 if(ielem->datalen == 1){
1782 ielem->data[0] = save_schar1;
1783 if(ielem->next && ielem->next->datalen)
1784 ielem->next->data[0] = save_schar2;
1786 else if(ielem->datalen > 1){
1787 ielem->data[0] = save_schar1;
1788 ielem->data[1] = save_schar2;
1792 else if(ifield->ctype == afld){
1793 ielem = ifield->ielem;
1794 if(ielem && ielem->datalen >= ifield->width)
1795 for(i = 0; i < ifield->width; i++)
1796 ielem->data[i] = ' ';
1798 else if(ifield->ctype == pfld){
1799 ielem = ifield->ielem;
1800 if(ielem && ielem->datalen > 0)
1801 ielem->data[0] = save_pchar;
1804 previfield = ifield;
1807 *p = '\0';
1809 if(draw_whole_line)
1810 PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
1812 else{
1813 int uc, ac, do_arrow;
1814 int i, drew_X = 0;
1815 int inverse_hack = 0, need_inverse_hack = 0;
1816 int doing_bold = 0;
1818 /* so we can restore current color at the end */
1819 if((uc=pico_usingcolor()) != 0)
1820 lastc = pico_get_cur_color();
1823 * There are two possible "arrow" cursors. One is the one that
1824 * you get when you are at a slow speed or you turn that slow
1825 * speed one on. It is drawn as part of the status column.
1826 * That one is the one associated with the variable "ac".
1827 * It is always the base_color or the inverse of the base_color.
1829 * The other "arrow" cursor is the one you get by including the
1830 * ARROW token in the index-format. It may be configured to
1831 * be colored.
1833 * The arrow cursors have two special properties that make
1834 * them different from other sections or fields.
1835 * First, the arrow cursors only show up on the current line.
1836 * Second, the arrow cursors are drawn with generated data, not
1837 * data that is present in the passed in data.
1840 /* ac is for the old integrated arrow cursor */
1841 ac = F_ON(F_FORCE_ARROW,ps_global);
1843 /* do_arrow is for the ARROW token in index-format */
1844 do_arrow = (afld != iNothing);
1846 MoveCursor(HEADER_ROWS(ps_global) + line, 0);
1847 if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global))
1848 CleartoEOLN();
1850 /* find the base color for the whole line */
1851 if(cur && !ac && !do_arrow){
1853 * This stanza handles the current line marking in the
1854 * regular, non-arrow-cursor case.
1858 * If the current line has a linecolor, apply the
1859 * appropriate reverse transformation to show it is current.
1861 if(uc && ice->linecolor && ice->linecolor->fg[0]
1862 && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
1863 base_color = apply_rev_color(ice->linecolor,
1864 ps_global->index_color_style);
1866 (void)pico_set_colorp(base_color, PSC_NONE);
1868 else{
1869 inverse_hack++;
1870 if(uc){
1871 COLOR_PAIR *rev;
1873 if((rev = pico_get_rev_color()) != NULL){
1874 base_color = new_color_pair(rev->fg, rev->bg);
1875 (void)pico_set_colorp(base_color, PSC_NONE);
1877 else
1878 base_color = lastc;
1882 else if(uc && ice->linecolor && ice->linecolor->fg[0]
1883 && ice->linecolor->bg[0]
1884 && pico_is_good_colorpair(ice->linecolor)){
1885 (void)pico_set_colorp(ice->linecolor, PSC_NONE);
1886 base_color = ice->linecolor;
1888 else
1889 base_color = lastc;
1891 memset(draw, 0, sizeof(draw));
1892 p = draw;
1894 doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
1896 /* draw each field */
1897 for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
1899 drew_X = 0;
1902 * Fix up the data for some special cases.
1905 /* message number string is generated on the fly */
1906 if(ifield->ctype == iMessNo){
1907 ielem = ifield->ielem;
1908 if(ielem && ielem->datalen >= ifield->width){
1909 snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
1910 ielem->data[ifield->width] = '\0';
1911 ielem->data[ielem->datalen] = '\0';
1915 if(ifield->ctype == sfld){
1916 ielem = ifield->ielem;
1917 if(ielem && ielem->datalen > 0){
1918 if(ielem->datalen == 1){
1919 save_schar1 = ielem->data[0];
1920 if(sel && !doing_bold){
1921 ielem->data[0] = 'X';
1922 drew_X++;
1924 else if(ac && cur && ielem->data[0] == ' ')
1925 ielem->data[0] = '-';
1927 if(ielem->next && ielem->next->datalen){
1928 save_schar2 = ielem->next->data[0];
1929 if(ac && cur && ielem->next->data[0] != '\0')
1930 ielem->next->data[0] = '>';
1933 else if(ielem->datalen > 1){
1934 if(sel && !doing_bold){
1935 ielem->data[0] = 'X';
1936 drew_X++;
1938 else if(ac && cur && ielem->data[0] == ' ')
1939 ielem->data[0] = '-';
1941 save_schar2 = ielem->data[1];
1942 if(ac && cur && ielem->data[1] != '\0')
1943 ielem->data[1] = '>';
1947 else if(ifield->ctype == afld && do_arrow && cur){
1949 ielem = ifield->ielem;
1950 if(ielem && ielem->datalen >= ifield->width){
1951 for(i = 0; i < ifield->width-1; i++)
1952 ielem->data[i] = cur ? '-' : ' ';
1954 ielem->data[i] = '>';
1957 else if(ifield->ctype == pfld){
1958 ielem = ifield->ielem;
1959 if(ielem && ielem->datalen > 0){
1960 save_pchar = ielem->data[0];
1961 ielem->data[0] = ice->plus;
1965 /* space between fields */
1966 if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
1967 if(inverse_hack)
1968 StartInverse();
1970 Write_to_screen(" ");
1971 if(inverse_hack)
1972 EndInverse();
1975 for(ielem = ifield->ielem; ielem; ielem = ielem->next){
1976 char *src;
1978 src = ielem->data;
1979 utf8_pad_to_width(draw, src, (n+1) * sizeof(char),
1980 ielem->wid, ifield->leftadj);
1981 draw[n] = '\0';
1984 * Switch to color for ielem.
1985 * But don't switch if we drew an X in this column,
1986 * because that overwrites the colored thing, and don't
1987 * switch if this is the ARROW field and this is not
1988 * the current message. ARROW field is only colored for
1989 * the current message.
1990 * And don't switch if current line and type eTypeCol.
1992 if(ielem->color && pico_is_good_colorpair(ielem->color)
1993 && !(do_arrow && ifield->ctype == afld && !cur)
1994 && (!drew_X || ielem != ifield->ielem)
1995 && !(cur && ielem->type == eTypeCol)){
1996 need_inverse_hack = 0;
1997 (void) pico_set_colorp(ielem->color, PSC_NORM);
1999 else
2000 need_inverse_hack = 1;
2002 if(need_inverse_hack && inverse_hack)
2003 StartInverse();
2005 Write_to_screen(draw);
2006 if(need_inverse_hack && inverse_hack)
2007 EndInverse();
2009 (void) pico_set_colorp(base_color, PSC_NORM);
2013 * Restore the data for the special cases.
2016 if(ifield->ctype == sfld){
2017 ielem = ifield->ielem;
2018 if(ielem && ielem->datalen > 0){
2019 if(ielem->datalen == 1){
2020 ielem->data[0] = save_schar1;
2021 if(ielem->next && ielem->next->datalen)
2022 ielem->next->data[0] = save_schar2;
2024 else if(ielem->datalen > 1){
2025 ielem->data[0] = save_schar1;
2026 ielem->data[1] = save_schar2;
2030 else if(ifield->ctype == afld){
2031 ielem = ifield->ielem;
2032 if(ielem && ielem->datalen >= ifield->width)
2033 for(i = 0; i < ifield->width; i++)
2034 ielem->data[i] = ' ';
2036 else if(ifield->ctype == pfld){
2037 ielem = ifield->ielem;
2038 if(ielem && ielem->datalen > 0)
2039 ielem->data[0] = save_pchar;
2042 previfield = ifield;
2045 if(doing_bold)
2046 EndBold();
2048 if(base_color && base_color != lastc && base_color != ice->linecolor)
2049 free_color_pair(&base_color);
2051 if(lastc){
2052 (void)pico_set_colorp(lastc, PSC_NORM);
2053 free_color_pair(&lastc);
2058 entry->hilite = cur;
2059 entry->bolded = sel;
2060 entry->msgno = msgno;
2061 entry->plus = ice->plus;
2062 entry->id = ice->id;
2064 if(!ice->color_lookup_done && pico_usingcolor())
2065 entry->id = 0;
2067 return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
2071 * setup_index_state - hooked onto pith_opt_save_index_state to setup
2072 * current_index_state after setup_{index,thread}_header_widths
2074 void
2075 setup_index_state(int threaded)
2077 if(current_index_state){
2078 if(threaded){
2079 current_index_state->status_col = 0;
2080 current_index_state->status_fld = iStatus;
2081 current_index_state->plus_fld = iNothing;
2082 current_index_state->arrow_fld = iNothing;
2083 } else {
2084 INDEX_COL_S *cdesc, *prevcdesc = NULL;
2085 IndexColType sfld, altfld, plusfld, arrowfld;
2086 int width, fld, col, pluscol, scol, altcol;
2088 col = 0;
2089 scol = -1;
2090 sfld = iNothing;
2091 altcol = -1;
2092 altfld = iNothing;
2093 /* figure out which field is status field */
2094 for(cdesc = ps_global->index_disp_format, fld = 0;
2095 cdesc->ctype != iNothing;
2096 cdesc++){
2097 width = cdesc->width;
2098 if(width == 0)
2099 continue;
2101 /* space between columns */
2102 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2103 col++;
2105 if(cdesc->ctype == iStatus){
2106 scol = col;
2107 sfld = cdesc->ctype;
2108 break;
2111 if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
2112 scol = col;
2113 sfld = cdesc->ctype;
2114 break;
2117 if(cdesc->ctype == iMessNo){
2118 altcol = col;
2119 altfld = cdesc->ctype;
2122 col += width;
2123 fld++;
2124 prevcdesc = cdesc;
2127 if(sfld == iNothing){
2128 if(altcol == -1){
2129 scol = 0;
2131 else{
2132 scol = altcol;
2133 sfld = altfld;
2138 current_index_state->status_col = scol;
2139 current_index_state->status_fld = sfld;
2141 col = 0;
2142 plusfld = iNothing;
2143 pluscol = -1;
2144 prevcdesc = NULL;
2145 /* figure out which column to use for threading '+' */
2146 if(THREADING()
2147 && ps_global->thread_disp_style != THREAD_NONE
2148 && ps_global->VAR_THREAD_MORE_CHAR[0]
2149 && ps_global->VAR_THREAD_EXP_CHAR[0])
2150 for(cdesc = ps_global->index_disp_format, fld = 0;
2151 cdesc->ctype != iNothing;
2152 cdesc++){
2153 width = cdesc->width;
2154 if(width == 0)
2155 continue;
2157 /* space between columns */
2158 if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
2159 col++;
2161 if((cdesc->ctype == iSubject
2162 || cdesc->ctype == iShortSubject
2163 || cdesc->ctype == iSubjectText
2164 || cdesc->ctype == iSubjKey
2165 || cdesc->ctype == iSubjKeyText
2166 || cdesc->ctype == iSubjKeyInit
2167 || cdesc->ctype == iSubjKeyInitText)
2168 && (ps_global->thread_disp_style == THREAD_STRUCT
2169 || ps_global->thread_disp_style == THREAD_MUTTLIKE
2170 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
2171 || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
2172 plusfld = cdesc->ctype;
2173 pluscol = col;
2174 break;
2177 if((cdesc->ctype == iFrom
2178 || cdesc->ctype == iFromToNotNews
2179 || cdesc->ctype == iFromTo
2180 || cdesc->ctype == iAddress
2181 || cdesc->ctype == iMailbox)
2182 && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
2183 || ps_global->thread_disp_style == THREAD_INDENT_FROM2
2184 || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
2185 plusfld = cdesc->ctype;
2186 pluscol = col;
2187 break;
2190 col += width;
2191 fld++;
2192 prevcdesc = cdesc;
2195 current_index_state->plus_fld = plusfld;
2196 current_index_state->plus_col = pluscol;
2198 arrowfld = iNothing;
2199 /* figure out which field is arrow field, if any */
2200 for(cdesc = ps_global->index_disp_format, fld = 0;
2201 cdesc->ctype != iNothing;
2202 cdesc++){
2203 width = cdesc->width;
2204 if(width == 0)
2205 continue;
2207 if(cdesc->ctype == iArrow){
2208 arrowfld = cdesc->ctype;
2209 break;
2212 fld++;
2215 current_index_state->arrow_fld = arrowfld;
2222 * insert_condensed_thread_cue - used on pith hook to add decoration to
2223 * subject or from text to show condensed thread info
2226 condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
2227 char **fieldstr, size_t *strsize, int width, int collapsed)
2229 if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
2231 * WARNING!
2232 * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
2233 * and VAR_THREAD_EXP_CHAR[0] are ascii.
2234 * Could do something similar to the conversions done with keyword
2235 * initials in key_str.
2237 if(ice)
2238 ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
2239 : (thd && thd->next)
2240 ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
2242 if(strsize && *strsize > 0 && width != 0){
2243 *(*fieldstr)++ = ' ';
2244 (*strsize)--;
2245 if(width > 0)
2246 width--;
2249 if(strsize && *strsize > 0 && width != 0){
2250 *(*fieldstr)++ = ' ';
2251 (*strsize)--;
2252 if(width > 0)
2253 width--;
2257 return(width);
2262 truncate_subj_and_from_strings(void)
2264 return 1;
2269 * paint_index_hline - paint index line given what we got
2271 void
2272 paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
2274 PINETHRD_S *thrd;
2277 * Trust only what we get back that isn't bogus since
2278 * we were prevented from doing any fetches and such...
2280 if((ps_global->redrawer == redraw_index_body
2281 || ps_global->prev_screen == mail_index_screen)
2282 && current_index_state
2283 && current_index_state->stream == stream
2284 && !ps_global->msgmap->hilited){
2285 int line;
2288 * This test isn't right if there are hidden lines. The line will
2289 * fail the test because it seems like it is past the end of the
2290 * screen but since the hidden lines don't take up space the line
2291 * might actually be on the screen. Don't know that it is worth
2292 * it to fix this, though, since you may have to file through
2293 * many hidden lines before finding the visible ones. I'm not sure
2294 * if the logic inside the if is correct when we do pass the
2295 * top-level test. Leave it for now. Hubert - 2002-06-28
2297 if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
2298 && line < current_index_state->lines_per_page){
2299 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
2300 long n;
2301 long zoomhide, collapsehide;
2303 zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
2304 collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
2307 * Line is visible if it is selected and not hidden due to
2308 * thread collapse, or if there is no zooming happening and
2309 * it is not hidden due to thread collapse.
2311 for(line = 0, n = current_index_state->msg_at_top;
2312 n != msgno;
2313 n++)
2314 if((zoomhide
2315 && get_lflag(stream, current_index_state->msgmap,
2316 n, MN_SLCT)
2317 && (!collapsehide
2318 || !get_lflag(stream, current_index_state->msgmap, n,
2319 MN_CHID)))
2321 (!zoomhide
2322 && !get_lflag(stream, current_index_state->msgmap,
2323 n, MN_CHID)))
2324 line++;
2327 thrd = NULL;
2328 if(THRD_INDX()){
2329 unsigned long rawno;
2331 rawno = mn_m2raw(current_index_state->msgmap, msgno);
2332 if(rawno)
2333 thrd = fetch_thread(stream, rawno);
2336 paint_index_line(ice, line,
2337 (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
2338 current_index_state->status_fld,
2339 current_index_state->plus_fld,
2340 current_index_state->arrow_fld,
2341 &current_index_state->entry_state[line],
2342 mn_is_cur(current_index_state->msgmap, msgno),
2343 THRD_INDX()
2344 ? (count_lflags_in_thread(stream, thrd,
2345 current_index_state->msgmap,
2346 MN_SLCT) > 0)
2347 : get_lflag(stream, current_index_state->msgmap,
2348 msgno, MN_SLCT));
2349 fflush(stdout);
2358 * pine_imap_env -- C-client's telling us an envelope just arrived
2359 * from the server. Use it if we can...
2361 void
2362 pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
2364 MESSAGECACHE *mc;
2366 dprint((7, "imap_env(%ld)\n", rawno));
2367 if(stream && !sp_mail_box_changed(stream)
2368 && stream == ps_global->mail_stream
2369 && rawno > 0L && rawno <= stream->nmsgs
2370 && (mc = mail_elt(stream,rawno))
2371 && mc->valid
2372 && mc->rfc822_size
2373 && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
2374 INDEXDATA_S idata;
2375 ICE_S *ice;
2377 memset(&idata, 0, sizeof(INDEXDATA_S));
2378 idata.no_fetch = 1;
2379 idata.size = mc->rfc822_size;
2380 idata.rawno = rawno;
2381 idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
2382 idata.stream = stream;
2384 index_data_env(&idata, env);
2387 * Look for resent-to already in MAILCACHE data
2389 if(mc->private.msg.header.text.data){
2390 STRINGLIST *lines;
2391 SIZEDTEXT szt;
2392 static char *linelist[] = {"resent-to" , NULL};
2394 if(mail_match_lines(lines = new_strlst(linelist),
2395 mc->private.msg.lines, 0L)){
2396 idata.valid_resent_to = 1;
2397 memset(&szt, 0, sizeof(SIZEDTEXT));
2398 textcpy(&szt, &mc->private.msg.header.text);
2399 mail_filter((char *) szt.data, szt.size, lines, 0L);
2400 idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
2401 if(szt.data)
2402 fs_give((void **) &szt.data);
2405 free_strlst(&lines);
2408 ice = (*format_index_line)(&idata);
2409 if(idata.bogus)
2410 clear_ice(&ice);
2411 else
2412 paint_index_hline(stream, idata.msgno, ice);
2417 /*----------------------------------------------------------------------
2418 Scroll to specified position.
2421 Args: pos - position to scroll to.
2423 Returns: TRUE - did the scroll operation.
2424 FALSE - was not able to do the scroll operation.
2425 ----*/
2427 index_scroll_to_pos (long int pos)
2429 static short bad_timing = 0;
2430 long i, j, k = 0;
2432 if(bad_timing)
2433 return (FALSE);
2436 * Put the requested line at the top of the screen...
2440 * Starting at msg 'pos' find next visible message.
2442 for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
2443 if(!msgline_hidden(current_index_state->stream,
2444 current_index_state->msgmap, i, 0)){
2445 current_index_state->msg_at_top = i;
2446 break;
2451 * If single selection, move selected message to be on the screen.
2453 if (mn_total_cur(current_index_state->msgmap) == 1L) {
2454 if (current_index_state->msg_at_top >
2455 mn_get_cur (current_index_state->msgmap)) {
2456 /* Selection was above screen, move to top of screen. */
2457 mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
2459 else {
2460 /* Scan through the screen. If selection found, leave where is.
2461 * Otherwise, move to end of screen */
2462 for( i = current_index_state->msg_at_top,
2463 j = current_index_state->lines_per_page;
2464 i != mn_get_cur(current_index_state->msgmap) &&
2465 i <= mn_get_total(current_index_state->msgmap) &&
2466 j > 0L;
2467 i++) {
2468 if(!msgline_hidden(current_index_state->stream,
2469 current_index_state->msgmap, i, 0)){
2470 j--;
2471 k = i;
2474 if(j <= 0L)
2475 /* Move to end of screen. */
2476 mn_set_cur(current_index_state->msgmap, k);
2480 bad_timing = 0;
2481 return (TRUE);
2486 /*----------------------------------------------------------------------
2487 Adjust the index display state down a line
2489 Args: scroll_count -- number of lines to scroll
2491 Returns: TRUE - did the scroll operation.
2492 FALSE - was not able to do the scroll operation.
2493 ----*/
2495 index_scroll_down(long int scroll_count)
2497 static short bad_timing = 0;
2498 long i, j, k;
2499 long cur, total;
2501 if(bad_timing)
2502 return (FALSE);
2504 bad_timing = 1;
2507 j = -1L;
2508 total = mn_get_total (current_index_state->msgmap);
2509 for(k = i = current_index_state->msg_at_top; ; i++){
2511 /* Only examine non-hidden messages. */
2512 if(!msgline_hidden(current_index_state->stream,
2513 current_index_state->msgmap, i, 0)){
2514 /* Remember this message */
2515 k = i;
2516 /* Increment count of lines. */
2517 if (++j >= scroll_count) {
2518 /* Counted enough lines, stop. */
2519 current_index_state->msg_at_top = k;
2520 break;
2524 /* If at last message, stop. */
2525 if (i >= total){
2526 current_index_state->msg_at_top = k;
2527 break;
2532 * If not multiple selection, see if selected message visible. if not
2533 * set it to last visible message.
2535 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2536 j = 0L;
2537 cur = mn_get_cur (current_index_state->msgmap);
2538 for (i = current_index_state->msg_at_top; i <= total; ++i) {
2539 if(!msgline_hidden(current_index_state->stream,
2540 current_index_state->msgmap, i, 0)){
2541 if (++j >= current_index_state->lines_per_page) {
2542 break;
2544 if (i == cur)
2545 break;
2548 if (i != cur)
2549 mn_set_cur(current_index_state->msgmap,
2550 current_index_state->msg_at_top);
2553 bad_timing = 0;
2554 return (TRUE);
2559 /*----------------------------------------------------------------------
2560 Adjust the index display state up a line
2562 Args: scroll_count -- number of lines to scroll
2564 Returns: TRUE - did the scroll operation.
2565 FALSE - was not able to do the scroll operation.
2567 ----*/
2569 index_scroll_up(long int scroll_count)
2571 static short bad_timing = 0;
2572 long i, j, k;
2573 long cur;
2575 if(bad_timing)
2576 return(FALSE);
2578 bad_timing = 1;
2580 j = -1L;
2581 for(k = i = current_index_state->msg_at_top; ; i--){
2583 /* Only examine non-hidden messages. */
2584 if(!msgline_hidden(current_index_state->stream,
2585 current_index_state->msgmap, i, 0)){
2586 /* Remember this message */
2587 k = i;
2588 /* Increment count of lines. */
2589 if (++j >= scroll_count) {
2590 /* Counted enough lines, stop. */
2591 current_index_state->msg_at_top = k;
2592 break;
2596 /* If at first message, stop */
2597 if (i <= 1L){
2598 current_index_state->msg_at_top = k;
2599 break;
2605 * If not multiple selection, see if selected message visible. if not
2606 * set it to last visible message.
2608 if(mn_total_cur(current_index_state->msgmap) == 1L) {
2609 j = 0L;
2610 cur = mn_get_cur (current_index_state->msgmap);
2611 for ( i = current_index_state->msg_at_top;
2612 i <= mn_get_total(current_index_state->msgmap);
2613 ++i) {
2614 if(!msgline_hidden(current_index_state->stream,
2615 current_index_state->msgmap, i, 0)){
2616 if (++j >= current_index_state->lines_per_page) {
2617 k = i;
2618 break;
2620 if (i == cur)
2621 break;
2624 if (i != cur)
2625 mn_set_cur(current_index_state->msgmap, k);
2629 bad_timing = 0;
2630 return (TRUE);
2635 /*----------------------------------------------------------------------
2636 Calculate the message number that should be at the top of the display
2638 Args: current - the current message number
2639 lines_per_page - the number of lines for the body of the index only
2641 Returns: -1 if the current message is -1
2642 the message entry for the first message at the top of the screen.
2644 When paging in the index it is always on even page boundies, and the
2645 current message is always on the page thus the top of the page is
2646 completely determined by the current message and the number of lines
2647 on the page.
2648 ----*/
2649 long
2650 top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
2652 long current, hidden, lastn;
2653 long n, m = 0L, t = 1L;
2655 current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
2657 if(current < 0L)
2658 return(-1);
2660 if(lines_per_page == 0L)
2661 return(current);
2663 if(THRD_INDX_ENABLED()){
2664 long rawno;
2665 PINETHRD_S *thrd = NULL;
2667 rawno = mn_m2raw(msgs, mn_get_cur(msgs));
2668 if(rawno)
2669 thrd = fetch_thread(stream, rawno);
2671 if(THRD_INDX()){
2673 if(any_lflagged(msgs, MN_HIDE)){
2674 PINETHRD_S *is_current_thrd;
2676 is_current_thrd = thrd;
2677 if(is_current_thrd){
2678 if(mn_get_revsort(msgs)){
2679 /* start with top of tail of thread list */
2680 thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
2681 if(thrd && thrd->top && thrd->top != thrd->rawno)
2682 thrd = fetch_thread(stream, thrd->top);
2684 else{
2685 /* start with head of thread list */
2686 thrd = fetch_head_thread(stream);
2689 t = 1L;
2690 m = 0L;
2691 if(thrd)
2692 n = mn_raw2m(msgs, thrd->rawno);
2694 while(thrd){
2695 if(!msgline_hidden(stream, msgs, n, 0)
2696 && (++m % lines_per_page) == 1L)
2697 t = n;
2699 if(thrd == is_current_thrd)
2700 break;
2702 if(mn_get_revsort(msgs) && thrd->prevthd)
2703 thrd = fetch_thread(stream, thrd->prevthd);
2704 else if(!mn_get_revsort(msgs) && thrd->nextthd)
2705 thrd = fetch_thread(stream, thrd->nextthd);
2706 else
2707 thrd = NULL;
2709 if(thrd)
2710 n = mn_raw2m(msgs, thrd->rawno);
2714 else{
2715 if(thrd){
2716 n = thrd->thrdno;
2717 m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
2718 n = thrd->rawno;
2720 * We want to find the m'th thread and the
2721 * message number that goes with that. We just have
2722 * to back up from where we are to get there.
2723 * If we have a reverse sort backing up is going
2724 * forward through the thread.
2726 while(thrd && m < thrd->thrdno){
2727 n = thrd->rawno;
2728 if(mn_get_revsort(msgs) && thrd->nextthd)
2729 thrd = fetch_thread(stream, thrd->nextthd);
2730 else if(!mn_get_revsort(msgs) && thrd->prevthd)
2731 thrd = fetch_thread(stream, thrd->prevthd);
2732 else
2733 thrd = NULL;
2736 if(thrd)
2737 n = thrd->rawno;
2739 t = mn_raw2m(msgs, n);
2743 else{ /* viewing a thread */
2745 lastn = mn_get_total(msgs);
2746 t = 1L;
2748 /* get top of thread */
2749 if(thrd && thrd->top && thrd->top != thrd->rawno)
2750 thrd = fetch_thread(stream, thrd->top);
2752 if(thrd){
2753 if(mn_get_revsort(msgs))
2754 lastn = mn_raw2m(msgs, thrd->rawno);
2755 else
2756 t = mn_raw2m(msgs, thrd->rawno);
2759 n = 0L;
2761 /* n is the end of this thread */
2762 while(thrd){
2763 n = mn_raw2m(msgs, thrd->rawno);
2764 if(thrd->branch)
2765 thrd = fetch_thread(stream, thrd->branch);
2766 else if(thrd->next)
2767 thrd = fetch_thread(stream, thrd->next);
2768 else
2769 thrd = NULL;
2772 if(n){
2773 if(mn_get_revsort(msgs))
2774 t = n;
2775 else
2776 lastn = n;
2779 for(m = 0L, n = t; n <= MIN(current, lastn); n++)
2780 if(!msgline_hidden(stream, msgs, n, 0)
2781 && (++m % lines_per_page) == 1L)
2782 t = n;
2785 return(t);
2787 else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
2789 if(current < mn_get_total(msgs) / 2){
2790 t = 1L;
2791 m = 0L;
2792 for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
2793 if(!msgline_hidden(stream, msgs, n, 0)
2794 && (++m % lines_per_page) == 1L)
2795 t = n;
2797 else{
2798 t = current+1L;
2799 m = mn_get_total(msgs)-hidden+1L;
2800 for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
2801 if(!msgline_hidden(stream, msgs, n, 0)
2802 && (--m % lines_per_page) == 1L)
2803 t = n;
2805 if(t > current)
2806 t = 1L;
2809 return(t);
2811 else
2812 return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
2816 /*----------------------------------------------------------------------
2817 Clear various bits that make up a healthy display
2819 ----*/
2820 void
2821 reset_index_border(void)
2823 mark_status_dirty();
2824 mark_keymenu_dirty();
2825 mark_titlebar_dirty();
2826 ps_global->mangled_screen = 1; /* signal FULL repaint */
2830 /*----------------------------------------------------------------------
2831 This redraws the body of the index screen, taking into
2832 account any change in the size of the screen. All the state needed to
2833 repaint is in the static variables so this can be called from
2834 anywhere.
2835 ----*/
2836 void
2837 redraw_index_body(void)
2839 int agg;
2841 if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
2842 restore_selected(current_index_state->msgmap);
2844 ps_global->mangled_body = 1;
2846 (void) update_index(ps_global, current_index_state);
2847 if(agg)
2848 pseudo_selected(current_index_state->stream, current_index_state->msgmap);
2852 /*----------------------------------------------------------------------
2853 Give hint about Other command being optional. Some people get the idea
2854 that it is required to use the commands on the 2nd and 3rd keymenus.
2856 Args: none
2858 Result: message may be printed to status line
2859 ----*/
2860 void
2861 warn_other_cmds(void)
2863 static int other_cmds = 0;
2865 other_cmds++;
2866 if(((ps_global->first_time_user || ps_global->show_new_version) &&
2867 other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
2868 q_status_message(SM_ASYNC, 0, 9,
2869 _("Remember the \"O\" command is always optional"));
2873 void
2874 thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
2875 UCS preloadkeystroke, int q_line)
2877 PINETHRD_S *thrd = NULL;
2878 unsigned long rawno, save_branch;
2879 int we_cancel = 0;
2880 int flags = AC_FROM_THREAD;
2882 if(!(stream && msgmap))
2883 return;
2885 rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
2886 if(rawno)
2887 thrd = fetch_thread(stream, rawno);
2889 if(!thrd)
2890 return;
2892 save_branch = thrd->branch;
2893 thrd->branch = 0L; /* branch is a sibling, not part of thread */
2895 if(!preloadkeystroke){
2896 if(!THRD_INDX()){
2897 if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
2898 flags |= AC_EXPN;
2899 else
2900 flags |= AC_COLL;
2903 if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
2904 == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
2905 flags |= AC_UNSEL;
2908 we_cancel = busy_cue(NULL, NULL, 1);
2910 /* save the SLCT flags in STMP for restoring at the bottom */
2911 copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
2913 /* clear the values from the SLCT flags */
2914 set_lflags(stream, msgmap, MN_SLCT, 0);
2916 /* set SLCT for thrd on down */
2917 set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
2918 thrd->branch = save_branch;
2920 if(we_cancel)
2921 cancel_busy_cue(0);
2923 (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
2924 q_line);
2926 /* restore the original flags */
2927 copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
2929 if(any_lflagged(msgmap, MN_HIDE) > 0L){
2930 /* if nothing left selected, unhide all */
2931 if(any_lflagged(msgmap, MN_SLCT) == 0L){
2932 (void) unzoom_index(ps_global, stream, msgmap);
2933 dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
2934 q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
2937 /* if current is hidden, adjust */
2938 adjust_cur_to_visible(stream, msgmap);
2943 /*----------------------------------------------------------------------
2944 Search the message headers as displayed in index
2946 Args: command_line -- The screen line to prompt on
2947 msg -- The current message number to start searching at
2948 max_msg -- The largest message number in the current folder
2950 The headers are searched exactly as they are displayed on the screen. The
2951 search will wrap around to the beginning if not string is not found right
2952 away.
2953 ----*/
2954 void
2955 index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
2957 int rc, select_all = 0, flags, prefetch, searchbound, otherbound, offset = 0, we_turned_on = 0;
2958 long i, sorted_msg, selected = 0L;
2959 char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
2960 char buf[MAX_SCREEN_COLS+1], *p;
2961 HelpType help;
2962 char search_string[MAX_SEARCH+1];
2963 ICE_S *ice, *ic;
2964 static int direction = 1;
2965 static HISTORY_S *history = NULL;
2966 static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
2967 {ctrl('Y'), 10, "^Y", N_("First Msg")},
2968 {ctrl('V'), 11, "^V", N_("Last Msg")},
2969 {ctrl('W'), 20, "^W", ""},
2970 {0, 0, NULL, NULL },
2971 {KEY_UP, 30, "", ""},
2972 {KEY_DOWN, 31, "", ""},
2973 {-1, 0, NULL, NULL} };
2974 #define KD_IS (3) /* index of Direction key */
2975 #define KU_IS (5) /* index of KEY_UP */
2976 #define PREFETCH_THIS_MANY_LINES (50)
2978 init_hist(&history, HISTSIZE);
2979 search_string[0] = '\0';
2980 if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
2981 strncpy(search_string, p, sizeof(search_string));
2982 search_string[sizeof(search_string)-1] = '\0';
2985 dprint((4, "\n - search headers - \n"));
2987 if(!any_messages(msgmap, NULL, "to search")){
2988 return;
2990 else if(mn_total_cur(msgmap) > 1L){
2991 q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
2992 comatose(mn_total_cur(msgmap)));
2993 return;
2995 else
2996 sorted_msg = mn_get_cur(msgmap);
2998 help = NO_HELP;
2999 new_string[0] = '\0';
3001 while(1) {
3002 snprintf(prompt, sizeof(prompt), _("Word to search %sfor [%s] : "),
3003 direction == -1 ? _("(backwards) ") : "", search_string);
3005 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3006 header_search_key[0].ch = ctrl('X');
3007 header_search_key[0].rval = 12;
3008 header_search_key[0].name = "^X";
3009 header_search_key[0].label = N_("Select Matches");
3011 else{
3012 header_search_key[0].ch = header_search_key[0].rval = 0;
3013 header_search_key[0].name = header_search_key[0].label = NULL;
3017 * 2 is really 1 because there will be one real entry and
3018 * one entry of "" because of the get_prev_hist above.
3020 if(items_in_hist(history) > 2){
3021 header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
3022 header_search_key[KU_IS].label = HISTORY_KEYLABEL;
3023 header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
3024 header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
3026 else{
3027 header_search_key[KU_IS].name = "";
3028 header_search_key[KU_IS].label = "";
3029 header_search_key[KU_IS+1].name = "";
3030 header_search_key[KU_IS+1].label = "";
3033 header_search_key[KD_IS].label = direction == 1 ? _("Backward") : _("Forward");
3035 flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
3037 rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
3038 prompt, header_search_key, help, &flags);
3040 if(rc == 3) {
3041 help = (help != NO_HELP) ? NO_HELP :
3042 F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
3043 : h_os_index_whereis;
3044 continue;
3046 else if(rc == 10){
3047 q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
3048 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3050 selected = sorted_msg;
3051 mn_dec_cur(stream, msgmap, MH_NONE);
3052 sorted_msg = mn_get_cur(msgmap);
3054 while(selected != sorted_msg);
3056 else
3057 sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
3059 mn_set_cur(msgmap, sorted_msg);
3060 return;
3062 else if(rc == 11){
3063 q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
3064 if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
3066 selected = sorted_msg;
3067 mn_inc_cur(stream, msgmap, MH_NONE);
3068 sorted_msg = mn_get_cur(msgmap);
3070 while(selected != sorted_msg);
3072 else
3073 sorted_msg = mn_get_total(msgmap);
3075 mn_set_cur(msgmap, sorted_msg);
3076 return;
3078 else if(rc == 12){
3079 select_all = 1;
3080 break;
3082 else if(rc == 30){
3083 if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
3084 strncpy(new_string, p, sizeof(new_string));
3085 new_string[sizeof(new_string)-1] = '\0';
3087 else
3088 Writechar(BELL, 0);
3090 continue;
3092 else if(rc == 31){
3093 if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
3094 strncpy(new_string, p, sizeof(new_string));
3095 new_string[sizeof(new_string)-1] = '\0';
3097 else
3098 Writechar(BELL, 0);
3100 continue;
3102 else if(rc == 20){
3103 direction *= -1;
3104 continue;
3107 if(rc != 4){ /* 4 is redraw */
3108 save_hist(history, new_string, 0, NULL);
3109 break;
3113 if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
3114 cmd_cancelled(_("Search"));
3115 return;
3118 if(new_string[0] == '\0'){
3119 strncpy(new_string, search_string, sizeof(new_string));
3120 new_string[sizeof(new_string)-1] = '\0';
3123 strncpy(search_string, new_string, sizeof(search_string));
3124 search_string[sizeof(search_string)-1] = '\0';
3126 we_turned_on = intr_handling_on();
3128 prefetch = 0;
3129 if (direction == -1) {
3130 searchbound = -1;
3131 otherbound = mn_get_total(msgmap);
3132 offset = 2;
3133 } else {
3134 searchbound = mn_get_total(msgmap);
3135 otherbound = 1;
3138 for(i = sorted_msg + ((select_all)?0:1) - offset;
3139 i * direction <= searchbound && !ps_global->intr_pending;
3140 i += direction){
3141 if(msgline_hidden(stream, msgmap, i, 0))
3142 continue;
3144 if(prefetch <= 0)
3145 prefetch = PREFETCH_THIS_MANY_LINES;
3147 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3149 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3151 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3152 search_string)){
3153 selected++;
3154 if(select_all)
3155 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3156 else
3157 break;
3161 prefetch = 0;
3162 if(i * direction > searchbound){
3163 for(i = otherbound;
3164 i * direction < sorted_msg * direction && !ps_global->intr_pending;
3165 i += direction ){
3167 if(msgline_hidden(stream, msgmap, i, 0))
3168 continue;
3170 if(prefetch <= 0)
3171 prefetch = PREFETCH_THIS_MANY_LINES;
3173 ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
3175 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3177 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3178 search_string)){
3179 selected++;
3180 if(select_all)
3181 set_lflag(stream, msgmap, i, MN_SLCT, 1);
3182 else
3183 break;
3188 /* search current line */
3189 if(!select_all && !selected){
3190 i = sorted_msg;
3191 if(!msgline_hidden(stream, msgmap, i, 0)){
3193 ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
3195 ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
3197 if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
3198 search_string)){
3199 selected++;
3204 if(ps_global->intr_pending){
3205 q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
3206 select_all ? _(" Selected set may be incomplete."):"");
3208 else if(select_all){
3209 if(selected
3210 && any_lflagged(msgmap, MN_SLCT) > 0L
3211 && !any_lflagged(msgmap, MN_HIDE)
3212 && F_ON(F_AUTO_ZOOM, state))
3213 (void) zoom_index(state, stream, msgmap, MN_SLCT);
3215 q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
3216 long2string(selected));
3218 else if(selected){
3219 q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
3220 (i * direction < sorted_msg * direction) ? _(". Search wrapped to beginning") :
3221 (i == sorted_msg) ? _(". Current line contains only match") : "");
3222 mn_set_cur(msgmap, i);
3224 else
3225 q_status_message(SM_ORDER, 0, 3, _("Word not found"));
3227 if(we_turned_on)
3228 intr_handling_off();
3233 * Original idea from Stephen Casner <casner@acm.org>.
3235 * Apply the appropriate reverse color transformation to the given
3236 * color pair and return a new color pair. The caller should free the
3237 * color pair.
3240 COLOR_PAIR *
3241 apply_rev_color(COLOR_PAIR *cp, int style)
3243 COLOR_PAIR *rc = pico_get_rev_color();
3245 if(rc){
3246 if(style == IND_COL_REV){
3247 /* just use Reverse color regardless */
3248 return(new_color_pair(rc->fg, rc->bg));
3250 else if(style == IND_COL_FG){
3252 * If changing to Rev fg is readable and different
3253 * from what it already is, do it.
3255 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
3256 return(new_color_pair(rc->fg, cp->bg));
3258 else if(style == IND_COL_BG){
3260 * If changing to Rev bg is readable and different
3261 * from what it already is, do it.
3263 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
3264 return(new_color_pair(cp->fg, rc->bg));
3266 else if(style == IND_COL_FG_NOAMBIG){
3268 * If changing to Rev fg is readable, different
3269 * from what it already is, and not the same as
3270 * the Rev color, do it.
3272 if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
3273 strcmp(rc->bg, cp->bg))
3274 return(new_color_pair(rc->fg, cp->bg));
3276 else if(style == IND_COL_BG_NOAMBIG){
3278 * If changing to Rev bg is readable, different
3279 * from what it already is, and not the same as
3280 * the Rev color, do it.
3282 if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
3283 strcmp(rc->fg, cp->fg))
3284 return(new_color_pair(cp->fg, rc->bg));
3288 /* come here for IND_COL_FLIP and for the cases which fail the tests */
3289 return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
3294 #ifdef _WINDOWS
3296 /*----------------------------------------------------------------------
3297 Callback to get the text of the current message. Used to display
3298 a message in an alternate window.
3300 Args: cmd - what type of scroll operation.
3301 text - filled with pointer to text.
3302 l - length of text.
3303 style - Returns style of text. Can be:
3304 GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
3305 lines
3306 GETTEXT_LINES - Is a pointer to NULL terminated array of
3307 char *. Each entry points to a line of
3308 text.
3310 this implementation always returns GETTEXT_TEXT.
3312 Returns: TRUE - did the scroll operation.
3313 FALSE - was not able to do the scroll operation.
3314 ----*/
3316 index_scroll_callback (cmd, scroll_pos)
3317 int cmd;
3318 long scroll_pos;
3320 int paint = TRUE;
3322 switch (cmd) {
3323 case MSWIN_KEY_SCROLLUPLINE:
3324 paint = index_scroll_up (scroll_pos);
3325 break;
3327 case MSWIN_KEY_SCROLLDOWNLINE:
3328 paint = index_scroll_down (scroll_pos);
3329 break;
3331 case MSWIN_KEY_SCROLLUPPAGE:
3332 paint = index_scroll_up (current_index_state->lines_per_page);
3333 break;
3335 case MSWIN_KEY_SCROLLDOWNPAGE:
3336 paint = index_scroll_down (current_index_state->lines_per_page);
3337 break;
3339 case MSWIN_KEY_SCROLLTO:
3340 /* Normalize msgno in zoomed case */
3341 if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
3342 long n, x;
3344 for(n = 1L, x = 0;
3345 x < scroll_pos && n < mn_get_total(ps_global->msgmap);
3346 n++)
3347 if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
3348 n, 0))
3349 x++;
3351 scroll_pos = n - 1; /* list-position --> message number */
3354 paint = index_scroll_to_pos (scroll_pos + 1);
3355 break;
3358 if(paint){
3359 mswin_beginupdate();
3360 update_titlebar_message();
3361 update_titlebar_status();
3362 redraw_index_body();
3363 mswin_endupdate();
3366 return(paint);
3370 /*----------------------------------------------------------------------
3371 MSWin scroll callback to get the text of the current message
3373 Args: title - title for new window
3374 text -
3375 l -
3376 style -
3378 Returns: TRUE - got the requested text
3379 FALSE - was not able to get the requested text
3380 ----*/
3382 index_gettext_callback(title, titlelen, text, l, style)
3383 char *title;
3384 size_t titlelen;
3385 void **text;
3386 long *l;
3387 int *style;
3389 int rv = 0;
3390 ENVELOPE *env;
3391 BODY *body;
3392 STORE_S *so;
3393 gf_io_t pc;
3395 if(mn_get_total(ps_global->msgmap) > 0L
3396 && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
3397 gf_set_so_writec(&pc, so);
3399 if((env = pine_mail_fetchstructure(ps_global->mail_stream,
3400 mn_m2raw(ps_global->msgmap,
3401 mn_get_cur(ps_global->msgmap)),
3402 &body))
3403 && format_message(mn_m2raw(ps_global->msgmap,
3404 mn_get_cur(ps_global->msgmap)),
3405 env, body, NULL, FM_NEW_MESS, pc)){
3406 snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
3407 strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
3408 mn_get_cur(ps_global->msgmap),
3409 mn_get_total(ps_global->msgmap));
3410 title[titlelen-1] = '\0';
3411 *text = so_text(so);
3412 *l = strlen((char *)so_text(so));
3413 *style = GETTEXT_TEXT;
3415 /* free alloc'd so, but preserve the text passed back to caller */
3416 so->txt = (void *) NULL;
3417 rv = 1;
3420 gf_clear_so_writec(so);
3421 so_give(&so);
3424 return(rv);
3432 index_sort_callback(set, order)
3433 int set;
3434 long order;
3436 int i = 0;
3438 if(set){
3439 sort_folder(ps_global->mail_stream, ps_global->msgmap,
3440 order & 0x000000ff,
3441 (order & 0x00000100) != 0, SRT_VRB);
3442 mswin_beginupdate();
3443 update_titlebar_message();
3444 update_titlebar_status();
3445 redraw_index_body();
3446 mswin_endupdate();
3447 flush_status_messages(1);
3449 else{
3450 i = (int) mn_get_sort(ps_global->msgmap);
3451 if(mn_get_revsort(ps_global->msgmap))
3452 i |= 0x0100;
3455 return(i);
3462 void
3463 index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
3465 int n = 0;
3466 int view_in_new_wind_index = -1;
3467 long rawno;
3468 MESSAGECACHE *mc;
3469 MPopup view_index_popup[32];
3470 struct key_menu *km = (style == ThreadIndex)
3471 ? &thread_keymenu
3472 : (ps_global->mail_stream != stream)
3473 ? &simple_index_keymenu
3474 : &index_keymenu;
3477 * Loosely follow the logic in do_index_border to figure
3478 * out which commands to show.
3481 if(full){
3482 if(km != &simple_index_keymenu){
3483 view_index_popup[n].type = tQueue;
3484 view_index_popup[n].label.string = (km == &thread_keymenu)
3485 ? "&View Thread" : "&View";
3486 view_index_popup[n].label.style = lNormal;
3487 view_index_popup[n++].data.val = 'V';
3490 if(km == &index_keymenu){
3491 view_in_new_wind_index = n;
3492 view_index_popup[n].type = tIndex;
3493 view_index_popup[n].label.style = lNormal;
3494 view_index_popup[n++].label.string = "View in New Window";
3497 if(km != &simple_index_keymenu)
3498 view_index_popup[n++].type = tSeparator;
3500 if(km == &thread_keymenu){
3501 view_index_popup[n].type = tQueue;
3502 view_index_popup[n].label.string = "&Delete Thread";
3503 view_index_popup[n].label.style = lNormal;
3504 view_index_popup[n++].data.val = 'D';
3506 view_index_popup[n].type = tQueue;
3507 view_index_popup[n].label.string = "&UnDelete Thread";
3508 view_index_popup[n].label.style = lNormal;
3509 view_index_popup[n++].data.val = 'U';
3511 else{
3512 /* Make "delete/undelete" item sensitive */
3513 mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
3514 && stream && rawno <= stream->nmsgs)
3515 ? mail_elt(stream, rawno) : NULL;
3516 view_index_popup[n].type = tQueue;
3517 view_index_popup[n].label.style = lNormal;
3518 if(mc && mc->deleted){
3519 view_index_popup[n].label.string = "&Undelete";
3520 view_index_popup[n++].data.val = 'U';
3522 else{
3523 view_index_popup[n].label.string = "&Delete";
3524 view_index_popup[n++].data.val = 'D';
3528 if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
3529 view_index_popup[n].type = tSubMenu;
3530 view_index_popup[n].label.string = "Flag";
3531 view_index_popup[n++].data.submenu = flag_submenu(mc);
3534 if(km == &simple_index_keymenu){
3535 view_index_popup[n].type = tQueue;
3536 view_index_popup[n].label.style = lNormal;
3537 view_index_popup[n].label.string = "&Select";
3538 view_index_popup[n++].data.val = 'S';
3540 else{
3541 view_index_popup[n].type = tQueue;
3542 view_index_popup[n].label.style = lNormal;
3543 view_index_popup[n].label.string = (km == &thread_keymenu)
3544 ? "&Save Thread" : "&Save";
3545 view_index_popup[n++].data.val = 'S';
3547 view_index_popup[n].type = tQueue;
3548 view_index_popup[n].label.style = lNormal;
3549 view_index_popup[n].label.string = (km == &thread_keymenu)
3550 ? "&Export Thread" : "&Export";
3551 view_index_popup[n++].data.val = 'E';
3553 view_index_popup[n].type = tQueue;
3554 view_index_popup[n].label.style = lNormal;
3555 view_index_popup[n].label.string = "Print";
3556 view_index_popup[n++].data.val = '%';
3558 view_index_popup[n].type = tQueue;
3559 view_index_popup[n].label.style = lNormal;
3560 view_index_popup[n].label.string = (km == &thread_keymenu)
3561 ? "&Reply To Thread" : "&Reply";
3562 view_index_popup[n++].data.val = 'R';
3564 view_index_popup[n].type = tQueue;
3565 view_index_popup[n].label.style = lNormal;
3566 view_index_popup[n].label.string = (km == &thread_keymenu)
3567 ? "&Forward Thread" : "&Forward";
3568 view_index_popup[n++].data.val = 'F';
3570 if(F_ON(F_ENABLE_BOUNCE, ps_global)){
3571 view_index_popup[n].type = tQueue;
3572 view_index_popup[n].label.style = lNormal;
3573 view_index_popup[n].label.string = (km == &thread_keymenu)
3574 ? "&Bounce Thread" : "&Bounce";
3575 view_index_popup[n++].data.val = 'B';
3578 view_index_popup[n].type = tQueue;
3579 view_index_popup[n].label.style = lNormal;
3580 view_index_popup[n].label.string = "&Take Addresses";
3581 view_index_popup[n++].data.val = 'T';
3583 if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
3584 view_index_popup[n].type = tQueue;
3585 view_index_popup[n].label.style = lNormal;
3586 view_index_popup[n].label.string = "[Un]Select Current";
3587 view_index_popup[n++].data.val = ':';
3591 view_index_popup[n].type = tQueue;
3592 view_index_popup[n].label.style = lNormal;
3593 view_index_popup[n].label.string = "&WhereIs";
3594 view_index_popup[n++].data.val = 'W';
3596 view_index_popup[n++].type = tSeparator;
3599 if(km == &simple_index_keymenu){
3600 view_index_popup[n].type = tQueue;
3601 view_index_popup[n].label.style = lNormal;
3602 view_index_popup[n].label.string = "&Exit Select";
3603 view_index_popup[n++].data.val = 'E';
3605 else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
3606 view_index_popup[n].type = tQueue;
3607 view_index_popup[n].label.style = lNormal;
3608 view_index_popup[n].label.string = "Thread Index";
3609 view_index_popup[n++].data.val = '<';
3611 else{
3612 view_index_popup[n].type = tQueue;
3613 view_index_popup[n].label.style = lNormal;
3614 view_index_popup[n].label.string = "Folder &List";
3615 view_index_popup[n++].data.val = '<';
3618 view_index_popup[n].type = tQueue;
3619 view_index_popup[n].label.style = lNormal;
3620 view_index_popup[n].label.string = "&Main Menu";
3621 view_index_popup[n++].data.val = 'M';
3623 view_index_popup[n].type = tTail;
3625 if(mswin_popup(view_index_popup) == view_in_new_wind_index
3626 && view_in_new_wind_index >= 0)
3627 view_in_new_window();
3631 char *
3632 pcpine_help_index(title)
3633 char *title;
3636 * Title is size 256 in pico. Put in args.
3638 if(title)
3639 strncpy(title, "Alpine MESSAGE INDEX Help", 256);
3641 return(pcpine_help(h_mail_index));
3644 char *
3645 pcpine_help_index_simple(title)
3646 char *title;
3649 * Title is size 256 in pico. Put in args.
3651 if(title)
3652 strncpy(title, "Alpine SELECT MESSAGE Help", 256);
3654 return(pcpine_help(h_simple_index));
3658 #include "../pico/osdep/mswin_tw.h"
3661 void
3662 view_in_new_window(void)
3664 char title[GETTEXT_TITLELEN+1];
3665 void *text;
3666 long len;
3667 int format;
3668 MSWIN_TEXTWINDOW *mswin_tw = NULL;
3670 /* Launch text in alt window. */
3671 if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
3672 if(format == GETTEXT_TEXT)
3673 mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
3674 NULL, MSWIN_DT_USEALTWINDOW);
3675 else if(format == GETTEXT_LINES)
3676 mswin_tw = mswin_displaytext(title, NULL, 0, text,
3677 NULL, MSWIN_DT_USEALTWINDOW);
3679 if(mswin_tw != NULL)
3680 mswin_set_readonly(mswin_tw, FALSE);
3684 #endif /* _WINDOWS */