summaryrefslogtreecommitdiff
path: root/alpine/mailindx.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /alpine/mailindx.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'alpine/mailindx.c')
-rw-r--r--alpine/mailindx.c3644
1 files changed, 3644 insertions, 0 deletions
diff --git a/alpine/mailindx.c b/alpine/mailindx.c
new file mode 100644
index 00000000..01e01558
--- /dev/null
+++ b/alpine/mailindx.c
@@ -0,0 +1,3644 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailindx.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "status.h"
+#include "context.h"
+#include "keymenu.h"
+#include "alpine.h"
+#include "help.h"
+#include "radio.h"
+#include "titlebar.h"
+#include "../pith/flag.h"
+#include "../pith/newmail.h"
+#include "../pith/thread.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/icache.h"
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+#include "../pith/news.h"
+#include "../pith/strlst.h"
+#include "../pith/sequence.h"
+#include "../pith/sort.h"
+#include "../pith/hist.h"
+#include "../pith/busy.h"
+#include "../pith/signal.h"
+
+
+struct save_thrdinfo {
+ ICE_S *(*format_index_line)(INDEXDATA_S *);
+ void (*setup_header_widths)(MAILSTREAM *);
+ unsigned viewing_a_thread:1;
+};
+
+
+static OtherMenu what_keymenu = FirstMenu;
+
+struct index_state *current_index_state = NULL;
+
+
+/*
+ * Internal prototypes
+ */
+void index_index_screen(struct pine *);
+void thread_index_screen(struct pine *);
+int update_index(struct pine *, struct index_state *);
+int index_scroll_up(long);
+int index_scroll_down(long);
+int index_scroll_to_pos(long);
+long top_ent_calc(MAILSTREAM *, MSGNO_S *, long, long);
+void reset_index_border(void);
+void redraw_index_body(void);
+int paint_index_line(ICE_S *, int, long, IndexColType, IndexColType, IndexColType,
+ struct entry_state *, int, int);
+void pine_imap_envelope(MAILSTREAM *, unsigned long, ENVELOPE *);
+void index_search(struct pine *, MAILSTREAM *, int, MSGNO_S *);
+#ifdef _WINDOWS
+int index_scroll_callback(int,long);
+int index_gettext_callback(char *, size_t, void **, long *, int *);
+void index_popup(IndexType style, MAILSTREAM *, MSGNO_S *, int);
+char *pcpine_help_index(char *);
+char *pcpine_help_index_simple(char *);
+#endif
+
+
+
+/*----------------------------------------------------------------------
+
+
+ ----*/
+struct key_menu *
+do_index_border(CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap,
+ IndexType style, int *which_keys, int flags)
+{
+ struct key_menu *km = (style == ThreadIndex)
+ ? &thread_keymenu
+ : (ps_global->mail_stream != stream)
+ ? &simple_index_keymenu
+ : &index_keymenu;
+
+ if(flags & INDX_CLEAR)
+ ClearScreen();
+
+ if(flags & INDX_HEADER)
+ set_titlebar((style == ThreadIndex)
+ /* TRANSLATORS: these are some screen titles */
+ ? _("THREAD INDEX")
+ : (stream == ps_global->mail_stream)
+ ? (style == MsgIndex || style == MultiMsgIndex)
+ ? _("MESSAGE INDEX")
+ : _("ZOOMED MESSAGE INDEX")
+ : (!strcmp(folder, INTERRUPTED_MAIL))
+ ? _("COMPOSE: SELECT INTERRUPTED")
+ : (ps_global->VAR_FORM_FOLDER
+ && !strcmp(ps_global->VAR_FORM_FOLDER, folder))
+ ? _("COMPOSE: SELECT FORM LETTER")
+ : _("COMPOSE: SELECT POSTPONED"),
+ stream, cntxt, folder, msgmap, 1,
+ (style == ThreadIndex) ? ThrdIndex
+ : (THREADING()
+ && sp_viewing_a_thread(stream))
+ ? ThrdMsgNum
+ : MessageNumber,
+ 0, 0, NULL);
+
+ if(flags & INDX_FOOTER) {
+ bitmap_t bitmap;
+ int cmd;
+
+ setbitmap(bitmap);
+
+ if(km == &index_keymenu){
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ menu_init_binding(km, '<', MC_THRDINDX, "<",
+ N_("ThrdIndex"), BACK_KEY);
+ menu_add_binding(km, ',', MC_THRDINDX);
+ }
+ else{
+ menu_init_binding(km, '<', MC_FOLDERS, "<",
+ N_("FldrList"), BACK_KEY);
+ menu_add_binding(km, ',', MC_FOLDERS);
+ }
+ if(F_OFF(F_ENABLE_PIPE,ps_global))
+ clrbitn(VIEW_PIPE_KEY, bitmap); /* always clear for DOS */
+ if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
+ clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
+ if(F_OFF(F_ENABLE_BOUNCE,ps_global))
+ clrbitn(BOUNCE_KEY, bitmap);
+ if(F_OFF(F_ENABLE_FLAG,ps_global))
+ clrbitn(FLAG_KEY, bitmap);
+ if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
+ clrbitn(SELECT_KEY, bitmap);
+ clrbitn(APPLY_KEY, bitmap);
+ clrbitn(SELCUR_KEY, bitmap);
+ if(style != ZoomIndex)
+ clrbitn(ZOOM_KEY, bitmap);
+
+ }
+
+ if(style == MultiMsgIndex){
+ clrbitn(PREVM_KEY, bitmap);
+ clrbitn(NEXTM_KEY, bitmap);
+ }
+ }
+
+ if(km == &index_keymenu || km == &thread_keymenu){
+ if(IS_NEWS(stream)){
+ km->keys[EXCLUDE_KEY].label = N_("eXclude");
+ KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_NONE);
+ }
+ else {
+ clrbitn(UNEXCLUDE_KEY, bitmap);
+ km->keys[EXCLUDE_KEY].label = N_("eXpunge");
+ KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_EXPUNGE);
+ }
+ }
+
+ if(km != &simple_index_keymenu && !THRD_COLLAPSE_ENABLE())
+ clrbitn(COLLAPSE_KEY, bitmap);
+
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
+ menu_add_binding(km, '>', cmd);
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+ }
+
+ if(menu_binding_index(km, MC_JUMP) >= 0){
+ for(cmd = 0; cmd < 10; cmd++)
+ if(F_ON(F_ENABLE_JUMP, ps_global))
+ (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
+ else
+ (void) menu_clear_binding(km, '0' + cmd);
+ }
+
+ draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, what_keymenu);
+ what_keymenu = SameMenu;
+ if(which_keys)
+ *which_keys = km->which; /* pass back to caller */
+ }
+
+ return(km);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Main loop executing commands for the mail index screen
+
+ Args: state -- the pine_state structure for next/prev screen pointers
+ and to pass to the index manager...
+ ----*/
+
+void
+mail_index_screen(struct pine *state)
+{
+ if(!state->mail_stream) {
+ q_status_message(SM_ORDER, 0, 3, _("No folder is currently open"));
+ state->prev_screen = mail_index_screen;
+ state->next_screen = main_menu_screen;
+ return;
+ }
+
+ state->prev_screen = mail_index_screen;
+ state->next_screen = SCREEN_FUN_NULL;
+
+ if(THRD_AUTO_VIEW()
+ && sp_viewing_a_thread(state->mail_stream)
+ && state->view_skipped_index
+ && unview_thread(state, state->mail_stream, state->msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ }
+
+ adjust_cur_to_visible(state->mail_stream, state->msgmap);
+
+ if(THRD_INDX())
+ thread_index_screen(state);
+ else
+ index_index_screen(state);
+}
+
+
+void
+index_index_screen(struct pine *state)
+{
+ dprint((1, "\n\n ---- MAIL INDEX ----\n"));
+
+ setup_for_index_index_screen();
+
+ index_lister(state, state->context_current, state->cur_folder,
+ state->mail_stream, state->msgmap);
+}
+
+
+void
+thread_index_screen(struct pine *state)
+{
+ dprint((1, "\n\n ---- THREAD INDEX ----\n"));
+
+ setup_for_thread_index_screen();
+
+ index_lister(state, state->context_current, state->cur_folder,
+ state->mail_stream, state->msgmap);
+}
+
+
+void *
+stop_threading_temporarily(void)
+{
+ struct save_thrdinfo *ti;
+
+ ps_global->turn_off_threading_temporarily = 1;
+
+ ti = (struct save_thrdinfo *) fs_get(sizeof(*ti));
+ ti->format_index_line = format_index_line;
+ ti->setup_header_widths = setup_header_widths;
+ ti->viewing_a_thread = sp_viewing_a_thread(ps_global->mail_stream);
+
+ setup_for_index_index_screen();
+
+ return((void *) ti);
+}
+
+
+void
+restore_threading(void **p)
+{
+ struct save_thrdinfo *ti;
+
+ ps_global->turn_off_threading_temporarily = 0;
+
+ if(p && *p){
+ ti = (struct save_thrdinfo *) (*p);
+ format_index_line = ti->format_index_line;
+ setup_header_widths = ti->setup_header_widths;
+ sp_set_viewing_a_thread(ps_global->mail_stream, ti->viewing_a_thread);
+
+ fs_give(p);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Main loop executing commands for the mail index screen
+
+ Args: state -- pine_state structure for display flags and such
+ msgmap -- c-client/pine message number mapping struct
+ ----*/
+
+int
+index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ UCS ch;
+ int cmd, which_keys, force,
+ cur_row, cur_col, km_popped, paint_status;
+ static int old_day = -1;
+ long i, j, k, old_max_msgno;
+ char *utf8str;
+ IndexType style, old_style = MsgIndex;
+ struct index_state id;
+ struct key_menu *km = NULL;
+
+
+ dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
+
+ ch = 'x'; /* For displaying msg 1st time thru */
+ force = 0;
+ km_popped = 0;
+ state->mangled_screen = 1;
+ what_keymenu = FirstMenu;
+ old_max_msgno = mn_get_total(msgmap);
+ memset((void *)&id, 0, sizeof(struct index_state));
+ current_index_state = &id;
+ id.msgmap = msgmap;
+ if(msgmap->top != 0L)
+ id.msg_at_top = msgmap->top;
+
+ id.stream = stream;
+ set_need_format_setup(stream);
+
+ while (1) {
+ ps_global->user_says_cancel = 0;
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(state);
+ if(!state->mangled_body
+ && id.entry_state
+ && id.lines_per_page > 1){
+ id.entry_state[id.lines_per_page-2].id = 0;
+ id.entry_state[id.lines_per_page-1].id = 0;
+ }
+ else
+ state->mangled_body = 1;
+ }
+ }
+
+ /*------- Check for new mail -------*/
+ new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
+ force = 0; /* may not need to next time around */
+
+ /*
+ * If the width of the message number field in the display changes
+ * we need to flush the cache and redraw. When the cache is cleared
+ * the widths are recalculated, taking into account the max msgno.
+ */
+
+ if(format_includes_msgno(stream) &&
+ ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
+ || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
+ || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
+ clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
+ state->mangled_body = 1;
+ }
+
+ old_max_msgno = mn_get_total(msgmap);
+
+ /*
+ * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
+ * then when the day changes the date column will change. All of the
+ * Today's will become Yesterday's at midnight. So we have to
+ * clear the cache at midnight.
+ */
+ if(format_includes_smartdate(stream)){
+ char db[200];
+ struct date nnow;
+
+ rfc822_date(db);
+ parse_date(db, &nnow);
+ if(old_day != -1 && nnow.day != old_day){
+ clear_index_cache(stream, 0);
+ state->mangled_body = 1;
+ }
+
+ old_day = nnow.day;
+ }
+
+ if(streams_died())
+ state->mangled_header = 1;
+
+ if(state->mangled_screen){
+ state->mangled_header = 1;
+ state->mangled_body = 1;
+ state->mangled_footer = 1;
+ state->mangled_screen = 0;
+ }
+
+ /*
+ * events may have occured that require us to shift from
+ * mode to another...
+ */
+ style = THRD_INDX()
+ ? ThreadIndex
+ : (any_lflagged(msgmap, MN_HIDE))
+ ? ZoomIndex
+ : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
+ if(style != old_style){
+ state->mangled_header = 1;
+ state->mangled_footer = 1;
+ old_style = style;
+ if(!(style == ThreadIndex || old_style == ThreadIndex))
+ id.msg_at_top = 0L;
+ }
+
+ /*------------ Update the title bar -----------*/
+ if(state->mangled_header) {
+ km = do_index_border(cntxt, folder, stream, msgmap,
+ style, NULL, INDX_HEADER);
+ state->mangled_header = 0;
+ paint_status = 0;
+ }
+ else if(mn_get_total(msgmap) > 0) {
+ update_titlebar_message();
+ /*
+ * If flags aren't available to update the status,
+ * defer it until after all the fetches associated
+ * with building index lines are done (no extra rtts!)...
+ */
+ paint_status = !update_titlebar_status();
+ }
+
+ current_index_state = &id;
+
+ /*------------ draw the index body ---------------*/
+ cur_row = update_index(state, &id);
+ if(F_OFF(F_SHOW_CURSOR, state)){
+ cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
+ cur_col = 0;
+ }
+ else if(id.status_col >= 0)
+ cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
+
+ ps_global->redrawer = redraw_index_body;
+
+ if(paint_status)
+ (void) update_titlebar_status();
+
+ /*------------ draw the footer/key menus ---------------*/
+ if(state->mangled_footer) {
+ if(!state->painted_footer_on_startup){
+ if(km_popped){
+ FOOTER_ROWS(state) = 3;
+ clearfooter(state);
+ }
+
+ km = do_index_border(cntxt, folder, stream, msgmap, style,
+ &which_keys, INDX_FOOTER);
+ if(km_popped){
+ FOOTER_ROWS(state) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ state->mangled_footer = 0;
+ }
+
+ state->painted_body_on_startup = 0;
+ state->painted_footer_on_startup = 0;
+
+ /*-- Display any queued message (eg, new mail, command result --*/
+ if(km_popped){
+ FOOTER_ROWS(state) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(state) = 1;
+ mark_status_unknown();
+ }
+
+ if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
+ cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
+ }
+
+ cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
+ MoveCursor(cur_row, cur_col);
+
+ /* Let read_command do the fflush(stdout) */
+
+ /*---------- Read command and validate it ----------------*/
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
+ state->ttyo->screen_cols);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback (index_scroll_callback);
+ mswin_sethelptextcallback((stream == state->mail_stream)
+ ? pcpine_help_index
+ : pcpine_help_index_simple);
+ mswin_setviewinwindcallback(view_in_new_window);
+#endif
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback(NULL);
+ mswin_sethelptextcallback(NULL);
+ mswin_setviewinwindcallback(NULL);
+#endif
+
+ cmd = menu_command(ch, km);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE :
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(state);
+ break;
+ }
+
+ /*----------- Execute the command ------------------*/
+ switch(cmd){
+
+ /*---------- Roll keymenu ----------*/
+ case MC_OTHER :
+ if(F_OFF(F_USE_FK, ps_global))
+ warn_other_cmds();
+
+ what_keymenu = NextMenu;
+ state->mangled_footer = 1;
+ break;
+
+
+ /*---------- Scroll line up ----------*/
+ case MC_CHARUP :
+ (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
+ index_scroll_up(1L);
+
+ break;
+
+
+ /*---------- Scroll line down ----------*/
+ case MC_CHARDOWN :
+ /*
+ * Special Page framing handling here. If we
+ * did something that should scroll-by-a-line, frame
+ * the page by hand here rather than leave it to the
+ * page-by-page framing in update_index()...
+ */
+ (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ for(j = 0L, k = i = id.msg_at_top; ; i++){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(j++ >= id.lines_per_page)
+ break;
+ }
+
+ if(i >= mn_get_total(msgmap)){
+ k = 0L; /* don't scroll */
+ break;
+ }
+ }
+
+ if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
+ index_scroll_down(1L);
+
+ break;
+
+
+ /*---------- Scroll page up ----------*/
+ case MC_PAGEUP :
+ j = -1L;
+ for(k = i = id.msg_at_top; ; i--){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(++j >= id.lines_per_page){
+ if((id.msg_at_top = i) == 1L)
+ q_status_message(SM_ORDER, 0, 1, _("First Index page"));
+
+ break;
+ }
+ }
+
+ if(i <= 1L){
+ if((!THREADING() && mn_get_cur(msgmap) == 1L)
+ || (THREADING()
+ && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
+ stream,
+ 0L,
+ FSF_SKIP_CHID)))
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already at start of Index"));
+
+ break;
+ }
+ }
+
+ if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, k);
+
+ break;
+
+
+ /*---------- Scroll page forward ----------*/
+ case MC_PAGEDN :
+ j = -1L;
+ for(k = i = id.msg_at_top; ; i++){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(++j >= id.lines_per_page){
+ if(i+id.lines_per_page > mn_get_total(msgmap))
+ q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
+
+ id.msg_at_top = i;
+ break;
+ }
+ }
+
+ if(i >= mn_get_total(msgmap)){
+ if(mn_get_cur(msgmap) == k)
+ q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
+
+ break;
+ }
+ }
+
+ if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, k);
+
+ break;
+
+
+ /*---------- Scroll to first page ----------*/
+ case MC_HOMEKEY :
+ if((mn_get_total(msgmap) > 0L)
+ && (mn_total_cur(msgmap) <= 1L)){
+ long cur_msg = mn_get_cur(msgmap), selected;
+
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do {
+ selected = cur_msg;
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ cur_msg = mn_get_cur(msgmap);
+ }
+ while(selected != cur_msg);
+ }
+ else
+ cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
+ mn_set_cur(msgmap, cur_msg);
+ q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
+ }
+ break;
+
+ /*---------- Scroll to last page ----------*/
+ case MC_ENDKEY :
+ if((mn_get_total(msgmap) > 0L)
+ && (mn_total_cur(msgmap) <= 1L)){
+ long cur_msg = mn_get_cur(msgmap), selected;
+
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do {
+ selected = cur_msg;
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ cur_msg = mn_get_cur(msgmap);
+ }
+ while(selected != cur_msg);
+ }
+ else
+ cur_msg = mn_get_total(msgmap);
+ mn_set_cur(msgmap, cur_msg);
+ q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
+ }
+ break;
+
+ /*---------- Search (where is command) ----------*/
+ case MC_WHEREIS :
+ index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
+ state->mangled_footer = 1;
+ break;
+
+
+ /*-------------- jump command -------------*/
+ /* NOTE: preempt the process_cmd() version because
+ * we need to get at the number..
+ */
+ case MC_JUMP :
+ j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
+ (style == ThreadIndex) ? ThrdIndx : MsgIndx);
+ if(j > 0L){
+ if(style == ThreadIndex){
+ PINETHRD_S *thrd;
+
+ thrd = find_thread_by_number(stream, msgmap, j, NULL);
+
+ if(thrd && thrd->rawno)
+ mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
+ }
+ else{
+ /* jump to message */
+ if(mn_total_cur(msgmap) > 1L){
+ mn_reset_cur(msgmap, j);
+ }
+ else{
+ mn_set_cur(msgmap, j);
+ }
+ }
+
+ id.msg_at_top = 0L;
+ }
+
+ state->mangled_footer = 1;
+ break;
+
+
+ case MC_VIEW_ENTRY : /* only happens in thread index */
+
+ /*
+ * If the feature F_THRD_AUTO_VIEW is turned on and there
+ * is only one message in the thread, then we skip the index
+ * view of the thread and go straight to the message view.
+ */
+view_a_thread:
+ if(THRD_AUTO_VIEW() && style == ThreadIndex){
+ PINETHRD_S *thrd;
+
+ thrd = fetch_thread(stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(thrd
+ && (count_lflags_in_thread(stream, thrd,
+ msgmap, MN_NONE) == 1)){
+ if(view_thread(state, stream, msgmap, 1)){
+ state->view_skipped_index = 1;
+ cmd = MC_VIEW_TEXT;
+ goto do_the_default;
+ }
+ }
+ }
+
+ if(view_thread(state, stream, msgmap, 1)){
+ ps_global->next_screen = mail_index_screen;
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+
+ break;
+
+
+ case MC_THRDINDX :
+ msgmap->top = msgmap->top_after_thrd;
+ if(unview_thread(state, stream, msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+
+ break;
+
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+ int new_cur;
+
+ mouse_get_last (NULL, &mp);
+ mp.row -= 2;
+
+ for(i = id.msg_at_top;
+ mp.row >= 0 && i <= mn_get_total(msgmap);
+ i++)
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ mp.row--;
+ new_cur = i;
+ }
+
+ if(mn_get_total(msgmap) && mp.row < 0){
+ switch(mp.button){
+ case M_BUTTON_LEFT :
+ if(mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, new_cur);
+
+ if(mp.flags & M_KEY_CONTROL){
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ (void) select_by_current(state, msgmap, MsgIndx);
+ }
+ }
+ else if(!(mp.flags & M_KEY_SHIFT)){
+ if (THREADING()
+ && mp.col >= 0
+ && mp.col == id.plus_col
+ && style != ThreadIndex){
+ collapse_or_expand(state, stream, msgmap,
+ mn_get_cur(msgmap));
+ }
+ else if (mp.doubleclick){
+ if(mp.button == M_BUTTON_LEFT){
+ if(stream == state->mail_stream){
+ if(THRD_INDX()){
+ cmd = MC_VIEW_ENTRY;
+ goto view_a_thread;
+ }
+ else{
+ cmd = MC_VIEW_TEXT;
+ goto do_the_default;
+ }
+ }
+
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+ }
+ }
+
+ break;
+
+ case M_BUTTON_MIDDLE:
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ if (!mp.doubleclick){
+ if(mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, new_cur);
+
+ cur_row = update_index(state, &id);
+
+ index_popup(style, stream, msgmap, TRUE);
+ }
+#endif
+ break;
+ }
+ }
+ else{
+ switch(mp.button){
+ case M_BUTTON_LEFT :
+ break;
+
+ case M_BUTTON_MIDDLE :
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ index_popup(style, stream, msgmap, FALSE);
+#endif
+ break;
+ }
+ }
+ }
+
+ break;
+#endif /* MOUSE */
+
+ /*---------- Resize ----------*/
+ case MC_RESIZE:
+ /*
+ * If we were smarter we could do the
+ * IC_CLEAR_WIDTHS_DONE trick here. The problem is
+ * that entire columns of the format can go away or
+ * appear because the width gets smaller or larger,
+ * so in that case we need to re-do. If we could tell
+ * when that happened or not we could set the flag
+ * selectively.
+ */
+ clear_index_cache(stream, 0);
+ reset_index_border();
+ break;
+
+ case MC_QUOTA:
+ cmd_quota(state);
+
+ /*---------- Redraw ----------*/
+ case MC_REPAINT :
+ force = 1; /* check for new mail! */
+ reset_index_border();
+ break;
+
+
+ /*---------- No op command ----------*/
+ case MC_NONE :
+ break; /* no op check for new mail */
+
+
+ /*--------- keystroke not bound to command --------*/
+ case MC_CHARRIGHT :
+ case MC_CHARLEFT :
+ case MC_GOTOBOL :
+ case MC_GOTOEOL :
+ case MC_UNKNOWN :
+ if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
+ q_status_message(SM_ORDER, 0, 1, "Already in Index");
+ else
+ bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
+
+ break;
+
+
+ case MC_COLLAPSE :
+ thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
+ break;
+
+ case MC_DELETE :
+ case MC_UNDELETE :
+ case MC_REPLY :
+ case MC_FORWARD :
+ case MC_TAKE :
+ case MC_SAVE :
+ case MC_EXPORT :
+ case MC_BOUNCE :
+ case MC_PIPE :
+ case MC_FLAG :
+ case MC_SELCUR :
+ { int collapsed = 0;
+ unsigned long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ if(THREADING()){
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ collapsed = thrd && thrd->next
+ && get_lflag(stream, NULL, rawno, MN_COLL);
+ }
+
+ if(collapsed){
+ thread_command(state, stream, msgmap,
+ ch, -FOOTER_ROWS(state));
+ /* increment current */
+ if(cmd == MC_DELETE){
+ advance_cur_after_delete(state, stream, msgmap,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View);
+ }
+ else if((cmd == MC_SELCUR
+ && (state->ugly_consider_advancing_bit
+ || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
+ || (state->ugly_consider_advancing_bit
+ && cmd == MC_SAVE
+ && F_ON(F_SAVE_ADVANCES, state))){
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ }
+ }
+ else
+ goto do_the_default;
+ }
+
+ break;
+
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, NULL);
+ break;
+
+
+ /*---------- First HELP command with menu hidden ----------*/
+ case MC_HELP :
+ if(FOOTER_ROWS(state) == 1 && km_popped == 0){
+ km_popped = 2;
+ mark_status_unknown();
+ mark_keymenu_dirty();
+ state->mangled_footer = 1;
+ break;
+ }
+ /* else fall thru to normal default */
+
+
+ /*---------- Default -- all other command ----------*/
+ default:
+ do_the_default:
+ if(stream == state->mail_stream){
+ msgmap->top = id.msg_at_top;
+ process_cmd(state, stream, msgmap, cmd,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ if(state->next_screen != SCREEN_FUN_NULL){
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+ else{
+ if(stream != state->mail_stream){
+ /*
+ * Must have had an failed open. repair our
+ * pointers...
+ */
+ id.stream = stream = state->mail_stream;
+ id.msgmap = msgmap = state->msgmap;
+ }
+
+ current_index_state = &id;
+
+ if(cmd == MC_ZOOM && THRD_INDX())
+ id.msg_at_top = 0L;
+ }
+ }
+ else{ /* special processing */
+ switch(cmd){
+ case MC_HELP :
+ helper(h_simple_index,
+ (!strcmp(folder, INTERRUPTED_MAIL))
+ ? _("HELP FOR SELECTING INTERRUPTED MSG")
+ : _("HELP FOR SELECTING POSTPONED MSG"),
+ HLPD_SIMPLE);
+ state->mangled_screen = 1;
+ break;
+
+ case MC_DELETE : /* delete */
+ dprint((3, "Special delete: msg %s\n",
+ long2string(mn_get_cur(msgmap))));
+ {
+ long raw, t;
+ int del = 0;
+ MESSAGECACHE *mc;
+
+ raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(raw > 0L && stream
+ && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw))
+ && !mc->deleted){
+ if((t = mn_get_cur(msgmap)) > 0L)
+ clear_index_cache_ent(stream, t, 0);
+
+ mail_setflag(stream,long2string(raw),"\\DELETED");
+ update_titlebar_status();
+ del++;
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ del ? _("Message %s deleted") : _("Message %s already deleted"),
+ long2string(mn_get_cur(msgmap)));
+ }
+
+ break;
+
+ case MC_UNDELETE : /* UNdelete */
+ dprint((3, "Special UNdelete: msg %s\n",
+ long2string(mn_get_cur(msgmap))));
+ {
+ long raw, t;
+ int del = 0;
+ MESSAGECACHE *mc;
+
+ raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(raw > 0L && stream
+ && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw))
+ && mc->deleted){
+ if((t = mn_get_cur(msgmap)) > 0L)
+ clear_index_cache_ent(stream, t, 0);
+
+ mail_clearflag(stream, long2string(raw),
+ "\\DELETED");
+ update_titlebar_status();
+ del++;
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
+ long2string(mn_get_cur(msgmap)));
+ }
+
+ break;
+
+ case MC_EXIT : /* exit */
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(1);
+
+ case MC_SELECT : /* select */
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+
+ case MC_PREVITEM : /* previous */
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ break;
+
+ case MC_NEXTITEM : /* next */
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ break;
+
+ default :
+ bogus_command(ch, NULL);
+ break;
+ }
+ }
+ } /* The big switch */
+ } /* the BIG while loop! */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Manage index body painting
+
+ Args: state - pine struct containing selected message data
+ index_state - struct describing what's currently displayed
+
+ Returns: screen row number of first highlighted message
+
+ The idea is pretty simple. Maintain an array of index line id's that
+ are displayed and their hilited state. Decide what's to be displayed
+ and update the screen appropriately. All index screen painting
+ is done here. Pretty simple, huh?
+ ----*/
+int
+update_index(struct pine *state, struct index_state *screen)
+{
+ int i, retval = -1, row, already_fetched = 0;
+ long n, visible;
+ PINETHRD_S *thrd = NULL;
+ int we_cancel = 0;
+
+ dprint((7, "--update_index--\n"));
+
+ if(!screen)
+ return(-1);
+
+#ifdef _WINDOWS
+ mswin_beginupdate();
+#endif
+
+ /*---- reset the works if necessary ----*/
+ if(state->mangled_body){
+ ClearBody();
+ if(screen->entry_state){
+ fs_give((void **)&(screen->entry_state));
+ screen->lines_per_page = 0;
+ }
+ }
+
+ state->mangled_body = 0;
+
+ /*---- make sure we have a place to write state ----*/
+ if(screen->lines_per_page
+ != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
+ - HEADER_ROWS(state))){
+ i = screen->lines_per_page;
+ screen->lines_per_page
+ = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
+ - HEADER_ROWS(state));
+ if(!i){
+ size_t len = screen->lines_per_page * sizeof(struct entry_state);
+ screen->entry_state = (struct entry_state *) fs_get(len);
+ }
+ else
+ fs_resize((void **)&(screen->entry_state),
+ (size_t)screen->lines_per_page);
+
+ for(; i < screen->lines_per_page; i++) /* init new entries */
+ memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
+ }
+
+ /*---- figure out the first message on the display ----*/
+ if(screen->msg_at_top < 1L
+ || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
+ screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
+ screen->msg_at_top,
+ screen->lines_per_page);
+ }
+ else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
+ long i, j, k;
+
+ /* scroll back a page at a time until current is displayed */
+ while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
+ for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
+ i > 0L && j > 0L;
+ j--)
+ if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
+ k = j;
+ i--;
+ }
+
+ if(i == screen->lines_per_page)
+ break; /* can't scroll back ? */
+ else
+ screen->msg_at_top = k;
+ }
+ }
+ else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
+ + screen->lines_per_page){
+ long i, j, k;
+
+ while(1){
+ for(i = screen->lines_per_page, j = k = screen->msg_at_top;
+ j <= mn_get_total(screen->msgmap) && i > 0L;
+ j++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
+ k = j;
+ i--;
+ if(mn_get_cur(screen->msgmap) <= k)
+ break;
+ }
+
+ if(mn_get_cur(screen->msgmap) <= k)
+ break;
+ else{
+ /* set msg_at_top to next displayed message */
+ for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
+ k = i;
+ break;
+ }
+
+ screen->msg_at_top = k;
+ }
+ }
+ }
+
+#ifdef _WINDOWS
+ /*
+ * Set scroll range and position. Note that message numbers start at 1
+ * while scroll position starts at 0.
+ */
+
+ if(THREADING() && sp_viewing_a_thread(screen->stream)
+ && mn_get_total(screen->msgmap) > 1L){
+ long x = 0L, range = 0L, lowest_numbered_msg;
+
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ thrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap,
+ mn_get_cur(screen->msgmap)));
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(screen->stream, thrd->top);
+
+ if(thrd){
+ if(mn_get_revsort(screen->msgmap)){
+ n = mn_raw2m(screen->msgmap, thrd->rawno);
+ while(n > 1L && get_lflag(screen->stream, screen->msgmap,
+ n-1L, MN_CHID2))
+ n--;
+
+ lowest_numbered_msg = n;
+ }
+ else
+ lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
+ }
+
+ if(thrd){
+ n = lowest_numbered_msg;
+ for(; n <= mn_get_total(screen->msgmap); n++){
+
+ if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
+ range++;
+ if(n < screen->msg_at_top)
+ x++;
+ }
+ }
+ }
+
+ scroll_setrange(screen->lines_per_page, range-1L);
+ scroll_setpos(x);
+ }
+ else if(THRD_INDX()){
+ if(any_lflagged(screen->msgmap, MN_HIDE)){
+ long x = 0L, range;
+
+ range = screen->msgmap->visible_threads - 1L;
+ scroll_setrange(screen->lines_per_page, range);
+ if(range >= screen->lines_per_page){ /* else not needed */
+ PINETHRD_S *topthrd;
+ int thrddir;
+ long xdir;
+
+ /* find top of currently displayed top line */
+ topthrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap,
+ screen->msg_at_top));
+ if(topthrd && topthrd->top != topthrd->rawno)
+ topthrd = fetch_thread(screen->stream, topthrd->top);
+
+ if(topthrd){
+ /*
+ * Split into two halves to speed up finding scroll pos.
+ * It's tricky because the thread list always goes from
+ * past to future but the thrdno's will be reversed if
+ * the sort is reversed and of course the order on the
+ * screen will be reversed.
+ */
+ if((!mn_get_revsort(screen->msgmap)
+ && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
+ ||
+ (mn_get_revsort(screen->msgmap)
+ && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
+
+ /* start with head of thread list */
+ if(topthrd && topthrd->head)
+ thrd = fetch_thread(screen->stream, topthrd->head);
+ else
+ thrd = NULL;
+
+ thrddir = 1;
+ }
+ else{
+ long tailrawno;
+
+ /*
+ * Start with tail thread and work back.
+ */
+ if(mn_get_revsort(screen->msgmap))
+ tailrawno = mn_m2raw(screen->msgmap, 1L);
+ else
+ tailrawno = mn_m2raw(screen->msgmap,
+ mn_get_total(screen->msgmap));
+
+ thrd = fetch_thread(screen->stream, tailrawno);
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(screen->stream, thrd->top);
+
+ thrddir = -1;
+ }
+
+ /*
+ * x is the scroll position. We try to use the fewest
+ * number of steps to find it, so we start with either
+ * the beginning or the end.
+ */
+ if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
+ x = 0L;
+ xdir = 1L;
+ }
+ else{
+ x = range;
+ xdir = -1L;
+ }
+
+ while(thrd && thrd != topthrd){
+ if(!msgline_hidden(screen->stream, screen->msgmap,
+ mn_raw2m(screen->msgmap,thrd->rawno),
+ 0))
+ x += xdir;
+
+ if(thrddir > 0 && thrd->nextthd)
+ thrd = fetch_thread(screen->stream, thrd->nextthd);
+ else if(thrddir < 0 && thrd->prevthd)
+ thrd = fetch_thread(screen->stream, thrd->prevthd);
+ else
+ thrd = NULL;
+ }
+ }
+
+ scroll_setpos(x);
+ }
+ }
+ else{
+ /*
+ * This works for forward or reverse sort because the thrdno's
+ * will have been reversed.
+ */
+ thrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap, screen->msg_at_top));
+ if(thrd){
+ scroll_setrange(screen->lines_per_page,
+ screen->msgmap->max_thrdno - 1L);
+ scroll_setpos(thrd->thrdno - 1L);
+ }
+ }
+ }
+ else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
+ long x, range;
+
+ range = mn_get_total(screen->msgmap) - n - 1L;
+ scroll_setrange(screen->lines_per_page, range);
+
+ if(range >= screen->lines_per_page){ /* else not needed */
+ if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
+ for(n = 1, x = 0; n != screen->msg_at_top; n++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ x++;
+ }
+ else{
+ for(n = mn_get_total(screen->msgmap), x = range;
+ n != screen->msg_at_top; n--)
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ x--;
+ }
+
+ scroll_setpos(x);
+ }
+ }
+ else{
+ scroll_setrange(screen->lines_per_page,
+ mn_get_total(screen->msgmap) - 1L);
+ scroll_setpos(screen->msg_at_top - 1L);
+ }
+#endif
+
+ /*
+ * Set up c-client call back to tell us about IMAP envelope arrivals
+ * Can't do it (easily) if single lines on the screen need information
+ * about more than a single message before they can be drawn.
+ */
+ if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
+ && !(THREADING() && (sp_viewing_a_thread(screen->stream)
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || any_lflagged(screen->msgmap, MN_COLL))))
+ mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
+
+ if(THRD_INDX())
+ visible = screen->msgmap->visible_threads;
+ else if(THREADING() && sp_viewing_a_thread(screen->stream)){
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ for(visible = 0L, n = screen->msg_at_top;
+ visible < (int) screen->lines_per_page
+ && n <= mn_get_total(screen->msgmap); n++){
+
+ if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ visible++;
+ }
+ }
+ else
+ visible = mn_get_total(screen->msgmap)
+ - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
+
+ /*---- march thru display lines, painting whatever is needed ----*/
+ for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
+ if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
+ if(screen->entry_state[i].id != LINE_HASH_N){
+ screen->entry_state[i].hilite = 0;
+ screen->entry_state[i].bolded = 0;
+ screen->entry_state[i].msgno = 0L;
+ screen->entry_state[i].id = LINE_HASH_N;
+ ClearLine(HEADER_ROWS(state) + i);
+ }
+ }
+ else{
+ ICE_S *ice;
+
+ /*
+ * This changes status_col as a side effect so it has to be
+ * executed before next line.
+ */
+ ice = build_header_line(state, screen->stream, screen->msgmap,
+ n, &already_fetched);
+ if(visible > 0L)
+ visible--;
+
+ if(THRD_INDX()){
+ unsigned long rawno;
+
+ rawno = mn_m2raw(screen->msgmap, n);
+ if(rawno)
+ thrd = fetch_thread(screen->stream, rawno);
+ }
+
+ row = paint_index_line(ice, i,
+ (THRD_INDX() && thrd) ? thrd->thrdno : n,
+ screen->status_fld, screen->plus_fld,
+ screen->arrow_fld, &screen->entry_state[i],
+ mn_is_cur(screen->msgmap, n),
+ THRD_INDX()
+ ? (count_lflags_in_thread(screen->stream,
+ thrd,
+ screen->msgmap,
+ MN_SLCT) > 0)
+ : get_lflag(screen->stream, screen->msgmap,
+ n, MN_SLCT));
+ fflush(stdout);
+ if(row && retval < 0)
+ retval = row;
+ }
+
+ /*--- increment n ---*/
+ while((visible == -1L || visible > 0L)
+ && ++n <= mn_get_total(screen->msgmap)
+ && msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ ;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
+
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+ fflush(stdout);
+ dprint((7, "--update_index done\n"));
+ return(retval);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Create a string summarizing the message header for index on screen
+
+ Args: stream -- mail stream to fetch envelope info from
+ msgmap -- message number to pine sort mapping
+ msgno -- Message number to create line for
+
+ Result: returns a malloced string
+ saves string in a cache for next call for same header
+ ----*/
+ICE_S *
+build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
+{
+ return(build_header_work(state, stream, msgmap, msgno,
+ current_index_state->msg_at_top,
+ current_index_state->lines_per_page,
+ already_fetched));
+}
+
+
+/*----------------------------------------------------------------------
+ Paint the given index line
+
+
+ Args: ice -- index cache entry
+ line -- index line number on screen, starting at 0 for first
+ visible line, 1, 2, ...
+ msgno -- for painting the message number field
+ sfld -- field type of the status field, which is
+ where we'll put the X for selected if necessary
+ pfld -- field type where the thread indicator symbol goes
+ afld -- field type of column which corresponds to the
+ index-format ARROW token
+ entry -- cache used to help us decide whether or not we need to
+ redraw the index line or if we can just leave it alone because
+ we know it is already correct
+ cur -- is this the current message?
+ sel -- is this message in the selected set?
+
+ Returns: screen row number if this is current message, else 0
+ ----*/
+int
+paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
+ IndexColType pfld, IndexColType afld, struct entry_state *entry,
+ int cur, int sel)
+{
+ COLOR_PAIR *lastc = NULL, *base_color = NULL;
+ ICE_S *ice;
+ IFIELD_S *ifield, *previfield = NULL;
+ IELEM_S *ielem;
+ int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
+ int draw_whole_line = 0, draw_partial_line = 0;
+ int n = MAX_SCREEN_COLS*6;
+ char draw[MAX_SCREEN_COLS*6+1], *p;
+
+ ice = (THRD_INDX() && argice) ? argice->tice : argice;
+
+ /* This better not happen! */
+ if(!ice){
+ q_status_message3(SM_ORDER | SM_DING, 5, 5,
+ "NULL ice in paint_index_line: %s, msgno=%s line=%s",
+ THRD_INDX() ? "THRD_INDX" : "reg index",
+ comatose(msgno), comatose(line));
+ dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
+ THRD_INDX() ? "THRD_INDX" : "reg index",
+ msgno, line));
+ return 0;
+ }
+
+ if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
+ entry->id = 0L;
+ entry->msgno = 0L;
+ draw_whole_line = 1;
+ }
+ else if((cur != entry->hilite) || (sel != entry->bolded)
+ || (ice->plus != entry->plus)){
+ draw_partial_line = 1;
+ }
+
+ if(draw_whole_line || draw_partial_line){
+
+ if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
+
+ memset(draw, 0, sizeof(draw));
+ p = draw;
+
+ for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
+ *p++ = ' ';
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ ielem->data[ifield->width] = '\0';
+ ielem->data[ielem->datalen] = '\0';
+ }
+ }
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(draw_partial_line)
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+
+ if(ielem->datalen == 1){
+ save_schar1 = ielem->data[0];
+ ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
+ if(draw_partial_line)
+ Writechar(ielem->data[0], 0);
+
+ if(ielem->next && ielem->next->datalen){
+ save_schar2 = ielem->next->data[0];
+ if(cur)
+ ielem->next->data[0] = '>';
+
+ if(draw_partial_line)
+ Writechar(ielem->next->data[0], 0);
+ }
+ }
+ else if(ielem->datalen > 1){
+ save_schar1 = ielem->data[0];
+ ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
+ if(draw_partial_line)
+ Writechar(ielem->data[0], 0);
+
+ save_schar2 = ielem->data[1];
+ if(cur){
+ ielem->data[1] = '>';
+ if(draw_partial_line)
+ Writechar(ielem->data[1], 0);
+ }
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+
+ if(draw_partial_line){
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+ for(i = 0; i < ifield->width-1; i++)
+ Writechar(cur ? '-' : ' ', 0);
+
+ Writechar(cur ? '>' : ' ', 0);
+ }
+
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ for(i = 0; i < ifield->width-1; i++)
+ ielem->data[i] = cur ? '-' : ' ';
+
+ ielem->data[i] = cur ? '>' : ' ';
+ }
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ save_pchar = ielem->data[0];
+ ielem->data[0] = ice->plus;
+
+ if(draw_partial_line){
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+ Writechar(ielem->data[0], 0);
+ Writechar(' ', 0);
+ }
+ }
+ }
+
+ for(ielem = ifield->ielem;
+ ielem && ielem->print_format && p-draw < n;
+ ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(p, src,
+ ((n+1)-(p-draw)) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ p += bytes_added;
+ }
+
+ draw[n] = '\0';
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ ielem->data[0] = save_schar1;
+ if(ielem->next && ielem->next->datalen)
+ ielem->next->data[0] = save_schar2;
+ }
+ else if(ielem->datalen > 1){
+ ielem->data[0] = save_schar1;
+ ielem->data[1] = save_schar2;
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width)
+ for(i = 0; i < ifield->width; i++)
+ ielem->data[i] = ' ';
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0)
+ ielem->data[0] = save_pchar;
+ }
+
+ previfield = ifield;
+ }
+
+ *p = '\0';
+
+ if(draw_whole_line)
+ PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
+ }
+ else{
+ int uc, ac, do_arrow;
+ int i, drew_X = 0;
+ int inverse_hack = 0, need_inverse_hack = 0;
+ int doing_bold = 0;
+
+ /* so we can restore current color at the end */
+ if((uc=pico_usingcolor()) != 0)
+ lastc = pico_get_cur_color();
+
+ /*
+ * There are two possible "arrow" cursors. One is the one that
+ * you get when you are at a slow speed or you turn that slow
+ * speed one on. It is drawn as part of the status column.
+ * That one is the one associated with the variable "ac".
+ * It is always the base_color or the inverse of the base_color.
+ *
+ * The other "arrow" cursor is the one you get by including the
+ * ARROW token in the index-format. It may be configured to
+ * be colored.
+ *
+ * The arrow cursors have two special properties that make
+ * them different from other sections or fields.
+ * First, the arrow cursors only show up on the current line.
+ * Second, the arrow cursors are drawn with generated data, not
+ * data that is present in the passed in data.
+ */
+
+ /* ac is for the old integrated arrow cursor */
+ ac = F_ON(F_FORCE_ARROW,ps_global);
+
+ /* do_arrow is for the ARROW token in index-format */
+ do_arrow = (afld != iNothing);
+
+ MoveCursor(HEADER_ROWS(ps_global) + line, 0);
+
+ /* find the base color for the whole line */
+ if(cur && !ac && !do_arrow){
+ /*
+ * This stanza handles the current line marking in the
+ * regular, non-arrow-cursor case.
+ */
+
+ /*
+ * If the current line has a linecolor, apply the
+ * appropriate reverse transformation to show it is current.
+ */
+ if(uc && ice->linecolor && ice->linecolor->fg[0]
+ && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
+ base_color = apply_rev_color(ice->linecolor,
+ ps_global->index_color_style);
+
+ (void)pico_set_colorp(base_color, PSC_NONE);
+ }
+ else{
+ inverse_hack++;
+ if(uc){
+ COLOR_PAIR *rev;
+
+ if((rev = pico_get_rev_color()) != NULL){
+ base_color = new_color_pair(rev->fg, rev->bg);
+ (void)pico_set_colorp(base_color, PSC_NONE);
+ }
+ else
+ base_color = lastc;
+ }
+ }
+ }
+ else if(uc && ice->linecolor && ice->linecolor->fg[0]
+ && ice->linecolor->bg[0]
+ && pico_is_good_colorpair(ice->linecolor)){
+ (void)pico_set_colorp(ice->linecolor, PSC_NONE);
+ base_color = ice->linecolor;
+ }
+ else
+ base_color = lastc;
+
+ memset(draw, 0, sizeof(draw));
+ p = draw;
+
+ doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
+
+ /* draw each field */
+ for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
+
+ drew_X = 0;
+
+ /*
+ * Fix up the data for some special cases.
+ */
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ ielem->data[ifield->width] = '\0';
+ ielem->data[ielem->datalen] = '\0';
+ }
+ }
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ save_schar1 = ielem->data[0];
+ if(sel && !doing_bold){
+ ielem->data[0] = 'X';
+ drew_X++;
+ }
+ else if(ac && cur && ielem->data[0] == ' ')
+ ielem->data[0] = '-';
+
+ if(ielem->next && ielem->next->datalen){
+ save_schar2 = ielem->next->data[0];
+ if(ac && cur && ielem->next->data[0] != '\0')
+ ielem->next->data[0] = '>';
+ }
+ }
+ else if(ielem->datalen > 1){
+ if(sel && !doing_bold){
+ ielem->data[0] = 'X';
+ drew_X++;
+ }
+ else if(ac && cur && ielem->data[0] == ' ')
+ ielem->data[0] = '-';
+
+ save_schar2 = ielem->data[1];
+ if(ac && cur && ielem->data[1] != '\0')
+ ielem->data[1] = '>';
+ }
+ }
+ }
+ else if(ifield->ctype == afld && do_arrow && cur){
+
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ for(i = 0; i < ifield->width-1; i++)
+ ielem->data[i] = cur ? '-' : ' ';
+
+ ielem->data[i] = '>';
+ }
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ save_pchar = ielem->data[0];
+ ielem->data[0] = ice->plus;
+ }
+ }
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
+ if(inverse_hack)
+ StartInverse();
+
+ Write_to_screen(" ");
+ if(inverse_hack)
+ EndInverse();
+ }
+
+ for(ielem = ifield->ielem; ielem; ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(draw, src,
+ (n+1) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ draw[n] = '\0';
+
+ /*
+ * Switch to color for ielem.
+ * But don't switch if we drew an X in this column,
+ * because that overwrites the colored thing, and don't
+ * switch if this is the ARROW field and this is not
+ * the current message. ARROW field is only colored for
+ * the current message.
+ * And don't switch if current line and type eTypeCol.
+ */
+ if(ielem->color && pico_is_good_colorpair(ielem->color)
+ && !(do_arrow && ifield->ctype == afld && !cur)
+ && (!drew_X || ielem != ifield->ielem)
+ && !(cur && ielem->type == eTypeCol)){
+ need_inverse_hack = 0;
+ (void) pico_set_colorp(ielem->color, PSC_NORM);
+ }
+ else
+ need_inverse_hack = 1;
+
+ if(need_inverse_hack && inverse_hack)
+ StartInverse();
+
+ Write_to_screen(draw);
+ if(need_inverse_hack && inverse_hack)
+ EndInverse();
+
+ (void) pico_set_colorp(base_color, PSC_NORM);
+ }
+
+ /*
+ * Restore the data for the special cases.
+ */
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ ielem->data[0] = save_schar1;
+ if(ielem->next && ielem->next->datalen)
+ ielem->next->data[0] = save_schar2;
+ }
+ else if(ielem->datalen > 1){
+ ielem->data[0] = save_schar1;
+ ielem->data[1] = save_schar2;
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width)
+ for(i = 0; i < ifield->width; i++)
+ ielem->data[i] = ' ';
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0)
+ ielem->data[0] = save_pchar;
+ }
+
+ previfield = ifield;
+ }
+
+ if(doing_bold)
+ EndBold();
+
+ if(base_color && base_color != lastc && base_color != ice->linecolor)
+ free_color_pair(&base_color);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NORM);
+ free_color_pair(&lastc);
+ }
+ }
+ }
+
+ entry->hilite = cur;
+ entry->bolded = sel;
+ entry->msgno = msgno;
+ entry->plus = ice->plus;
+ entry->id = ice->id;
+
+ if(!ice->color_lookup_done && pico_usingcolor())
+ entry->id = 0;
+
+ return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
+}
+
+/*
+ * setup_index_state - hooked onto pith_opt_save_index_state to setup
+ * current_index_state after setup_{index,thread}_header_widths
+ */
+void
+setup_index_state(int threaded)
+{
+ if(current_index_state){
+ if(threaded){
+ current_index_state->status_col = 0;
+ current_index_state->status_fld = iStatus;
+ current_index_state->plus_fld = iNothing;
+ current_index_state->arrow_fld = iNothing;
+ } else {
+ INDEX_COL_S *cdesc, *prevcdesc = NULL;
+ IndexColType sfld, altfld, plusfld, arrowfld;
+ int width, fld, col, pluscol, scol, altcol;
+
+ col = 0;
+ scol = -1;
+ sfld = iNothing;
+ altcol = -1;
+ altfld = iNothing;
+ /* figure out which field is status field */
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ /* space between columns */
+ if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
+ col++;
+
+ if(cdesc->ctype == iStatus){
+ scol = col;
+ sfld = cdesc->ctype;
+ break;
+ }
+
+ if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
+ scol = col;
+ sfld = cdesc->ctype;
+ break;
+ }
+
+ if(cdesc->ctype == iMessNo){
+ altcol = col;
+ altfld = cdesc->ctype;
+ }
+
+ col += width;
+ fld++;
+ prevcdesc = cdesc;
+ }
+
+ if(sfld == iNothing){
+ if(altcol == -1){
+ scol = 0;
+ }
+ else{
+ scol = altcol;
+ sfld = altfld;
+ }
+ }
+
+
+ current_index_state->status_col = scol;
+ current_index_state->status_fld = sfld;
+
+ col = 0;
+ plusfld = iNothing;
+ pluscol = -1;
+ prevcdesc = NULL;
+ /* figure out which column to use for threading '+' */
+ if(THREADING()
+ && ps_global->thread_disp_style != THREAD_NONE
+ && ps_global->VAR_THREAD_MORE_CHAR[0]
+ && ps_global->VAR_THREAD_EXP_CHAR[0])
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ /* space between columns */
+ if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
+ col++;
+
+ if((cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText)
+ && (ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
+ plusfld = cdesc->ctype;
+ pluscol = col;
+ break;
+ }
+
+ if((cdesc->ctype == iFrom
+ || cdesc->ctype == iFromToNotNews
+ || cdesc->ctype == iFromTo
+ || cdesc->ctype == iAddress
+ || cdesc->ctype == iMailbox)
+ && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
+ || ps_global->thread_disp_style == THREAD_INDENT_FROM2
+ || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
+ plusfld = cdesc->ctype;
+ pluscol = col;
+ break;
+ }
+
+ col += width;
+ fld++;
+ prevcdesc = cdesc;
+ }
+
+ current_index_state->plus_fld = plusfld;
+ current_index_state->plus_col = pluscol;
+
+ arrowfld = iNothing;
+ /* figure out which field is arrow field, if any */
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ if(cdesc->ctype == iArrow){
+ arrowfld = cdesc->ctype;
+ break;
+ }
+
+ fld++;
+ }
+
+ current_index_state->arrow_fld = arrowfld;
+ }
+ }
+}
+
+
+/*
+ * insert_condensed_thread_cue - used on pith hook to add decoration to
+ * subject or from text to show condensed thread info
+ */
+int
+condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
+ char **fieldstr, size_t *strsize, int width, int collapsed)
+{
+ if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
+ * and VAR_THREAD_EXP_CHAR[0] are ascii.
+ * Could do something similar to the conversions done with keyword
+ * initials in key_str.
+ */
+ if(ice)
+ ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
+ : (thd && thd->next)
+ ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
+
+ if(strsize && *strsize > 0 && width != 0){
+ *(*fieldstr)++ = ' ';
+ (*strsize)--;
+ if(width > 0)
+ width--;
+ }
+
+ if(strsize && *strsize > 0 && width != 0){
+ *(*fieldstr)++ = ' ';
+ (*strsize)--;
+ if(width > 0)
+ width--;
+ }
+ }
+
+ return(width);
+}
+
+
+int
+truncate_subj_and_from_strings(void)
+{
+ return 1;
+}
+
+
+/*
+ * paint_index_hline - paint index line given what we got
+ */
+void
+paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
+{
+ PINETHRD_S *thrd;
+
+ /*
+ * Trust only what we get back that isn't bogus since
+ * we were prevented from doing any fetches and such...
+ */
+ if((ps_global->redrawer == redraw_index_body
+ || ps_global->prev_screen == mail_index_screen)
+ && current_index_state
+ && current_index_state->stream == stream
+ && !ps_global->msgmap->hilited){
+ int line;
+
+ /*
+ * This test isn't right if there are hidden lines. The line will
+ * fail the test because it seems like it is past the end of the
+ * screen but since the hidden lines don't take up space the line
+ * might actually be on the screen. Don't know that it is worth
+ * it to fix this, though, since you may have to file through
+ * many hidden lines before finding the visible ones. I'm not sure
+ * if the logic inside the if is correct when we do pass the
+ * top-level test. Leave it for now. Hubert - 2002-06-28
+ */
+ if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
+ && line < current_index_state->lines_per_page){
+ if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
+ long n;
+ long zoomhide, collapsehide;
+
+ zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
+ collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
+
+ /*
+ * Line is visible if it is selected and not hidden due to
+ * thread collapse, or if there is no zooming happening and
+ * it is not hidden due to thread collapse.
+ */
+ for(line = 0, n = current_index_state->msg_at_top;
+ n != msgno;
+ n++)
+ if((zoomhide
+ && get_lflag(stream, current_index_state->msgmap,
+ n, MN_SLCT)
+ && (!collapsehide
+ || !get_lflag(stream, current_index_state->msgmap, n,
+ MN_CHID)))
+ ||
+ (!zoomhide
+ && !get_lflag(stream, current_index_state->msgmap,
+ n, MN_CHID)))
+ line++;
+ }
+
+ thrd = NULL;
+ if(THRD_INDX()){
+ unsigned long rawno;
+
+ rawno = mn_m2raw(current_index_state->msgmap, msgno);
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+ }
+
+ paint_index_line(ice, line,
+ (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
+ current_index_state->status_fld,
+ current_index_state->plus_fld,
+ current_index_state->arrow_fld,
+ &current_index_state->entry_state[line],
+ mn_is_cur(current_index_state->msgmap, msgno),
+ THRD_INDX()
+ ? (count_lflags_in_thread(stream, thrd,
+ current_index_state->msgmap,
+ MN_SLCT) > 0)
+ : get_lflag(stream, current_index_state->msgmap,
+ msgno, MN_SLCT));
+ fflush(stdout);
+ }
+ }
+}
+
+
+
+
+/*
+ * pine_imap_env -- C-client's telling us an envelope just arrived
+ * from the server. Use it if we can...
+ */
+void
+pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
+{
+ MESSAGECACHE *mc;
+
+ dprint((7, "imap_env(%ld)\n", rawno));
+ if(stream && !sp_mail_box_changed(stream)
+ && stream == ps_global->mail_stream
+ && rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && mc->valid
+ && mc->rfc822_size
+ && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
+ INDEXDATA_S idata;
+ ICE_S *ice;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.no_fetch = 1;
+ idata.size = mc->rfc822_size;
+ idata.rawno = rawno;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
+ idata.stream = stream;
+
+ index_data_env(&idata, env);
+
+ /*
+ * Look for resent-to already in MAILCACHE data
+ */
+ if(mc->private.msg.header.text.data){
+ STRINGLIST *lines;
+ SIZEDTEXT szt;
+ static char *linelist[] = {"resent-to" , NULL};
+
+ if(mail_match_lines(lines = new_strlst(linelist),
+ mc->private.msg.lines, 0L)){
+ idata.valid_resent_to = 1;
+ memset(&szt, 0, sizeof(SIZEDTEXT));
+ textcpy(&szt, &mc->private.msg.header.text);
+ mail_filter((char *) szt.data, szt.size, lines, 0L);
+ idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
+ if(szt.data)
+ fs_give((void **) &szt.data);
+ }
+
+ free_strlst(&lines);
+ }
+
+ ice = (*format_index_line)(&idata);
+ if(idata.bogus)
+ clear_ice(&ice);
+ else
+ paint_index_hline(stream, idata.msgno, ice);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Scroll to specified postion.
+
+
+ Args: pos - position to scroll to.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_to_pos (long int pos)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+
+ if(bad_timing)
+ return (FALSE);
+
+ /*
+ * Put the requested line at the top of the screen...
+ */
+
+ /*
+ * Starting at msg 'pos' find next visible message.
+ */
+ for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ current_index_state->msg_at_top = i;
+ break;
+ }
+ }
+
+ /*
+ * If single selection, move selected message to be on the screen.
+ */
+ if (mn_total_cur(current_index_state->msgmap) == 1L) {
+ if (current_index_state->msg_at_top >
+ mn_get_cur (current_index_state->msgmap)) {
+ /* Selection was above screen, move to top of screen. */
+ mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
+ }
+ else {
+ /* Scan through the screen. If selection found, leave where is.
+ * Otherwise, move to end of screen */
+ for( i = current_index_state->msg_at_top,
+ j = current_index_state->lines_per_page;
+ i != mn_get_cur(current_index_state->msgmap) &&
+ i <= mn_get_total(current_index_state->msgmap) &&
+ j > 0L;
+ i++) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ j--;
+ k = i;
+ }
+ }
+ if(j <= 0L)
+ /* Move to end of screen. */
+ mn_set_cur(current_index_state->msgmap, k);
+ }
+ }
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Adjust the index display state down a line
+
+ Args: scroll_count -- number of lines to scroll
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_down(long int scroll_count)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+ long cur, total;
+
+ if(bad_timing)
+ return (FALSE);
+
+ bad_timing = 1;
+
+
+ j = -1L;
+ total = mn_get_total (current_index_state->msgmap);
+ for(k = i = current_index_state->msg_at_top; ; i++){
+
+ /* Only examine non-hidden messages. */
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ /* Remember this message */
+ k = i;
+ /* Increment count of lines. */
+ if (++j >= scroll_count) {
+ /* Counted enough lines, stop. */
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /* If at last message, stop. */
+ if (i >= total){
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /*
+ * If not multiple selection, see if selected message visable. if not
+ * set it to last visable message.
+ */
+ if(mn_total_cur(current_index_state->msgmap) == 1L) {
+ j = 0L;
+ cur = mn_get_cur (current_index_state->msgmap);
+ for (i = current_index_state->msg_at_top; i <= total; ++i) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ if (++j >= current_index_state->lines_per_page) {
+ break;
+ }
+ if (i == cur)
+ break;
+ }
+ }
+ if (i != cur)
+ mn_set_cur(current_index_state->msgmap,
+ current_index_state->msg_at_top);
+ }
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Adjust the index display state up a line
+
+ Args: scroll_count -- number of lines to scroll
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+
+ ----*/
+int
+index_scroll_up(long int scroll_count)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+ long cur;
+
+ if(bad_timing)
+ return(FALSE);
+
+ bad_timing = 1;
+
+ j = -1L;
+ for(k = i = current_index_state->msg_at_top; ; i--){
+
+ /* Only examine non-hidden messages. */
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ /* Remember this message */
+ k = i;
+ /* Increment count of lines. */
+ if (++j >= scroll_count) {
+ /* Counted enough lines, stop. */
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /* If at first message, stop */
+ if (i <= 1L){
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+
+ /*
+ * If not multiple selection, see if selected message visable. if not
+ * set it to last visable message.
+ */
+ if(mn_total_cur(current_index_state->msgmap) == 1L) {
+ j = 0L;
+ cur = mn_get_cur (current_index_state->msgmap);
+ for ( i = current_index_state->msg_at_top;
+ i <= mn_get_total(current_index_state->msgmap);
+ ++i) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ if (++j >= current_index_state->lines_per_page) {
+ k = i;
+ break;
+ }
+ if (i == cur)
+ break;
+ }
+ }
+ if (i != cur)
+ mn_set_cur(current_index_state->msgmap, k);
+ }
+
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Calculate the message number that should be at the top of the display
+
+ Args: current - the current message number
+ lines_per_page - the number of lines for the body of the index only
+
+ Returns: -1 if the current message is -1
+ the message entry for the first message at the top of the screen.
+
+When paging in the index it is always on even page boundies, and the
+current message is always on the page thus the top of the page is
+completely determined by the current message and the number of lines
+on the page.
+ ----*/
+long
+top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
+{
+ long current, hidden, lastn;
+ long n, m = 0L, t = 1L;
+
+ current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
+
+ if(current < 0L)
+ return(-1);
+
+ if(lines_per_page == 0L)
+ return(current);
+
+ if(THRD_INDX_ENABLED()){
+ long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ rawno = mn_m2raw(msgs, mn_get_cur(msgs));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(THRD_INDX()){
+
+ if(any_lflagged(msgs, MN_HIDE)){
+ PINETHRD_S *is_current_thrd;
+
+ is_current_thrd = thrd;
+ if(is_current_thrd){
+ if(mn_get_revsort(msgs)){
+ /* start with top of tail of thread list */
+ thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+ }
+ else{
+ /* start with head of thread list */
+ thrd = fetch_head_thread(stream);
+ }
+
+ t = 1L;
+ m = 0L;
+ if(thrd)
+ n = mn_raw2m(msgs, thrd->rawno);
+
+ while(thrd){
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+
+ if(thrd == is_current_thrd)
+ break;
+
+ if(mn_get_revsort(msgs) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else if(!mn_get_revsort(msgs) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+
+ if(thrd)
+ n = mn_raw2m(msgs, thrd->rawno);
+ }
+ }
+ }
+ else{
+ if(thrd){
+ n = thrd->thrdno;
+ m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
+ n = thrd->rawno;
+ /*
+ * We want to find the m'th thread and the
+ * message number that goes with that. We just have
+ * to back up from where we are to get there.
+ * If we have a reverse sort backing up is going
+ * forward through the thread.
+ */
+ while(thrd && m < thrd->thrdno){
+ n = thrd->rawno;
+ if(mn_get_revsort(msgs) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else if(!mn_get_revsort(msgs) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else
+ thrd = NULL;
+ }
+
+ if(thrd)
+ n = thrd->rawno;
+
+ t = mn_raw2m(msgs, n);
+ }
+ }
+ }
+ else{ /* viewing a thread */
+
+ lastn = mn_get_total(msgs);
+ t = 1L;
+
+ /* get top of thread */
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ if(thrd){
+ if(mn_get_revsort(msgs))
+ lastn = mn_raw2m(msgs, thrd->rawno);
+ else
+ t = mn_raw2m(msgs, thrd->rawno);
+ }
+
+ n = 0L;
+
+ /* n is the end of this thread */
+ while(thrd){
+ n = mn_raw2m(msgs, thrd->rawno);
+ if(thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
+ else if(thrd->next)
+ thrd = fetch_thread(stream, thrd->next);
+ else
+ thrd = NULL;
+ }
+
+ if(n){
+ if(mn_get_revsort(msgs))
+ t = n;
+ else
+ lastn = n;
+ }
+
+ for(m = 0L, n = t; n <= MIN(current, lastn); n++)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+ }
+
+ return(t);
+ }
+ else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
+
+ if(current < mn_get_total(msgs) / 2){
+ t = 1L;
+ m = 0L;
+ for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+ }
+ else{
+ t = current+1L;
+ m = mn_get_total(msgs)-hidden+1L;
+ for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (--m % lines_per_page) == 1L)
+ t = n;
+
+ if(t > current)
+ t = 1L;
+ }
+
+ return(t);
+ }
+ else
+ return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
+}
+
+
+/*----------------------------------------------------------------------
+ Clear various bits that make up a healthy display
+
+ ----*/
+void
+reset_index_border(void)
+{
+ mark_status_dirty();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+ ps_global->mangled_screen = 1; /* signal FULL repaint */
+}
+
+
+/*----------------------------------------------------------------------
+ This redraws the body of the index screen, taking into
+account any change in the size of the screen. All the state needed to
+repaint is in the static variables so this can be called from
+anywhere.
+ ----*/
+void
+redraw_index_body(void)
+{
+ int agg;
+
+ if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
+ restore_selected(current_index_state->msgmap);
+
+ ps_global->mangled_body = 1;
+
+ (void) update_index(ps_global, current_index_state);
+ if(agg)
+ pseudo_selected(current_index_state->stream, current_index_state->msgmap);
+}
+
+
+/*----------------------------------------------------------------------
+ Give hint about Other command being optional. Some people get the idea
+ that it is required to use the commands on the 2nd and 3rd keymenus.
+
+ Args: none
+
+ Result: message may be printed to status line
+ ----*/
+void
+warn_other_cmds(void)
+{
+ static int other_cmds = 0;
+
+ other_cmds++;
+ if(((ps_global->first_time_user || ps_global->show_new_version) &&
+ other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
+ q_status_message(SM_ASYNC, 0, 9,
+ _("Remember the \"O\" command is always optional"));
+}
+
+
+void
+thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ UCS preloadkeystroke, int q_line)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, save_branch;
+ int we_cancel = 0;
+ int flags = AC_FROM_THREAD;
+
+ if(!(stream && msgmap))
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return;
+
+ save_branch = thrd->branch;
+ thrd->branch = 0L; /* branch is a sibling, not part of thread */
+
+ if(!preloadkeystroke){
+ if(!THRD_INDX()){
+ if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
+ flags |= AC_EXPN;
+ else
+ flags |= AC_COLL;
+ }
+
+ if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
+ == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
+ flags |= AC_UNSEL;
+ }
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /* save the SLCT flags in STMP for restoring at the bottom */
+ copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
+
+ /* clear the values from the SLCT flags */
+ set_lflags(stream, msgmap, MN_SLCT, 0);
+
+ /* set SLCT for thrd on down */
+ set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
+ thrd->branch = save_branch;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
+ q_line);
+
+ /* restore the original flags */
+ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
+
+ if(any_lflagged(msgmap, MN_HIDE) > 0L){
+ /* if nothing left selected, unhide all */
+ if(any_lflagged(msgmap, MN_SLCT) == 0L){
+ (void) unzoom_index(ps_global, stream, msgmap);
+ dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
+ q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
+ }
+
+ /* if current is hidden, adjust */
+ adjust_cur_to_visible(stream, msgmap);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Search the message headers as displayed in index
+
+ Args: command_line -- The screen line to prompt on
+ msg -- The current message number to start searching at
+ max_msg -- The largest message number in the current folder
+
+ The headers are searched exactly as they are displayed on the screen. The
+search will wrap around to the beginning if not string is not found right
+away.
+ ----*/
+void
+index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
+{
+ int rc, select_all = 0, flags, prefetch, we_turned_on = 0;
+ long i, sorted_msg, selected = 0L;
+ char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
+ char buf[MAX_SCREEN_COLS+1], *p;
+ HelpType help;
+ char search_string[MAX_SEARCH+1];
+ ICE_S *ice, *ic;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
+ {ctrl('Y'), 10, "^Y", N_("First Msg")},
+ {ctrl('V'), 11, "^V", N_("Last Msg")},
+ {KEY_UP, 30, "", ""},
+ {KEY_DOWN, 31, "", ""},
+ {-1, 0, NULL, NULL} };
+#define KU_IS (3) /* index of KEY_UP */
+#define PREFETCH_THIS_MANY_LINES (50)
+
+ init_hist(&history, HISTSIZE);
+ search_string[0] = '\0';
+ if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
+ strncpy(search_string, p, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ dprint((4, "\n - search headers - \n"));
+
+ if(!any_messages(msgmap, NULL, "to search")){
+ return;
+ }
+ else if(mn_total_cur(msgmap) > 1L){
+ q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
+ comatose(mn_total_cur(msgmap)));
+ return;
+ }
+ else
+ sorted_msg = mn_get_cur(msgmap);
+
+ help = NO_HELP;
+ new_string[0] = '\0';
+
+ while(1) {
+ snprintf(prompt, sizeof(prompt), _("Word to search for [%s] : "), search_string);
+
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ header_search_key[0].ch = ctrl('X');
+ header_search_key[0].rval = 12;
+ header_search_key[0].name = "^X";
+ header_search_key[0].label = N_("Select Matches");
+ }
+ else{
+ header_search_key[0].ch = header_search_key[0].rval = 0;
+ header_search_key[0].name = header_search_key[0].label = NULL;
+ }
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
+ header_search_key[KU_IS].label = HISTORY_KEYLABEL;
+ header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
+ header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ header_search_key[KU_IS].name = "";
+ header_search_key[KU_IS].label = "";
+ header_search_key[KU_IS+1].name = "";
+ header_search_key[KU_IS+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+
+ rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
+ prompt, header_search_key, help, &flags);
+
+ if(rc == 3) {
+ help = (help != NO_HELP) ? NO_HELP :
+ F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
+ : h_os_index_whereis;
+ continue;
+ }
+ else if(rc == 10){
+ q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do{
+ selected = sorted_msg;
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ sorted_msg = mn_get_cur(msgmap);
+ }
+ while(selected != sorted_msg);
+ }
+ else
+ sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
+
+ mn_set_cur(msgmap, sorted_msg);
+ return;
+ }
+ else if(rc == 11){
+ q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do{
+ selected = sorted_msg;
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ sorted_msg = mn_get_cur(msgmap);
+ }
+ while(selected != sorted_msg);
+ }
+ else
+ sorted_msg = mn_get_total(msgmap);
+
+ mn_set_cur(msgmap, sorted_msg);
+ return;
+ }
+ else if(rc == 12){
+ select_all = 1;
+ break;
+ }
+ else if(rc == 30){
+ if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
+ strncpy(new_string, p, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 31){
+ if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
+ strncpy(new_string, p, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+
+ if(rc != 4){ /* 4 is redraw */
+ save_hist(history, new_string, 0, NULL);
+ break;
+ }
+ }
+
+ if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
+ cmd_cancelled(_("Search"));
+ return;
+ }
+
+ if(new_string[0] == '\0'){
+ strncpy(new_string, search_string, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+
+ strncpy(search_string, new_string, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+
+ we_turned_on = intr_handling_on();
+
+ prefetch = 0;
+ for(i = sorted_msg + ((select_all)?0:1);
+ i <= mn_get_total(msgmap) && !ps_global->intr_pending;
+ i++){
+ if(msgline_hidden(stream, msgmap, i, 0))
+ continue;
+
+ if(prefetch <= 0)
+ prefetch = PREFETCH_THIS_MANY_LINES;
+
+ ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ if(select_all)
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+ else
+ break;
+ }
+ }
+
+ prefetch = 0;
+ if(i > mn_get_total(msgmap)){
+ for(i = 1; i < sorted_msg && !ps_global->intr_pending; i++){
+ if(msgline_hidden(stream, msgmap, i, 0))
+ continue;
+
+ if(prefetch <= 0)
+ prefetch = PREFETCH_THIS_MANY_LINES;
+
+ ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ if(select_all)
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+ else
+ break;
+ }
+ }
+ }
+
+ /* search current line */
+ if(!select_all && !selected){
+ i = sorted_msg;
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+
+ ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ }
+ }
+ }
+
+ if(ps_global->intr_pending){
+ q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
+ select_all ? _(" Selected set may be incomplete."):"");
+ }
+ else if(select_all){
+ if(selected
+ && any_lflagged(msgmap, MN_SLCT) > 0L
+ && !any_lflagged(msgmap, MN_HIDE)
+ && F_ON(F_AUTO_ZOOM, state))
+ (void) zoom_index(state, stream, msgmap, MN_SLCT);
+
+ q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
+ long2string(selected));
+ }
+ else if(selected){
+ q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
+ (i < sorted_msg) ? _(". Search wrapped to beginning") :
+ (i == sorted_msg) ? _(". Current line contains only match") : "");
+ mn_set_cur(msgmap, i);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Word not found"));
+
+ if(we_turned_on)
+ intr_handling_off();
+}
+
+
+/*
+ * Original idea from Stephen Casner <casner@acm.org>.
+ *
+ * Apply the appropriate reverse color transformation to the given
+ * color pair and return a new color pair. The caller should free the
+ * color pair.
+ *
+ */
+COLOR_PAIR *
+apply_rev_color(COLOR_PAIR *cp, int style)
+{
+ COLOR_PAIR *rc = pico_get_rev_color();
+
+ if(rc){
+ if(style == IND_COL_REV){
+ /* just use Reverse color regardless */
+ return(new_color_pair(rc->fg, rc->bg));
+ }
+ else if(style == IND_COL_FG){
+ /*
+ * If changing to Rev fg is readable and different
+ * from what it already is, do it.
+ */
+ if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
+ return(new_color_pair(rc->fg, cp->bg));
+ }
+ else if(style == IND_COL_BG){
+ /*
+ * If changing to Rev bg is readable and different
+ * from what it already is, do it.
+ */
+ if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
+ return(new_color_pair(cp->fg, rc->bg));
+ }
+ else if(style == IND_COL_FG_NOAMBIG){
+ /*
+ * If changing to Rev fg is readable, different
+ * from what it already is, and not the same as
+ * the Rev color, do it.
+ */
+ if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
+ strcmp(rc->bg, cp->bg))
+ return(new_color_pair(rc->fg, cp->bg));
+ }
+ else if(style == IND_COL_BG_NOAMBIG){
+ /*
+ * If changing to Rev bg is readable, different
+ * from what it already is, and not the same as
+ * the Rev color, do it.
+ */
+ if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
+ strcmp(rc->fg, cp->fg))
+ return(new_color_pair(cp->fg, rc->bg));
+ }
+ }
+
+ /* come here for IND_COL_FLIP and for the cases which fail the tests */
+ return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
+}
+
+
+
+#ifdef _WINDOWS
+
+/*----------------------------------------------------------------------
+ Callback to get the text of the current message. Used to display
+ a message in an alternate window.
+
+ Args: cmd - what type of scroll operation.
+ text - filled with pointer to text.
+ l - length of text.
+ style - Returns style of text. Can be:
+ GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
+ lines
+ GETTEXT_LINES - Is a pointer to NULL terminated array of
+ char *. Each entry points to a line of
+ text.
+
+ this implementation always returns GETTEXT_TEXT.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_callback (cmd, scroll_pos)
+int cmd;
+long scroll_pos;
+{
+ int paint = TRUE;
+
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ paint = index_scroll_up (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ paint = index_scroll_down (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ paint = index_scroll_up (current_index_state->lines_per_page);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ paint = index_scroll_down (current_index_state->lines_per_page);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ /* Normalize msgno in zoomed case */
+ if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
+ long n, x;
+
+ for(n = 1L, x = 0;
+ x < scroll_pos && n < mn_get_total(ps_global->msgmap);
+ n++)
+ if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
+ n, 0))
+ x++;
+
+ scroll_pos = n - 1; /* list-position --> message number */
+ }
+
+ paint = index_scroll_to_pos (scroll_pos + 1);
+ break;
+ }
+
+ if(paint){
+ mswin_beginupdate();
+ update_titlebar_message();
+ update_titlebar_status();
+ redraw_index_body();
+ mswin_endupdate();
+ }
+
+ return(paint);
+}
+
+
+/*----------------------------------------------------------------------
+ MSWin scroll callback to get the text of the current message
+
+ Args: title - title for new window
+ text -
+ l -
+ style -
+
+ Returns: TRUE - got the requested text
+ FALSE - was not able to get the requested text
+ ----*/
+int
+index_gettext_callback(title, titlelen, text, l, style)
+ char *title;
+ size_t titlelen;
+ void **text;
+ long *l;
+ int *style;
+{
+ int rv = 0;
+ ENVELOPE *env;
+ BODY *body;
+ STORE_S *so;
+ gf_io_t pc;
+
+ if(mn_get_total(ps_global->msgmap) > 0L
+ && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
+ gf_set_so_writec(&pc, so);
+
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ &body))
+ && format_message(mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ env, body, NULL, FM_NEW_MESS, pc)){
+ snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
+ strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
+ mn_get_cur(ps_global->msgmap),
+ mn_get_total(ps_global->msgmap));
+ title[titlelen-1] = '\0';
+ *text = so_text(so);
+ *l = strlen((char *)so_text(so));
+ *style = GETTEXT_TEXT;
+
+ /* free alloc'd so, but preserve the text passed back to caller */
+ so->txt = (void *) NULL;
+ rv = 1;
+ }
+
+ gf_clear_so_writec(so);
+ so_give(&so);
+ }
+
+ return(rv);
+}
+
+
+/*
+ *
+ */
+int
+index_sort_callback(set, order)
+ int set;
+ long order;
+{
+ int i = 0;
+
+ if(set){
+ sort_folder(ps_global->mail_stream, ps_global->msgmap,
+ order & 0x000000ff,
+ (order & 0x00000100) != 0, SRT_VRB);
+ mswin_beginupdate();
+ update_titlebar_message();
+ update_titlebar_status();
+ redraw_index_body();
+ mswin_endupdate();
+ flush_status_messages(1);
+ }
+ else{
+ i = (int) mn_get_sort(ps_global->msgmap);
+ if(mn_get_revsort(ps_global->msgmap))
+ i |= 0x0100;
+ }
+
+ return(i);
+}
+
+
+/*
+ *
+ */
+void
+index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
+{
+ int n = 0;
+ int view_in_new_wind_index = -1;
+ long rawno;
+ MESSAGECACHE *mc;
+ MPopup view_index_popup[32];
+ struct key_menu *km = (style == ThreadIndex)
+ ? &thread_keymenu
+ : (ps_global->mail_stream != stream)
+ ? &simple_index_keymenu
+ : &index_keymenu;
+
+ /*
+ * Loosely follow the logic in do_index_border to figure
+ * out which commands to show.
+ */
+
+ if(full){
+ if(km != &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&View Thread" : "&View";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'V';
+ }
+
+ if(km == &index_keymenu){
+ view_in_new_wind_index = n;
+ view_index_popup[n].type = tIndex;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].label.string = "View in New Window";
+ }
+
+ if(km != &simple_index_keymenu)
+ view_index_popup[n++].type = tSeparator;
+
+ if(km == &thread_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = "&Delete Thread";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'D';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = "&UnDelete Thread";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'U';
+ }
+ else{
+ /* Make "delete/undelete" item sensitive */
+ mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ if(mc && mc->deleted){
+ view_index_popup[n].label.string = "&Undelete";
+ view_index_popup[n++].data.val = 'U';
+ }
+ else{
+ view_index_popup[n].label.string = "&Delete";
+ view_index_popup[n++].data.val = 'D';
+ }
+ }
+
+ if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
+ view_index_popup[n].type = tSubMenu;
+ view_index_popup[n].label.string = "Flag";
+ view_index_popup[n++].data.submenu = flag_submenu(mc);
+ }
+
+ if(km == &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Select";
+ view_index_popup[n++].data.val = 'S';
+ }
+ else{
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Save Thread" : "&Save";
+ view_index_popup[n++].data.val = 'S';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Export Thread" : "&Export";
+ view_index_popup[n++].data.val = 'E';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Print";
+ view_index_popup[n++].data.val = '%';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Reply To Thread" : "&Reply";
+ view_index_popup[n++].data.val = 'R';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Forward Thread" : "&Forward";
+ view_index_popup[n++].data.val = 'F';
+
+ if(F_ON(F_ENABLE_BOUNCE, ps_global)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Bounce Thread" : "&Bounce";
+ view_index_popup[n++].data.val = 'B';
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Take Addresses";
+ view_index_popup[n++].data.val = 'T';
+
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "[Un]Select Current";
+ view_index_popup[n++].data.val = ':';
+ }
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&WhereIs";
+ view_index_popup[n++].data.val = 'W';
+
+ view_index_popup[n++].type = tSeparator;
+ }
+
+ if(km == &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Exit Select";
+ view_index_popup[n++].data.val = 'E';
+ }
+ else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Thread Index";
+ view_index_popup[n++].data.val = '<';
+ }
+ else{
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Folder &List";
+ view_index_popup[n++].data.val = '<';
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Main Menu";
+ view_index_popup[n++].data.val = 'M';
+
+ view_index_popup[n].type = tTail;
+
+ if(mswin_popup(view_index_popup) == view_in_new_wind_index
+ && view_in_new_wind_index >= 0)
+ view_in_new_window();
+}
+
+
+char *
+pcpine_help_index(title)
+ char *title;
+{
+ /*
+ * Title is size 256 in pico. Put in args.
+ */
+ if(title)
+ strncpy(title, "Alpine MESSAGE INDEX Help", 256);
+
+ return(pcpine_help(h_mail_index));
+}
+
+char *
+pcpine_help_index_simple(title)
+ char *title;
+{
+ /*
+ * Title is size 256 in pico. Put in args.
+ */
+ if(title)
+ strncpy(title, "Alpine SELECT MESSAGE Help", 256);
+
+ return(pcpine_help(h_simple_index));
+}
+
+
+#include "../pico/osdep/mswin_tw.h"
+
+
+void
+view_in_new_window(void)
+{
+ char title[GETTEXT_TITLELEN+1];
+ void *text;
+ long len;
+ int format;
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ /* Launch text in alt window. */
+ if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
+ if(format == GETTEXT_TEXT)
+ mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
+ NULL, MSWIN_DT_USEALTWINDOW);
+ else if(format == GETTEXT_LINES)
+ mswin_tw = mswin_displaytext(title, NULL, 0, text,
+ NULL, MSWIN_DT_USEALTWINDOW);
+
+ if(mswin_tw)
+ mswin_set_readonly(mswin_tw, FALSE);
+ }
+}
+
+#endif /* _WINDOWS */