7bdde85c8b
OBS-URL: https://build.opensuse.org/package/show/server:mail/alpine?expand=0&rev=0df79cc66d158318ac0ab6fbb1c8a0df
3199 lines
105 KiB
Diff
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
|
|
<End of help on this topic>
|
|
</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>
|
|
+<End of help on this topic>
|
|
+</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 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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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 INDEX screen is
|
|
+the same as sorting by <A HREF="h_thread_index_sort_subj">Subject</A>.
|
|
+
|
|
+<P>
|
|
+<End of help on this topic>
|
|
+</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 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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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>
|
|
+<End of help on this topic>
|
|
+</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
|
|
<End of help on this topic>
|
|
</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 "removed" 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
|
|
+"Thread".
|
|
+
|
|
+<P>
|
|
+<UL>
|
|
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
|
|
+</UL><P>
|
|
+<End of help on this topic>
|
|
+</BODY>
|
|
+</HTML>
|
|
====== h_config_other_startup =====
|
|
<HTML>
|
|
<HEAD>
|
|
@@ -30485,6 +30690,23 @@ Reply Use, Forward Use, and Compose Use.
|
|
<End of help on this topic>
|
|
</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>
|
|
+<End of help on this topic>
|
|
+</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;
|
|
}
|