#if !defined(lint) && !defined(DOS) static char rcsid[] = "$Id: mailindx.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $"; #endif /* * ======================================================================== * Copyright 2013-2021 Eduardo Chappa * Copyright 2006-2008 University of Washington * * 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 = 0, km_popped, paint_status = 0; static int old_day = -1; long i, j, k, old_max_msgno; long lflagged, old_lflagged = 0L; 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 occurred 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; lflagged = any_lflagged(msgmap, MN_HIDE); if(style != old_style || lflagged < old_lflagged){ state->mangled_header = 1; state->mangled_footer = 1; old_style = style; old_lflagged = lflagged; 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 : if(any_lflagged(msgmap, MN_SLCT)){ PINETHRD_S *thrd, *topthrd; for(i = 1L; i > 0L && i <= mn_get_total(msgmap);){ thrd = fetch_thread(stream, i); if(thrd && thrd->top) topthrd = fetch_thread(stream, thrd->top); else topthrd = NULL; if(topthrd){ set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1); set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0); set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0); set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1); } i = thrd->nextthd; } } 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 = 0; 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); if(F_ON(F_ENABLE_DEL_WHEN_WRITING, ps_global)) CleartoEOLN(); /* 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; src = ielem->data; 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 == iShortSubject || 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, ¤t_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 position. 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 = 0; 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 visible. if not * set it to last visible 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 visible. if not * set it to last visible 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, searchbound, otherbound, offset = 0, 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 int direction = 1; 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")}, {ctrl('W'), 20, "^W", ""}, {0, 0, NULL, NULL }, {KEY_UP, 30, "", ""}, {KEY_DOWN, 31, "", ""}, {-1, 0, NULL, NULL} }; #define KD_IS (3) /* index of Direction key */ #define KU_IS (5) /* 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 %sfor [%s] : "), direction == -1 ? _("(backwards) ") : "", 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 = ""; } header_search_key[KD_IS].label = direction == 1 ? _("Backward") : _("Forward"); 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; } else if(rc == 20){ direction *= -1; 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; if (direction == -1) { searchbound = -1; otherbound = mn_get_total(msgmap); offset = 2; } else { searchbound = mn_get_total(msgmap); otherbound = 1; } for(i = sorted_msg + ((select_all)?0:1) - offset; i * direction <= searchbound && !ps_global->intr_pending; i += direction){ 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 * direction > searchbound){ for(i = otherbound; i * direction < sorted_msg * direction && !ps_global->intr_pending; i += direction ){ 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 * direction < sorted_msg * direction) ? _(". 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 . * * 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 = NULL; /* 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 != NULL) mswin_set_readonly(mswin_tw, FALSE); } } #endif /* _WINDOWS */