* New version 2.21.999
[alpine.git] / alpine / context.c
blob2b3f68a49c331f17b3b785ac00ae972023a64a07
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: context.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 #include "headers.h"
20 #include "context.h"
21 #include "confscroll.h"
22 #include "status.h"
23 #include "folder.h"
24 #include "radio.h"
25 #include "alpine.h"
26 #include "mailindx.h"
27 #include "mailcmd.h"
28 #include "send.h"
29 #include "../pith/list.h"
30 #include "../pith/state.h"
31 #include "../pith/conf.h"
32 #include "../pith/thread.h"
36 * Internal prototypes
38 int context_config_tool(struct pine *, int, CONF_S **, unsigned);
39 int context_config_add(struct pine *, CONF_S **);
40 int context_config_shuffle(struct pine *, CONF_S **);
41 int context_config_edit(struct pine *, CONF_S **);
42 int context_config_delete(struct pine *, CONF_S **);
43 int ccs_var_delete(struct pine *, CONTEXT_S *);
44 int ccs_var_insert(struct pine *, char *, struct variable *, char **, int);
45 int context_select_tool(struct pine *, int, CONF_S **, unsigned);
49 * Setup CollectionLists. Build a context list on the fly from the config
50 * variable and edit it. This won't include Incoming-Folders because that
51 * is a pseudo collection, but that's ok since we can't do the operations
52 * on it, anyway. Reset real config list at the end.
54 void
55 context_config_screen(struct pine *ps, CONT_SCR_S *cs, int edit_exceptions)
57 CONTEXT_S *top, **clist, *cp;
58 CONF_S *ctmpa, *first_line, *heading;
59 OPT_SCREEN_S screen;
60 int i, readonly_warning, some_defined, ret;
61 int reinit_contexts = 0, prime;
62 char **lval, **lval1, **lval2, ***alval;
63 struct variable fake_fspec_var, fake_nspec_var;
64 struct variable *fake_fspec, *fake_nspec;
66 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
68 if(ps->restricted)
69 readonly_warning = 1;
70 else{
71 PINERC_S *prc = NULL;
73 switch(ew){
74 case Main:
75 prc = ps->prc;
76 break;
77 case Post:
78 prc = ps->post_prc;
79 break;
80 default:
81 break;
84 readonly_warning = prc ? prc->readonly : 1;
85 if(prc && prc->quit_to_edit){
86 quit_to_edit_msg(prc);
87 return;
91 go_again:
92 top = NULL; ctmpa = NULL; first_line = NULL;
93 some_defined = 0, prime = 0;
94 fake_fspec = &fake_fspec_var;
95 fake_nspec = &fake_nspec_var;
96 memset((void *)fake_fspec, 0, sizeof(*fake_fspec));
97 memset((void *)fake_nspec, 0, sizeof(*fake_nspec));
99 /* so fixed_var() will work right */
100 fake_fspec->is_list = 1;
101 fake_nspec->is_list = 1;
102 if((ps->vars[V_FOLDER_SPEC]).is_fixed){
103 fake_fspec->is_fixed = 1;
104 if((ps->vars[V_FOLDER_SPEC]).fixed_val.l
105 && (ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]){
106 fake_fspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
107 fake_fspec->fixed_val.l[0]
108 = cpystr((ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]);
109 fake_fspec->fixed_val.l[1] = NULL;
113 if((ps->vars[V_NEWS_SPEC]).is_fixed){
114 fake_nspec->is_fixed = 1;
115 if((ps->vars[V_NEWS_SPEC]).fixed_val.l
116 && (ps->vars[V_NEWS_SPEC]).fixed_val.l[0]){
117 fake_nspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
118 fake_nspec->fixed_val.l[0] = cpystr(INHERIT);
119 fake_nspec->fixed_val.l[0]
120 = cpystr((ps->vars[V_NEWS_SPEC]).fixed_val.l[0]);
121 fake_nspec->fixed_val.l[1] = NULL;
125 clist = ⊤
126 lval1 = LVAL(&ps->vars[V_FOLDER_SPEC], ew);
127 lval2 = LVAL(&ps->vars[V_NEWS_SPEC], ew);
129 alval = ALVAL(fake_fspec, ew);
130 if(lval1)
131 *alval = copy_list_array(lval1);
132 else if(!edit_exceptions && ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[0] &&
133 ps->VAR_FOLDER_SPEC[0][0])
134 *alval = copy_list_array(ps->VAR_FOLDER_SPEC);
135 else
136 fake_fspec = NULL;
138 if(fake_fspec){
139 lval = LVAL(fake_fspec, ew);
140 for(i = 0; lval && lval[i]; i++){
141 cp = NULL;
142 if(i == 0 && !strcmp(lval[i], INHERIT)){
143 cp = (CONTEXT_S *)fs_get(sizeof(*cp));
144 memset((void *)cp, 0, sizeof(*cp));
145 cp->use = CNTXT_INHERIT;
146 cp->label = cpystr("Default collections are inherited");
148 else if((cp = new_context(lval[i], &prime)) != NULL){
149 cp->var.v = fake_fspec;
150 cp->var.i = i;
153 if(cp){
154 *clist = cp; /* add it to list */
155 clist = &cp->next; /* prepare for next */
159 set_current_val(fake_fspec, FALSE, FALSE);
162 alval = ALVAL(fake_nspec, ew);
163 if(lval2)
164 *alval = copy_list_array(lval2);
165 else if(!edit_exceptions && ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
166 ps->VAR_NEWS_SPEC[0][0])
167 *alval = copy_list_array(ps->VAR_NEWS_SPEC);
168 else
169 fake_nspec = NULL;
171 if(fake_nspec){
172 lval = LVAL(fake_nspec, ew);
173 for(i = 0; lval && lval[i]; i++){
174 cp = NULL;
175 if(i == 0 && !strcmp(lval[i], INHERIT)){
176 cp = (CONTEXT_S *)fs_get(sizeof(*cp));
177 memset((void *)cp, 0, sizeof(*cp));
178 cp->use = CNTXT_INHERIT;
179 cp->label = cpystr("Default collections are inherited");
181 else if((cp = new_context(lval[i], &prime)) != NULL){
182 cp->var.v = fake_nspec;
183 cp->var.i = i;
186 if(cp){
187 *clist = cp; /* add it to list */
188 clist = &cp->next; /* prepare for next */
192 set_current_val(fake_nspec, FALSE, FALSE);
195 for(cp = top; cp; cp = cp->next)
196 if(!(cp->use & CNTXT_INHERIT)){
197 some_defined++;
198 break;
201 if(edit_exceptions && !some_defined){
202 q_status_message(SM_ORDER, 3, 7,
203 _("No exceptions to edit. First collection exception must be set by editing file"));
204 free_contexts(&top);
205 if(reinit_contexts){
206 free_contexts(&ps_global->context_list);
207 init_folders(ps_global);
210 return;
214 /* fix up prev pointers */
215 for(cp = top; cp; cp = cp->next)
216 if(cp->next)
217 cp->next->prev = cp;
219 new_confline(&ctmpa); /* blank line */
220 ctmpa->keymenu = cs->keymenu;
221 ctmpa->help = cs->help.text;
222 ctmpa->help_title = cs->help.title;
223 ctmpa->tool = context_config_tool;
224 ctmpa->flags |= (CF_NOSELECT | CF_B_LINE);
226 for(cp = top; cp; cp = cp->next){
227 new_confline(&ctmpa);
228 heading = ctmpa;
229 if(!(cp->use & CNTXT_INHERIT))
230 ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
232 ctmpa->var = cp->var.v;
233 ctmpa->keymenu = cs->keymenu;
234 ctmpa->help = cs->help.text;
235 ctmpa->help_title = cs->help.title;
236 ctmpa->tool = context_config_tool;
237 ctmpa->flags |= CF_STARTITEM;
238 ctmpa->valoffset = 4;
239 ctmpa->d.c.ct = cp;
240 ctmpa->d.c.cs = cs;
241 if(cp->use & CNTXT_INHERIT)
242 ctmpa->flags |= CF_INHERIT | CF_NOSELECT;
244 if((!first_line && !(cp->use & CNTXT_INHERIT)) ||
245 (!(cp->use & CNTXT_INHERIT) &&
246 cp->var.v &&
247 (cs->starting_var == cp->var.v) &&
248 (cs->starting_varmem == cp->var.i)))
249 first_line = ctmpa;
251 /* Add explanatory text */
252 new_confline(&ctmpa);
253 ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
254 ctmpa->keymenu = cs->keymenu;
255 ctmpa->help = cs->help.text;
256 ctmpa->help_title = cs->help.title;
257 ctmpa->tool = context_config_tool;
258 ctmpa->flags |= CF_NOSELECT;
259 ctmpa->valoffset = 8;
261 /* Always add blank line, make's shuffling a little easier */
262 new_confline(&ctmpa);
263 heading->headingp = ctmpa; /* use headingp to mark end */
264 ctmpa->keymenu = cs->keymenu;
265 ctmpa->help = cs->help.text;
266 ctmpa->help_title = cs->help.title;
267 ctmpa->tool = context_config_tool;
268 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
269 ctmpa->valoffset = 0;
272 cs->starting_var = NULL;
275 memset(&screen, 0, sizeof(screen));
276 screen.ro_warning = readonly_warning;
277 ret = conf_scroll_screen(ps, &screen, first_line, cs->title,
278 cs->print_string, 0, NULL);
280 free_contexts(&top);
282 if(ret)
283 reinit_contexts++;
286 * 15 means the tool wants us to reset and go again. The config var
287 * has been changed. The contexts will be built again from the
288 * config variable and the real contexts will be rebuilt below.
289 * This is easier and safer than having the tools rebuild the context
290 * list and the display correct. It's difficult to do because of all
291 * the inheriting and defaulting going on.
293 if(ret == 15){
294 if(edit_exceptions && !LVAL(fake_fspec, ew) && !LVAL(fake_nspec, ew)){
295 if(want_to(_("Really delete last exceptional collection"),
296 'n', 'n', h_config_context_del_except,
297 WT_FLUSH_IN) != 'y'){
298 free_variable_values(fake_fspec);
299 free_variable_values(fake_nspec);
300 goto go_again;
304 /* resolve variable changes */
305 if((lval1 && !equal_list_arrays(lval1, LVAL(fake_fspec, ew))) ||
306 (fake_fspec && !equal_list_arrays(ps->VAR_FOLDER_SPEC,
307 LVAL(fake_fspec, ew)))){
308 i = set_variable_list(V_FOLDER_SPEC,
309 LVAL(fake_fspec, ew), TRUE, ew);
310 set_current_val(&ps->vars[V_FOLDER_SPEC], TRUE, FALSE);
312 if(i)
313 q_status_message(SM_ORDER, 3, 3,
314 _("Trouble saving change, cancelled"));
315 else if(!edit_exceptions && lval1 && !LVAL(fake_fspec, ew)){
316 cs->starting_var = fake_fspec;
317 cs->starting_varmem = 0;
318 q_status_message(SM_ORDER, 3, 3,
319 _("Deleted last Folder-Collection, reverting to default"));
321 else if(!edit_exceptions && !lval1 && !LVAL(fake_fspec, ew)){
322 cs->starting_var = fake_fspec;
323 cs->starting_varmem = 0;
324 q_status_message(SM_ORDER, 3, 3,
325 _("Deleted default Folder-Collection, reverting back to default"));
329 if((lval2 && !equal_list_arrays(lval2, LVAL(fake_nspec, ew))) ||
330 (fake_nspec && !equal_list_arrays(ps->VAR_NEWS_SPEC,
331 LVAL(fake_nspec, ew)))){
332 i = set_variable_list(V_NEWS_SPEC,
333 LVAL(fake_nspec, ew), TRUE, ew);
334 set_news_spec_current_val(TRUE, FALSE);
336 if(i)
337 q_status_message(SM_ORDER, 3, 3,
338 _("Trouble saving change, cancelled"));
339 else if(!edit_exceptions && lval2 && !LVAL(fake_nspec, ew) &&
340 ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
341 ps->VAR_NEWS_SPEC[0][0]){
342 cs->starting_var = fake_nspec;
343 cs->starting_varmem = 0;
344 q_status_message(SM_ORDER, 3, 3,
345 _("Deleted last News-Collection, reverting to default"));
347 else if(!edit_exceptions && !lval2 && !LVAL(fake_nspec, ew) &&
348 ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
349 ps->VAR_NEWS_SPEC[0][0]){
350 cs->starting_var = fake_nspec;
351 cs->starting_varmem = 0;
352 q_status_message(SM_ORDER, 3, 3,
353 _("Deleted default News-Collection, reverting back to default"));
357 free_variable_values(fake_fspec);
358 free_variable_values(fake_nspec);
359 goto go_again;
362 ps->mangled_screen = 1;
364 /* make the real context list match the changed config variables */
365 if(reinit_contexts){
366 free_contexts(&ps_global->context_list);
367 init_folders(ps_global);
372 /*----------------------------------------------------------------------
373 Function to display/manage collections
375 ----*/
376 CONTEXT_S *
377 context_select_screen(struct pine *ps, CONT_SCR_S *cs, int ro_warn)
379 CONTEXT_S *cp;
380 CONF_S *ctmpa = NULL, *first_line = NULL, *heading;
381 OPT_SCREEN_S screen, *saved_screen;
382 int readonly_warning = 0;
384 /* restrict to normal config */
385 ew = Main;
387 if(!cs->edit)
388 readonly_warning = 0;
389 else if(ps->restricted)
390 readonly_warning = 1;
391 else{
392 PINERC_S *prc = NULL;
394 switch(ew){
395 case Main:
396 prc = ps->prc;
397 break;
398 case Post:
399 prc = ps->post_prc;
400 break;
401 default:
402 break;
405 readonly_warning = prc ? prc->readonly : 1;
406 if(ro_warn && prc && prc->quit_to_edit){
407 quit_to_edit_msg(prc);
408 return(NULL);
412 readonly_warning *= ro_warn;
415 * Loop thru available contexts, setting up for display
416 * (note: if no "cp" we're hosed. should never happen ;)
418 for(cp = *cs->contexts; cp->prev; cp = cp->prev)
421 /* delimiter for Mail Collections */
422 new_confline(&ctmpa); /* blank line */
423 ctmpa->keymenu = cs->keymenu;
424 ctmpa->help = cs->help.text;
425 ctmpa->help_title = cs->help.title;
426 ctmpa->tool = context_select_tool;
427 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
430 new_confline(&ctmpa);
431 heading = ctmpa;
432 ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
433 ctmpa->var = cp->var.v;
434 ctmpa->keymenu = cs->keymenu;
435 ctmpa->help = cs->help.text;
436 ctmpa->help_title = cs->help.title;
437 ctmpa->tool = context_select_tool;
438 ctmpa->flags |= CF_STARTITEM;
439 ctmpa->valoffset = 4;
440 ctmpa->d.c.ct = cp;
441 ctmpa->d.c.cs = cs;
443 if(!first_line || cp == cs->start)
444 first_line = ctmpa;
446 /* Add explanatory text */
447 new_confline(&ctmpa);
448 ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
449 ctmpa->keymenu = cs->keymenu;
450 ctmpa->help = cs->help.text;
451 ctmpa->help_title = cs->help.title;
452 ctmpa->tool = context_select_tool;
453 ctmpa->flags |= CF_NOSELECT;
454 ctmpa->valoffset = 8;
456 /* Always add blank line, make's shuffling a little easier */
457 new_confline(&ctmpa);
458 heading->headingp = ctmpa;
459 ctmpa->keymenu = cs->keymenu;
460 ctmpa->help = cs->help.text;
461 ctmpa->help_title = cs->help.title;
462 ctmpa->tool = context_select_tool;
463 ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
464 ctmpa->valoffset = 0;
466 while((cp = cp->next) != NULL);
469 saved_screen = opt_screen;
470 memset(&screen, 0, sizeof(screen));
471 screen.ro_warning = readonly_warning;
472 (void) conf_scroll_screen(ps, &screen, first_line, cs->title,
473 cs->print_string, 0, NULL);
474 opt_screen = saved_screen;
475 ps->mangled_screen = 1;
476 return(cs->selected);
481 context_config_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
483 int retval = 0;
485 switch(cmd){
486 case MC_DELETE :
487 if(!fixed_var((*cl)->var, "delete", "collection"))
488 retval = context_config_delete(ps, cl);
490 break;
492 case MC_EDIT :
493 if(!fixed_var((*cl)->var, "change", "collection"))
494 retval = context_config_edit(ps, cl);
496 break;
498 case MC_ADD :
499 if(!fixed_var((*cl)->var, "add to", "collection"))
500 retval = context_config_add(ps, cl);
502 break;
504 case MC_SHUFFLE :
505 if(!fixed_var((*cl)->var, "shuffle", "collection"))
506 retval = context_config_shuffle(ps, cl);
508 break;
510 case MC_EXIT :
511 retval = simple_exit_cmd(flags);
512 break;
514 default:
515 retval = -1;
516 break;
519 if(retval > 0)
520 ps->mangled_body = 1;
522 return(retval);
527 context_config_add(struct pine *ps, CONF_S **cl)
529 char *raw_ctxt;
530 struct variable *var;
531 char **lval;
532 int count;
534 if((raw_ctxt = context_edit_screen(ps, "ADD", NULL, NULL, NULL, NULL)) != NULL){
537 * If var is non-NULL we add to the end of that var.
538 * If it is NULL, that means we're adding to the current_val, so
539 * we'll have to soak up the default values from there into our
540 * own variable.
542 if((*cl)->var){
543 var = (*cl)->var;
544 lval = LVAL((*cl)->var, ew);
546 else{
547 q_status_message(SM_ORDER|SM_DING, 3, 3,
548 "Programmer botch in context_config_add");
549 return(0);
552 for(count = 0; lval && lval[count]; count++)
555 if(!ccs_var_insert(ps, raw_ctxt, var, lval, count)){
556 fs_give((void **)&raw_ctxt);
557 q_status_message(SM_ORDER|SM_DING, 3, 3,
558 _("Error adding new collection"));
559 return(0);
562 fs_give((void **)&raw_ctxt);
564 (*cl)->d.c.cs->starting_var = var;
565 (*cl)->d.c.cs->starting_varmem = count;
566 q_status_message(SM_ORDER, 0, 3,
567 _("New collection added. Use \"$\" to adjust order."));
568 return(15);
571 ps->mangled_screen = 1;
572 return(0);
577 context_config_shuffle(struct pine *ps, CONF_S **cl)
579 char prompt[256];
580 int n = 0, cmd, i1, i2, count = 0, insert_num, starting_varmem;
581 int news_problem = 0, deefault = 0;
582 ESCKEY_S ekey[3];
583 CONTEXT_S *cur_ctxt, *other_ctxt;
584 char *tmp, **lval, **lval1, **lval2;
585 struct variable *cur_var, *other_var;
587 if(!((*cl)->d.c.ct && (*cl)->var))
588 return(0);
590 /* enable UP? */
591 if((*cl)->d.c.ct->prev && !((*cl)->d.c.ct->prev->use & CNTXT_INHERIT)){
593 * Don't allow shuffling news collection up to become
594 * the primary collection. That would happen if prev is primary
595 * and this one is news.
597 if((*cl)->d.c.ct->prev->use & CNTXT_SAVEDFLT &&
598 (*cl)->d.c.ct->use & CNTXT_NEWS)
599 news_problem++;
600 else{
601 ekey[n].ch = 'u';
602 ekey[n].rval = 'u';
603 ekey[n].name = "U";
604 ekey[n++].label = N_("Up");
605 deefault = 'u';
609 /* enable DOWN? */
610 if((*cl)->d.c.ct->next && !((*cl)->d.c.ct->next->use & CNTXT_INHERIT)){
612 * Don't allow shuffling down past news collection if this
613 * is primary collection.
615 if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
616 (*cl)->d.c.ct->next->use & CNTXT_NEWS)
617 news_problem++;
618 else{
619 ekey[n].ch = 'd';
620 ekey[n].rval = 'd';
621 ekey[n].name = "D";
622 ekey[n++].label = N_("Down");
623 if(!deefault)
624 deefault = 'd';
628 if(n){
629 ekey[n].ch = -1;
630 snprintf(prompt, sizeof(prompt), _("Shuffle selected context %s%s%s? "),
631 (ekey[0].ch == 'u') ? _("UP") : "",
632 (n > 1) ? " or " : "",
633 (ekey[0].ch == 'd' || n > 1) ? _("DOWN") : "");
634 prompt[sizeof(prompt)-1] = '\0';
636 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey,
637 deefault, 'x', NO_HELP, RB_NORM);
638 switch(cmd){
639 case 'x':
640 default:
641 cmd_cancelled("Shuffle");
642 return(0);
644 case 'u':
645 case 'd':
646 break;
650 * This is complicated by the fact that there are two
651 * vars involved, the folder-collections and the news-collections.
652 * We may have to shuffle across collections.
654 cur_ctxt = (*cl)->d.c.ct;
655 if(cmd == 'd')
656 other_ctxt = (*cl)->d.c.ct->next;
657 else if(cmd == 'u')
658 other_ctxt = (*cl)->d.c.ct->prev;
660 cur_var = cur_ctxt->var.v;
661 other_var = other_ctxt->var.v;
663 /* swap elements of config var */
664 if(cur_var == other_var){
665 i1 = cur_ctxt->var.i;
666 i2 = other_ctxt->var.i;
667 lval = LVAL(cur_var, ew);
668 if(lval){
669 tmp = lval[i1];
670 lval[i1] = lval[i2];
671 lval[i2] = tmp;
674 starting_varmem = i2;
676 else{
677 /* swap into the other_var */
678 i1 = cur_ctxt->var.i;
679 i2 = other_ctxt->var.i;
680 lval1 = LVAL(cur_var, ew);
681 lval2 = LVAL(other_var, ew);
682 /* count */
683 for(count = 0; lval2 && lval2[count]; count++)
685 if(cmd == 'd')
686 insert_num = count ? 1 : 0;
687 else{
688 insert_num = count ? count - 1 : count;
691 starting_varmem = insert_num;
692 if(ccs_var_insert(ps,lval1[i1],other_var,lval2,insert_num)){
693 if(!ccs_var_delete(ps, cur_ctxt)){
694 q_status_message(SM_ORDER|SM_DING, 3, 3,
695 _("Error deleting shuffled context"));
696 return(0);
699 else{
700 q_status_message(SM_ORDER, 3, 3,
701 _("Trouble shuffling, cancelled"));
702 return(0);
706 (*cl)->d.c.cs->starting_var = other_var;
707 (*cl)->d.c.cs->starting_varmem = starting_varmem;
709 q_status_message(SM_ORDER, 0, 3, _("Collections shuffled"));
710 return(15);
713 if(news_problem)
714 /* TRANSLATORS: Sorry, can't move news group collections above email collections */
715 q_status_message(SM_ORDER, 0, 3, _("Sorry, cannot Shuffle news to top"));
716 else
717 q_status_message(SM_ORDER, 0, 3, _("Sorry, nothing to Shuffle"));
719 return(0);
724 context_config_edit(struct pine *ps, CONF_S **cl)
726 char *raw_ctxt, tpath[MAILTMPLEN], *p, **lval;
727 struct variable *var;
728 int i;
730 if(!(*cl)->d.c.ct)
731 return(0);
733 /* Undigest the context */
734 strncpy(tpath, ((*cl)->d.c.ct->context[0] == '{'
735 && (p = strchr((*cl)->d.c.ct->context, '}')))
736 ? ++p
737 : (*cl)->d.c.ct->context, sizeof(tpath)-1);
738 tpath[sizeof(tpath)-1] = '\0';
740 if((p = strstr(tpath, "%s")) != NULL)
741 *p = '\0';
743 if((raw_ctxt = context_edit_screen(ps, "EDIT", (*cl)->d.c.ct->nickname,
744 (*cl)->d.c.ct->server, tpath,
745 (*cl)->d.c.ct->dir ?
746 (*cl)->d.c.ct->dir->view.user
747 : NULL)) != NULL){
749 if((*cl)->var){
750 var = (*cl)->var;
751 lval = LVAL(var, ew);
752 i = (*cl)->d.c.ct->var.i;
754 if(lval && lval[i] && !strcmp(lval[i], raw_ctxt))
755 q_status_message(SM_ORDER, 0, 3, _("No change"));
756 else if(lval){
757 if(lval[i])
758 fs_give((void **) &lval[i]);
760 lval[i] = raw_ctxt;
761 raw_ctxt = NULL;
763 q_status_message(SM_ORDER, 0, 3, _("Collection list entry updated"));
766 (*cl)->d.c.cs->starting_var = var;
767 (*cl)->d.c.cs->starting_varmem = i;
769 if(raw_ctxt)
770 fs_give((void **) &raw_ctxt);
772 else{
773 q_status_message(SM_ORDER|SM_DING, 3, 3,
774 "Programmer botch in context_config_edit");
775 return(0);
778 return(15);
781 ps->mangled_screen = 1;
782 return(0);
787 context_config_delete(struct pine *ps, CONF_S **cl)
789 char tmp[MAILTMPLEN];
791 if(!(*cl)->var){
792 q_status_message(SM_ORDER|SM_DING, 3, 3,
793 "Programmer botch in context_config_delete");
794 return(0);
797 if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
798 (*cl)->d.c.ct->next &&
799 (*cl)->d.c.ct->next->use & CNTXT_NEWS &&
800 (*cl)->d.c.ct->var.v == (*cl)->d.c.ct->next->var.v){
801 q_status_message(SM_ORDER|SM_DING, 3, 3,
802 _("Sorry, cannot Delete causing news to move to top"));
803 return(0);
806 snprintf(tmp, sizeof(tmp), _("Delete the collection definition for \"%s\""),
807 (*cl)->value);
808 tmp[sizeof(tmp)-1] = '\0';
809 if(want_to(tmp, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
811 (*cl)->d.c.cs->starting_var = (*cl)->var;
812 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
813 if((*cl)->d.c.ct->next){
814 if((*cl)->d.c.ct->next->var.v != (*cl)->var){
815 (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->next->var.v;
816 (*cl)->d.c.cs->starting_varmem = 0;
818 else{
819 (*cl)->d.c.cs->starting_var = (*cl)->var;
820 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
823 else{
824 if((*cl)->d.c.ct->var.i > 0){
825 (*cl)->d.c.cs->starting_var = (*cl)->var;
826 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i - 1;
828 else{
829 if((*cl)->d.c.ct->prev){
830 (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->prev->var.v;
831 (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->prev->var.i;
836 /* Remove from var list */
837 if(!ccs_var_delete(ps, (*cl)->d.c.ct)){
838 q_status_message(SM_ORDER|SM_DING, 3, 3,
839 _("Error deleting renamed context"));
840 return(0);
843 q_status_message(SM_ORDER, 0, 3, _("Collection deleted"));
845 return(15);
848 q_status_message(SM_ORDER, 0, 3, _("No collections deleted"));
849 return(0);
854 ccs_var_delete(struct pine *ps, CONTEXT_S *ctxt)
856 int count, i;
857 char **newl = NULL, **lval, **lp, ***alval;
859 if(ctxt)
860 lval = LVAL(ctxt->var.v, ew);
861 else
862 lval = NULL;
864 for(count = 0; lval && lval[count]; count++)
865 ; /* sum the list */
867 if(count > 1){
868 newl = (char **) fs_get(count * sizeof(char *));
869 for(i = 0, lp = newl; lval[i]; i++)
870 if(i != ctxt->var.i)
871 *lp++ = cpystr(lval[i]);
873 *lp = NULL;
876 alval = ALVAL(ctxt->var.v, ew);
877 if(alval){
878 free_list_array(alval);
879 if(newl){
880 for(i = 0; newl[i] ; i++) /* count elements */
883 *alval = (char **) fs_get((i+1) * sizeof(char *));
885 for(i = 0; newl[i] ; i++)
886 (*alval)[i] = cpystr(newl[i]);
888 (*alval)[i] = NULL;
892 free_list_array(&newl);
893 return(1);
898 * Insert into "var", which currently has values "oldvarval", the "newline"
899 * at position "insert".
902 ccs_var_insert(struct pine *ps, char *newline, struct variable *var, char **oldvarval, int insert)
904 int count, i, offset;
905 char **newl, ***alval;
907 for(count = 0; oldvarval && oldvarval[count]; count++)
910 if(insert < 0 || insert > count){
911 q_status_message(SM_ORDER,3,5, "unexpected problem inserting folder");
912 return(0);
915 newl = (char **)fs_get((count + 2) * sizeof(char *));
916 newl[insert] = cpystr(newline);
917 newl[count + 1] = NULL;
918 for(i = offset = 0; oldvarval && oldvarval[i]; i++){
919 if(i == insert)
920 offset = 1;
922 newl[i + offset] = cpystr(oldvarval[i]);
925 alval = ALVAL(var, ew);
926 if(alval){
927 free_list_array(alval);
928 if(newl){
929 for(i = 0; newl[i] ; i++) /* count elements */
932 *alval = (char **) fs_get((i+1) * sizeof(char *));
934 for(i = 0; newl[i] ; i++)
935 (*alval)[i] = cpystr(newl[i]);
937 (*alval)[i] = NULL;
941 free_list_array(&newl);
942 return(1);
947 context_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
949 int retval = 0;
951 switch(cmd){
952 case MC_CHOICE :
953 (*cl)->d.c.cs->selected = (*cl)->d.c.ct;
954 retval = simple_exit_cmd(flags);
955 break;
957 case MC_EXIT :
958 retval = simple_exit_cmd(flags);
959 break;
961 case MC_MAIN :
962 retval = simple_exit_cmd(flags);
963 ps_global->next_screen = main_menu_screen;
964 break;
966 case MC_INDEX :
967 if(THREADING()
968 && sp_viewing_a_thread(ps_global->mail_stream)
969 && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
970 ps_global->next_screen = mail_index_screen;
971 ps_global->view_skipped_index = 0;
972 ps_global->mangled_screen = 1;
975 retval = simple_exit_cmd(flags);
976 ps_global->next_screen = mail_index_screen;
977 break;
979 case MC_COMPOSE :
980 retval = simple_exit_cmd(flags);
981 ps_global->next_screen = compose_screen;
982 break;
984 case MC_ROLE :
985 retval = simple_exit_cmd(flags);
986 ps_global->next_screen = alt_compose_screen;
987 break;
989 case MC_GOTO :
991 int notrealinbox;
992 CONTEXT_S *c = (*cl)->d.c.ct;
993 char *new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &notrealinbox, &c);
995 if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
996 ps_global->next_screen = mail_index_screen;
997 retval = simple_exit_cmd(flags);
999 else
1000 ps->mangled_footer = 1;
1003 break;
1005 case MC_QUIT :
1006 retval = simple_exit_cmd(flags);
1007 ps_global->next_screen = quit_screen;
1008 break;
1010 default:
1011 retval = -1;
1012 break;
1015 if(retval > 0)
1016 ps->mangled_body = 1;
1018 return(retval);