alpine/chappa-fancy.patch

3199 lines
105 KiB
Diff

---
alpine/arg.c | 14
alpine/confscroll.c | 66 +++
alpine/confscroll.h | 2
alpine/keymenu.c | 47 ++
alpine/keymenu.h | 13
alpine/mailcmd.c | 464 +++++++++++++++++++++++++-
alpine/mailcmd.h | 13
alpine/mailindx.c | 124 ++++++-
alpine/mailindx.h | 2
alpine/mailview.c | 46 ++
alpine/roleconf.c | 10
alpine/setup.c | 60 +++
pith/conf.c | 24 +
pith/conf.h | 3
pith/conftype.h | 3
pith/flag.c | 6
pith/indxtype.h | 2
pith/mailindx.c | 58 ++-
pith/pattern.c | 2
pith/pine.hlp | 222 ++++++++++++
pith/sort.c | 33 +
pith/sort.h | 6
pith/state.c | 1
pith/state.h | 5
pith/thread.c | 772 ++++++++++++++++++++++++++++++++++++++++----
pith/thread.h | 23 +
web/src/alpined.d/alpined.c | 4
27 files changed, 1870 insertions(+), 155 deletions(-)
Index: alpine-2.20/alpine/arg.c
===================================================================
--- alpine-2.20.orig/alpine/arg.c
+++ alpine-2.20/alpine/arg.c
@@ -64,6 +64,7 @@ static char args_err_non_abs_pwdcertdir[
#endif /* SMIME inside PASSFILE */
#endif
static char args_err_missing_sort[] = N_("missing argument for option \"-sort\"");
+static char args_err_missing_thread_sort[] = N_("missing argument for option \"-threadsort\"");
static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\"");
static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\"");
static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\"");
@@ -107,6 +108,7 @@ N_(" -k \t\tKeys - Force use of function
N_(" -z \t\tSuspend - allow use of ^Z suspension"),
N_(" -r \t\tRestricted - can only send mail to oneself"),
N_(" -sort <sort>\tSort - Specify sort order of folder:"),
+N_(" -threadsort <sort>\tSort - Specify sort order of thread index screen:"),
N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"),
N_("\t\t\tfrom, size, score, to, cc, /reverse"),
N_(" -i\t\tIndex - Go directly to index, bypassing main menu"),
@@ -200,6 +202,7 @@ pine_args(struct pine *pine_state, int a
char *cmd_list = NULL;
char *debug_str = NULL;
char *sort = NULL;
+ char *threadsort = NULL;
char *pinerc_file = NULL;
char *lc = NULL;
int do_help = 0;
@@ -396,6 +399,17 @@ Loop: while(--ac > 0)
goto Loop;
}
+ else if(strcmp(*av, "threadsort") == 0){
+ if(--ac){
+ threadsort = *++av;
+ COM_THREAD_SORT_KEY = cpystr(threadsort);
+ }
+ else{
+ display_args_err(_(args_err_missing_thread_sort), NULL, 1);
+ ++usage;
+ }
+ goto Loop;
+ }
else if(strcmp(*av, "url") == 0){
if(args->action == aaFolder && !args->data.folder){
args->action = aaURL;
Index: alpine-2.20/alpine/confscroll.c
===================================================================
--- alpine-2.20.orig/alpine/confscroll.c
+++ alpine-2.20/alpine/confscroll.c
@@ -139,7 +139,7 @@ char *yesno_pretty_value(struct pine
char *radio_pretty_value(struct pine *, CONF_S *);
char *sigfile_pretty_value(struct pine *, CONF_S *);
char *color_pretty_value(struct pine *, CONF_S *);
-char *sort_pretty_value(struct pine *, CONF_S *);
+char *sort_pretty_value(struct pine *, CONF_S *, int);
int longest_feature_name(void);
COLOR_PAIR *sample_color(struct pine *, struct variable *);
COLOR_PAIR *sampleexc_color(struct pine *, struct variable *);
@@ -287,7 +287,8 @@ set_radio_pretty_vals(struct pine *ps, C
CONF_S *ctmp;
if(!(cl && *cl &&
- ((*cl)->var == &ps->vars[V_SORT_KEY] ||
+ (((*cl)->var == &ps->vars[V_SORT_KEY]) ||
+ ((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]) ||
standard_radio_var(ps, (*cl)->var) ||
(*cl)->var == startup_ptr)))
return;
@@ -2927,7 +2928,7 @@ radiobutton_tool(struct pine *ps, int cm
}
set_current_val((*cl)->var, TRUE, TRUE);
- if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){
+ if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev,0) != -1){
ps->def_sort = def_sort;
ps->def_sort_rev = def_sort_rev;
}
@@ -2936,6 +2937,37 @@ radiobutton_tool(struct pine *ps, int cm
ps->mangled_body = 1; /* BUG: redraw it all for now? */
rv = 1;
}
+ else if((*cl)->var == &ps->vars[V_THREAD_SORT_KEY]){
+ SortOrder thread_def_sort;
+ int thread_def_sort_rev;
+
+ thread_def_sort_rev = (*cl)->varmem >= (short) EndofList;
+ thread_def_sort = (SortOrder) ((*cl)->varmem - (thread_def_sort_rev
+ * EndofList));
+ sprintf(tmp_20k_buf, "%s%s", sort_name(thread_def_sort),
+ (thread_def_sort_rev) ? "/Reverse" : "");
+
+ if((*cl)->var->cmdline_val.p)
+ fs_give((void **)&(*cl)->var->cmdline_val.p);
+
+ if(apval){
+ if(*apval)
+ fs_give((void **)apval);
+
+ *apval = cpystr(tmp_20k_buf);
+ }
+
+ set_current_val((*cl)->var, TRUE, TRUE);
+ if(decode_sort(ps->VAR_THREAD_SORT_KEY, &thread_def_sort,
+ &thread_def_sort_rev, 1) != -1){
+ ps->thread_def_sort = thread_def_sort;
+ ps->thread_def_sort_rev = thread_def_sort_rev;
+ }
+
+ set_radio_pretty_vals(ps, cl);
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+ }
else
q_status_message(SM_ORDER | SM_DING, 3, 6,
"Programmer botch! Unknown radiobutton type.");
@@ -3799,7 +3831,9 @@ pretty_value(struct pine *ps, CONF_S *cl
else if(standard_radio_var(ps, v) || v == startup_ptr)
return(radio_pretty_value(ps, cl));
else if(v == &ps->vars[V_SORT_KEY])
- return(sort_pretty_value(ps, cl));
+ return(sort_pretty_value(ps, cl, 0));
+ else if(v == &ps->vars[V_THREAD_SORT_KEY])
+ return(sort_pretty_value(ps, cl, 1));
else if(v == &ps->vars[V_SIGNATURE_FILE])
return(sigfile_pretty_value(ps, cl));
else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME])
@@ -4330,14 +4364,14 @@ color_pretty_value(struct pine *ps, CONF
char *
-sort_pretty_value(struct pine *ps, CONF_S *cl)
+sort_pretty_value(struct pine *ps, CONF_S *cl, int thread)
{
- return(generalized_sort_pretty_value(ps, cl, 1));
+ return(generalized_sort_pretty_value(ps, cl, 1, thread));
}
char *
-generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
+generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok, int thread)
{
char tmp[6*MAXPATH];
char *pvalnorm, *pvalexc, *pval;
@@ -4387,7 +4421,7 @@ generalized_sort_pretty_value(struct pin
}
else if(fixed){
pval = v->fixed_val.p;
- decode_sort(pval, &var_sort, &var_sort_rev);
+ decode_sort(pval, &var_sort, &var_sort_rev, thread);
is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort);
utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s",
@@ -4398,9 +4432,9 @@ generalized_sort_pretty_value(struct pin
is_the_one ? " (value is fixed)" : "");
}
else if(is_set_for_this_level){
- decode_sort(pval, &var_sort, &var_sort_rev);
+ decode_sort(pval, &var_sort, &var_sort_rev, thread);
is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort);
- decode_sort(pvalexc, &exc_sort, &exc_sort_rev);
+ decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread);
the_exc_one = (editing_normal_which_isnt_except && pvalexc &&
exc_sort_rev == line_sort_rev && exc_sort == line_sort);
utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s",
@@ -4418,7 +4452,7 @@ generalized_sort_pretty_value(struct pin
}
else{
if(pvalexc){
- decode_sort(pvalexc, &exc_sort, &exc_sort_rev);
+ decode_sort(pvalexc, &exc_sort, &exc_sort_rev, thread);
is_the_one = (exc_sort_rev == line_sort_rev &&
exc_sort == line_sort);
utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s",
@@ -4429,7 +4463,7 @@ generalized_sort_pretty_value(struct pin
}
else{
pval = v->current_val.p;
- decode_sort(pval, &var_sort, &var_sort_rev);
+ decode_sort(pval, &var_sort, &var_sort_rev, thread);
is_the_one = ((pval || default_ok) &&
var_sort_rev == line_sort_rev &&
var_sort == line_sort);
@@ -5571,9 +5605,15 @@ fix_side_effects(struct pine *ps, struct
else if(revert && var == &ps->vars[V_SORT_KEY]){
int def_sort_rev;
- decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev);
+ decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev, 0);
ps->def_sort_rev = def_sort_rev;
}
+ else if(revert && var == &ps->vars[V_THREAD_SORT_KEY]){
+ int thread_def_sort_rev;
+
+ decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort, &thread_def_sort_rev, 1);
+ ps->thread_def_sort_rev = thread_def_sort_rev;
+ }
else if(var == &ps->vars[V_THREAD_MORE_CHAR] ||
var == &ps->vars[V_THREAD_EXP_CHAR] ||
var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){
Index: alpine-2.20/alpine/confscroll.h
===================================================================
--- alpine-2.20.orig/alpine/confscroll.h
+++ alpine-2.20/alpine/confscroll.h
@@ -95,7 +95,7 @@ int checkbox_tool(struct pine *, int, C
int radiobutton_tool(struct pine *, int, CONF_S **, unsigned);
int yesno_tool(struct pine *, int, CONF_S **, unsigned);
int text_toolit(struct pine *, int, CONF_S **, unsigned, int);
-char *generalized_sort_pretty_value(struct pine *, CONF_S *, int);
+char *generalized_sort_pretty_value(struct pine *, CONF_S *, int, int);
int exclude_config_var(struct pine *, struct variable *, int);
int config_exit_cmd(unsigned);
int simple_exit_cmd(unsigned);
Index: alpine-2.20/alpine/keymenu.c
===================================================================
--- alpine-2.20.orig/alpine/keymenu.c
+++ alpine-2.20/alpine/keymenu.c
@@ -650,10 +650,25 @@ struct key index_keys[] =
RCOMPOSE_MENU,
HOMEKEY_MENU,
ENDKEY_MENU,
- NULL_MENU,
+ {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE},
/* TRANSLATORS: toggles a collapsed view or an expanded view
of a message thread on and off */
{"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE},
+ /* TRANSLATORS: Collapse all threads */
+ {"{",N_("Collapse All"),{MC_KOLAPSE,1,{'{'}},KS_NONE},
+ /* TRANSLATORS: Expand all threads */
+ {"}",N_("Expand All"), {MC_EXPTHREAD,1,{'}'}},KS_NONE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ {")","Next Threa",{MC_NEXTHREAD,1,{')'}},KS_NONE},
+ {"(","Prev Threa",{MC_PRETHREAD,1,{'('}},KS_NONE},
+ {"^R","Remove Thr",{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE},
+ {"^U","Undel Thre",{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE},
+ {"^T","Select Thr",{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE},
+ NULL_MENU,
+ {"[","Close Thre",{MC_CTHREAD,1,{'['}},KS_NONE},
+ {"]","Open Threa",{MC_OTHREAD,1,{']'}},KS_NONE},
{"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE},
NULL_MENU};
INST_KEY_MENU(index_keymenu, index_keys);
@@ -728,9 +743,22 @@ struct key thread_keys[] =
RCOMPOSE_MENU,
HOMEKEY_MENU,
ENDKEY_MENU,
- NULL_MENU,
+ {"]",N_("Open Thread"),{MC_OTHREAD,1,{']'}},KS_NONE},
{"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE},
+ {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE},
+ {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE},
+
+ HELP_MENU,
+ OTHER_MENU,
{"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE},
+ NULL_MENU,
+ {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE},
+ {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE},
+ {"^T",N_("SelecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"K","Sort Thread",{MC_SORTHREAD,1,{'k'}},KS_NONE},
NULL_MENU};
INST_KEY_MENU(thread_keymenu, thread_keys);
@@ -880,7 +908,20 @@ struct key view_keys[] =
NULL_MENU,
NULL_MENU,
NULL_MENU,
- NULL_MENU};
+ NULL_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"(",N_("Prev Thread"),{MC_PRETHREAD,1,{'('}},KS_NONE},
+ {")",N_("Next Thread"),{MC_NEXTHREAD,1,{')'}},KS_NONE},
+ {"^R",N_("Remove Thread"),{MC_DELTHREAD,1,{ctrl('r')}},KS_NONE},
+ {"^U",N_("Undelete Thread"),{MC_UNDTHREAD,1,{ctrl('u')}},KS_NONE},
+ {"^T",N_("selecT Thread"),{MC_SELTHREAD,1,{ctrl('t')}},KS_NONE}};
INST_KEY_MENU(view_keymenu, view_keys);
Index: alpine-2.20/alpine/keymenu.h
===================================================================
--- alpine-2.20.orig/alpine/keymenu.h
+++ alpine-2.20/alpine/keymenu.h
@@ -215,6 +215,19 @@ struct key_menu {
#define MC_DECRYPT 802
#define MC_QUOTA 803
#define MC_ADDHEADER 804
+#define MC_DELTHREAD 805
+#define MC_UNDTHREAD 806
+#define MC_SELTHREAD 807
+#define MC_SSUTHREAD 808
+#define MC_DSUTHREAD 809
+#define MC_USUTHREAD 810
+#define MC_SORTHREAD 811
+#define MC_NEXTHREAD 812
+#define MC_KOLAPSE 813
+#define MC_EXPTHREAD 814
+#define MC_PRETHREAD 815
+#define MC_CTHREAD 816
+#define MC_OTHREAD 817
/* Commands for S/MIME screens */
Index: alpine-2.20/alpine/mailcmd.c
===================================================================
--- alpine-2.20.orig/alpine/mailcmd.c
+++ alpine-2.20/alpine/mailcmd.c
@@ -113,7 +113,7 @@ int select_by_thread(MAILSTREAM *, MSG
char *choose_a_rule(int);
int select_by_keyword(MAILSTREAM *, SEARCHSET **);
char *choose_a_keyword(void);
-int select_sort(struct pine *, int, SortOrder *, int *);
+int select_sort(struct pine *, int, SortOrder *, int *, int);
int print_index(struct pine *, MSGNO_S *, int);
/*
@@ -1371,7 +1371,7 @@ get_out:
if(any_messages(msgmap, NULL, NULL)){
if(any_lflagged(msgmap, MN_SLCT) > 0L){
if(apply_command(state, stream, msgmap, 0,
- AC_NONE, question_line)){
+ AC_NONE, question_line, 1)){
if(F_ON(F_AUTO_UNSELECT, state)){
agg_select_all(stream, msgmap, NULL, 0);
unzoom_index(state, stream, msgmap);
@@ -1389,23 +1389,35 @@ get_out:
/*-------- Sort command -------*/
case MC_SORT :
+ case MC_SORTHREAD:
{
int were_threading = THREADING();
SortOrder sort = mn_get_sort(msgmap);
int rev = mn_get_revsort(msgmap);
+ int thread = (command == MC_SORT) ? 0 : 1;
dprint((1,"MAIL_CMD: sort\n"));
- if(select_sort(state, question_line, &sort, &rev)){
+ if(sort == SortThread)
+ sort = ps_global->thread_cur_sort;
+ if(select_sort(state, question_line, &sort, &rev, thread)){
/* $ command reinitializes threading collapsed/expanded info */
if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX())
erase_threading_info(stream, msgmap);
+ if(command == MC_SORTHREAD){
+ ps_global->thread_cur_sort = sort;
+ sort = SortThread;
+ }
+ else if(sort == SortThread) /* command = MC_SORT */
+ ps_global->thread_cur_sort = F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)
+ ? SortArrival : ps_global->thread_def_sort;
+
if(ps_global && ps_global->ttyo){
blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
ps_global->mangled_footer = 1;
}
- sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN);
+ sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN, 1);
}
state->mangled_footer = 1;
@@ -3286,6 +3298,10 @@ cmd_expunge(struct pine *state, MAILSTRE
if(SORT_IS_THREADED(msgmap))
refresh_sort(stream, msgmap, SRT_NON);
+ if (msgmap->nmsgs
+ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS())
+ kolapse_thread(state, stream, msgmap, '[', 0);
+
state->mangled_body = 1;
state->mangled_header = 1;
q_status_message2(SM_ORDER, 0, 4,
@@ -3387,6 +3403,9 @@ cmd_expunge(struct pine *state, MAILSTRE
*/
if(SORT_IS_THREADED(msgmap))
refresh_sort(stream, msgmap, SRT_NON);
+ if (msgmap->nmsgs
+ && F_ON(F_ENHANCED_THREAD, state) && COLL_THRDS())
+ kolapse_thread(state, stream, msgmap, '[', 0);
}
else{
if(del_count){
@@ -7090,7 +7109,7 @@ select_by_current(struct pine *state, MS
* Maybe it makes sense to zoom after a select but not after a colon
* command even though they are very similar.
*/
- thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state));
+ thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state), 1);
}
else{
if((all_selected =
@@ -7146,7 +7165,7 @@ select_by_current(struct pine *state, MS
----*/
int
apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
- UCS preloadkeystroke, int flags, int q_line)
+ UCS preloadkeystroke, int flags, int q_line, int display)
{
int i = 8, /* number of static entries in sel_opts3 */
rv = 0,
@@ -7305,9 +7324,19 @@ apply_command(struct pine *state, MAILST
collapse_or_expand(state, stream, msgmap,
F_ON(F_SLASH_COLL_ENTIRE, ps_global)
? 0L
- : mn_get_cur(msgmap));
+ : mn_get_cur(msgmap),
+ display);
break;
+ case '[' :
+ collapse_this_thread(state, stream, msgmap, display, 0);
+ break;
+
+ case ']' :
+ expand_this_thread(state, stream, msgmap, display, 0);
+ break;
+
+
case ':' :
select_thread_stmp(state, stream, msgmap);
break;
@@ -9171,10 +9200,10 @@ Args: state -- pine state pointer
Returns 0 if it was cancelled, 1 otherwise.
----*/
int
-select_sort(struct pine *state, int ql, SortOrder *sort, int *rev)
+select_sort(struct pine *state, int ql, SortOrder *sort, int *rev, int thread)
{
char prompt[200], tmp[3], *p;
- int s, i;
+ int s, i, j;
int deefault = 'a', retval = 1;
HelpType help;
ESCKEY_S sorts[14];
@@ -9207,17 +9236,26 @@ select_sort(struct pine *state, int ql,
strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "),
sizeof(prompt));
- for(i = 0; state->sort_types[i] != EndofList; i++) {
- sorts[i].rval = i;
- p = sorts[i].label = sort_name(state->sort_types[i]);
- while(*(p+1) && islower((unsigned char)*p))
- p++;
-
- sorts[i].ch = tolower((unsigned char)(tmp[0] = *p));
- sorts[i].name = cpystr(tmp);
-
- if(mn_get_sort(state->msgmap) == state->sort_types[i])
- deefault = sorts[i].rval;
+ for(i = 0, j = 0; state->sort_types[i] != EndofList; i++) {
+ sorts[i].rval = i;
+ sorts[i].name = cpystr("");
+ sorts[i].label = "";
+ sorts[i].ch = -2;
+ if (!thread || allowed_thread_key(state->sort_types[i])){
+ p = sorts[j].label = sort_name(state->sort_types[i]);
+ while(*(p+1) && islower((unsigned char)*p))
+ p++;
+ sorts[j].ch = tolower((unsigned char)(tmp[0] = *p));
+ sorts[j++].name = cpystr(tmp);
+ }
+
+ if (thread){
+ if (state->thread_def_sort == state->sort_types[i])
+ deefault = sorts[j-1].rval;
+ }
+ else
+ if(mn_get_sort(state->msgmap) == state->sort_types[i])
+ deefault = sorts[i].rval;
}
sorts[i].ch = 'r';
@@ -9241,8 +9279,17 @@ select_sort(struct pine *state, int ql,
state->mangled_body = 1; /* signal screen's changed */
if(s == 'r')
*rev = !mn_get_revsort(state->msgmap);
- else
+ else{
+ if(thread){
+ for(i = 0; state->sort_types[i] != EndofList; i++){
+ if(struncmp(sort_name(state->sort_types[i]),
+ sorts[s].label, strlen(sorts[s].label)) == 0)
+ break;
+ }
+ s = i;
+ }
*sort = state->sort_types[s];
+ }
if(F_ON(F_SHOW_SORT, ps_global))
ps_global->mangled_header = 1;
@@ -9627,3 +9674,378 @@ flag_submenu(mc)
}
#endif /* _WINDOWS */
+
+void
+cmd_delete_this_thread(state, stream, msgmap)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+{
+ unsigned long rawno, top, save_kolapsed;
+ PINETHRD_S *thrd = NULL, *nxthrd;
+
+ if(!stream)
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_this_thread(stream, msgmap, rawno);
+ top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return;
+
+ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, top);
+ collapse_this_thread(state, stream, msgmap, 0, 0);
+ thread_command(state, stream, msgmap, 'd', -FOOTER_ROWS(state), 1);
+ if (!save_kolapsed)
+ expand_this_thread(state, stream, msgmap, 0, 0);
+}
+
+void
+cmd_delete_thread(state, stream, msgmap)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+{
+ unsigned long rawno, top, orig_top, topnxt, save_kolapsed;
+ PINETHRD_S *thrd = NULL, *nxthrd;
+ int done = 0, count;
+
+ if(!stream)
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_thread(stream, msgmap, rawno);
+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return;
+
+ while (!done){
+ cmd_delete_this_thread(state, stream, msgmap);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno));
+ cmd_delete(state, msgmap, MCMD_NONE, cmd_delete_index);
+ count = count_thread(state, stream, msgmap, rawno);
+ q_status_message2(SM_ORDER, 0, 1, "%s message%s marked deleted",
+ int2string(count), plural(count));
+}
+
+int
+collapse_this_thread(state, stream, msgmap, display, special)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ int display;
+ int special;
+{
+ int collapsed, rv = 1, done = 0;
+ PINETHRD_S *thrd = NULL, *nthrd;
+ unsigned long rawno, orig, msgno;
+
+ if(!stream)
+ return 0;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return rv;
+
+ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno);
+
+ if (special && collapsed){
+ expand_this_thread(state, stream, msgmap, 0, 0);
+ collapsed = 0;
+ }
+
+ clear_index_cache_ent(stream, rawno, 0);
+
+ if (!collapsed && thrd->next){
+ if (thrd->rawno == top_thread(stream, thrd->rawno))
+ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display);
+ else{
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 1);
+ set_thread_subtree(stream, thrd, msgmap, 1, MN_CHID);
+ }
+ }
+ else{
+ if (!collapsed && special
+ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next)
+ || F_ON(F_ENHANCED_THREAD, state))){
+ if (thrd->toploose){
+ if (thrd->rawno != thrd->toploose)
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID,
+ 1);
+ else
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL,
+ 1);
+ }
+ }
+ else{
+ rv = 0;
+ if (display)
+ q_status_message(SM_ORDER, 0, 1, "Thread already collapsed");
+ }
+ }
+ return rv;
+}
+
+void
+collapse_thread(state, stream, msgmap, display)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ int display;
+{
+ int collapsed, rv = 1, done = 0;
+ PINETHRD_S *thrd = NULL;
+ unsigned long orig, orig_top, top;
+
+ if(!stream)
+ return;
+
+ expand_this_thread(state, stream, msgmap, display, 1);
+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_thread(stream, msgmap,orig);
+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return;
+
+ while (!done){
+ collapse_this_thread(state, stream, msgmap, display, 1);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top));
+}
+
+int
+expand_this_thread(state, stream, msgmap, display, special)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ int display;
+ int special;
+{
+ int collapsed, rv = 1, done = 0;
+ PINETHRD_S *thrd = NULL, *nthrd;
+ unsigned long rawno, orig, msgno;
+
+ if(!stream)
+ return 0;
+
+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_this_thread(stream, msgmap,orig);
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return rv;
+
+ collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno);
+
+ if (special && !collapsed){
+ collapse_this_thread(state, stream, msgmap, 0, 0);
+ collapsed = 1;
+ }
+
+ clear_index_cache_ent(stream, rawno, 0);
+
+ if (collapsed && thrd->next){
+ if (thrd->rawno == top_thread(stream, thrd->rawno))
+ collapse_or_expand(state, stream, msgmap, mn_get_cur(msgmap), display);
+ else{
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno), MN_COLL, 0);
+ set_thread_subtree(stream, thrd, msgmap, 0, MN_CHID);
+ }
+ }
+ else{
+ if (collapsed && special
+ && ((F_OFF(F_ENHANCED_THREAD, state) && !thrd->next)
+ || F_ON(F_ENHANCED_THREAD, state))){
+ if (thrd->toploose)
+ if (thrd->rawno != thrd->toploose)
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_CHID, 0);
+ else
+ set_lflag(stream, msgmap, mn_raw2m(msgmap,thrd->rawno),MN_COLL, 0);
+ }
+ else{
+ rv = 0;
+ if (display)
+ q_status_message(SM_ORDER, 0, 1, "Thread already expanded");
+ }
+ }
+ return rv;
+}
+
+void
+expand_thread(state, stream, msgmap, display)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ int display;
+{
+ int collapsed, rv = 1, done = 0;
+ PINETHRD_S *thrd = NULL;
+ unsigned long orig, orig_top, top;
+
+ if(!stream)
+ return;
+
+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return;
+
+ while (!done){
+ expand_this_thread(state, stream, msgmap, display, 1);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig_top));
+}
+
+
+void
+cmd_undelete_this_thread(state, stream, msgmap)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+{
+ unsigned long rawno;
+ int save_kolapsed;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ save_kolapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno);
+ collapse_this_thread(state, stream, msgmap, 0, 0);
+ thread_command(state, stream, msgmap, 'u', -FOOTER_ROWS(state), 1);
+ if (!save_kolapsed)
+ expand_this_thread(state, stream, msgmap, 0, 0);
+}
+
+void
+cmd_undelete_thread(state, stream, msgmap)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, top, orig_top;
+ int done = 0, count;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_thread(stream, msgmap, rawno);
+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return;
+
+ while (!done){
+ cmd_undelete_this_thread(state, stream, msgmap);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ mn_set_cur(msgmap,mn_raw2m(msgmap, rawno));
+ count = count_thread(state, stream, msgmap, rawno);
+ q_status_message2(SM_ORDER, 0, 1, "Deletion mark removed from %s message%s",
+ int2string(count), plural(count));
+}
+
+void
+kolapse_thread(state, stream, msgmap, ch, display)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ char ch;
+ int display;
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno;
+ int rv = 1, done = 0;
+
+ if(!stream)
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return;
+
+ clear_index_cache(stream, 0);
+ mn_set_cur(msgmap,1); /* go to the first message */
+ while (!done){
+ if (ch == '[')
+ collapse_thread(state, stream, msgmap, display);
+ else
+ expand_thread(state, stream, msgmap, display);
+ if ((rv = move_next_thread(state, stream, msgmap, 0)) <= 0)
+ done++;
+ }
+
+ if (rv < 0){
+ if (display)
+ q_status_message(SM_ORDER, 0, 1, (ch == '[')
+ ? "Error while collapsing thread"
+ : "Error while expanding thread");
+ }
+ else
+ if(display)
+ q_status_message(SM_ORDER, 0, 1, (ch == '[')
+ ? "All threads collapsed. Use \"}\" to expand them"
+ : "All threads expanded. Use \"{\" to collapse them");
+
+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,rawno)));
+}
+
+void
+cmd_select_thread(state, stream, msgmap)
+ struct pine *state;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+{
+ unsigned long rawno;
+ int save_kolapsed;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ save_kolapsed = thread_is_kolapsed(state, stream, msgmap, rawno);
+ collapse_thread(state, stream, msgmap, 0);
+ thread_command(state, stream, msgmap, ':', -FOOTER_ROWS(state), 1);
+ if (!save_kolapsed)
+ expand_thread(state, stream, msgmap, 0);
+}
+
Index: alpine-2.20/alpine/mailcmd.h
===================================================================
--- alpine-2.20.orig/alpine/mailcmd.h
+++ alpine-2.20/alpine/mailcmd.h
@@ -87,7 +87,7 @@ char *broach_folder(int, int, int *,
int ask_mailbox_reopen(struct pine *, int *);
void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long);
int select_by_current(struct pine *, MSGNO_S *, CmdWhere);
-int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int);
+int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int, int);
char **choose_list_of_keywords(void);
char *choose_a_charset(int);
char **choose_list_of_charsets(void);
@@ -105,6 +105,15 @@ int any_selected_callback(int, long)
int flag_callback(int, long);
MPopup *flag_submenu(MESSAGECACHE *);
#endif
-
+void cmd_delete_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+void cmd_delete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+void cmd_undelete_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+void cmd_undelete_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+void cmd_select_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+void kolapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, char, int);
+void collapse_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+void expand_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+int collapse_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int);
+int expand_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int, int);
#endif /* PINE_MAILCMD_INCLUDED */
Index: alpine-2.20/alpine/mailindx.c
===================================================================
--- alpine-2.20.orig/alpine/mailindx.c
+++ alpine-2.20/alpine/mailindx.c
@@ -561,6 +561,7 @@ index_lister(struct pine *state, CONTEXT
/*---------- Scroll line up ----------*/
case MC_CHARUP :
+previtem:
(void) process_cmd(state, stream, msgmap, MC_PREVITEM,
(style == MsgIndex
|| style == MultiMsgIndex
@@ -578,6 +579,7 @@ index_lister(struct pine *state, CONTEXT
/*---------- Scroll line down ----------*/
case MC_CHARDOWN :
+nextitem:
/*
* Special Page framing handling here. If we
* did something that should scroll-by-a-line, frame
@@ -795,6 +797,7 @@ view_a_thread:
case MC_THRDINDX :
+mc_thrdindx:
msgmap->top = msgmap->top_after_thrd;
if(unview_thread(state, stream, msgmap)){
state->next_screen = mail_index_screen;
@@ -845,7 +848,7 @@ view_a_thread:
&& mp.col == id.plus_col
&& style != ThreadIndex){
collapse_or_expand(state, stream, msgmap,
- mn_get_cur(msgmap));
+ mn_get_cur(msgmap), 1);
}
else if (mp.doubleclick){
if(mp.button == M_BUTTON_LEFT){
@@ -954,9 +957,105 @@ view_a_thread:
case MC_COLLAPSE :
- thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
+ thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state), 1);
break;
+ case MC_CTHREAD :
+ if (SEP_THRDINDX())
+ goto mc_thrdindx;
+ else
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL,
+ "to collapse a thread"))
+ collapse_thread(state, stream,msgmap, 1);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_OTHREAD :
+ if (SEP_THRDINDX())
+ goto view_a_thread;
+ else
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to expand a thread"))
+ expand_thread(state, stream,msgmap, 1);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_NEXTHREAD:
+ case MC_PRETHREAD:
+ if (THRD_INDX()){
+ if (cmd == MC_NEXTHREAD)
+ goto nextitem;
+ else
+ goto previtem;
+ }
+ else
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL,
+ "to move to other thread"))
+ move_thread(state, stream, msgmap,
+ cmd == MC_NEXTHREAD ? 1 : -1);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_KOLAPSE:
+ case MC_EXPTHREAD:
+ if (SEP_THRDINDX()){
+ q_status_message(SM_ORDER, 0, 1,
+ "Command not available in this screen");
+ }
+ else{
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL,
+ cmd == MC_KOLAPSE ? "to collapse" : "to expand"))
+ kolapse_thread(state, stream, msgmap,
+ (cmd == MC_KOLAPSE) ? '[' : ']', 1);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ }
+ break;
+
+ case MC_DELTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to delete"))
+ cmd_delete_thread(state, stream, msgmap);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_UNDTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to undelete"))
+ cmd_undelete_thread(state, stream, msgmap);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_SELTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to undelete"))
+ cmd_select_thread(state, stream, msgmap);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
case MC_DELETE :
case MC_UNDELETE :
case MC_REPLY :
@@ -977,13 +1076,12 @@ view_a_thread:
if(rawno)
thrd = fetch_thread(stream, rawno);
- collapsed = thrd && thrd->next
- && get_lflag(stream, NULL, rawno, MN_COLL);
+ collapsed = thread_is_kolapsed(ps_global, stream, msgmap, rawno);
}
if(collapsed){
thread_command(state, stream, msgmap,
- ch, -FOOTER_ROWS(state));
+ ch, -FOOTER_ROWS(state),1);
/* increment current */
if(cmd == MC_DELETE){
advance_cur_after_delete(state, stream, msgmap,
@@ -2673,6 +2771,7 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S
n = mn_raw2m(msgs, thrd->rawno);
while(thrd){
+ unsigned long branch;
if(!msgline_hidden(stream, msgs, n, 0)
&& (++m % lines_per_page) == 1L)
t = n;
@@ -2741,11 +2840,12 @@ top_ent_calc(MAILSTREAM *stream, MSGNO_S
/* n is the end of this thread */
while(thrd){
+ unsigned long next = 0L, branch = 0L;
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);
+ if(branch = get_branch(stream,thrd))
+ thrd = fetch_thread(stream, branch);
+ else if(next = get_next(stream,thrd))
+ thrd = fetch_thread(stream, next);
else
thrd = NULL;
}
@@ -2853,7 +2953,7 @@ warn_other_cmds(void)
void
thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
- UCS preloadkeystroke, int q_line)
+ UCS preloadkeystroke, int q_line, int display)
{
PINETHRD_S *thrd = NULL;
unsigned long rawno, save_branch;
@@ -2902,7 +3002,7 @@ thread_command(struct pine *state, MAILS
cancel_busy_cue(0);
(void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
- q_line);
+ q_line, display);
/* restore the original flags */
copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
@@ -3396,7 +3496,7 @@ index_sort_callback(set, order)
if(set){
sort_folder(ps_global->mail_stream, ps_global->msgmap,
order & 0x000000ff,
- (order & 0x00000100) != 0, SRT_VRB);
+ (order & 0x00000100) != 0, SRT_VRB, 1);
mswin_beginupdate();
update_titlebar_message();
update_titlebar_status();
Index: alpine-2.20/alpine/mailindx.h
===================================================================
--- alpine-2.20.orig/alpine/mailindx.h
+++ alpine-2.20/alpine/mailindx.h
@@ -103,7 +103,7 @@ int truncate_subj_and_from_strings(voi
void paint_index_hline(MAILSTREAM *, long, ICE_S *);
void setup_index_state(int);
void warn_other_cmds(void);
-void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int);
+void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int);
COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int);
#ifdef _WINDOWS
int index_sort_callback(int, long);
Index: alpine-2.20/alpine/mailview.c
===================================================================
--- alpine-2.20.orig/alpine/mailview.c
+++ alpine-2.20/alpine/mailview.c
@@ -3369,6 +3369,52 @@ scrolltool(SCROLL_S *sparms)
print_to_printer(sparms);
break;
+ case MC_NEXTHREAD:
+ case MC_PRETHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL,
+ "to move to other thread"))
+ move_thread(ps_global, ps_global->mail_stream, ps_global->msgmap,
+ cmd == MC_NEXTHREAD ? 1 : -1);
+ done = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_DELTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to delete"))
+ cmd_delete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap);
+ done = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_UNDTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to undelete"))
+ cmd_undelete_thread(ps_global, ps_global->mail_stream, ps_global->msgmap);
+ done = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
+
+ case MC_SELTHREAD:
+ if (THREADING()){
+ if (any_messages(ps_global->msgmap, NULL, "to undelete"))
+ cmd_select_thread(ps_global, ps_global->mail_stream, ps_global->msgmap);
+ done = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ "Command available in threaded mode only");
+ break;
/* ------- First handle on Line ------ */
case MC_GOTOBOL :
Index: alpine-2.20/alpine/roleconf.c
===================================================================
--- alpine-2.20.orig/alpine/roleconf.c
+++ alpine-2.20/alpine/roleconf.c
@@ -4478,11 +4478,11 @@ role_config_edit_screen(struct pine *ps,
ctmp->tool = role_sort_tool;
ctmp->valoffset = rindent;
ctmp->flags |= CF_NOSELECT;
- ctmp->value = cpystr(set_choose); \
+ ctmp->value = cpystr(set_choose);
pval = PVAL(&sort_act_var, ew);
if(pval)
- decode_sort(pval, &def_sort, &def_sort_rev);
+ decode_sort(pval, &def_sort, &def_sort_rev, 0);
/* allow user to set their default sort order */
new_confline(&ctmp)->var = &sort_act_var;
@@ -4492,7 +4492,7 @@ role_config_edit_screen(struct pine *ps,
ctmp->tool = role_sort_tool;
ctmp->valoffset = rindent;
ctmp->varmem = -1;
- ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0);
+ ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0, 0);
for(j = 0; j < 2; j++){
for(i = 0; ps->sort_types[i] != EndofList; i++){
@@ -4504,7 +4504,7 @@ role_config_edit_screen(struct pine *ps,
ctmp->valoffset = rindent;
ctmp->varmem = i + (j * EndofList);
ctmp->value = generalized_sort_pretty_value(ps, ctmp,
- 0);
+ 0, 0);
}
}
@@ -5437,7 +5437,7 @@ role_config_edit_screen(struct pine *ps,
(*result)->patgrp->stat_boy = PAT_STAT_EITHER;
if(sort_act){
- decode_sort(sort_act, &def_sort, &def_sort_rev);
+ decode_sort(sort_act, &def_sort, &def_sort_rev, 0);
(*result)->action->sort_is_set = 1;
(*result)->action->sortorder = def_sort;
(*result)->action->revsort = (def_sort_rev ? 1 : 0);
Index: alpine-2.20/alpine/setup.c
===================================================================
--- alpine-2.20.orig/alpine/setup.c
+++ alpine-2.20/alpine/setup.c
@@ -258,7 +258,7 @@ option_screen(struct pine *ps, int edit_
ctmpa->flags |= CF_NOSELECT;
ctmpa->value = cpystr("--- ----------------------");
- decode_sort(pval, &def_sort, &def_sort_rev);
+ decode_sort(pval, &def_sort, &def_sort_rev, 0);
for(j = 0; j < 2; j++){
for(i = 0; ps->sort_types[i] != EndofList; i++){
@@ -273,6 +273,55 @@ option_screen(struct pine *ps, int edit_
}
}
}
+ else if(vtmp == &ps->vars[V_THREAD_SORT_KEY]){ /* radio case */
+ SortOrder thread_def_sort;
+ int thread_def_sort_rev, lv;
+
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("Set Thread Sort Options");
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- ----------------------");
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++)
+ if(lv < (j = strlen(sort_name(ps->sort_types[i]))))
+ lv = j;
+
+ decode_sort(pval, &thread_def_sort, &thread_def_sort_rev, 1);
+
+ for(j = 0; j < 2; j++){
+ for(i = 0; ps->sort_types[i] != EndofList; i++){
+ if (allowed_thread_key(ps->sort_types[i])){
+ new_confline(&ctmpa)->var = vtmp;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = config_help(vtmp - ps->vars, 0);
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->varmem = i + (j * EndofList);
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ }
+ }
+ }
else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */
ctmpa->keymenu = &config_yesno_keymenu;
ctmpa->tool = yesno_tool;
@@ -465,6 +514,15 @@ option_screen(struct pine *ps, int edit_
}
}
+ pval = PVAL(&ps->vars[V_THREAD_SORT_KEY], ew);
+ if(vsave[V_THREAD_SORT_KEY].saved_user_val.p && pval
+ && strcmp(vsave[V_THREAD_SORT_KEY].saved_user_val.p, pval)){
+ if(!mn_get_mansort(ps_global->msgmap)){
+ clear_index_cache(ps_global->mail_stream, 0);
+ reset_sort_order(SRT_VRB);
+ }
+ }
+
treat_color_vars_as_text = 0;
free_saved_config(ps, &vsave, expose_hidden_config);
#ifdef _WINDOWS
Index: alpine-2.20/pith/conf.c
===================================================================
--- alpine-2.20.orig/pith/conf.c
+++ alpine-2.20/pith/conf.c
@@ -206,6 +206,8 @@ CONF_TXT_T cf_text_fcc_name_rule[] = "De
CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\".";
+CONF_TXT_T cf_text_thread_sort_key[] = "#Sets presentation order of threads in thread index. Choices:\n#arrival, and thread.";
+
CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\".";
CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\".";
@@ -524,6 +526,8 @@ static struct variable variables[] = {
NULL, cf_text_fcc_name_rule},
{"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
NULL, cf_text_sort_key},
+{"thread-sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_thread_sort_key},
{"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
"Address Book Sort Rule", cf_text_addrbook_sort_rule},
{"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
@@ -1576,7 +1580,7 @@ init_vars(struct pine *ps, void (*cmds_f
register struct variable *vars = ps->vars;
int obs_header_in_reply = 0, /* the obs_ variables are to */
obs_old_style_reply = 0, /* support backwards compatibility */
- obs_save_by_sender, i, def_sort_rev;
+ obs_save_by_sender, i, def_sort_rev, thread_def_sort_rev;
long rvl;
PINERC_S *fixedprc = NULL;
FeatureLevel obs_feature_level;
@@ -1601,6 +1605,7 @@ init_vars(struct pine *ps, void (*cmds_f
GLO_FEATURE_LEVEL = cpystr("sappling");
GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY);
GLO_SORT_KEY = cpystr(DF_SORT_KEY);
+ GLO_THREAD_SORT_KEY = cpystr(DF_THREAD_SORT_KEY);
GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE);
GLO_FCC_RULE = cpystr(DF_FCC_RULE);
GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE);
@@ -2517,7 +2522,7 @@ init_vars(struct pine *ps, void (*cmds_f
set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE);
set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE);
set_current_val(&vars[V_SORT_KEY], TRUE, TRUE);
- if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){
+ if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev,0) == -1){
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY);
init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
ps->def_sort = SortArrival;
@@ -2526,6 +2531,17 @@ init_vars(struct pine *ps, void (*cmds_f
else
ps->def_sort_rev = def_sort_rev;
+ set_current_val(&vars[V_THREAD_SORT_KEY], TRUE, TRUE);
+ if(decode_sort(VAR_THREAD_SORT_KEY, &ps->thread_def_sort,
+ &thread_def_sort_rev, 1) == -1){
+ sprintf(tmp_20k_buf, "Sort type \"%s\" is invalid", VAR_THREAD_SORT_KEY);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ ps->thread_def_sort = SortThread;
+ ps->thread_def_sort_rev = 0;
+ }
+ else
+ ps->thread_def_sort_rev = thread_def_sort_rev;
+
cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE);
{NAMEVAL_S *v; int i;
for(i = 0; (v = save_msg_rules(i)); i++)
@@ -2949,6 +2965,8 @@ feature_list(int index)
F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0},
{"thread-sorts-by-arrival", "Thread Sorts by Arrival",
F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0},
+ {"enhanced-fancy-thread-support", "Enhanced Fancy Thread Support",
+ F_ENHANCED_THREAD, h_config_enhanced_thread, PREF_INDX, 0},
/* Viewer prefs */
{"enable-msg-view-addresses", "Enable Message View Address Links",
@@ -7685,6 +7703,8 @@ config_help(int var, int feature)
return(h_config_fcc_rule);
case V_SORT_KEY :
return(h_config_sort_key);
+ case V_THREAD_SORT_KEY :
+ return(h_config_thread_sort_key);
case V_AB_SORT_RULE :
return(h_config_ab_sort_rule);
case V_FLD_SORT_RULE :
Index: alpine-2.20/pith/conf.h
===================================================================
--- alpine-2.20.orig/pith/conf.h
+++ alpine-2.20/pith/conf.h
@@ -144,6 +144,9 @@
#define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p
#define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p
#define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p
+#define VAR_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].current_val.p
+#define GLO_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].global_val.p
+#define COM_THREAD_SORT_KEY vars[V_THREAD_SORT_KEY].cmdline_val.p
#define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p
#define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p
#define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p
Index: alpine-2.20/pith/conftype.h
===================================================================
--- alpine-2.20.orig/pith/conftype.h
+++ alpine-2.20/pith/conftype.h
@@ -59,6 +59,7 @@ typedef enum { V_PERSONAL_NAME = 0
, V_SAVED_MSG_NAME_RULE
, V_FCC_RULE
, V_SORT_KEY
+ , V_THREAD_SORT_KEY
, V_AB_SORT_RULE
, V_FLD_SORT_RULE
, V_GOTO_DEFAULT_RULE
@@ -511,6 +512,7 @@ typedef enum {
F_QUELL_TIMEZONE,
F_QUELL_USERAGENT,
F_COLOR_LINE_IMPORTANT,
+ F_ENHANCED_THREAD,
F_SLASH_COLL_ENTIRE,
F_ENABLE_FULL_HDR_AND_TEXT,
F_QUELL_FULL_HDR_RESET,
@@ -766,5 +768,6 @@ typedef struct smime_stuff {
/* exported protoypes */
+#define DF_THREAD_SORT_KEY "thread"
#endif /* PITH_CONFTYPE_INCLUDED */
Index: alpine-2.20/pith/flag.c
===================================================================
--- alpine-2.20.orig/pith/flag.c
+++ alpine-2.20/pith/flag.c
@@ -594,14 +594,16 @@ set_lflag(MAILSTREAM *stream, MSGNO_S *m
was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0;
+ thrd = fetch_thread(stream, rawno);
+
if((chk_thrd_cnt = ((msgs->visible_threads >= 0L)
&& THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){
thrd = fetch_thread(stream, rawno);
if(thrd && thrd->top){
- if(thrd->top == thrd->rawno)
+ if(top_thread(stream, thrd->top) == thrd->rawno)
topthrd = thrd;
else
- topthrd = fetch_thread(stream, thrd->top);
+ topthrd = fetch_thread(stream, top_thread(stream, thrd->top));
}
if(topthrd){
Index: alpine-2.20/pith/indxtype.h
===================================================================
--- alpine-2.20.orig/pith/indxtype.h
+++ alpine-2.20/pith/indxtype.h
@@ -76,7 +76,7 @@ typedef enum {iNothing, iStatus, iFStatu
iKey, iKeyInit,
iPrefDate, iPrefTime, iPrefDateTime,
iCurPrefDate, iCurPrefTime, iCurPrefDateTime,
- iSize, iSizeComma, iSizeNarrow, iDescripSize,
+ iSize, iSizeComma, iSizeNarrow, iDescripSize, iSizeThread,
iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews,
iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips,
iCurNews, iArrow,
Index: alpine-2.20/pith/mailindx.c
===================================================================
--- alpine-2.20.orig/pith/mailindx.c
+++ alpine-2.20/pith/mailindx.c
@@ -228,6 +228,7 @@ init_index_format(char *format, INDEX_CO
case iSTime:
case iKSize:
case iSize:
+ case iSizeThread:
case iPrioAlpha:
(*answer)[column].req_width = 7;
break;
@@ -452,6 +453,7 @@ static INDEX_PARSE_T itokens[] = {
{"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX},
{"SIZE", iSize, FOR_INDEX},
{"SIZECOMMA", iSizeComma, FOR_INDEX},
+ {"SIZETHREAD", iSizeThread, FOR_INDEX},
{"SIZENARROW", iSizeNarrow, FOR_INDEX},
{"KSIZE", iKSize, FOR_INDEX},
{"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
@@ -943,7 +945,7 @@ static IndexColType fixed_ctypes[] = {
iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
iSDateTimeIso24, iSDateTimeIsoS24,
iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
- iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize,
+ iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize, iSizeThread,
iPrio, iPrioBang, iPrioAlpha, iInit,
iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit,
iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek
@@ -1136,6 +1138,7 @@ setup_index_header_widths(MAILSTREAM *st
case iTime12:
case iSize:
case iKSize:
+ case iSizeThread:
cdesc->actual_length = 7;
cdesc->adjustment = Right;
break;
@@ -1229,7 +1232,7 @@ setup_index_header_widths(MAILSTREAM *st
cdesc->ctype != iNothing;
cdesc++)
if(cdesc->ctype == iSize || cdesc->ctype == iKSize ||
- cdesc->ctype == iSizeNarrow ||
+ cdesc->ctype == iSizeNarrow || cdesc->ctype == iSizeThread ||
cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){
if(cdesc->actual_length == 0){
if((fix=cdesc->width) > 0){ /* had this reserved */
@@ -1612,10 +1615,12 @@ build_header_work(struct pine *state, MA
/* find next thread which is visible */
do{
+ unsigned long branch;
if(mn_get_revsort(msgmap) && thrd->prevthd)
thrd = fetch_thread(stream, thrd->prevthd);
- else if(!mn_get_revsort(msgmap) && thrd->nextthd)
- thrd = fetch_thread(stream, thrd->nextthd);
+ /*branch = get_branch(stream,thrd)*/
+ else if(!mn_get_revsort(msgmap) && thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
else
thrd = NULL;
} while(thrd
@@ -2027,13 +2032,10 @@ format_index_index_line(INDEXDATA_S *ida
*/
ice = copy_ice(ice);
+ thrd = fetch_thread(idata->stream, idata->rawno);
/* is this a collapsed thread index line? */
- if(!idata->bogus && THREADING()){
- thrd = fetch_thread(idata->stream, idata->rawno);
- collapsed = thrd && thrd->next
- && get_lflag(idata->stream, NULL,
- idata->rawno, MN_COLL);
- }
+ if(!idata->bogus && THREADING())
+ collapsed = thrd && thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno);
/* calculate contents of the required fields */
for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
@@ -2531,7 +2533,30 @@ format_index_index_line(INDEXDATA_S *ida
break;
+ case iSizeThread:
+ if (!THREADING()){
+ goto getsize;
+ } else if (collapsed){
+ l = count_flags_in_thread(idata->stream, thrd, F_NONE);
+ snprintf(str, sizeof(str), "(%lu)", l);
+ }
+ else{
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ if(!thrd)
+ snprintf(str, sizeof(str), "%s", "Error");
+ else{
+ long lengthb;
+ lengthb = get_length_branch(idata->stream, idata->rawno);
+ if (lengthb > 0L)
+ snprintf(str, sizeof(str), "(%lu)", lengthb);
+ else
+ snprintf(str,sizeof(str), "%s", " ");
+ }
+ }
+ break;
+
case iSize:
+getsize:
/* 0 ... 9999 */
if((l = fetch_size(idata)) < 10*1000L)
snprintf(str, sizeof(str), "(%lu)", l);
@@ -5413,10 +5438,8 @@ subj_str(INDEXDATA_S *idata, char *str,
if(pith_opt_condense_thread_cue)
width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
- thd && thd->next
- && get_lflag(idata->stream,
- NULL,idata->rawno,
- MN_COLL));
+ this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) &&
+ (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1));
/*
* width is < available strsize and
@@ -6044,11 +6067,8 @@ from_str(IndexColType ctype, INDEXDATA_S
border = str + width;
if(pith_opt_condense_thread_cue)
width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
- thd && thd->next
- && get_lflag(idata->stream,
- NULL,idata->rawno,
- MN_COLL));
-
+ this_thread_is_kolapsed(ps_global, idata->stream, ps_global->msgmap, idata->rawno) &&
+ (count_thread(ps_global,idata->stream, ps_global->msgmap, idata->rawno) != 1));
fptr = str;
if(thd)
Index: alpine-2.20/pith/pattern.c
===================================================================
--- alpine-2.20.orig/pith/pattern.c
+++ alpine-2.20/pith/pattern.c
@@ -1756,7 +1756,7 @@ parse_action_slash(char *str, ACTION_S *
SortOrder def_sort;
int def_sort_rev;
- if(decode_sort(p, &def_sort, &def_sort_rev) != -1){
+ if(decode_sort(p, &def_sort, &def_sort_rev, 0) != -1){
action->sort_is_set = 1;
action->sortorder = def_sort;
action->revsort = (def_sort_rev ? 1 : 0);
Index: alpine-2.20/pith/pine.hlp
===================================================================
--- alpine-2.20.orig/pith/pine.hlp
+++ alpine-2.20/pith/pine.hlp
@@ -3866,6 +3866,7 @@ There are also additional details on
<li><a href="h_config_signature_file">OPTION: <!--#echo var="VAR_signature-file"--></a>
<li><a href="h_config_smtp_server">OPTION: <!--#echo var="VAR_smtp-server"--></a>
<li><a href="h_config_sort_key">OPTION: <!--#echo var="VAR_sort-key"--></a>
+<li><a href="h_config_thread_sort_key">OPTION: <!--#echo var="VAR_thread-sort-key"--></a>
<li><a href="h_config_speller">OPTION: <!--#echo var="VAR_speller"--></a>
<li><a href="h_config_sshcmd">OPTION: <!--#echo var="VAR_ssh-command"--></a>
<li><a href="h_config_ssh_open_timeo">OPTION: <!--#echo var="VAR_ssh-open-timeout"--></a>
@@ -5802,6 +5803,163 @@ the names of the carbon copy addresses o
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+======= h_thread_index_sort_arrival =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Arrival</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Arrival</H1>
+
+The <EM>Arrival</EM> sort option arranges threads according to the last
+time that a message was added to it. In this order the last thread
+contains the most recent message in the folder.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_date =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Date</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Date</H1>
+
+The <EM>Date</EM> sort option in the THREAD&nbsp;INDEX screen sorts
+threads by the date in which messages were sent. The thread containing the
+last message in this order is displayed last.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_subj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Subject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Subject</H1>
+
+The <EM>Subject</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_ordsubj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: OrderedSubject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: OrderedSubject</H1>
+
+The <EM>OrderedSubject</EM> sort option in the THREAD&nbsp;INDEX screen is
+the same as sorting by <A HREF="h_thread_index_sort_subj">Subject</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_thread =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Thread</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Thread</H1>
+
+The <EM>Thread</EM> sort option in the THREAD&nbsp;INDEX screen sorts all
+messages by the proposed algorithm by Crispin and Murchison. In this
+method of sorting once threads have been isolated they are sorted by the
+date of their parents, or if that is missing, the first message in that
+thread.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_from =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: From</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: From</H1>
+
+The <EM>From</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_size =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Size</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Size</H1>
+
+The <EM>Size</EM> sort option sorts threads by their size (the number
+of messages in the thread). This could be used to find conversations
+where no reply has been sent by any of the participants in the thread
+(e.g. those whose length is equal to one). Longer threads appear
+below shorter ones.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_score =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Score</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Score</H1>
+
+The <EM>Score</EM> sort option means that threads are sorted according to
+the maximum score of a message in that thread. A thread all of whose
+messages contain a smaller score than a message in some other thread is
+placed in an earlier place in the list of messages for that folder; that
+is, threads with the highest scores appear at the bottom of the index
+list.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_to =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: To</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: To</H1>
+
+The <EM>To</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_thread_index_sort_cc =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Cc</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Cc</H1>
+
+The <EM>Cc</EM> sort option has not been defined yet.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
======= h_index_cmd_whereis =======
<HTML>
<HEAD>
@@ -19184,6 +19342,14 @@ The progression of sizes used looks like
<P>
</DD>
+<DT>SIZETHREAD</DT>
+<DD>
+This token represents the total size of the thread for a collapsed thread
+or the size of the branch for an expanded thread. The field is omitted for
+messages that are not top of threads nor branches and it defaults to
+the SIZE token when your folders is not sorted by thread.
+</DD>
+
<DT>SIZENARROW</DT>
<DD>
This token represents the total size, in bytes, of the message.
@@ -22655,6 +22821,45 @@ command, then it will not be re-sorted u
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_thread_sort_key =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_thread-sort-key--></TITLE></H1>
+
+This option determines the order in which threads will be displayed. You
+can choose from the options listed below. Each folder is sorted in one of
+the sort orders displayed below first, then the thread containing the last
+message of that sorted list is put at the end of the index. All messages
+of that thread are &quot;removed&quot; from the sorted list and the
+process is repeated with the remaining messages in that list.
+
+<P>
+<UL>
+ <LI> <A HREF="h_thread_index_sort_arrival">Arrival</A>
+ <LI> <A HREF="h_thread_index_sort_date">Date</A>
+<!-- <LI> <A HREF="h_thread_index_sort_subj">Subject</A>
+ <LI> <A HREF="h_thread_index_sort_ordsubj">OrderedSubj</A>-->
+ <LI> <A HREF="h_thread_index_sort_thread">Thread</A>
+<!-- <LI> <A HREF="h_thread_index_sort_from">From</A> -->
+ <LI> <A HREF="h_thread_index_sort_size">Size</A>
+ <LI> <A HREF="h_thread_index_sort_score">Score</A>
+<!-- <LI> <A HREF="h_thread_index_sort_to">To</A>
+ <LI> <A HREF="h_thread_index_sort_cc">Cc</A>-->
+</UL>
+
+<P> Each type of sort may also be reversed. Normal default is by
+&quot;Thread&quot;.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_other_startup =====
<HTML>
<HEAD>
@@ -30485,6 +30690,23 @@ Reply Use, Forward Use, and Compose Use.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_enhanced_thread =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enhanced-fancy-thread-support"--></H1>
+
+If this option is set certain commands in Pine will operate in loose
+threads too. For example, the command ^R marks a thread deleted, but if
+this feature is set, it will remove all threads that share the same missing
+parent with this thread.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_news_cross_deletes =====
<HTML>
<HEAD>
Index: alpine-2.20/pith/sort.c
===================================================================
--- alpine-2.20.orig/pith/sort.c
+++ alpine-2.20/pith/sort.c
@@ -91,7 +91,7 @@ Args: msgmap --
----*/
void
sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort,
- int new_rev, unsigned int flags)
+ int new_rev, unsigned int flags, int first)
{
long raw_current, i, j;
unsigned long *sort = NULL;
@@ -101,6 +101,15 @@ sort_folder(MAILSTREAM *stream, MSGNO_S
int current_rev;
MESSAGECACHE *mc;
+ if (first){
+ if (new_sort == SortThread)
+ find_msgmap(stream, msgmap, flags,
+ ps_global->thread_cur_sort, new_rev);
+ else
+ sort_folder(stream, msgmap, new_sort, new_rev, flags, 0);
+ return;
+ }
+
dprint((2, "Sorting by %s%s\n",
sort_name(new_sort), new_rev ? "/reverse" : ""));
@@ -530,20 +539,20 @@ percent_sorted(void)
* argument also means arrival/reverse.
*/
int
-decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev)
+decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev, int thread)
{
char *sep;
char *fix_this = NULL;
- int x, reverse;
+ int x = 0, reverse;
if(!sort_spec || !*sort_spec){
- *def_sort = SortArrival;
+ *def_sort = thread ? SortThread : SortArrival;
*def_sort_rev = 0;
return(0);
}
if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){
- *def_sort = SortArrival;
+ *def_sort = thread ? SortThread : SortArrival;
*def_sort_rev = 1;
return(0);
}
@@ -572,7 +581,7 @@ decode_sort(char *sort_spec, SortOrder *
if(ps_global->sort_types[x] == EndofList)
return(-1);
- *def_sort = ps_global->sort_types[x];
+ *def_sort = ps_global->sort_types[x];
*def_sort_rev = reverse;
return(0);
}
@@ -689,7 +698,9 @@ reset_sort_order(unsigned int flags)
/* set default order */
the_sort_order = ps_global->def_sort;
- sort_is_rev = ps_global->def_sort_rev;
+ sort_is_rev = the_sort_order == SortThread
+ ? (ps_global->thread_def_sort_rev + ps_global->def_sort_rev) % 2
+ : ps_global->def_sort_rev;
if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
@@ -702,9 +713,15 @@ reset_sort_order(unsigned int flags)
&& pat->action->sort_is_set){
the_sort_order = pat->action->sortorder;
sort_is_rev = pat->action->revsort;
+ sort_is_rev = the_sort_order == SortThread
+ ? (ps_global->thread_def_sort_rev + pat->action->revsort) % 2
+ : pat->action->revsort;
}
}
+ if(the_sort_order == SortThread && !(flags & SRT_MAN))
+ ps_global->thread_cur_sort = ps_global->thread_def_sort;
+
sort_folder(ps_global->mail_stream, ps_global->msgmap,
- the_sort_order, sort_is_rev, flags);
+ the_sort_order, sort_is_rev, flags, 1);
}
Index: alpine-2.20/pith/sort.h
===================================================================
--- alpine-2.20.orig/pith/sort.h
+++ alpine-2.20/pith/sort.h
@@ -22,7 +22,7 @@
#define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \
- mn_get_revsort(M), (F))
+ mn_get_revsort(M), (F), 1)
struct global_sort_data {
MSGNO_S *msgmap;
@@ -41,8 +41,8 @@ extern struct global_sort_data g_sort;
/* exported protoypes */
char *sort_name(SortOrder);
-void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned);
-int decode_sort(char *, SortOrder *, int *);
+void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned, int);
+int decode_sort(char *, SortOrder *, int *, int);
void reset_sort_order(unsigned);
Index: alpine-2.20/pith/state.c
===================================================================
--- alpine-2.20.orig/pith/state.c
+++ alpine-2.20/pith/state.c
@@ -74,6 +74,7 @@ new_pine_struct(void)
p = (struct pine *)fs_get(sizeof (struct pine));
memset((void *) p, 0, sizeof(struct pine));
+ p->thread_def_sort = SortDate;
p->def_sort = SortArrival;
p->sort_types[0] = SortSubject;
p->sort_types[1] = SortArrival;
Index: alpine-2.20/pith/state.h
===================================================================
--- alpine-2.20.orig/pith/state.h
+++ alpine-2.20/pith/state.h
@@ -137,6 +137,8 @@ struct pine {
unsigned unseen_in_view:1;
unsigned start_in_context:1; /* start fldr_scrn in current cntxt */
unsigned def_sort_rev:1; /* true if reverse sort is default */
+ unsigned thread_def_sort_rev:1; /* true if reverse sort is default in thread screen */
+ unsigned msgmap_thread_def_sort_rev:1; /* true if reverse sort is being used in thread screen */
unsigned restricted:1;
unsigned tcptimeout:1; /* a tcp timeout is in progress */
@@ -291,6 +293,9 @@ struct pine {
EditWhich ew_for_srch_take;
SortOrder def_sort, /* Default sort type */
+ thread_def_sort, /* Default Sort Type in Thread Screen */
+ thread_cur_sort, /* current sort style for threads */
+ msgmap_thread_sort,
sort_types[22];
int preserve;
Index: alpine-2.20/pith/thread.c
===================================================================
--- alpine-2.20.orig/pith/thread.c
+++ alpine-2.20/pith/thread.c
@@ -30,12 +30,18 @@ static char rcsid[] = "$Id: thread.c 942
#include "../pith/mailcmd.h"
#include "../pith/ablookup.h"
+static int erase_thread_info = 1;
+
+typedef struct sizethread_t {
+ int count;
+ long pos;
+} SIZETHREAD_T;
/*
* Internal prototypes
*/
long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *,
- char *, long, PINETHRD_S *, unsigned);
+ char *, long, PINETHRD_S *, unsigned, int, long, long);
void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int);
THREADNODE *collapse_threadnode_tree(THREADNODE *);
THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *);
@@ -43,6 +49,7 @@ THREADNODE *sort_threads_and_collapse(
THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *);
unsigned long branch_greatest_num(THREADNODE *, int);
long calculate_visible_threads(MAILSTREAM *);
+int pine_compare_size_thread(const qsort_t *, const qsort_t *);
PINETHRD_S *
@@ -95,20 +102,22 @@ void
set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v)
{
PINETHRD_S *nthrd, *bthrd;
+ unsigned long next = 0L, branch = 0L;
if(!(stream && thrd && msgmap))
return;
set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v);
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream,thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
set_flags_for_thread(stream, msgmap, f, nthrd, v);
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+
+ if(branch = get_branch(stream, thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
set_flags_for_thread(stream, msgmap, f, bthrd, v);
}
@@ -122,7 +131,7 @@ erase_threading_info(MAILSTREAM *stream,
MESSAGECACHE *mc;
PINELT_S *peltp;
- if(!(stream && stream->spare))
+ if(!(stream && stream->spare) || !erase_thread_info)
return;
ps_global->view_skipped_index = 0;
@@ -155,7 +164,7 @@ sort_thread_callback(MAILSTREAM *stream,
PINETHRD_S *thrd = NULL;
unsigned long msgno, rawno;
int un_view_thread = 0;
- long raw_current;
+ long raw_current, branch;
char *dup_chk = NULL;
@@ -168,10 +177,11 @@ sort_thread_callback(MAILSTREAM *stream,
* way. If the dummy node is at the top-level, then its children are
* promoted to the top-level as separate threads.
*/
- if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global))
- collapsed_tree = collapse_threadnode_tree_sorted(tree);
- else
- collapsed_tree = collapse_threadnode_tree(tree);
+ collapsed_tree = F_ON(F_ENHANCED_THREAD, ps_global)
+ ? copy_tree(tree)
+ : (F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global)
+ ? collapse_threadnode_tree_sorted(tree)
+ : collapse_threadnode_tree(tree));
/* dup_chk is like sort with an origin of 1 */
dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
@@ -182,7 +192,7 @@ sort_thread_callback(MAILSTREAM *stream,
(void) sort_thread_flatten(collapsed_tree, stream,
&g_sort.msgmap->sort[1],
dup_chk, mn_get_nmsgs(g_sort.msgmap),
- NULL, THD_TOP);
+ NULL, THD_TOP, 0, 1L, 0L);
/* reset the inverse array */
msgno_reset_isort(g_sort.msgmap);
@@ -340,12 +350,14 @@ sort_thread_callback(MAILSTREAM *stream,
else{
thrd = fetch_head_thread(stream);
while(thrd){
+ unsigned long raw = thrd->rawno;
+ unsigned long top = top_thread(stream, raw);
/*
* The top-level threads aren't hidden by collapse.
*/
msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
- if(msgno)
- set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
+ if(msgno && !get_lflag(stream, NULL,thrd->rawno, MN_COLL))
+ set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
if(thrd->next){
PINETHRD_S *nthrd;
@@ -359,9 +371,10 @@ sort_thread_callback(MAILSTREAM *stream,
MN_COLL));
}
- if(thrd->nextthd)
- thrd = fetch_thread(stream, thrd->nextthd);
- else
+ while (thrd && top_thread(stream, thrd->rawno) == top
+ && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ if (!(thrd && thrd->nextthd))
thrd = NULL;
}
}
@@ -412,7 +425,7 @@ make_thrdflags_consistent(MAILSTREAM *st
int a_parent_is_collapsed)
{
PINETHRD_S *nthrd, *bthrd;
- unsigned long msgno;
+ unsigned long msgno, next, branch;
if(!thrd)
return;
@@ -430,8 +443,8 @@ make_thrdflags_consistent(MAILSTREAM *st
set_lflag(stream, msgmap, msgno, MN_CHID, 0);
}
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream, thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
make_thrdflags_consistent(stream, msgmap, nthrd,
a_parent_is_collapsed
@@ -440,8 +453,8 @@ make_thrdflags_consistent(MAILSTREAM *st
MN_COLL));
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+ if(branch = get_branch(stream, thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
make_thrdflags_consistent(stream, msgmap, bthrd,
a_parent_is_collapsed);
@@ -488,9 +501,10 @@ calculate_visible_threads(MAILSTREAM *st
long *
sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream,
long *entry, char *dup_chk, long maxno,
- PINETHRD_S *thrd, unsigned int flags)
+ PINETHRD_S *thrd, unsigned int flags,
+ int adopted, long top, long threadno)
{
- PINETHRD_S *newthrd = NULL;
+ PINETHRD_S *newthrd = NULL, *save_thread = NULL;
if(node){
if(node->num > 0L && node->num <= maxno){ /* holes happen */
@@ -498,6 +512,9 @@ sort_thread_flatten(THREADNODE *node, MA
*entry = node->num;
dup_chk[node->num] = 1;
+ if(adopted == 2)
+ top = node->num;
+
/*
* Build a richer threading structure that will help us paint
* and operate on threads and subthreads.
@@ -506,20 +523,51 @@ sort_thread_flatten(THREADNODE *node, MA
if(newthrd){
entry++;
+ if(adopted == 2)
+ threadno = newthrd->thrdno;
+ if(adopted){
+ newthrd->toploose = top;
+ newthrd->thrdno = threadno;
+ }
+ adopted = adopted ? 1 : 0;
if(node->next)
entry = sort_thread_flatten(node->next, stream,
entry, dup_chk, maxno,
- newthrd, THD_NEXT);
+ newthrd, THD_NEXT, adopted, top, threadno);
if(node->branch)
entry = sort_thread_flatten(node->branch, stream,
entry, dup_chk, maxno,
newthrd,
- (flags == THD_TOP) ? THD_TOP
- : THD_BRANCH);
+ ((flags == THD_TOP) ? THD_TOP
+ : THD_BRANCH),
+ adopted, top, threadno);
}
}
}
+ else{
+ adopted = 2;
+ if(node->next)
+ entry = sort_thread_flatten(node->next, stream, entry, dup_chk,
+ maxno, thrd, THD_TOP, adopted, top, threadno);
+ adopted = 0;
+ if(node->branch){
+ if(entry){
+ long *last_entry = entry;
+
+ do{
+ last_entry--;
+ save_thread = ((PINELT_S *)mail_elt(stream, *last_entry)->sparep)->pthrd;
+ } while (save_thread->parent != 0L);
+ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk,
+ maxno, save_thread, (flags == THD_TOP ? THD_TOP : THD_BRANCH),
+ adopted, top, threadno);
+ }
+ else
+ entry = sort_thread_flatten(node->branch, stream, entry, dup_chk,
+ maxno, NULL, THD_TOP, adopted, top, threadno);
+ }
+ }
}
return(entry);
@@ -788,7 +836,7 @@ msgno_thread_info(MAILSTREAM *stream, lo
*/
void
collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
- long unsigned int msgno)
+ long unsigned int msgno, int display)
{
int collapsed, adjust_current = 0;
PINETHRD_S *thrd = NULL, *nthrd;
@@ -841,7 +889,7 @@ collapse_or_expand(struct pine *state, M
if(!thrd)
return;
- collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next;
+ collapsed = this_thread_is_kolapsed(ps_global, stream, msgmap, thrd->rawno);
if(collapsed){
msgno = mn_raw2m(msgmap, thrd->rawno);
@@ -859,13 +907,13 @@ collapse_or_expand(struct pine *state, M
msgno = mn_raw2m(msgmap, thrd->rawno);
if(msgno > 0L && msgno <= mn_get_total(msgmap)){
set_lflag(stream, msgmap, msgno, MN_COLL, 1);
- if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
+ if((thrd->next) && ((nthrd = fetch_thread(stream, thrd->next)) != NULL))
set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
clear_index_cache_ent(stream, msgno, 0);
}
}
- else
+ else if(display)
q_status_message(SM_ORDER, 0, 1,
_("No thread to collapse or expand on this line"));
@@ -952,18 +1000,19 @@ count_flags_in_thread(MAILSTREAM *stream
unsigned long count = 0;
PINETHRD_S *nthrd, *bthrd;
MESSAGECACHE *mc;
+ unsigned long next = 0L, branch = 0L;
if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
return count;
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream, thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
count += count_flags_in_thread(stream, nthrd, flags);
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+ if(branch = get_branch(stream, thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
count += count_flags_in_thread(stream, bthrd, flags);
}
@@ -1051,20 +1100,21 @@ int
mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap)
{
int count = 0;
+ long next, branch;
PINETHRD_S *nthrd, *bthrd;
MESSAGECACHE *mc;
if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
return count;
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream, thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
count += mark_msgs_in_thread(stream, nthrd, msgmap);
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+ if(branch = get_branch(stream, thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
count += mark_msgs_in_thread(stream, bthrd, msgmap);
}
@@ -1098,7 +1148,7 @@ set_thread_lflags(MAILSTREAM *stream, PI
/* flags to set or clear */
/* set or clear? */
{
- unsigned long msgno;
+ unsigned long msgno, next, branch;
PINETHRD_S *nthrd, *bthrd;
if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
@@ -1122,14 +1172,14 @@ set_thread_lflags(MAILSTREAM *stream, PI
if(msgno > 0L && flags == MN_CHID2 && v == 1)
clear_index_cache_ent(stream, msgno, 0);
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream, thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
set_thread_lflags(stream, nthrd, msgmap, flags, v);
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+ if(branch = get_branch(stream,thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
set_thread_lflags(stream, bthrd, msgmap, flags, v);
}
@@ -1218,19 +1268,20 @@ to_us_symbol_for_thread(MAILSTREAM *stre
char to_us = ' ';
char branch_to_us = ' ';
PINETHRD_S *nthrd, *bthrd;
+ unsigned long next = 0L, branch = 0L;
MESSAGECACHE *mc;
if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
return to_us;
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next = get_next(stream,thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged);
}
if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+'))
- && thrd->branch){
+ && (branch = get_branch(stream, thrd))){
bthrd = fetch_thread(stream, thrd->branch);
if(bthrd)
branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged);
@@ -1280,7 +1331,7 @@ to_us_symbol_for_thread(MAILSTREAM *stre
break;
}
- if(to_us != '+' && resent_to_us(&idata))
+ if(to_us != '+' && !idata.bogus && resent_to_us(&idata))
to_us = '+';
if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
@@ -1328,7 +1379,8 @@ set_thread_subtree(MAILSTREAM *stream, P
set_lflag(stream, msgmap, msgno, flags, v);
- if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){
+ if(thrd->next
+ && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){
nthrd = fetch_thread(stream, thrd->next);
if(nthrd)
set_thread_subtree(stream, nthrd, msgmap, v, flags);
@@ -1368,8 +1420,8 @@ view_thread(struct pine *state, MAILSTRE
if(rawno)
thrd = fetch_thread(stream, rawno);
- if(thrd && thrd->top && thrd->top != thrd->rawno)
- thrd = fetch_thread(stream, thrd->top);
+ if(thrd && thrd->top && top_thread(stream,thrd->top) != thrd->rawno)
+ thrd = fetch_thread(stream, top_thread(stream,thrd->top));
if(!thrd)
return 0;
@@ -1433,7 +1485,7 @@ unview_thread(struct pine *state, MAILST
thrd = fetch_thread(stream, rawno);
if(thrd && thrd->top)
- topthrd = fetch_thread(stream, thrd->top);
+ topthrd = fetch_thread(stream, top_thread(stream,thrd->top));
if(!topthrd)
return 0;
@@ -1539,6 +1591,7 @@ void
set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset)
{
PINETHRD_S *nthrd, *bthrd;
+ unsigned long next, branch;
if(!(stream && thrd))
return;
@@ -1547,15 +1600,622 @@ set_search_bit_for_thread(MAILSTREAM *st
&& (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno)))
mm_searched(stream, thrd->rawno);
- if(thrd->next){
- nthrd = fetch_thread(stream, thrd->next);
+ if(next= get_next(stream, thrd)){
+ nthrd = fetch_thread(stream, next);
if(nthrd)
set_search_bit_for_thread(stream, nthrd, msgset);
}
- if(thrd->branch){
- bthrd = fetch_thread(stream, thrd->branch);
+ if(branch = get_branch(stream, thrd)){
+ bthrd = fetch_thread(stream, branch);
if(bthrd)
set_search_bit_for_thread(stream, bthrd, msgset);
}
}
+
+/*
+ * Make a copy of c-client's THREAD tree
+ */
+THREADNODE *
+copy_tree(THREADNODE *tree)
+{
+ THREADNODE *newtree = NULL;
+
+ if(tree){
+ newtree = mail_newthreadnode(NULL);
+ newtree->num = tree->num;
+ if(tree->next)
+ newtree->next = copy_tree(tree->next);
+
+ if(tree->branch)
+ newtree->branch = copy_tree(tree->branch);
+ }
+ return(newtree);
+}
+
+long
+top_thread(MAILSTREAM *stream, long rawmsgno)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno;
+
+ if(!stream)
+ return -1L;
+
+ if(rawmsgno)
+ thrd = fetch_thread(stream, rawmsgno);
+
+ if(!thrd)
+ return -1L;
+
+ return F_ON(F_ENHANCED_THREAD, ps_global)
+ ? (thrd->toploose ? thrd->toploose : thrd->top)
+ : thrd->top;
+}
+
+void
+move_top_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno)
+{
+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream, rawmsgno)));
+}
+
+long
+top_this_thread(MAILSTREAM *stream, long rawmsgno)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno;
+
+ if(!stream)
+ return -1L;
+
+ if(rawmsgno)
+ thrd = fetch_thread(stream, rawmsgno);
+
+ if(!thrd)
+ return -1L;
+
+ return thrd->top;
+}
+
+void
+move_top_this_thread(MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno)
+{
+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_this_thread(stream, rawmsgno)));
+}
+
+int
+thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno)
+{
+ int collapsed;
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, orig, orig_rawno;
+
+ if(!stream)
+ return -1;
+
+ orig = mn_get_cur(msgmap);
+ move_top_thread(stream, msgmap, rawmsgno);
+ rawno = orig_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return -1;
+
+ while(collapsed = this_thread_is_kolapsed(state, stream, msgmap, rawno))
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(rawno = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_rawno != top_thread(stream, rawno)))
+ break;
+
+ mn_set_cur(msgmap,orig); /* return home */
+
+ return collapsed;
+}
+
+/* this function tells us if the thread (or branch in the case of loose threads)
+ * is collapsed
+ */
+
+int
+this_thread_is_kolapsed(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawmsgno)
+{
+ int collapsed;
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, orig;
+
+ if(!stream)
+ return -1;
+
+ rawno = rawmsgno;
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return -1;
+
+ collapsed = get_lflag(stream, NULL, rawno, MN_COLL | MN_CHID);
+
+ if (!thrd->next){
+ if (thrd->rawno != top_thread(stream, thrd->rawno))
+ collapsed = get_lflag(stream, NULL, rawno, MN_CHID);
+ else
+ collapsed = get_lflag(stream, NULL, rawno, MN_COLL);
+ }
+
+ return collapsed;
+}
+
+/*
+ * This function assumes that it is called at a top of a thread in its
+ * first call
+ */
+
+int
+count_this_thread(MAILSTREAM *stream, unsigned long rawno)
+{
+ unsigned long top, orig_top, topnxt;
+ PINETHRD_S *thrd = NULL;
+ int count = 1;
+
+ if(!stream)
+ return 0;
+
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return 0;
+
+ if (thrd->next)
+ count += count_this_thread(stream, thrd->next);
+
+ if (thrd->branch)
+ count += count_this_thread(stream, thrd->branch);
+
+ return count;
+}
+
+int
+count_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long rawno)
+{
+ unsigned long top, orig, orig_top;
+ PINETHRD_S *thrd = NULL;
+ int done = 0, count = 0;
+
+ if(!stream)
+ return 0;
+
+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_thread(stream, msgmap,rawno);
+ top = orig_top = top_thread(stream, rawno);
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return 0;
+
+ while (!done){
+ count += count_this_thread(stream, top);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || (move_next_this_thread(state, stream, msgmap, 0) <= 0)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ mn_set_cur(msgmap,mn_raw2m(msgmap, orig));
+ return count;
+}
+
+unsigned long
+get_branch(MAILSTREAM *stream, PINETHRD_S *thrd)
+{
+ PINETHRD_S *nthrd = NULL;
+ unsigned long top;
+
+ if (thrd->toploose && thrd->nextthd)
+ nthrd = fetch_thread(stream, thrd->nextthd);
+ if (!nthrd)
+ return thrd->branch;
+ top = top_thread(stream, thrd->rawno);
+ return thrd->branch
+ ? thrd->branch
+ : (F_ON(F_ENHANCED_THREAD, ps_global)
+ ? (top == top_thread(stream, nthrd->rawno) ? thrd->nextthd : 0L)
+ : 0L);
+}
+
+unsigned long
+get_next(MAILSTREAM *stream, PINETHRD_S *thrd)
+{
+ return thrd->next;
+}
+
+long
+get_length_branch(MAILSTREAM *stream, long rawno)
+{
+ int branchp = 0, done = 0;
+ long top, count = 1L, raw;
+ PINETHRD_S *thrd, *pthrd = NULL, *nthrd;
+
+ thrd = fetch_thread(stream, rawno);
+
+ if (!thrd)
+ return -1L;
+
+ top = thrd->top;
+
+ if (thrd->parent)
+ pthrd = fetch_thread(stream, thrd->parent);
+
+ if (thrd->rawno == top)
+ branchp++;
+
+ if (!branchp && !pthrd){ /* what!!?? */
+ raw = top;
+ while (!done){
+ pthrd = fetch_thread(stream, raw);
+ if ((pthrd->next == rawno) || (pthrd->branch == rawno))
+ done++;
+ else{
+ if (pthrd->next)
+ raw = pthrd->next;
+ else if (pthrd->branch)
+ raw = pthrd->branch;
+ }
+ }
+ }
+
+ if (pthrd && pthrd->next == thrd->rawno && thrd->branch)
+ branchp++;
+
+ if (pthrd && pthrd->next && pthrd->next != thrd->rawno){
+ nthrd = fetch_thread(stream, pthrd->next);
+ while (nthrd && nthrd->branch && nthrd->branch != thrd->rawno)
+ nthrd = fetch_thread(stream, nthrd->branch);
+ if(nthrd && nthrd->branch && nthrd->branch == thrd->rawno)
+ branchp++;
+ }
+
+ if(branchp){
+ int entry = 0;
+ while(thrd && thrd->next){
+ entry = 1;
+ count++;
+ thrd = fetch_thread(stream, thrd->next);
+ if (thrd->branch)
+ break;
+ }
+ if (entry && thrd->branch)
+ count--;
+ }
+ return branchp ? (count ? count : 1L) : 0L;
+}
+
+int pine_compare_size_thread(const qsort_t *a, const qsort_t *b)
+{
+ SIZETHREAD_T *s = (SIZETHREAD_T *) a, *t = (SIZETHREAD_T *) b;
+
+ return s->count == t->count ? s->pos - t->pos : s->count - t->count;
+}
+
+
+
+void
+find_msgmap(MAILSTREAM *stream, MSGNO_S *msgmap, int flags, SortOrder ordersort, unsigned is_rev)
+{
+ long *old_arrival,*new_arrival;
+ long init_thread, end_thread, current;
+ long i, j, k;
+ long tmsg, ntmsg, nthreads;
+ SIZETHREAD_T *l;
+ PINETHRD_S *thrd;
+
+ erase_thread_info = 0;
+ current = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ switch(ordersort){
+ case SortSize:
+ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0);
+ tmsg = mn_get_total(msgmap) + 1;
+
+ if(tmsg <= 1)
+ return;
+
+ for (i= 1L, k = 0L; i <= mn_get_total(msgmap); i += count_thread(ps_global, stream, msgmap, msgmap->sort[i]), k++);
+ l = (SIZETHREAD_T *) fs_get(k*sizeof(SIZETHREAD_T));
+ for (j = 0L, i=1L; j < k && i<= mn_get_total(msgmap); ){
+ l[j].count = count_thread(ps_global, stream, msgmap, msgmap->sort[i]);
+ l[j].pos = i;
+ i += l[j].count;
+ j++;
+ }
+ qsort((void *)l, (size_t) k, sizeof(SIZETHREAD_T), pine_compare_size_thread);
+ old_arrival = (long *) fs_get(tmsg * sizeof(long));
+ for(i = 1L, j = 0; j < k; j++){ /* copy thread of length .count */
+ int p;
+ for(p = 0; p < l[j].count; p++)
+ old_arrival[i++] = msgmap->sort[l[j].pos + p];
+ }
+ fs_give((void **)&l);
+ break;
+ default:
+ sort_folder(stream, msgmap, ordersort, 0, SRT_VRB, 0);
+ tmsg = mn_get_total(msgmap) + 1;
+
+ if (tmsg <= 1)
+ return;
+
+ old_arrival = (long *) fs_get(tmsg * sizeof(long));
+ for (i= 1L;(i <= mn_get_total(msgmap)) && (old_arrival[i] = msgmap->sort[i]); i++);
+ /* sort by thread */
+ sort_folder(stream, msgmap, SortThread, 0, SRT_VRB, 0);
+ break;
+
+ }
+
+ ntmsg = mn_get_total(msgmap) + 1;
+ if (tmsg != ntmsg){ /* oh oh, something happened, we better try again */
+ fs_give((void **)&old_arrival);
+ find_msgmap(stream, msgmap, flags, ordersort, is_rev);
+ return;
+ }
+
+ /* reconstruct the msgmap */
+
+ new_arrival = (long *) fs_get(tmsg * sizeof(long));
+ memset(new_arrival, 0, tmsg*sizeof(long));
+ i = mn_get_total(msgmap);
+ /* we copy from the bottom, the last one to be filled is new_arrival[1] */
+ while (new_arrival[1] == 0){
+ int done = 0;
+ long n;
+
+ init_thread = top_thread(stream, old_arrival[i]);
+ thrd = fetch_thread(stream, init_thread);
+ for (n = mn_get_total(msgmap); new_arrival[n] != 0 && !done; n--)
+ done = (new_arrival[n] == init_thread);
+ if (!done){
+ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread));
+ if(move_next_thread(ps_global, stream, msgmap, 0) <= 0)
+ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1;
+ else
+ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread);
+ end_thread = mn_raw2m(msgmap, init_thread) + j;
+ for(k = 1L; k <= j; k++)
+ new_arrival[tmsg - k] = msgmap->sort[end_thread - k];
+ tmsg -= j;
+ }
+ i--;
+ }
+ relink_threads(stream, msgmap, new_arrival);
+ for (i = 1; (i <= mn_get_total(msgmap))
+ && (msgmap->sort[i] = new_arrival[i]); i++);
+ msgno_reset_isort(msgmap);
+
+ fs_give((void **)&new_arrival);
+ fs_give((void **)&old_arrival);
+
+
+ if(is_rev && (mn_get_total(msgmap) > 1L)){
+ long *rev_sort;
+ long i = 1L, l = mn_get_total(msgmap);
+
+ rev_sort = (long *) fs_get((mn_get_total(msgmap)+1L) * sizeof(long));
+ memset(rev_sort, 0, (mn_get_total(msgmap)+1L)*sizeof(long));
+ while (l > 0L){
+ if (top_thread(stream, msgmap->sort[l]) == msgmap->sort[l]){
+ long init_thread = msgmap->sort[l];
+ long j, k;
+
+ mn_set_cur(msgmap, mn_raw2m(msgmap, init_thread));
+ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0)
+ j = mn_get_total(msgmap) - mn_raw2m(msgmap, init_thread) + 1;
+ else
+ j = mn_get_cur(msgmap) - mn_raw2m(msgmap, init_thread);
+ for (k = 0L; (k < j) && (rev_sort[i+k] = msgmap->sort[l+k]); k++);
+ i += j;
+ }
+ l--;
+ }
+ relink_threads(stream, msgmap, rev_sort);
+ for (i = 1L; i <= mn_get_total(msgmap); i++)
+ msgmap->sort[i] = rev_sort[i];
+ msgno_reset_isort(msgmap);
+ fs_give((void **)&rev_sort);
+ }
+ mn_reset_cur(msgmap, first_sorted_flagged(is_rev ? F_NONE : F_SRCHBACK,
+ stream, mn_raw2m(msgmap, current), FSF_SKIP_CHID));
+ msgmap->top = -1L;
+
+ sp_set_unsorted_newmail(ps_global->mail_stream, 0);
+
+ for(i = 1L; i <= ps_global->mail_stream->nmsgs; i++)
+ mail_elt(ps_global->mail_stream, i)->spare7 = 0;
+
+ mn_set_sort(msgmap, SortThread);
+ mn_set_revsort(msgmap, is_rev);
+ erase_thread_info = 1;
+ clear_index_cache(stream, 0);
+}
+
+void
+move_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int direction)
+{
+ long new_cursor, old_cursor = mn_get_cur(msgmap);
+ int rv;
+ PINETHRD_S *thrd;
+
+ rv = direction > 0 ? move_next_thread(state, stream, msgmap, 1):
+ move_prev_thread(state, stream, msgmap, 1);
+ if (rv > 0 && THRD_INDX_ENABLED()){
+ new_cursor = mn_get_cur(msgmap);
+ mn_set_cur(msgmap, old_cursor);
+ unview_thread(state, stream, msgmap);
+ thrd = fetch_thread(stream,mn_m2raw(msgmap, new_cursor));
+ mn_set_cur(msgmap, new_cursor);
+ view_thread(state, stream, msgmap, 1);
+ state->next_screen = SCREEN_FUN_NULL;
+ }
+}
+
+void
+relink_threads(MAILSTREAM *stream, MSGNO_S *msgmap, long *new_arrival)
+{
+ long last_thread = 0L;
+ long i = 0L, j = 1L, k;
+ PINETHRD_S *thrd, *nthrd;
+
+ while (j <= mn_get_total(msgmap)){
+ i++;
+ thrd = fetch_thread(stream, new_arrival[j]);
+ if (!thrd) /* sort failed!, better leave from here now!!! */
+ break;
+ thrd->prevthd = last_thread;
+ thrd->thrdno = i;
+ thrd->head = new_arrival[1];
+ last_thread = thrd->rawno;
+ mn_set_cur(msgmap, mn_raw2m(msgmap,thrd->top));
+ k = mn_get_cur(msgmap);
+ if (move_next_thread(ps_global, stream, msgmap, 0) <= 0)
+ j += mn_get_total(msgmap) + 1 - k;
+ else
+ j += mn_get_cur(msgmap) - k;
+ if (!thrd->toploose)
+ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L;
+ else{
+ int done = 0;
+ while(thrd->nextthd && !done){
+ thrd->thrdno = i;
+ thrd->head = new_arrival[1];
+ if (thrd->nextthd)
+ nthrd = fetch_thread(stream, thrd->nextthd);
+ else
+ done++;
+ if(top_thread(stream, thrd->rawno) == top_thread(stream, nthrd->rawno))
+ thrd = nthrd;
+ else
+ done++;
+ }
+ thrd->nextthd = (j <= mn_get_total(msgmap)) ? new_arrival[j] : 0L;
+ last_thread = thrd->rawno;
+ }
+ }
+}
+
+int
+move_next_this_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display)
+{
+ PINETHRD_S *thrd = NULL, *thrdnxt;
+ unsigned long rawno, top;
+ int rv = 1;
+
+ if(!stream)
+ return -1;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return -1;
+
+ top = top_thread(stream, rawno);
+
+ thrdnxt = (top == rawno) ? fetch_thread(stream, top) : thrd;
+ if (thrdnxt->nextthd)
+ mn_set_cur(msgmap,mn_raw2m(msgmap, thrdnxt->nextthd));
+ else{
+ rv = 0;
+ if (display)
+ q_status_message(SM_ORDER, 0, 1, "No more Threads to advance");
+ }
+ return rv;
+}
+
+int
+move_next_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display)
+{
+ int collapsed, rv = 1, done = 0;
+ PINETHRD_S *thrd = NULL;
+ unsigned long orig, orig_top, top;
+
+ if(!stream)
+ return 0;
+
+ orig = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ move_top_thread(stream, msgmap,orig);
+ top = orig_top = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(top)
+ thrd = fetch_thread(stream, top);
+
+ if(!thrd)
+ return 0;
+
+ while (rv > 0 && !done){
+ rv = move_next_this_thread(state, stream, msgmap, display);
+ if (F_OFF(F_ENHANCED_THREAD, state)
+ || !(top = mn_m2raw(msgmap, mn_get_cur(msgmap)))
+ || (orig_top != top_thread(stream, top)))
+ done++;
+ }
+ if (display){
+ if (rv > 0 && SEP_THRDINDX())
+ q_status_message(SM_ORDER, 0, 2, "Viewing next thread");
+ if (!rv)
+ q_status_message(SM_ORDER, 0, 2, "No more threads to advance");
+ }
+ if(rv <= 0){
+ rv = 0;
+ mn_set_cur(msgmap, mn_raw2m(msgmap, orig));
+ }
+
+ return rv;
+}
+
+int
+move_prev_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int display)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, top;
+ int rv = 1;
+
+ if(!stream)
+ return -1;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return -1;
+
+ top = top_thread(stream, rawno);
+
+ if (top != rawno)
+ mn_set_cur(msgmap,mn_raw2m(msgmap, top));
+ else if (thrd->prevthd)
+ mn_set_cur(msgmap,mn_raw2m(msgmap, top_thread(stream,thrd->prevthd)));
+ else
+ rv = 0;
+ if (display){
+ if (rv && SEP_THRDINDX())
+ q_status_message(SM_ORDER, 0, 2, "Viewing previous thread");
+ if (!rv)
+ q_status_message(SM_ORDER, 0, 2, "No more threads to go back");
+ }
+
+ return rv;
+}
+
+/* add more keys to this list */
+int
+allowed_thread_key(SortOrder sort)
+{
+ return sort == SortArrival || sort == SortDate
+ || sort == SortScore || sort == SortThread
+ || sort == SortSize;
+}
+
Index: alpine-2.20/pith/thread.h
===================================================================
--- alpine-2.20.orig/pith/thread.h
+++ alpine-2.20/pith/thread.h
@@ -37,6 +37,7 @@ typedef struct pine_thrd {
unsigned long nextthd; /* next thread, only tops have this */
unsigned long prevthd; /* previous thread, only tops have this */
unsigned long top; /* top of this thread */
+ unsigned long toploose; /* top of this thread, if is loose */
unsigned long head; /* head of the whole thread list */
} PINETHRD_S;
@@ -92,7 +93,7 @@ void erase_threading_info(MAILSTRE
void sort_thread_callback(MAILSTREAM *, THREADNODE *);
void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *);
PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned);
-void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long);
+void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long, int);
void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *);
unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long);
unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int);
@@ -106,6 +107,24 @@ int view_thread(struct pine *, MAI
int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *);
void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **);
-
+void find_msgmap(MAILSTREAM *, MSGNO_S *, int, SortOrder, unsigned);
+void move_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+void relink_threads(MAILSTREAM *, MSGNO_S *, long *);
+long top_thread(MAILSTREAM *, long);
+long top_this_thread(MAILSTREAM *, long);
+long get_length_branch(MAILSTREAM *, long);
+unsigned long get_next(MAILSTREAM *,PINETHRD_S *);
+unsigned long get_branch(MAILSTREAM *,PINETHRD_S *);
+int count_thread(struct pine *, MAILSTREAM *, MSGNO_S *, long);
+int count_this_thread(MAILSTREAM *, unsigned long);
+int this_thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long);
+int thread_is_kolapsed(struct pine *, MAILSTREAM *, MSGNO_S *, long);
+int move_prev_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+int move_next_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+int move_next_this_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+void move_top_thread(MAILSTREAM *, MSGNO_S *, long);
+void move_top_this_thread(MAILSTREAM *, MSGNO_S *, long);
+THREADNODE *copy_tree(THREADNODE *);
+int allowed_thread_key(SortOrder sort);
#endif /* PITH_THREAD_INCLUDED */
Index: alpine-2.20/web/src/alpined.d/alpined.c
===================================================================
--- alpine-2.20.orig/web/src/alpined.d/alpined.c
+++ alpine-2.20/web/src/alpined.d/alpined.c
@@ -2755,7 +2755,7 @@ PEConfigCmd(ClientData clientData, Tcl_I
init_save_defaults();
break;
case V_SORT_KEY:
- decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev);
+ decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev, 0);
break;
case V_VIEW_HDR_COLORS :
set_custom_spec_colors(ps_global);
@@ -6331,7 +6331,7 @@ PEMailboxCmd(ClientData clientData, Tcl_
&& mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed))
sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
ps_global->sort_types[i],
- reversed, 0);
+ reversed, 0, 1);
break;
}