1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: addrbook.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2021 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 /*======================================================================
21 display, browse and edit the address book.
31 #include "confscroll.h"
39 #include "../pith/adrbklib.h"
40 #include "../pith/abdlc.h"
41 #include "../pith/addrbook.h"
42 #include "../pith/ablookup.h"
43 #include "../pith/addrstring.h"
44 #include "../pith/bldaddr.h"
45 #include "../pith/state.h"
46 #include "../pith/bitmap.h"
47 #include "../pith/newmail.h"
48 #include "../pith/remote.h"
49 #include "../pith/util.h"
50 #include "../pith/thread.h"
51 #include "../pith/hist.h"
52 #include "../pith/list.h"
55 HelpType gAbookHelp
= NO_HELP
;
58 /* internal prototypes */
59 void end_adrbks(void);
60 int init_disp_form_prefix(PerAddrBook
*, int *);
61 void display_book(int, int, int, int, Pos
*);
62 void paint_line(int, long, int, Pos
*);
63 void redraw_addr_screen(void);
64 char *addr_book(AddrBookArg
, char *, char **);
65 void ab_zoom(EXPANDED_S
*, int *);
66 void ab_unzoom(int *);
67 void empty_warning(long);
68 void clickable_warning(long);
69 void no_tabs_warning(void);
70 int ab_apply_cmd(struct pine
*, AdrBk
*, long, int);
71 void ab_select(struct pine
*, AdrBk
*, long, int, int *);
72 int ab_select_type(AdrBk
*, int);
73 int ab_select_text(AdrBk
*, int);
74 int match_check(AdrBk_Entry
*, int, char *);
75 void ab_goto_folder(int);
76 long ab_whereis(int *, int);
77 int any_addrs_avail(long);
78 int entry_is_clickable(long);
79 int entry_is_clickable_title(long);
81 int entry_is_listent(long);
82 int entry_is_addkey(long);
83 int entry_is_askserver(long);
84 int add_is_global(long);
85 int next_selectable_line(long, long *);
86 int prev_selectable_line(long, long *);
87 void erase_checks(void);
88 int search_book(long, int, long *, int *, int *);
89 int find_in_book(long, char *, long *, int *);
90 int search_in_one_line(AddrScrn_Disp
*, AdrBk_Entry
*, char *, char *);
91 int abook_select_tool(struct pine
*, int, CONF_S
**, unsigned);
92 char *choose_an_address_with_this_prefix(COMPLETE_S
*);
94 int addr_scroll_up(long);
95 int addr_scroll_down(long);
96 int addr_scroll_to_pos(long);
97 int addr_scroll_callback(int, long);
98 char *pcpine_help_addrbook(char *);
102 /* TRANSLATORS: This is a placeholder for a list of addresses in address book */
103 #define CLICKHERE _("[ Address List ]")
104 /* TRANSLATORS: This is an empty address list */
105 #define EMPTY _("[ Empty ]")
106 #define ZOOM_EMPTY _("[ No Selected Entries in this Address Book ]")
107 /* TRANSLATORS: Move here means to move the cursor to this line of the screen */
108 #define ADD_PERSONAL _(" [ Move here to add a Personal Address Book ]")
109 /* TRANSLATORS: A global address book is a shared address book */
110 #define ADD_GLOBAL _(" [ Move here to add a Global Address Book ]")
111 /* TRANSLATORS: A heading for an address book distribution list */
112 #define DISTLIST _("DISTRIBUTION LIST:")
113 #define NOABOOKS _("[ No Address Book Configured ]")
114 /* TRANSLATORS: Select here means to move the cursor to this line and then type
115 the Select command. */
116 #define CLICKHERECMB _("[ Select Here to See Expanded List ]")
121 * Returns the index of the current address book.
126 return(adrbk_num_from_lineno(as
.top_ent
+ as
.cur_row
));
131 * Returns 1 if current abook is open, else 0.
136 int current
, ret
= 0;
140 (current
= cur_addr_book()) >= 0 &&
141 current
< as
.n_addrbk
&&
142 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
143 ret
= (as
.adrbks
[current
].ostatus
== Open
);
154 dprint((2, "- end_adrbks -\n"));
159 for(i
= 0; i
< as
.n_addrbk
; i
++)
160 init_abook(&as
.adrbks
[i
], Closed
);
164 ab_nesting_level
= 0;
170 * We have to call this to set up the format of the columns. There is a
171 * separate format for each addrbook, so we need to call this for each
172 * addrbook. We call it when the pab's are built. It also depends on
173 * whether or not as.checkboxes is set, so if we go into a Select mode
174 * from the address book maintenance screen we need to re-call this. Since
175 * we can't go back out of ListMode we don't have that problem. Restore_state
176 * has to call it because of the as.checkboxes possibly being different in
180 init_disp_form(PerAddrBook
*pab
, char **list
, int addrbook_num
)
182 addrbook_new_disp_form(pab
, list
, addrbook_num
, init_disp_form_prefix
);
187 init_disp_form_prefix(PerAddrBook
*pab
, int *columnp
)
192 pab
->disp_form
[(*columnp
)].wtype
= Fixed
;
193 pab
->disp_form
[(*columnp
)].req_width
= 3;
194 pab
->disp_form
[(*columnp
)++].type
= Checkbox
;
196 else if(as
.selections
){
197 if(F_ON(F_SELECTED_SHOWN_BOLD
, ps_global
) && StartBold()){
202 pab
->disp_form
[(*columnp
)].wtype
= Fixed
;
203 pab
->disp_form
[(*columnp
)].req_width
= 1;
204 pab
->disp_form
[(*columnp
)++].type
= Selected
;
213 * save_and_restore - single interface to save_state and restore_state
216 save_and_restore(int cmd
, SAVE_STATE_S
*state
)
223 restore_state(state
);
230 * Save the screen state and the Open or Closed status of the addrbooks.
233 save_state(SAVE_STATE_S
*state
)
238 dprint((9, "- save_state -\n"));
240 /* allocate space for saving the screen structure and save it */
241 state
->savep
= (AddrScrState
*)fs_get(sizeof(AddrScrState
));
242 *(state
->savep
) = as
; /* copy the struct */
246 /* allocate space for saving the ostatus for each addrbook */
247 state
->stp
= (OpenStatus
*)fs_get(as
.n_addrbk
* sizeof(OpenStatus
));
249 for(i
= 0; i
< as
.n_addrbk
; i
++)
250 (state
->stp
)[i
] = as
.adrbks
[i
].ostatus
;
253 state
->dlc_to_warp_to
= (DL_CACHE_S
*)fs_get(sizeof(DL_CACHE_S
));
254 dlc
= get_dlc(as
.top_ent
+ as
.cur_row
);
255 *(state
->dlc_to_warp_to
) = *dlc
; /* copy the struct */
263 * Side effect: Flushes addrbook entry cache entries so they need to be
264 * re-fetched afterwords. This only applies to entries obtained since
265 * the call to save_state.
266 * Also flushes all dlc cache entries, so dlist calls need to be repeated.
269 restore_state(SAVE_STATE_S
*state
)
273 dprint((9, "- restore_state -\n"));
275 as
= *(state
->savep
); /* put back cur_row and all that */
278 /* restore addressbook OpenStatus to what it was before */
279 for(i
= 0; i
< as
.n_addrbk
; i
++){
280 init_disp_form(&as
.adrbks
[i
], ps_global
->VAR_ABOOK_FORMATS
, i
);
281 init_abook(&as
.adrbks
[i
], (state
->stp
)[i
]);
285 * jump cache back to where we were
287 warp_to_dlc(state
->dlc_to_warp_to
, as
.top_ent
+as
.cur_row
);
289 fs_give((void **)&state
->dlc_to_warp_to
);
290 fs_give((void **)&state
->stp
);
294 fs_give((void **)&state
->savep
);
299 * Returns the addrbook entry for this display row.
310 if(!(type
== Simple
|| type
== ListHead
||
311 type
== ListEnt
|| type
== ListClickHere
))
312 return((AdrBk_Entry
*)NULL
);
314 pab
= &as
.adrbks
[adrbk_num_from_lineno(row
)];
316 return(adrbk_get_ae(pab
->address_book
, (a_c_arg_t
) dl
->elnum
));
321 * Args: start_disp -- line to start displaying on when redrawing, 0 is
323 * cur_line -- current line number (0 is 1st line we display)
324 * old_line -- old line number
325 * redraw -- flag requesting redraw as opposed to update of
327 * start_pos -- return position where highlighted text begins here
329 * Result: lines painted on the screen
331 * It either redraws the screen from line "start_disp" down or
332 * moves the cursor from one field to another.
335 display_book(int start_disp
, int cur_line
, int old_line
, int redraw
, Pos
*start_pos
)
337 int screen_row
, highlight
;
342 "- display_book() -\n top %d start %d cur_line %d old_line %d redraw %d\n",
343 as
.top_ent
, start_disp
, cur_line
, old_line
, redraw
));
357 /*--- Repaint all of the screen or bottom part of screen ---*/
358 global_row
= as
.top_ent
+ start_disp
;
359 for(screen_row
= start_disp
;
360 screen_row
< as
.l_p_page
;
361 screen_row
++, global_row
++){
363 highlight
= (screen_row
== cur_line
);
364 ClearLine(screen_row
+ HEADER_ROWS(ps_global
));
365 paint_line(screen_row
+ HEADER_ROWS(ps_global
), global_row
,
367 if(start_pos
&& highlight
)
373 /*--- Only update current, or move the cursor ---*/
374 if(cur_line
!= old_line
){
376 /*--- Repaint old position to erase "cursor" ---*/
377 if(F_ON(F_ENABLE_DEL_WHEN_WRITING
, ps_global
))
378 ClearLine(old_line
+ HEADER_ROWS(ps_global
));
379 paint_line(old_line
+ HEADER_ROWS(ps_global
), as
.top_ent
+ old_line
,
383 /*--- paint the position with the cursor ---*/
384 if(F_ON(F_ENABLE_DEL_WHEN_WRITING
, ps_global
))
385 ClearLine(cur_line
+ HEADER_ROWS(ps_global
));
386 paint_line(cur_line
+ HEADER_ROWS(ps_global
), as
.top_ent
+ cur_line
,
393 scroll_setpos(as
.top_ent
);
401 * Paint a line on the screen
403 * Args: line -- Line on screen to paint
404 * global_row -- Row number of line to paint
405 * highlight -- Line should be highlighted
406 * start_pos -- return position where text begins here
408 * Result: Line is painted
410 * The three field widths for the formatting are passed in. There is an
411 * implicit 2 spaces between the fields.
413 * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
416 paint_line(int line
, long int global_row
, int highlight
, Pos
*start_pos
)
418 int scol
, bolden
= 0;
419 char *start_hilite_here
, *end_hilite_here
;
421 char lbuf
[6*MAX_SCREEN_COLS
+ 1];
424 dprint((10, "- paint_line(%d, %d) -\n", line
, highlight
));
426 dl
= dlist(global_row
);
427 start_pos
->row
= line
;
428 start_pos
->col
= 0; /* default */
439 p
= get_abook_display_line(global_row
, line
== HEADER_ROWS(ps_global
),
440 highlight
? &start_hilite_here
: NULL
,
441 highlight
? &end_hilite_here
: NULL
,
442 &scol
, lbuf
, sizeof(lbuf
));
446 (dl
->type
== ListHead
|| dl
->type
== Simple
)){
449 pab
= &as
.adrbks
[adrbk_num_from_lineno(global_row
)];
450 if(entry_is_selected(pab
->address_book
->selects
, (a_c_arg_t
)dl
->elnum
)){
463 * print part before highlight starts
465 if(start_hilite_here
!= NULL
){
466 save_char
= *start_hilite_here
;
467 *start_hilite_here
= '\0';
469 *start_hilite_here
= save_char
;
473 * print highlighted part
476 if(end_hilite_here
!= NULL
){
477 save_char
= *end_hilite_here
;
478 *end_hilite_here
= '\0';
482 Write_to_screen(start_hilite_here
? start_hilite_here
: p
);
486 * print part after highlight ends
488 if(end_hilite_here
!= NULL
){
489 *end_hilite_here
= save_char
;
490 Write_to_screen(end_hilite_here
);
494 PutLine0(line
, 0, p
);
501 start_pos
->col
= scol
;
506 * Assemble a line suitable for displaying on screen
508 * Args: global_row -- Row number of line to assemble.
509 * continuation -- This is the top line of screen display
510 * s_hilite -- Location in the returned line where highlight
511 * should start, if desired
512 * e_hilite -- Location in the returned line where highlight
513 * should end, if desired
514 * retcol -- Return column where text begins here.
515 * lbuf -- Put the output here. Lbuf should be at least
516 * size 6*screen_cols+1.
517 * lbufsize -- Size of lbuf array
519 * Result: Pointer to lbuf is returned.
521 * There is an implicit 2 spaces between the fields.
523 * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
526 get_abook_display_line(long int global_row
, int continuation
, char **s_hilite
,
527 char **e_hilite
, int *retcol
, char *lbuf
, size_t lbufsize
)
529 int fld_col
[NFIELDS
],
532 col
, special_col
= 0,
536 char *string
, *writeptr
;
537 char special
[6*MAX_SCREEN_COLS
-1];
541 #define LSPACE() (lbufsize - (writeptr - lbuf) - 1)
543 dprint((10, "- get_display_line(%d) -\n", global_row
));
545 dl
= dlist(global_row
);
547 *retcol
= 0; /* default */
550 *s_hilite
= NULL
; /* NULL in these means to highlight whole line */
555 memset(writeptr
, 0, lbufsize
);
556 memset(special
, 0, sizeof(special
));
567 screen_width
= ps_global
->ttyo
->screen_cols
;
569 /* the types in this set span all columns */
579 if(dl
->type
== Empty
)
581 else if(dl
->type
== ZoomEmpty
)
583 else if(dl
->type
== NoAbooks
)
585 else if(dl
->type
== ClickHereCmb
)
586 string
= CLICKHERECMB
;
591 col
= (screen_width
- (int) utf8_width(string
))/2;
595 /* col spaces to start */
596 if(col
> 0 && LSPACE() >= col
){
597 memset(writeptr
, ' ', col
);
599 width_consumed
+= col
;
602 if((width
=utf8_width(string
)) <= screen_width
-col
){
603 strncpy(writeptr
, string
, LSPACE());
604 width_consumed
+= width
;
607 utf8_pad_to_width(writeptr
, string
, LSPACE(), screen_width
-col
, 1);
608 width_consumed
= screen_width
;
611 if(s_hilite
&& *s_hilite
== NULL
){
612 *s_hilite
= writeptr
;
613 *e_hilite
= writeptr
+ strlen(writeptr
);
616 writeptr
+= strlen(writeptr
);
617 if(width_consumed
< screen_width
)
618 memset(writeptr
, ' ', screen_width
-width_consumed
);
625 /* left adjust these */
629 if(dl
->type
== AddFirstPers
)
630 string
= ADD_PERSONAL
;
631 else if(dl
->type
== AddFirstGlob
)
639 if((width
=utf8_width(string
)) <= screen_width
){
640 strncpy(writeptr
, string
, LSPACE());
641 width_consumed
+= width
;
644 utf8_pad_to_width(writeptr
, string
, LSPACE(), screen_width
, 1);
645 width_consumed
= screen_width
;
648 if(s_hilite
&& *s_hilite
== NULL
){
649 *s_hilite
= writeptr
;
650 *e_hilite
= writeptr
+ strlen(writeptr
);
653 writeptr
+= strlen(writeptr
);
654 if(width_consumed
< screen_width
)
655 memset(writeptr
, ' ', screen_width
-width_consumed
);
666 pab
= &as
.adrbks
[adrbk_num_from_lineno(global_row
)];
667 for(fld
= 0; fld
< NFIELDS
; fld
++)
668 fld_width
[fld
] = pab
->disp_form
[fld
].width
;
671 for(fld
= 1; fld
< NFIELDS
; fld
++)
672 fld_col
[fld
] = MIN(fld_col
[fld
-1]+fld_width
[fld
-1]+2, screen_width
);
676 /* fill in the fields */
677 for(fld
= 0; fld
< NFIELDS
; fld
++){
678 if(fld_width
[fld
] == 0
679 && !(pab
->disp_form
[fld
].type
== Addr
680 && (dl
->type
== ListClickHere
|| dl
->type
== ListEmpty
)))
683 switch(pab
->disp_form
[fld
].type
){
691 abe
= ae(global_row
);
692 string
= (abe
&& abe
->nickname
) ? abe
->nickname
: "";
696 /* left adjust string in field */
697 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
698 writeptr
+= strlen(writeptr
);
699 width_consumed
+= fld_width
[fld
];
712 abe
= ae(global_row
);
713 string
= (abe
&& abe
->fullname
) ? abe
->fullname
: "";
717 /* left adjust string in field */
718 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
719 writeptr
+= strlen(writeptr
);
720 width_consumed
+= fld_width
[fld
];
724 /* continuation line */
728 width2
= MIN(11, fld_width
[fld
]);
729 width1
= MAX(fld_width
[fld
] - width2
- 1, 0);
731 abe
= ae(global_row
);
732 string
= (abe
&& abe
->fullname
) ? abe
->fullname
: "";
735 utf8_pad_to_width(writeptr
, string
, LSPACE(), width1
, 1);
736 writeptr
+= strlen(writeptr
);
741 /* TRANSLATORS: continuation line meaning there is more on the next page */
742 if(width2
&& LSPACE() >= strlen(_("(continued)"))){
743 strncpy(writeptr
, _("(continued)"), width2
);
744 lbuf
[lbufsize
-1] = '\0';
745 writeptr
+= strlen(writeptr
);
748 width_consumed
+= fld_width
[fld
];
750 lbuf
[lbufsize
-1] = '\0';
765 if(dl
->type
== ListClickHere
)
770 if((width
=utf8_width(string
)) <= fld_width
[fld
]){
772 strncpy(writeptr
, string
, LSPACE());
774 if(s_hilite
&& *s_hilite
== NULL
){
775 *s_hilite
= writeptr
;
776 *e_hilite
= writeptr
+ strlen(writeptr
);
779 writeptr
+= strlen(writeptr
);
780 width_consumed
+= width
;
787 * Place the string in special array and overlay it
788 * onto the right edge of the screen at the end.
790 if(width
<= screen_width
){
791 strncpy(special
, string
, sizeof(special
));
792 special_col
= screen_width
- width
;
795 utf8_pad_to_width(special
, string
, sizeof(special
), screen_width
, 1);
799 special
[sizeof(special
)-1] = '\0';
809 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
810 writeptr
+= strlen(writeptr
);
811 width_consumed
+= fld_width
[fld
];
815 abe
= ae(global_row
);
817 if(abe
&& abe
->addr
.addr
&&
818 !strncmp(abe
->addr
.addr
, QRUN_LDAP
, LEN_QRL
))
822 string
= (abe
&& abe
->tag
== Single
&& abe
->addr
.addr
) ?
827 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
828 writeptr
+= strlen(writeptr
);
829 width_consumed
+= fld_width
[fld
];
833 string
= listmem(global_row
) ? listmem(global_row
) : "";
837 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
839 if(s_hilite
&& *s_hilite
== NULL
){
840 *s_hilite
= writeptr
;
841 *e_hilite
= writeptr
+ strlen(writeptr
);
844 writeptr
+= strlen(writeptr
);
845 width_consumed
+= fld_width
[fld
];
859 abe
= ae(global_row
);
860 if(pab
->disp_form
[fld
].type
== Filecopy
)
861 string
= (abe
&& abe
->fcc
) ? abe
->fcc
: "";
863 string
= (abe
&& abe
->extra
) ? abe
->extra
: "";
868 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
869 writeptr
+= strlen(writeptr
);
870 width_consumed
+= fld_width
[fld
];
883 if(entry_is_checked(pab
->address_book
->checks
,
884 (a_c_arg_t
)dl
->elnum
))
892 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
893 writeptr
+= strlen(writeptr
);
894 width_consumed
+= fld_width
[fld
];
907 if(entry_is_selected(pab
->address_book
->selects
,
908 (a_c_arg_t
)dl
->elnum
))
916 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
917 writeptr
+= strlen(writeptr
);
918 width_consumed
+= fld_width
[fld
];
927 case WhenNoAddrDisplayed
:
932 if(dl
->type
== ListClickHere
)
934 else if(dl
->type
== ListEmpty
)
937 string
= listmem(global_row
) ? listmem(global_row
) : "";
939 if((width
=utf8_width(string
)) <= fld_width
[fld
])
940 strncpy(special
, string
, sizeof(special
));
942 utf8_pad_to_width(special
, string
, sizeof(special
), fld_width
[fld
], 1);
944 special
[sizeof(special
)-1] = '\0';
945 special_col
= screen_width
- fld_width
[fld
];
946 special_col
= MAX(0, special_col
);
960 while(width_consumed
< fld_col
[fld
+1] && LSPACE() > 0){
967 * Right adjust the special string in lbuf, writing over
968 * anything in our way.
973 writeptr
= utf8_count_forw_width(lbuf
, special_col
, &got_width
);
975 strncpy(writeptr
, special
, LSPACE());
980 if(s_hilite
&& *s_hilite
== NULL
){
981 *s_hilite
= writeptr
;
982 *e_hilite
= writeptr
+ strlen(writeptr
);
985 writeptr
+= strlen(writeptr
);
987 width_consumed
= (int) utf8_width(lbuf
);
988 while(width_consumed
++ < screen_width
&& LSPACE() > 0)
995 if(scol
> 0 && retcol
)
998 lbuf
[lbufsize
-1] = '\0';
1004 * Set field widths for the columns of the display. The idea is to
1005 * try to come up with something that works pretty well. Getting it just
1006 * right isn't important.
1008 * Col1 and col2 are arrays which contain some widths useful for
1009 * formatting the screen. The 0th element is the max
1010 * width in that column. The 1st element is the max of the third largest
1011 * width in each addressbook (yup, strange).
1013 * The info above applies to the default case (AllAuto). The newer methods
1014 * where the user specifies the format is the else part of the big if and
1015 * is quite a bit different. It's all sort of ad hoc when we're asked
1016 * to calculate one of the fields for the user.
1018 * Returns non-zero if the widths changed since the last time called.
1021 calculate_field_widths(void)
1023 int space_left
, i
, j
, screen_width
;
1026 WIDTH_INFO_S
*widths
;
1027 int max_nick
, max_full
, max_addr
, third_full
, third_addr
, third_fcc
;
1028 int col1
[5], col2
[5];
1029 int nick
= -1, full
= -1, addr
= -1;
1030 #define SEP 2 /* space between columns */
1032 dprint((9, "- calculate_field_widths -\n"));
1034 screen_width
= ps_global
->ttyo
->screen_cols
;
1036 /* calculate widths for each addrbook independently */
1037 for(j
= 0; j
< as
.n_addrbk
; j
++){
1038 pab
= &as
.adrbks
[j
];
1047 if(pab
->address_book
){
1048 widths
= &pab
->address_book
->widths
;
1049 max_nick
= MIN(MAX(max_nick
, widths
->max_nickname_width
), 25);
1050 max_full
= MAX(max_full
, widths
->max_fullname_width
);
1051 max_addr
= MAX(max_addr
, widths
->max_addrfield_width
);
1052 third_full
= MAX(third_full
, widths
->third_biggest_fullname_width
);
1054 third_full
= MAX(third_full
, 2*max_full
/3);
1056 third_addr
= MAX(third_addr
, widths
->third_biggest_addrfield_width
);
1058 third_addr
= MAX(third_addr
, 2*max_addr
/3);
1060 third_fcc
= MAX(third_fcc
, widths
->third_biggest_fccfield_width
);
1062 third_fcc
= MAX(third_fcc
, 2*widths
->max_fccfield_width
/3);
1065 /* figure out which order they're in and reset widths */
1066 for(i
= 0; i
< NFIELDS
; i
++){
1067 pab
->disp_form
[i
].width
= 0;
1068 switch(pab
->disp_form
[i
].type
){
1086 /* Compute default format */
1087 if(pab
->disp_form
[1].wtype
== AllAuto
){
1091 col1
[1] = third_full
;
1092 col2
[1] = third_addr
;
1100 space_left
= screen_width
;
1102 if(pab
->disp_form
[0].type
== Selected
||
1103 pab
->disp_form
[0].type
== Checkbox
){
1104 pab
->disp_form
[0].width
= MIN(pab
->disp_form
[0].req_width
,space_left
);
1105 space_left
= MAX(space_left
-(pab
->disp_form
[0].width
+ SEP
), 0);
1109 * All of the nickname field should be visible,
1110 * and make it at least 3.
1112 pab
->disp_form
[nick
].width
= MIN(MAX(max_nick
, 3), space_left
);
1115 * The SEP is for two blank columns between nickname and next field.
1116 * Those blank columns are taken automatically in paint_line().
1118 space_left
-= (pab
->disp_form
[nick
].width
+ SEP
);
1121 for(i
= 0; i
< 5; i
++){
1122 /* try fitting most of each field in if possible */
1123 if(col1
[i
] + SEP
+ col2
[i
] <= space_left
){
1126 extra
= space_left
- col1
[i
] - SEP
- col2
[i
];
1128 * try to stabilize nickname column shifts
1129 * so that screen doesn't jump around when we make changes
1131 if(i
== 0 && pab
->disp_form
[nick
].width
< 7 &&
1132 extra
>= (7 - pab
->disp_form
[nick
].width
)){
1133 extra
-= (7 - pab
->disp_form
[nick
].width
);
1134 space_left
-= (7 - pab
->disp_form
[nick
].width
);
1135 pab
->disp_form
[nick
].width
= 7;
1138 pab
->disp_form
[addr
].width
= col2
[i
] + extra
/2;
1139 pab
->disp_form
[full
].width
=
1140 space_left
- SEP
- pab
->disp_form
[addr
].width
;
1146 * None of them would fit. Toss addr field.
1149 pab
->disp_form
[full
].width
= space_left
;
1150 pab
->disp_form
[addr
].width
= 0;
1154 pab
->disp_form
[full
].width
= 0;
1155 pab
->disp_form
[addr
].width
= 0;
1158 dprint((10, "Using %s choice: %d %d %d", enth_string(i
+1),
1159 pab
->disp_form
[nick
].width
, pab
->disp_form
[full
].width
,
1160 pab
->disp_form
[addr
].width
));
1162 else{ /* non-default case */
1163 int some_to_calculate
= 0;
1167 int all_percents
= 1;
1171 * First count how many fields there are.
1172 * Fill in all the Fixed's while we're at it.
1174 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1175 if(pab
->disp_form
[i
].wtype
== Fixed
){
1176 pab
->disp_form
[i
].width
= pab
->disp_form
[i
].req_width
;
1179 else if(pab
->disp_form
[i
].wtype
== WeCalculate
){
1180 pab
->disp_form
[i
].width
= pab
->disp_form
[i
].req_width
; /* for now */
1181 some_to_calculate
++;
1185 if(pab
->disp_form
[i
].wtype
!= Special
){
1186 used
+= pab
->disp_form
[i
].width
;
1191 used
+= ((columns
-1) * SEP
);
1192 avail_screen
= screen_width
- used
;
1195 * Now that we know how much space we've got, we can
1196 * calculate the Percent columns.
1198 if(avail_screen
> 0){
1199 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1200 if(pab
->disp_form
[i
].wtype
== Percent
){
1201 /* The 2, 200, and +100 are because we're rounding */
1202 pab
->disp_form
[i
].width
=
1203 ((2*pab
->disp_form
[i
].req_width
*avail_screen
)+100) / 200;
1204 used
+= pab
->disp_form
[i
].width
;
1209 space_left
= screen_width
- used
;
1213 * If they're all percentages, and the percentages add up to 100,
1214 * then we should fix the rounding problem.
1218 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++)
1219 if(pab
->disp_form
[i
].wtype
== Percent
)
1220 pc_tot
+= pab
->disp_form
[i
].req_width
;
1223 /* fix the rounding problem */
1224 if(all_percents
&& pc_tot
<= 100){
1227 int fix
= used
- screen_width
;
1233 /* find col'th column */
1234 for(i
=0, this_col
=0; i
< NFIELDS
; i
++){
1235 if(pab
->disp_form
[i
].wtype
== Percent
){
1236 if(col
== ++this_col
)
1241 pab
->disp_form
[i
].width
--;
1246 * Assume they meant to have them add up to over 100%, so we
1247 * just truncate the right hand edge.
1250 int this_fix
, space_over
;
1252 /* have to reduce space_over down to zero. */
1253 space_over
= used
- screen_width
;
1254 for(i
=NFIELDS
-1; i
>= 0 && space_over
> 0; i
--){
1255 if(pab
->disp_form
[i
].type
!= Notused
){
1256 this_fix
= MIN(pab
->disp_form
[i
].width
, space_over
);
1257 pab
->disp_form
[i
].width
-= this_fix
;
1258 space_over
-= this_fix
;
1263 else if(space_left
> 0){
1264 if(some_to_calculate
){
1265 /* make nickname big enough to show all nicknames */
1266 if(nick
>= 0 && pab
->disp_form
[nick
].wtype
== WeCalculate
){
1267 --some_to_calculate
;
1268 if(pab
->disp_form
[nick
].width
!= max_nick
){
1271 this_fix
= MIN(max_nick
-pab
->disp_form
[nick
].width
, space_left
);
1272 pab
->disp_form
[nick
].width
+= this_fix
;
1273 space_left
-= this_fix
;
1277 if(!some_to_calculate
&& space_left
> 0)
1278 goto none_to_calculate
;
1284 /* add up total weight */
1285 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1286 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1287 switch(pab
->disp_form
[i
].type
){
1289 weight
+= MAX(third_full
, pab
->disp_form
[i
].width
);
1290 used_wt
+= pab
->disp_form
[i
].width
;
1294 weight
+= MAX(third_addr
, pab
->disp_form
[i
].width
);
1295 used_wt
+= pab
->disp_form
[i
].width
;
1299 weight
+= MAX(third_fcc
, pab
->disp_form
[i
].width
);
1300 used_wt
+= pab
->disp_form
[i
].width
;
1312 if(weight
- used_wt
<= space_left
){
1314 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1316 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1317 switch(pab
->disp_form
[i
].type
){
1319 this_fix
= third_full
- pab
->disp_form
[i
].width
;
1320 space_left
-= this_fix
;
1321 pab
->disp_form
[i
].width
+= this_fix
;
1325 this_fix
= third_addr
- pab
->disp_form
[i
].width
;
1326 space_left
-= this_fix
;
1327 pab
->disp_form
[i
].width
+= this_fix
;
1331 this_fix
= third_fcc
- pab
->disp_form
[i
].width
;
1332 space_left
-= this_fix
;
1333 pab
->disp_form
[i
].width
+= this_fix
;
1342 /* if still space left and a comment field, all to comment */
1345 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1347 if(pab
->disp_form
[i
].type
== Comment
&&
1348 pab
->disp_form
[i
].wtype
== WeCalculate
){
1349 pab
->disp_form
[i
].width
+= space_left
;
1355 else{ /* not enough space, dole out weighted pieces */
1356 int was_sl
= space_left
;
1359 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1361 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1362 switch(pab
->disp_form
[i
].type
){
1365 this_fix
= (third_full
* was_sl
)/weight
;
1366 space_left
-= this_fix
;
1367 pab
->disp_form
[i
].width
+= this_fix
;
1371 this_fix
= (third_addr
* was_sl
)/weight
;
1372 space_left
-= this_fix
;
1373 pab
->disp_form
[i
].width
+= this_fix
;
1377 this_fix
= (third_fcc
* was_sl
)/weight
;
1378 space_left
-= this_fix
;
1379 pab
->disp_form
[i
].width
+= this_fix
;
1391 while(space_left
> 0){
1392 for(i
=NFIELDS
-1; i
>= 0 && space_left
> 0; i
--){
1393 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1394 pab
->disp_form
[i
].width
++;
1403 * If they're all percentages, and the percentages add up to 100,
1404 * then we just have to fix a rounding problem. Otherwise, we'll
1405 * assume the user meant to have them add up to less than 100.
1410 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++)
1411 if(pab
->disp_form
[i
].wtype
== Percent
)
1412 pc_tot
+= pab
->disp_form
[i
].req_width
;
1415 if(all_percents
&& pc_tot
>= 100){
1418 int fix
= screen_width
- used
;
1424 /* find col'th column */
1425 for(i
=0, this_col
=0; i
< NFIELDS
; i
++){
1426 if(pab
->disp_form
[i
].wtype
== Percent
){
1427 if(col
== ++this_col
)
1432 pab
->disp_form
[i
].width
++;
1436 /* else, user specified less than 100%, leave it */
1439 /* else space_left == zero, nothing to do */
1442 * Check for special case. If we find it, this is the case where
1443 * we want to display the list entry field even though there is no
1444 * address field displayed. All of the display width is probably
1445 * used up by now, so we just need to pick some arbitrary width
1446 * for these lines. Since these lines are separate from the other
1447 * lines we've been calculating, we don't have to worry about running
1448 * into them, except for list continuation lines which we're not
1449 * going to worry about.
1451 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1452 if(pab
->disp_form
[i
].wtype
== Special
){
1453 pab
->disp_form
[i
].width
= MIN(utf8_width(CLICKHERE
), screen_width
);
1459 /* check for width changes */
1460 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1461 if(pab
->disp_form
[i
].width
!= pab
->disp_form
[i
].old_width
){
1462 ret
++; /* Tell the caller the screen changed */
1463 pab
->disp_form
[i
].old_width
= pab
->disp_form
[i
].width
;
1467 pab
->nick_is_displayed
= 0;
1468 pab
->full_is_displayed
= 0;
1469 pab
->addr_is_displayed
= 0;
1470 pab
->fcc_is_displayed
= 0;
1471 pab
->comment_is_displayed
= 0;
1472 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1473 if(pab
->disp_form
[i
].width
> 0){
1474 switch(pab
->disp_form
[i
].type
){
1476 pab
->nick_is_displayed
++;
1479 pab
->full_is_displayed
++;
1482 pab
->addr_is_displayed
++;
1485 pab
->fcc_is_displayed
++;
1488 pab
->comment_is_displayed
++;
1498 dprint((9, " some widths changed\n"));
1505 redraw_addr_screen(void)
1507 dprint((7, "- redraw_addr_screen -\n"));
1510 if(as
.l_p_page
<= 0)
1513 (void)calculate_field_widths();
1514 display_book(0, as
.cur_row
, -1, 1, (Pos
*)NULL
);
1519 * Little front end for address book screen so it can be called out
1520 * of the main command loop in alpine.c
1523 addr_book_screen(struct pine
*pine_state
)
1525 dprint((3, "\n\n --- ADDR_BOOK_SCREEN ---\n\n"));
1527 mailcap_free(); /* free resources we won't be using for a while */
1529 if(setjmp(addrbook_changed_unexpectedly
)){
1530 /* TRANSLATORS: a warning message telling the user that the address book
1531 is being reset (re-sychronized, restarted) */
1532 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1533 dprint((1, "RESETTING address book... addr_book_screen!\n"));
1537 ab_nesting_level
= 1; /* come here only from main menu */
1539 /* TRANSLATORS: a screen title for address book screen. Almost all of
1540 the translatable strings which are all upper case are screen titles. */
1541 (void)addr_book(AddrBookScreen
, _("ADDRESS BOOK"), NULL
);
1544 pine_state
->prev_screen
= addr_book_screen
;
1549 addr_book_config(struct pine
*pine_state
, int edit_exceptions
)
1551 if(edit_exceptions
){
1552 q_status_message(SM_ORDER
, 3, 7,
1553 _("Exception Setup not implemented for address books"));
1557 dprint((3, "\n\n --- ADDR_BOOK_CONFIG ---\n\n"));
1559 mailcap_free(); /* free resources we won't be using for a while */
1561 if(setjmp(addrbook_changed_unexpectedly
)){
1562 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1563 dprint((1, "RESETTING address book... addr_book_config!\n"));
1567 ab_nesting_level
= 1;
1569 /* TRANSLATORS: A screen title */
1570 (void)addr_book(AddrBookConfig
, _("SETUP ADDRESS BOOKS"), NULL
);
1573 pine_state
->prev_screen
= addr_book_screen
;
1578 * Return a single address
1580 * Returns: pointer to returned address, or NULL if nothing returned
1583 addr_book_oneaddr(void)
1586 jmp_buf save_jmp_buf
;
1587 int *save_nesting_level
;
1589 dprint((3, "--- addr_book_oneaddr ---\n"));
1591 save_nesting_level
= cpyint(ab_nesting_level
);
1592 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1593 if(setjmp(addrbook_changed_unexpectedly
)){
1594 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1595 dprint((1, "RESETTING address book... addr_book_oneaddr!\n"));
1597 ab_nesting_level
= *save_nesting_level
;
1602 /* TRANSLATORS: a screen title */
1603 p
= addr_book(SelectAddr
, _("SELECT ADDRESS"), NULL
);
1605 if(ab_nesting_level
<= 1)
1610 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1611 if(save_nesting_level
)
1612 fs_give((void **)&save_nesting_level
);
1619 * Return a list of addresses without fullname
1621 * Returns: pointer to returned address, or NULL if nothing returned
1624 addr_book_multaddr_nf(void)
1627 jmp_buf save_jmp_buf
;
1628 int *save_nesting_level
;
1630 dprint((3, "--- addr_book_multaddr_nf ---\n"));
1632 save_nesting_level
= cpyint(ab_nesting_level
);
1633 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1634 if(setjmp(addrbook_changed_unexpectedly
)){
1635 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1636 dprint((1, "RESETTING address book... addr_book_multaddr_nf!\n"));
1638 ab_nesting_level
= *save_nesting_level
;
1643 p
= addr_book(SelectMultNoFull
, _("SELECT ADDRESS"), NULL
);
1645 if(ab_nesting_level
<= 1)
1650 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1651 if(save_nesting_level
)
1652 fs_give((void **)&save_nesting_level
);
1659 * Return a single address without fullname phrase
1661 * Returns: pointer to returned address, or NULL if nothing returned
1664 addr_book_oneaddr_nf(void)
1667 jmp_buf save_jmp_buf
;
1668 int *save_nesting_level
;
1670 dprint((3, "--- addr_book_oneaddr_nf ---\n"));
1672 save_nesting_level
= cpyint(ab_nesting_level
);
1673 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1674 if(setjmp(addrbook_changed_unexpectedly
)){
1675 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1676 dprint((1, "RESETTING address book... addr_book_oneaddr_nf!\n"));
1678 ab_nesting_level
= *save_nesting_level
;
1683 p
= addr_book(SelectAddrNoFull
, _("SELECT ADDRESS"), NULL
);
1685 if(ab_nesting_level
<= 1)
1690 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1691 if(save_nesting_level
)
1692 fs_give((void **)&save_nesting_level
);
1699 * Call address book from message composer
1701 * Args: error_mess -- pointer to return error messages in (unused here)
1703 * Returns: pointer to returned address, or NULL if nothing returned
1706 addr_book_compose(char **error
)
1709 jmp_buf save_jmp_buf
;
1710 int *save_nesting_level
;
1712 dprint((3, "--- addr_book_compose ---\n"));
1714 save_nesting_level
= cpyint(ab_nesting_level
);
1715 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1716 if(setjmp(addrbook_changed_unexpectedly
)){
1717 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1718 dprint((1, "RESETTING address book... addr_book_compose!\n"));
1720 ab_nesting_level
= *save_nesting_level
;
1725 /* TRANSLATORS: a screen title */
1726 p
= addr_book(SelectNicksCom
, _("COMPOSER: SELECT ADDRESS"), error
);
1728 if(ab_nesting_level
<= 1)
1733 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1734 if(save_nesting_level
)
1735 fs_give((void **)&save_nesting_level
);
1742 * Call address book from message composer for Lcc line
1744 * Args: error_mess -- pointer to return error messages in (unused here)
1746 * Returns: pointer to returned address, or NULL if nothing returned
1749 addr_book_compose_lcc(char **error
)
1752 jmp_buf save_jmp_buf
;
1753 int *save_nesting_level
;
1755 dprint((3, "--- addr_book_compose_lcc ---\n"));
1757 save_nesting_level
= cpyint(ab_nesting_level
);
1758 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1759 if(setjmp(addrbook_changed_unexpectedly
)){
1760 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1761 dprint((1, "RESETTING address book... addr_book_compose_lcc!\n"));
1763 ab_nesting_level
= *save_nesting_level
;
1769 * We used to use SelectAddrLccCom here but decided it wasn't necessary
1770 * to restrict the selection to a list.
1772 /* TRANSLATORS: a screen title, user is composing a message and should select
1773 a distribution list from a list of addresses in the address book. */
1774 p
= addr_book(SelectNicksCom
, _("COMPOSER: SELECT LIST"), error
);
1776 if(ab_nesting_level
<= 1)
1781 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1782 if(save_nesting_level
)
1783 fs_give((void **)&save_nesting_level
);
1790 * Call address book from message composer for Lcc line
1792 * Args: error_mess -- pointer to return error messages in (unused here)
1794 * Returns: pointer to returned address, or NULL if nothing returned
1797 addr_book_change_list(char **error
)
1800 jmp_buf save_jmp_buf
;
1801 int *save_nesting_level
;
1803 dprint((3, "--- addr_book_change_list ---\n"));
1805 save_nesting_level
= cpyint(ab_nesting_level
);
1806 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1807 if(setjmp(addrbook_changed_unexpectedly
)){
1808 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1809 dprint((1, "RESETTING address book... addr_book_change_list!\n"));
1811 ab_nesting_level
= *save_nesting_level
;
1816 /* TRANSLATORS: a screen title */
1817 p
= addr_book(SelectNicksCom
, _("ADDRESS BOOK (Update): SELECT ADDRESSES"),
1820 if(ab_nesting_level
<= 1)
1825 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1826 if(save_nesting_level
)
1827 fs_give((void **)&save_nesting_level
);
1834 * Call address book from pine_simple_send
1836 * Returns: pointer to returned address, or NULL if nothing returned
1839 addr_book_bounce(void)
1842 jmp_buf save_jmp_buf
;
1843 int *save_nesting_level
;
1845 dprint((3, "- addr_book_bounce -\n"));
1847 save_nesting_level
= cpyint(ab_nesting_level
);
1848 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1849 if(setjmp(addrbook_changed_unexpectedly
)){
1850 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1851 dprint((1, "RESETTING address book...addr_book_bounce!\n"));
1853 ab_nesting_level
= *save_nesting_level
;
1858 p
= addr_book(SelectManyNicks
, _("SELECT ADDRESSES"), NULL
);
1860 if(ab_nesting_level
<= 1)
1865 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1866 if(save_nesting_level
)
1867 fs_give((void **)&save_nesting_level
);
1874 * Call address book from take address screen
1876 * Returns: pointer to returned nickname, or NULL if nothing returned
1879 addr_book_takeaddr(void)
1882 jmp_buf save_jmp_buf
;
1883 int *save_nesting_level
;
1885 dprint((3, "- addr_book_takeaddr -\n"));
1887 save_nesting_level
= cpyint(ab_nesting_level
);
1888 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1889 if(setjmp(addrbook_changed_unexpectedly
)){
1890 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1891 dprint((1, "RESETTING address book...addr_book_takeaddr!\n"));
1893 ab_nesting_level
= *save_nesting_level
;
1898 /* TRANSLATORS: a screen title */
1899 p
= addr_book(SelectNickTake
, _("TAKEADDR: SELECT NICKNAME"), NULL
);
1901 if(ab_nesting_level
<= 1)
1906 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1907 if(save_nesting_level
)
1908 fs_give((void **)&save_nesting_level
);
1915 * Call address book from editing screen for nickname field.
1917 * Returns: pointer to returned nickname, or NULL if nothing returned
1920 addr_book_nick_for_edit(char **error
)
1923 jmp_buf save_jmp_buf
;
1924 int *save_nesting_level
;
1927 dprint((3, "- addr_book_nick_for_edit -\n"));
1929 save_n_serv
= as
.n_serv
;
1931 save_nesting_level
= cpyint(ab_nesting_level
);
1932 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1933 if(setjmp(addrbook_changed_unexpectedly
)){
1934 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1935 dprint((1, "RESETTING address book...addr_book_nick_for_edit!\n"));
1937 ab_nesting_level
= *save_nesting_level
;
1943 * This is kind of hoaky. We want to prevent the Directory Query
1944 * options from turning on when we're coming in looking for a nickname
1945 * and this seemed to be the easiest way to accomplish that.
1948 /* TRANSLATORS: a screen title, user selecting a nickname from the address book */
1949 p
= addr_book(SelectNickCom
, _("SELECT NICKNAME"), error
);
1950 as
.n_serv
= save_n_serv
;
1952 if(ab_nesting_level
<= 1)
1957 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1958 if(save_nesting_level
)
1959 fs_give((void **)&save_nesting_level
);
1966 * Call address book for generic nickname select
1968 * Returns: pointer to returned nickname, or NULL if nothing returned
1971 addr_book_selnick(void)
1974 jmp_buf save_jmp_buf
;
1975 int *save_nesting_level
;
1977 dprint((3, "- addr_book_selnick -\n"));
1979 save_nesting_level
= cpyint(ab_nesting_level
);
1980 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1981 if(setjmp(addrbook_changed_unexpectedly
)){
1982 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1983 dprint((1, "RESETTING address book...addr_book_selnick!\n"));
1985 ab_nesting_level
= *save_nesting_level
;
1990 p
= addr_book(SelectNick
, _("SELECT NICKNAME"), NULL
);
1992 if(ab_nesting_level
<= 1)
1997 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1998 if(save_nesting_level
)
1999 fs_give((void **)&save_nesting_level
);
2006 * Main address book screen
2008 * Control loop for address book. Commands are executed out of a big
2009 * switch and screen painting is done.
2010 * The style argument controls whether or not it is called to return an address
2011 * to the composer, a nickname to the TakeAddr screen, or just for address
2014 * Args: style -- how we were called
2016 * Return: might return a string for the composer to edit, or a nickname, ...
2019 addr_book(AddrBookArg style
, char *title
, char **error_message
)
2025 quit
, /* loop control */
2026 km_popped
, /* menu is popped up in blank menu mode */
2027 current_changed_flag
, /* only current row needs update */
2028 was_clickable
, is_clickable
,
2029 was_collapsible_listent
, is_collapsible_listent
,
2030 was_global_config
, is_global_config
,
2031 was_addkey
, is_addkey
,
2032 was_opened
, is_opened
,
2033 was_custom_title
, is_custom_title
,
2035 start_disp
, /* Paint from this line down (0 is top) */
2036 rdonly
, /* cur addrbook read only */
2037 empty
, /* cur addrbook empty */
2038 are_selecting
, /* called as ^T selector */
2040 directory_ok
, /* called from composer, not Lcc */
2042 from_composer
, /* from composer */
2043 listmode_ok
, /* ok to do ListMode with this style */
2045 selecting_mult_nicks
,
2046 checkedn
, /* how many are checked */
2047 def_key
, /* default key */
2048 warped
; /* we warped through hyperspace to a
2049 new location in the display list */
2051 new_top_ent
, /* entry on top of screen after oper */
2052 new_line
; /* new line number after operation */
2056 struct key_menu
*km
;
2058 PerAddrBook
*pab
= NULL
;
2063 dprint((2, "--- addr_book --- (%s)\n",
2064 style
==AddrBookScreen
? "AddrBookScreen" :
2065 style
==SelectAddrLccCom
? "SelectAddrLccCom" :
2066 style
==SelectNicksCom
? "SelectNicksCom" :
2067 style
==SelectNick
? "SelectNick" :
2068 style
==SelectNickTake
? "SelectNickTake" :
2069 style
==SelectNickCom
? "SelectNickCom" :
2070 style
==AddrBookConfig
? "AddrBookConfig" :
2071 style
==SelectManyNicks
? "SelectManyNicks" :
2072 style
==SelectAddr
? "SelectAddr" :
2073 style
==SelectAddrNoFull
? "SelectAddrNoFull" :
2074 style
==SelectMultNoFull
? "SelectMultNoFull":
2079 ps
->next_screen
= SCREEN_FUN_NULL
;
2081 from_composer
= (style
== SelectAddrLccCom
2082 || style
== SelectNicksCom
2083 || style
== SelectNickCom
);
2084 are_selecting
= (style
!= AddrBookScreen
2085 && style
!= AddrBookConfig
);
2086 selecting_one_nick
= (style
== SelectNick
2087 || style
== SelectNickTake
2088 || style
== SelectNickCom
);
2089 selecting_mult_nicks
= (style
== SelectAddrLccCom
2090 || style
== SelectNicksCom
2091 || style
== SelectManyNicks
);
2092 listmode_ok
= (style
== SelectAddrLccCom
2093 || style
== SelectNicksCom
2094 || style
== SelectManyNicks
2095 || style
== SelectMultNoFull
);
2096 as
.config
= (style
== AddrBookConfig
);
2098 directory_ok
= (style
== SelectNicksCom
2099 || style
== AddrBookScreen
2100 || style
== SelectManyNicks
);
2103 /* Coming in from the composer, may need to reset the window */
2107 mark_status_dirty();
2108 mark_titlebar_dirty();
2109 mark_keymenu_dirty();
2112 command_line
= -FOOTER_ROWS(ps
); /* third line from the bottom */
2114 c
= 'x'; /* For display_message the first time through */
2116 if(ps
->remote_abook_validity
> 0)
2117 (void)adrbk_check_and_fix_all(ab_nesting_level
== 1, 0, 0);
2119 if(!init_addrbooks(HalfOpen
, 1, !as
.config
, !are_selecting
)){
2121 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
2122 _("No Address Book Configured"));
2127 else if(!as
.config
){
2128 ps
->next_screen
= main_menu_screen
;
2129 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2130 _("No Address Book Configured, Use SETUP Addressbook screen"));
2131 ps
->mangled_screen
= 1;
2135 else if(style
== AddrBookScreen
&& as
.n_addrbk
== 1 && as
.n_serv
== 0){
2136 if(as
.adrbks
[0].access
== ReadOnly
)
2137 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
2138 else if(as
.adrbks
[0].access
== NoAccess
)
2139 q_status_message(SM_ORDER
, 0, 4,
2140 _("AddressBook not accessible, permission denied"));
2143 if(as
.l_p_page
< 1){
2144 q_status_message(SM_ORDER
, 3, 3,
2145 _("Screen too small to use Address book"));
2153 (void) calculate_field_widths();
2157 ps
->mangled_screen
= 1;
2158 current_changed_flag
= 0;
2162 was_collapsible_listent
= 0;
2163 is_collapsible_listent
= 0;
2164 was_global_config
= 0;
2165 is_global_config
= 0;
2170 was_custom_title
= 0;
2171 is_custom_title
= 0;
2177 ps
->user_says_cancel
= 0;
2183 * Have to repaint from earliest change down, including
2184 * at least the last two body lines.
2186 if(ps
->mangled_body
) /* it was already mangled */
2187 start_disp
= MIN(start_disp
, as
.l_p_page
-2);
2188 else if(current_changed_flag
){
2189 ps
->mangled_body
= 1;
2190 start_disp
= MIN(MIN(as
.cur_row
, as
.l_p_page
-2),
2194 ps
->mangled_body
= 1;
2195 start_disp
= as
.l_p_page
-2;
2200 ps
->redrawer
= redraw_addr_screen
;
2202 if(new_mail(0, NM_TIMING(c
), NM_STATUS_MSG
| NM_DEFER_SORT
) >= 0)
2203 ps
->mangled_header
= 1;
2206 ps
->mangled_header
= 1;
2208 if(ps
->mangled_screen
){
2209 ps
->mangled_header
= 1;
2210 ps
->mangled_body
= 1;
2211 ps
->mangled_footer
= 1;
2213 current_changed_flag
= 0;
2214 ps
->mangled_screen
= 0;
2217 if(ps
->mangled_body
){
2219 if(calculate_field_widths())
2222 display_book(start_disp
,
2228 as
.old_cur_row
= as
.cur_row
;
2229 ps
->mangled_body
= 0;
2231 as
.cur
= cur_addr_book();
2232 pab
= (as
.n_addrbk
&&
2233 as
.cur
>= 0 && as
.cur
< as
.n_addrbk
&&
2234 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
2235 ? &as
.adrbks
[as
.cur
] : NULL
;
2241 for(i
= as
.top_ent
; dlist(i
)->type
!= End
; i
++)
2242 next_selectable_line(i
,&last_sel
);
2245 scroll_setrange(as
.l_p_page
, last_sel
);
2249 /* current entry has been changed */
2250 else if(current_changed_flag
){
2253 need_redraw
= calculate_field_widths();
2255 /*---------- Update the current entry, (move or change) -------*/
2256 display_book(need_redraw
? 0 : as
.cur_row
,
2262 as
.old_cur_row
= as
.cur_row
;
2263 current_changed_flag
= 0;
2264 as
.cur
= cur_addr_book();
2265 pab
= (as
.n_addrbk
&&
2266 as
.cur
>= 0 && as
.cur
< as
.n_addrbk
&&
2267 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
2268 ? &as
.adrbks
[as
.cur
] : NULL
;
2271 is_custom_title
= (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) &&
2272 style
== AddrBookScreen
&&
2277 if(( was_custom_title
&& !is_custom_title
) ||
2278 (!was_custom_title
&& is_custom_title
)){
2279 ps
->mangled_header
= 1;
2280 was_custom_title
= is_custom_title
;
2284 if(ps
->mangled_header
){
2287 if(style
== AddrBookScreen
){
2288 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
2290 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOKS"));
2292 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOK"));
2295 /* TRANSLATORS: a screen title, the %s arguments are for a custom part
2296 of the title. Move them as a group. There are two normal cases.
2297 Either ADDRESS BOOK LIST when a list of names of address books is
2298 displayed, or ADDRESS BOOK <name of address book> when one is open. */
2299 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOK%s%s%s"),
2300 /* TRANSLATORS: This is the LIST that goes with title above */
2301 is_custom_title
? " <" : cur_is_open() ? "" : _(" LIST"),
2302 is_custom_title
? pab
->abnick
: "",
2303 is_custom_title
? ">" : "");
2305 buf
[sizeof(buf
)-1] = '\0';
2312 set_titlebar(bp
, ps
->mail_stream
,
2313 ps
->context_current
, ps
->cur_folder
,
2315 FolderName
, 0, 0, NULL
);
2316 ps
->mangled_header
= 0;
2319 dprint((9, "addr_book: top of loop, addrbk %d top_ent %ld cur_row %d\n", as
.cur
, as
.top_ent
, as
.cur_row
));
2322 * This is a check to catch when a move from one row to another
2323 * should cause the list of commands to change.
2325 is_clickable
= entry_is_clickable(as
.top_ent
+as
.cur_row
);
2326 is_collapsible_listent
= F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2327 entry_is_listent(as
.top_ent
+as
.cur_row
);
2328 is_global_config
= as
.config
&& pab
&& pab
->type
& GLOBAL
;
2329 is_addkey
= entry_is_addkey(as
.top_ent
+as
.cur_row
);
2330 is_opened
= (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) &&
2332 if(( was_clickable
&& !is_clickable
) ||
2333 (!was_clickable
&& is_clickable
) ||
2334 ( was_collapsible_listent
&& !is_collapsible_listent
) ||
2335 (!was_collapsible_listent
&& is_collapsible_listent
) ||
2336 ( was_global_config
&& !is_global_config
) ||
2337 (!was_global_config
&& is_global_config
) ||
2338 ( was_addkey
&& !is_addkey
) ||
2339 (!was_addkey
&& is_addkey
) ||
2340 ( was_opened
&& !is_opened
) ||
2341 (!was_opened
&& is_opened
) ||
2344 ps
->mangled_footer
= 1;
2345 was_clickable
= is_clickable
;
2346 was_collapsible_listent
= is_collapsible_listent
;
2347 was_global_config
= is_global_config
;
2348 was_addkey
= is_addkey
;
2349 was_opened
= is_opened
;
2354 if(ps
->mangled_footer
){
2357 menu_clear_binding(km
, '>');
2358 menu_clear_binding(km
, '.');
2359 menu_clear_binding(km
, KEY_RIGHT
);
2360 menu_clear_binding(km
, ctrl('M'));
2361 menu_clear_binding(km
, ctrl('J'));
2362 menu_clear_binding(km
, KEY_LEFT
);
2363 menu_clear_binding(km
, '<');
2364 menu_clear_binding(km
, ',');
2365 def_key
= THREE_KEY
; /* default default key */
2369 clrbitn(OTHER_KEY
, bitmap
);
2370 /* TRANSLATORS: a command name for a particular key */
2371 menu_init_binding(km
, 'E', MC_EXIT
, "E", N_("Exit Setup"), TWO_KEY
);
2372 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_EXITMODE
);
2375 * Don't show Delete or Shuffle command if there is nothing
2376 * to delete or shuffle.
2379 clrbitn(DELETE_KEY
, bitmap
);
2380 clrbitn(SENDTO_KEY
, bitmap
);
2381 clrbitn(THREE_KEY
, bitmap
);
2382 menu_init_binding(km
, 'A', MC_ADDABOOK
, "A",
2383 add_is_global(as
.top_ent
+as
.cur_row
)
2384 /* TRANSLATORS: Add Global Address book (one defined by someone else) */
2385 ? "[" N_("Add Glob Abook") "]"
2386 /* TRANSLATORS: Add Personal Address book */
2387 : "[" N_("Add Pers Abook") "]",
2392 /* TRANSLATORS: Delete Address book command */
2393 menu_init_binding(km
, 'D', MC_DELABOOK
, "D", N_("Del Abook"),
2395 /* TRANSLATORS: Shuffle refers to shuffling the order of things,
2396 that is, changing the order */
2397 menu_init_binding(km
, '$', MC_SHUFFLE
, "$", N_("Shuffle"),
2399 /* TRANSLATORS: Change is a command meaning Change some item,
2400 or Edit some item to change it */
2401 menu_init_binding(km
, 'C', MC_EDITABOOK
, "C", "[" N_("Change") "]",
2403 menu_init_binding(km
, 'A', MC_ADDABOOK
, "A",
2404 add_is_global(as
.top_ent
+as
.cur_row
)
2405 ? N_("Add Glob Abook")
2406 : N_("Add Pers Abook"),
2410 else if(are_selecting
){
2414 * The OTHER_KEY is used as the Exit key in selection mode.
2415 * This is because the TWO_KEY is being used for < actions.
2417 /* TRANSLATORS: Command to Exit the Select screen. This would
2418 be a way to make no Selection and go back to where you
2420 menu_init_binding(km
, 'E', MC_EXIT
, "E", N_("ExitSelect"),
2422 KS_OSDATASET(&km
->keys
[OTHER_KEY
], KS_EXITMODE
);
2425 * Use the TWO_KEY for the go back key.
2427 if(cur_is_open() && (as
.n_addrbk
> 1 || as
.n_serv
)){
2428 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2429 entry_is_listent(as
.top_ent
+as
.cur_row
))
2431 else if(F_OFF(F_CMBND_ABOOK_DISP
,ps_global
))
2437 clrbitn(TWO_KEY
, bitmap
);
2439 menu_init_binding(km
, '<', cmd
, "<",
2440 cmd
== MC_POPUP
? N_("AddressBkList")
2441 /* TRANSLATORS: Unexpand is the opposite of Expand.
2442 We might expand an address book distribution
2443 list to see all the members of the list. */
2446 menu_add_binding(km
, ',', cmd
);
2447 if(F_ON(F_ARROW_NAV
,ps
))
2448 menu_add_binding(km
, KEY_LEFT
, cmd
);
2451 else if(as
.checkboxes
&& (as
.n_addrbk
> 1 || as
.n_serv
)){
2453 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2454 menu_init_binding(km
, 'S', MC_CHOICE
, "S",
2455 /* TRANSLATORS: Select something, choose something */
2456 N_("Select"), TWO_KEY
);
2459 menu_init_binding(km
, 'S', MC_CHOICE
, "S",
2460 "[" N_("Select") "]", TWO_KEY
);
2465 menu_init_binding(km
, 'S', MC_CHOICE
, "S", N_("Select"),
2469 clrbitn(TWO_KEY
, bitmap
);
2472 * The THREE_KEY is used as the select key in selection mode,
2473 * but it doesn't show up at the top-level. Instead, the
2474 * key becomes the ViewAbook key.
2476 if(entry_is_askserver(as
.top_ent
+as
.cur_row
) && !as
.checkboxes
){
2477 menu_init_binding(km
, '>', MC_QUERY_SERV
, ">", "[" N_("Search") "]",
2479 menu_add_binding(km
, 's', MC_QUERY_SERV
);
2480 menu_add_binding(km
, '.', MC_QUERY_SERV
);
2481 if(F_ON(F_ARROW_NAV
,ps
))
2482 menu_add_binding(km
, KEY_RIGHT
, MC_QUERY_SERV
);
2484 else if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2485 /* TRANSLATORS: View this address book */
2486 menu_init_binding(km
, '>', MC_OPENABOOK
, ">", "[" N_("ViewAbook") "]",
2488 menu_add_binding(km
, 'v', MC_OPENABOOK
);
2489 menu_add_binding(km
, '.', MC_OPENABOOK
);
2490 if(F_ON(F_ARROW_NAV
,ps
))
2491 menu_add_binding(km
, KEY_RIGHT
, MC_OPENABOOK
);
2493 else if(cur_is_open()){
2494 menu_init_binding(km
, 'S', MC_CHOICE
, "S", "[" N_("Select") "]",
2498 clrbitn(THREE_KEY
, bitmap
);
2500 KS_OSDATASET(&km
->keys
[THREE_KEY
], KS_NONE
);
2503 * The Expand command gets stuck out in right field.
2505 if(entry_is_clickable(as
.top_ent
+as
.cur_row
) &&
2506 !entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2507 menu_init_binding(km
, '>', MC_EXPAND
, ">", N_("Expand"),
2509 menu_add_binding(km
, '.', MC_EXPAND
);
2510 if(F_ON(F_ARROW_NAV
,ps
))
2511 menu_add_binding(km
, KEY_RIGHT
, MC_EXPAND
);
2514 clrbitn(SENDTO_KEY
, bitmap
);
2516 if(cur_is_open() && as
.checkboxes
){
2517 /* TRANSLATORS: Set/Unset means that this particular command
2518 will toggle between setting something (turning it on) and
2519 unsetting it (turning it off). For example, it might be
2520 a program option that can be turned on or off or it might
2521 be a way to mark which addresses to send a message to. */
2522 menu_init_binding(km
, 'X', MC_TOGGLE
, "X", N_("Set/Unset"),
2526 else if(cur_is_open() && listmode_ok
){
2527 /* TRANSLATORS: List mode is a type of screen in pine that
2528 allows the user to select several of something. This is
2529 the name of the command to go into the List mode style
2531 menu_init_binding(km
, 'L', MC_LISTMODE
, "L", N_("ListMode"),
2535 clrbitn(DELETE_KEY
, bitmap
);
2537 if(cur_is_open() && as
.checkboxes
){
2538 menu_init_binding(km
, 'A', MC_SELALL
, "A",
2539 /* TRANSLATORS: when selecting from a list of items
2540 the unsetall (unset all) means to start over
2541 with nothing selected.
2542 The set all command means select everything
2544 checkedn
? N_("unsetAll") : N_("setAll"),
2548 clrbitn(ADD_KEY
, bitmap
);
2550 KS_OSDATASET(&km
->keys
[DELETE_KEY
], KS_NONE
);
2554 * Reset first Other key. Selection screen may have
2555 * blasted it. Do this by hand because menu_init_binding
2556 * will remove the other two OTHER CMDS bindings.
2557 * Should figure out how to do this correctly with a
2558 * reasonable function call.
2560 km
->keys
[OTHER_KEY
].name
= "O";
2561 /* TRANSLATORS: This is the name of the command that will show
2562 which other commands are available. 12 commands are shown at
2563 the bottom of the screen, this command would show the next set
2565 km
->keys
[OTHER_KEY
].label
= N_("OTHER CMDS");
2566 km
->keys
[OTHER_KEY
].bind
.cmd
= MC_OTHER
;
2567 km
->keys
[OTHER_KEY
].bind
.ch
[0] = 'O';
2568 km
->keys
[OTHER_KEY
].bind
.nch
= 1;
2569 KS_OSDATASET(&km
->keys
[OTHER_KEY
], KS_NONE
);
2573 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
2574 if(F_ON(F_ENABLE_AGG_OPS
, ps
))
2578 * The TWO_KEY is used as the go back key in
2579 * non-selection mode.
2581 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2582 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2584 menu_init_binding(km
, '<', cmd
, "<", N_("Unexpand"),
2586 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2590 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2592 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2597 * Add or delete entries from this address book.
2599 /* TRANSLATORS: Add a new entry (this is a command) */
2600 menu_init_binding(km
, '@', MC_ADD
, "@", N_("AddNew"),
2602 menu_init_binding(km
, 'D', MC_DELETE
, "D", N_("Delete"),
2604 /* TRANSLATORS: Compose a message to be sent to the current address
2606 menu_init_binding(km
, 'C', MC_COMPOSE
, "C", N_("ComposeTo"),
2608 KS_OSDATASET(&km
->keys
[SENDTO_KEY
], KS_COMPOSER
);
2609 menu_init_binding(km
, '#', MC_ROLE
, "#", N_("Role"),
2613 clrbitn(ADD_KEY
, bitmap
);
2614 clrbitn(DELETE_KEY
, bitmap
);
2615 clrbitn(SENDTO_KEY
, bitmap
);
2616 clrbitn(RCOMPOSE_KEY
, bitmap
);
2617 clrbitn(SAVE_KEY
, bitmap
);
2618 clrbitn(TAKE_KEY
, bitmap
);
2619 clrbitn(FORW_KEY
, bitmap
);
2622 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2624 else if(cur_is_open()){
2625 if(F_ON(F_ENABLE_AGG_OPS
, ps
))
2629 * The TWO_KEY is used as the go back key in
2630 * non-selection mode.
2632 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2633 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2635 menu_init_binding(km
, '<', cmd
, "<", N_("Unexpand"),
2637 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2640 if(as
.n_addrbk
> 1 || as
.n_serv
){
2642 menu_init_binding(km
, '<', cmd
, "<",
2643 N_("AddressBkList"), TWO_KEY
);
2644 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2648 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2650 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2654 if(pab
->access
!= NoAccess
){
2656 * Add or delete entries from this address book.
2658 menu_init_binding(km
, '@', MC_ADD
, "@", N_("AddNew"),
2660 menu_init_binding(km
, 'D', MC_DELETE
, "D", N_("Delete"),
2664 clrbitn(ADD_KEY
, bitmap
);
2665 clrbitn(DELETE_KEY
, bitmap
);
2668 /* Find someplace to put Main Menu command */
2669 if(cmd
== MC_POPUP
){
2670 menu_init_binding(km
, 'M', MC_MAIN
, "M", N_("Main Menu"),
2671 SECONDARY_MAIN_KEY
);
2672 KS_OSDATASET(&km
->keys
[SECONDARY_MAIN_KEY
],KS_MAINMENU
);
2675 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2677 menu_init_binding(km
, 'C', MC_COMPOSE
, "C", N_("ComposeTo"),
2679 KS_OSDATASET(&km
->keys
[SENDTO_KEY
], KS_COMPOSER
);
2680 menu_init_binding(km
, '#', MC_ROLE
, "#", N_("Role"),
2685 * The TWO_KEY is used as the go back key in
2686 * non-selection mode.
2689 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2691 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2693 clrbitn(SENDTO_KEY
, bitmap
);
2694 clrbitn(RCOMPOSE_KEY
, bitmap
);
2695 clrbitn(ADD_KEY
, bitmap
);
2696 clrbitn(DELETE_KEY
, bitmap
);
2697 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2698 clrbitn(SAVE_KEY
, bitmap
);
2699 clrbitn(TAKE_KEY
, bitmap
);
2700 clrbitn(FORW_KEY
, bitmap
);
2703 /* can't be on third menu if we just reduced to 2 */
2704 if(km
->how_many
== 2 && km
->which
== 2)
2707 menu_add_binding(km
, '<', cmd
);
2708 menu_add_binding(km
, ',', cmd
);
2709 if(F_ON(F_ARROW_NAV
,ps
))
2710 menu_add_binding(km
, KEY_LEFT
, cmd
);
2712 KS_OSDATASET(&km
->keys
[DELETE_KEY
], KS_DELETE
);
2715 * The THREE_KEY is the burrow into the hierarchy key.
2717 if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
2718 cmd
= MC_QUERY_SERV
;
2719 else if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
2720 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
))
2726 cmd
= MC_VIEW_ENTRY
;
2728 menu_init_binding(km
, '>', cmd
, ">",
2729 cmd
== MC_EXPAND
? "[" N_("Expand") "]" :
2730 cmd
== MC_QUERY_SERV
? "[" N_("Search") "]" :
2731 /* TRANSLATORS: In the address book the user can
2732 view a particular address book entry. It is
2733 called View/Update because the way to update
2734 an entry is to first view it and then there
2735 will be an opportunity to update it from there. */
2736 cur_is_open() ? "[" N_("View/Update") "]"
2737 : "[" N_("ViewAbook") "]",
2740 if(cmd
== MC_QUERY_SERV
)
2741 menu_add_binding(km
, 's', cmd
);
2742 else if(cmd
== MC_OPENABOOK
|| cmd
== MC_VIEW_ENTRY
)
2743 menu_add_binding(km
, 'v', cmd
);
2745 menu_add_binding(km
, '.', cmd
);
2746 if(F_ON(F_ARROW_NAV
,ps
))
2747 menu_add_binding(km
, KEY_RIGHT
, cmd
);
2750 menu_add_binding(km
, ctrl('M'), km
->keys
[def_key
].bind
.cmd
);
2751 menu_add_binding(km
, ctrl('J'), km
->keys
[def_key
].bind
.cmd
);
2754 FOOTER_ROWS(ps
) = 3;
2758 draw_keymenu(km
, bitmap
, ps
->ttyo
->screen_cols
,
2759 1-FOOTER_ROWS(ps
), 0, what
);
2760 ps
->mangled_footer
= 0;
2763 FOOTER_ROWS(ps
) = 1;
2764 mark_keymenu_dirty();
2768 rdonly
= (pab
&& pab
->access
== ReadOnly
);
2769 empty
= is_empty(as
.cur_row
+as
.top_ent
);
2771 /*------------ display any status messages ------------------*/
2773 FOOTER_ROWS(ps
) = 3;
2774 mark_status_unknown();
2779 FOOTER_ROWS(ps
) = 1;
2780 mark_status_unknown();
2783 if(F_OFF(F_SHOW_CURSOR
, ps
)){
2784 /* reset each time through to catch screen size changes */
2785 cursor_pos
.row
= ps
->ttyo
->screen_rows
-FOOTER_ROWS(ps
);
2789 MoveCursor(cursor_pos
.row
, cursor_pos
.col
);
2792 /*---------------- Get command and validate -------------------*/
2794 mouse_in_content(KEY_MOUSE
, -1, -1, 0, 0);
2795 register_mfunc(mouse_in_content
, HEADER_ROWS(ps
), 0,
2796 ps
->ttyo
->screen_rows
-(FOOTER_ROWS(ps
)+1),
2797 ps
->ttyo
->screen_cols
);
2800 /* sort out what help needs to be displayed if asked for */
2802 gAbookHelp
= h_abook_config
;
2803 else if(are_selecting
){
2805 if(style
== SelectNickTake
)
2806 gAbookHelp
= h_abook_select_nicks_take
;
2807 else if(selecting_one_nick
)
2808 gAbookHelp
= h_abook_select_nick
;
2809 else if(as
.checkboxes
)
2810 gAbookHelp
= h_abook_select_checks
;
2811 else if(listmode_ok
)
2812 gAbookHelp
= h_abook_select_listmode
;
2814 gAbookHelp
= h_abook_select_addr
;
2817 gAbookHelp
= h_abook_select_top
;
2820 gAbookHelp
= cur_is_open() ? h_abook_opened
: h_abook_top
;
2823 mswin_setscrollcallback(addr_scroll_callback
);
2824 mswin_sethelptextcallback(pcpine_help_addrbook
);
2826 c
= READ_COMMAND(&utf8str
);
2828 clear_mfunc(mouse_in_content
);
2831 mswin_setscrollcallback(NULL
);
2832 mswin_sethelptextcallback(NULL
);
2834 cmd
= menu_command(c
, km
);
2836 dprint((2, "Addrbook command: %d (0x%x %s)\n", cmd
,
2837 c
, pretty_command(c
)));
2839 /* this may be a safe place to update addrbooks */
2840 if(ps
->remote_abook_validity
> 0 &&
2841 adrbk_check_and_fix_all(ab_nesting_level
< 2 &&
2842 !any_ab_open() && checkedn
== 0, 0, 0))
2843 ps
->mangled_footer
= 1;
2859 /*------------- execute command ----------------*/
2862 /*------------ Noop (new mail check) --------------*/
2867 /*----------- Help -------------------*/
2869 if(FOOTER_ROWS(ps
) == 1 && km_popped
== 0){
2871 ps
->mangled_footer
= 1;
2876 helper(gAbookHelp
, _("HELP ON CONFIGURING ADDRESS BOOKS"),
2878 else if(are_selecting
)
2879 helper(gAbookHelp
, _("HELP ON ADDRESS BOOK"),
2880 HLPD_SIMPLE
| HLPD_NEWWIN
);
2881 else /* general maintenance screen */
2882 helper(gAbookHelp
, _("HELP ON ADDRESS BOOK"), HLPD_NONE
);
2885 * Helper() may have a Main Menu key. If user types that
2886 * they'll set next_screen. We don't have to do anything
2887 * special but we want to make sure that that doesn't happen
2888 * when we're selecting, even though it shouldn't be
2889 * possible because HLPD_SIMPLE is set.
2892 ps
->next_screen
= SCREEN_FUN_NULL
; /* probably not needed */
2894 ps
->mangled_screen
= 1;
2898 /*---------- display other key bindings ------*/
2902 ps
->mangled_footer
= 1;
2906 /*------------ Unexpand list -----------------*/
2908 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2909 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2910 DL_CACHE_S
*dlc_to_flush
;
2911 long global_row_num
;
2918 * redraw screen starting with first ListEnt
2920 dl
= dlist(as
.top_ent
+as
.cur_row
);
2921 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
2924 * New cur_row should be the line after the ListHead line
2925 * that takes the place of the first address in the list.
2927 as
.cur_row
= as
.cur_row
- dl
->l_offset
;
2930 * If the list header for the new row is off the screen,
2933 if(as
.cur_row
< 0){ /* center it */
2934 global_row_num
= dlc_to_flush
->global_row
-
2935 dlc_to_flush
->dlcoffset
;
2936 as
.top_ent
= first_line(global_row_num
- as
.l_p_page
/2L);
2937 as
.cur_row
= global_row_num
- as
.top_ent
;
2940 else if(as
.cur_row
== 0){ /* just slide up in this case */
2946 start_disp
= as
.cur_row
;
2948 exp_unset_expanded(pab
->address_book
->exp
,
2949 (a_c_arg_t
)dlc_to_flush
->dlcelnum
);
2950 flush_dlc_from_cache(dlc_to_flush
);
2951 ps
->mangled_body
= 1;
2952 ps
->mangled_footer
= 1;
2955 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2956 "Can't happen in MC_UNEXPAND");
2960 /*------ Popup to top level display ----------*/
2962 if(F_ON(F_EXPANDED_DISTLISTS
,ps
) ||
2963 !entry_is_listent(as
.top_ent
+as
.cur_row
)){
2964 DL_CACHE_S dlc_restart
;
2967 (void)init_addrbooks((checkedn
|| as
.selections
)
2968 ? ThreeQuartOpen
: HalfOpen
,
2969 0, 0, !are_selecting
);
2970 dlc_restart
.adrbk_num
= as
.cur
;
2971 dlc_restart
.type
= DlcTitle
;
2972 warp_to_dlc(&dlc_restart
, 0L);
2974 * Put the current entry in a nice spot on the screen.
2975 * Will everything above fit and still leave ours on screen?
2977 new_row
= LINES_PER_ABOOK
* as
.cur
+
2978 (((pab
->type
& GLOBAL
) &&
2979 (as
.how_many_personals
> 0 || as
.config
))
2980 ? XTRA_LINES_BETWEEN
: 0) +
2981 ((as
.how_many_personals
== 0 && as
.config
)
2982 ? LINES_PER_ADD_LINE
: 0);
2983 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
2985 as
.cur_row
= new_row
;
2986 as
.top_ent
= 0L - as
.cur_row
;
2988 ps
->mangled_screen
= 1;
2991 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2992 "Can't happen in MC_POPUP");
2997 /*------------- Back to main menu or exit to caller -------*/
3001 ps
->next_screen
= main_menu_screen
;
3003 if(!(are_selecting
&& as
.checkboxes
&& checkedn
> 0)
3004 /* TRANSLATORS: we are asking for confirmation about abandonding selections
3005 in the address book. */
3006 || want_to(_("Really abandon your selections "),
3007 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
3010 ps
->next_screen
= SCREEN_FUN_NULL
;
3015 /*------- Open an address book ----------*/
3018 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
3019 DL_CACHE_S
*dlc_to_flush
;
3021 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3022 if(dlc_to_flush
->type
== DlcTitle
||
3023 dlc_to_flush
->type
== DlcClickHereCmb
){
3026 * open this addrbook and fill in display list
3029 init_abook(pab
, Open
);
3031 if(pab
->access
== ReadWrite
|| pab
->access
== ReadOnly
){
3032 if(!are_selecting
&& pab
->access
== ReadOnly
)
3033 q_status_message1(SM_ORDER
, 0, 4, _("AddressBook %s is Read Only"), pab
->abnick
);
3036 * successful open, burrow into addrbook
3038 if(F_OFF(F_CMBND_ABOOK_DISP
,ps_global
)){
3039 warp_to_top_of_abook(as
.cur
);
3043 ps
->mangled_screen
= 1;
3046 flush_dlc_from_cache(dlc_to_flush
);
3047 start_disp
= as
.cur_row
;
3048 ps
->mangled_footer
= 1;
3049 ps
->mangled_body
= 1;
3052 else{ /* open failed */
3054 * Flush the title line so that it will change into
3055 * a permission denied line,
3057 flush_dlc_from_cache(dlc_to_flush
);
3058 start_disp
= as
.cur_row
;
3059 ps
->mangled_footer
= 1;
3060 ps
->mangled_body
= 1;
3063 else if(dlc_to_flush
->type
== DlcTitleNoPerm
)
3064 q_status_message(SM_ORDER
, 0, 4,
3065 _("Cannot access address book."));
3068 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3069 "Can't happen in MC_OPENABOOK");
3074 /*------- Expand addresses in List ------*/
3077 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
3078 DL_CACHE_S
*dlc_to_flush
;
3080 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3081 if(dlc_to_flush
->type
== DlcListClickHere
){
3082 start_disp
= as
.cur_row
; /* will redraw from here down */
3085 * Mark this list expanded, then flush the
3086 * current line from dlc cache. When we get the
3087 * line again it will notice the expanded flag and change
3088 * the type to DlcListEnt (if any entries).
3091 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
))
3092 exp_set_expanded(pab
->address_book
->exp
,
3093 (a_c_arg_t
)dlc_to_flush
->dlcelnum
);
3095 flush_dlc_from_cache(dlc_to_flush
);
3096 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3098 * If list is empty, back cursor up one.
3100 if(dlc_to_flush
->type
== DlcListEmpty
){
3110 ps
->mangled_body
= 1;
3114 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3115 "Can't happen in MC_EXPAND");
3120 /*------- Select ---------------*/
3124 /* Select an entry to mail to or a nickname to add to */
3125 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
3126 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
3127 _("No entries in address book. Use ExitSelect to leave address books"));
3131 if(as
.checkboxes
|| is_addr(as
.top_ent
+as
.cur_row
)){
3137 dl
= dlist(as
.top_ent
+as
.cur_row
);
3139 if(selecting_one_nick
){
3140 char nickbuf
[MAX_NICKNAME
+ 1];
3143 ae(as
.top_ent
+as
.cur_row
)->nickname
,
3145 nickbuf
[sizeof(nickbuf
)-1] = '\0';
3146 return(cpystr(nickbuf
));
3148 else if(as
.checkboxes
&& checkedn
<= 0){
3149 q_status_message(SM_ORDER
, 0, 1,
3150 _("Use \"X\" to mark addresses or lists"));
3153 else if(as
.checkboxes
){
3154 size_t incr
= 100, avail
, alloced
;
3157 * Have to run through all of the checked entries
3158 * in all of the address books.
3159 * Put the nicknames together into one long
3160 * string with comma separators and let
3161 * our_build_address handle the parsing.
3163 to
= (char *)fs_get(incr
);
3168 for(i
= 0; i
< as
.n_addrbk
; i
++){
3169 EXPANDED_S
*next_one
;
3171 AddrScrn_Disp fake_dl
;
3174 pab
= &as
.adrbks
[i
];
3175 if(pab
->address_book
)
3176 next_one
= pab
->address_book
->checks
;
3180 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
3181 abe
= adrbk_get_ae(pab
->address_book
,
3184 * Since we're picking up address book entries
3185 * directly from the address books and have
3186 * no knowledge of the display lines they came
3187 * from, we don't know the dl's that go with
3188 * them. We need to pass a dl to abe_to_nick
3189 * but it really is only going to use the
3190 * type in this case.
3193 dl
->type
= (abe
->tag
== Single
) ? Simple
3195 a_string
= abe_to_nick_or_addr_string(abe
, dl
, i
);
3197 while(abe
&& avail
< (size_t)strlen(a_string
)+1){
3200 fs_resize((void **)&to
, alloced
);
3204 strncpy(to
, a_string
, alloced
);
3205 to
[alloced
-1] = '\0';
3208 strncat(to
, ",", alloced
-strlen(to
)-1);
3209 strncat(to
, a_string
, alloced
-strlen(to
)-1);
3212 avail
-= (strlen(a_string
) + 1);
3217 * Return the nickname list for lcc so that the
3218 * correct fullname can make it to the To line.
3219 * If we expand it ahead of time, the list name
3220 * and first user's fullname will get mushed together.
3221 * If an entry doesn't have a nickname then we're
3222 * out of luck as far as getting the right entry
3223 * in the To line goes.
3225 if(selecting_mult_nicks
)
3232 /* Select an address, but not using checkboxes */
3233 if(selecting_mult_nicks
){
3234 if(dl
->type
!= ListHead
&& style
== SelectAddrLccCom
){
3235 q_status_message(SM_ORDER
, 0, 4,
3236 _("You may only select lists for Lcc, use Bcc for other addresses"));
3241 * Even though we're supposedly selecting
3242 * nicknames, we have a special case here to
3243 * select a single member of a distribution
3244 * list. This happens with style SelectNicksCom
3245 * which is the regular ^T entry from the
3246 * composer, and it allows somebody to mail to
3247 * a single member of a distribution list.
3249 abe
= ae(as
.top_ent
+as
.cur_row
);
3250 return(abe_to_nick_or_addr_string(abe
, dl
, as
.cur
));
3254 if(dl
->type
== ListEnt
){
3257 listmem_from_dl(pab
->address_book
, dl
);
3261 bldto
.arg
.abe
= ae(as
.top_ent
+as
.cur_row
);
3266 (void)our_build_address(bldto
, &addr
, &error
, NULL
, save_and_restore
);
3267 /* Have to rfc1522_decode the addr */
3269 char *tmp_a_string
, *p
;
3272 if(style
== SelectAddrNoFull
){
3273 tmp_a_string
= cpystr(addr
);
3274 rfc822_parse_adrlist(&a
,tmp_a_string
,ps
->maildomain
);
3275 fs_give((void **)&tmp_a_string
);
3277 fs_give((void **)&addr
);
3278 addr
= cpystr(simple_addr_string(a
, tmp_20k_buf
,
3280 mail_free_address(&a
);
3283 else if(style
== SelectMultNoFull
){
3284 tmp_a_string
= cpystr(addr
);
3285 rfc822_parse_adrlist(&a
,tmp_a_string
,ps
->maildomain
);
3286 fs_give((void **)&tmp_a_string
);
3288 fs_give((void **)&addr
);
3289 addr
= cpystr(simple_mult_addr_string(a
,
3293 mail_free_address(&a
);
3298 len
= 4*strlen(addr
)+1;
3299 p
= (char *)fs_get(len
* sizeof(char));
3300 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, addr
) == (unsigned char *)p
){
3301 fs_give((void **)&addr
);
3305 fs_give((void **)&p
);
3310 fs_give((void **)&to
);
3313 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
3314 fs_give((void **)&error
);
3317 return(addr
); /* Caller frees this */
3320 if(entry_is_clickable(as
.top_ent
+as
.cur_row
))
3321 clickable_warning(as
.top_ent
+as
.cur_row
);
3322 else if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
3323 q_status_message(SM_ORDER
, 3, 4, _("Use select to select an address or addresses from address books"));
3325 q_status_message(SM_ORDER
, 3, 4, _("No address selected"));
3331 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3332 "Can't happen in MC_CHOICE");
3337 /*----- View an entry --------------------*/
3341 empty_warning(as
.top_ent
+as
.cur_row
);
3345 view_abook_entry(ps
, as
.top_ent
+as
.cur_row
);
3349 /*----- Add new ---------*/
3351 {long old_l_p_p
, old_top_ent
, old_cur_row
;
3353 if(adrbk_check_all_validity_now()){
3354 if(resync_screen(pab
, style
, checkedn
)){
3355 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3356 _("Address book changed. AddNew cancelled. Try again."));
3357 ps
->mangled_screen
= 1;
3363 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
3369 "Calling edit_entry to add entry manually\n"));
3370 /* TRANSLATORS: add as in add a new entry to something */
3371 edit_entry(pab
->address_book
, (AdrBk_Entry
*)NULL
, NO_NEXT
,
3372 NotSet
, 0, &warped
, _("add"));
3375 * Warped means we got plopped down somewhere in the display
3376 * list so that we don't know where we are relative to where
3377 * we were before we warped. The current line number will
3378 * be zero, since that is what the warp would have set.
3381 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
3382 as
.cur_row
= 0L - as
.top_ent
;
3386 * If we didn't warp, that means we didn't change at all,
3387 * so keep old screen.
3389 old_l_p_p
= as
.l_p_page
;
3390 old_top_ent
= as
.top_ent
;
3391 old_cur_row
= as
.cur_row
;
3394 /* Window size may have changed while in pico. */
3397 /* fix up what ab_resize messed up */
3398 if(!warped
&& old_l_p_p
== as
.l_p_page
){
3399 as
.top_ent
= old_top_ent
;
3400 as
.cur_row
= old_cur_row
;
3401 as
.old_cur_row
= old_cur_row
;
3405 ps
->mangled_screen
= 1;
3409 /*---------- Add a new address book -------------------*/
3411 {int old_abook_num
, new_abook_num
, new_row
, global
;
3412 int stay_put
, old_cur_row
;
3415 stay_put
= (dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstPers
||
3416 dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstGlob
);
3417 old_cur_row
= as
.cur_row
;
3418 old_abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3419 global
= (dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstGlob
||
3420 (dlist(as
.top_ent
+as
.cur_row
)->type
!= AddFirstPers
&&
3421 (old_abook_num
>= as
.how_many_personals
) &&
3424 ab_add_abook(global
,
3426 : adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
)))
3428 DL_CACHE_S dlc_restart
;
3430 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3435 dlc_restart
.adrbk_num
= new_abook_num
;
3436 dlc_restart
.type
= DlcTitle
;
3437 warp_to_dlc(&dlc_restart
, 0L);
3440 * Put the current entry in a nice spot on the screen.
3442 new_row
= old_cur_row
+
3445 : LINES_PER_ABOOK
*(new_abook_num
- old_abook_num
));
3448 new_row
<= (as
.l_p_page
-VIS_LINES_PER_ABOOK
))/* ok, use it */
3449 as
.cur_row
= new_row
;
3452 * Will everything above fit and still leave ours on screen?
3454 new_row
= LINES_PER_ABOOK
* new_abook_num
+
3456 (as
.how_many_personals
> 0 || as
.config
))
3457 ? XTRA_LINES_BETWEEN
: 0) +
3458 ((as
.how_many_personals
== 0 && as
.config
)
3459 ? LINES_PER_ADD_LINE
: 0);
3460 new_row
= MAX(MIN(new_row
,
3461 as
.l_p_page
- VIS_LINES_PER_ABOOK
), 0);
3462 as
.cur_row
= new_row
;
3465 as
.top_ent
= 0L - as
.cur_row
;
3466 dprint((5, "addrbook added: %s\n",
3467 as
.adrbks
[new_abook_num
].filename
3468 ? as
.adrbks
[new_abook_num
].filename
: "?"));
3471 ps
->mangled_screen
= 1;
3478 /*---------- Change address book config -------------------*/
3481 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
3482 int abook_num
, old_cur_row
, global
;
3484 DL_CACHE_S dlc_restart
, *dlc
;
3485 char *serv
= NULL
, *folder
= NULL
, *p
, *q
;
3486 char *nick
= NULL
, *file
= NULL
;
3488 abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3489 global
= (abook_num
>= as
.how_many_personals
);
3490 old_cur_row
= as
.cur_row
;
3491 old_top_ent
= as
.top_ent
;
3492 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
3496 q
= ps_global
->VAR_GLOB_ADDRBOOK
[abook_num
-
3497 as
.how_many_personals
];
3499 q
= ps_global
->VAR_ADDRESSBOOK
[abook_num
];
3501 get_pair(q
, &nick
, &file
, 0, 0);
3504 fs_give((void **)&nick
);
3506 if(file
&& *file
== '{'){
3508 if((p
= strindex(file
, '}'))){
3514 q_status_message1(SM_ORDER
|SM_DING
, 0, 4,
3515 _("Missing \"}\" in config: %s"), q
);
3517 fs_give((void **)&nick
);
3519 fs_give((void **)&file
);
3527 if(ab_edit_abook(global
, abook_num
, serv
, folder
, nick
) >= 0){
3528 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3533 dlc_restart
.type
= DlcTitle
;
3534 warp_to_dlc(&dlc_restart
, old_top_ent
+(long)old_cur_row
);
3535 as
.cur_row
= old_cur_row
;
3536 as
.top_ent
= old_top_ent
;
3538 dprint((5, "addrbook config edited: %s\n",
3539 as
.adrbks
[dlc_restart
.adrbk_num
].filename
3540 ? as
.adrbks
[dlc_restart
.adrbk_num
].filename
: "?"));
3544 fs_give((void **)&nick
);
3546 fs_give((void **)&file
);
3548 ps
->mangled_screen
= 1;
3551 /* TRANSLATORS: the user tried to change the current line of the address
3552 book but the line could not be changed */
3553 q_status_message(SM_ORDER
, 0, 4, _("Not a changeable line"));
3558 /*---------- Delete an address book -------------------*/
3560 if(as
.n_addrbk
== 0){
3561 q_status_message(SM_ORDER
, 0, 4, _("Nothing to delete"));
3567 if(ab_del_abook(as
.top_ent
+as
.cur_row
, command_line
, &err
) >= 0){
3568 DL_CACHE_S dlc_restart
;
3569 int new_abook_num
, old_abook_num
, old_pers
, old_glob
, new_row
;
3572 * Careful, these are only ok because ab_del_abook didn't
3573 * mess with the as globals, like as.how_many_personals.
3574 * The addrbook_reset does reset them, of course.
3576 old_abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3577 old_pers
= as
.how_many_personals
;
3578 old_glob
= as
.n_addrbk
- as
.how_many_personals
;
3580 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3585 if(old_abook_num
>= as
.n_addrbk
) /* we deleted last addrbook */
3586 new_abook_num
= as
.n_addrbk
- 1;
3588 new_abook_num
= old_abook_num
;
3591 * Pick a line to highlight and center
3593 if(as
.how_many_personals
== 0 && old_pers
== 1)
3594 dlc_restart
.type
= DlcPersAdd
;
3595 else if((as
.n_addrbk
- as
.how_many_personals
) == 0 &&
3597 dlc_restart
.type
= DlcGlobAdd
;
3598 else if(as
.n_addrbk
== 0)
3599 dlc_restart
.type
= DlcPersAdd
;
3601 dlc_restart
.adrbk_num
= new_abook_num
;
3602 dlc_restart
.type
= DlcTitle
;
3605 warp_to_dlc(&dlc_restart
, 0L);
3608 * Will everything above fit and still leave ours on screen?
3610 if(dlc_restart
.type
== DlcTitle
)
3611 new_row
= LINES_PER_ABOOK
* new_abook_num
+
3612 (((as
.adrbks
[new_abook_num
].type
& GLOBAL
) &&
3613 (as
.how_many_personals
> 0 || as
.config
))
3614 ? XTRA_LINES_BETWEEN
: 0) +
3615 ((as
.how_many_personals
== 0 && as
.config
)
3616 ? LINES_PER_ADD_LINE
: 0);
3617 else if(dlc_restart
.type
== DlcGlobAdd
)
3618 new_row
= LINES_PER_ABOOK
* as
.n_addrbk
+
3619 ((as
.how_many_personals
> 0 || as
.config
)
3620 ? XTRA_LINES_BETWEEN
: 0) +
3621 ((as
.how_many_personals
== 0 && as
.config
)
3622 ? LINES_PER_ADD_LINE
: 0);
3626 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
3627 as
.cur_row
= new_row
;
3628 as
.top_ent
= 0L - as
.cur_row
;
3630 ps
->mangled_body
= 1;
3631 ps
->mangled_footer
= 1;
3632 /* TRANSLATORS: This is just comforting confirmation that the
3633 address book being deleted was successfully deleted */
3634 q_status_message(SM_ORDER
, 0, 3, _("Address book deleted"));
3638 q_status_message(SM_ORDER
, 0, 4, err
);
3639 dprint((5, "addrbook delete failed: %s\n",
3649 /*---- Reorder an addressbook list ---------*/
3651 if(entry_is_addkey(as
.top_ent
+as
.cur_row
)){
3652 q_status_message(SM_ORDER
, 0, 4,
3653 _("Highlight entry you wish to shuffle"));
3661 if((ret
= ab_shuffle(pab
, &slide
, command_line
, &msg
)) > 0){
3662 DL_CACHE_S dlc_restart
;
3665 new_anum
= ret
- 1; /* see ab_shuffle return value */
3667 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3672 /* put cursor on new_anum */
3673 dlc_restart
.adrbk_num
= new_anum
;
3674 dlc_restart
.type
= DlcTitle
;
3675 warp_to_dlc(&dlc_restart
, 0L);
3677 * Will everything above fit and still leave ours on screen?
3679 new_row
= LINES_PER_ABOOK
* new_anum
+
3680 (((as
.adrbks
[new_anum
].type
& GLOBAL
) &&
3681 (as
.how_many_personals
> 0 || as
.config
))
3682 ? XTRA_LINES_BETWEEN
: 0) +
3683 ((as
.how_many_personals
== 0 && as
.config
)
3684 ? LINES_PER_ADD_LINE
: 0);
3685 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
3686 as
.cur_row
= new_row
;
3687 as
.top_ent
= 0L - as
.cur_row
;
3689 ps
->mangled_body
= 1;
3692 DL_CACHE_S
*dlc_to_flush
;
3694 if(slide
< 0){ /* moved it up */
3695 as
.cur_row
+= slide
;
3696 start_disp
= MAX(as
.cur_row
- 1, 0);
3697 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3699 * If we backed off the top of the screen, just
3700 * inch the display up so we can see it.
3703 as
.top_ent
+= as
.cur_row
; /* cur_row is negative */
3708 else{ /* moved it down */
3709 start_disp
= as
.cur_row
;
3710 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3711 as
.cur_row
+= slide
;
3712 if(as
.cur_row
> as
.l_p_page
- VIS_LINES_PER_ABOOK
){
3713 as
.top_ent
+= (as
.cur_row
-
3714 (as
.l_p_page
- VIS_LINES_PER_ABOOK
));
3715 as
.cur_row
= MAX(as
.l_p_page
-VIS_LINES_PER_ABOOK
, 0);
3720 flush_dlc_from_cache(dlc_to_flush
);
3721 ps
->mangled_body
= 1;
3724 q_status_message(SM_ORDER
, 0, 3,
3726 (ret
< 0) ? _("Shuffle failed") :
3727 _("Address books shuffled"));
3729 dprint((5, "addrbook shuffle failed: %s\n",
3733 fs_give((void **)&msg
);
3740 /*----------------------- Move Up ---------------------*/
3743 r
= prev_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
);
3745 /* find first line so everything is displayed */
3746 new_top_ent
= as
.cur_row
+as
.top_ent
;
3747 for(dl
=dlist(new_top_ent
-1);
3748 dl
->type
!= Beginning
;
3749 dl
= dlist((--new_top_ent
) - 1))
3752 if(new_top_ent
== as
.top_ent
||
3753 (as
.cur_row
+ (as
.top_ent
-new_top_ent
) > as
.l_p_page
- 1)){
3754 q_status_message(SM_INFO
, 0, 1, _("Already on first line."));
3757 as
.cur_row
+= (as
.top_ent
- new_top_ent
);
3758 as
.top_ent
= new_top_ent
;
3760 ps
->mangled_body
= 1;
3766 i
= ((cmd
== MC_CHARUP
)
3767 && dlist(as
.top_ent
- 1L)->type
!= Beginning
)
3768 ? MIN(HS_MARGIN(ps
), as
.cur_row
) : 0;
3769 as
.cur_row
= new_line
- as
.top_ent
;
3770 if(as
.cur_row
- i
< 0){
3771 if(cmd
== MC_CHARUP
){
3772 /*-- Past top of page --*/
3773 as
.top_ent
+= (as
.cur_row
- i
);
3777 new_top_ent
= first_line(as
.top_ent
- as
.l_p_page
);
3778 as
.cur_row
+= (as
.top_ent
- new_top_ent
);
3779 as
.top_ent
= new_top_ent
;
3780 /* if it is still off screen */
3781 if(as
.cur_row
- i
< 0){
3782 as
.top_ent
+= (as
.cur_row
- i
);
3788 ps
->mangled_body
= 1;
3791 current_changed_flag
++;
3796 /*------------------- Move Down -------------------*/
3799 r
= next_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
);
3803 /* find last line so everything is displayed */
3804 new_end_line
= as
.cur_row
+as
.top_ent
;
3805 for(dl
=dlist(new_end_line
+1);
3807 dl
= dlist((++new_end_line
)+1))
3810 if(new_end_line
- as
.top_ent
<= as
.l_p_page
- 1 ||
3811 as
.cur_row
- (new_end_line
-as
.top_ent
-(as
.l_p_page
-1)) < 0){
3812 q_status_message(SM_INFO
, 0, 1, _("Already on last line."));
3815 as
.cur_row
-= (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
3816 as
.top_ent
+= (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
3818 ps
->mangled_body
= 1;
3824 /* adjust for scrolling margin */
3825 i
= (cmd
== MC_CHARDOWN
) ? HS_MARGIN(ps
) : 0;
3826 as
.cur_row
= new_line
- as
.top_ent
;
3827 if(as
.cur_row
>= as
.l_p_page
- i
){
3828 if(cmd
== MC_CHARDOWN
){
3829 /*-- Past bottom of page --*/
3830 as
.top_ent
+= (as
.cur_row
- (as
.l_p_page
- i
) + 1);
3831 as
.cur_row
= (as
.l_p_page
- i
) - 1;
3834 /*-- Changed pages --*/
3835 as
.top_ent
+= as
.l_p_page
;
3836 as
.cur_row
-= as
.l_p_page
;
3837 /* if it is still off screen */
3838 if(as
.cur_row
>= as
.l_p_page
- i
){
3839 as
.top_ent
+= (as
.cur_row
- (as
.l_p_page
- i
) + 1);
3840 as
.cur_row
= (as
.l_p_page
- i
) - 1;
3845 ps
->mangled_body
= 1;
3848 current_changed_flag
++;
3859 * Get the mouse down. Convert to content row number.
3860 * If the row is selectable, do the single or double click
3863 mouse_get_last(NULL
, &mp
);
3864 mp
.row
-= HEADER_ROWS(ps
);
3865 if(line_is_selectable(as
.top_ent
+ mp
.row
)){
3866 if(mp
.button
== M_BUTTON_LEFT
){
3869 * A mouse double click does the default action to
3870 * the selected line. Since we need to do a goto
3871 * to get there, we have this ugly bit of code.
3873 * On the first mouse click we go around the loop
3874 * once and set def_key appropriately for the
3875 * line as.top_ent+mp.row, which will then be
3876 * the same as as.top_ent+as.cur_row.
3878 switch(km
->keys
[def_key
].bind
.cmd
){
3880 if (as
.checkboxes
) goto togglex
;
3901 q_status_message(SM_INFO
, 0, 1,
3902 "Can't happen in MC_MOUSE");
3907 as
.cur_row
= mp
.row
;
3908 current_changed_flag
++;
3910 else if(mp
.button
== M_BUTTON_RIGHT
){
3914 as
.cur_row
= mp
.row
;
3915 current_changed_flag
++;
3919 need_redraw
= calculate_field_widths();
3921 /*---------- Update the current entry, (move or change) -------*/
3922 display_book(need_redraw
? 0 : as
.cur_row
,
3928 as
.old_cur_row
= as
.cur_row
;
3929 current_changed_flag
= 0;
3930 as
.cur
= cur_addr_book();
3931 pab
= (as
.n_addrbk
&&
3932 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
3933 ? &as
.adrbks
[as
.cur
] : NULL
;
3935 if(!mp
.doubleclick
){
3939 /* Make what they clicked "current" */
3940 memset(addr_pu
, 0, 20 * sizeof(MPopup
));
3942 addr_pu
[n
= 0].type
= tQueue
;
3943 addr_pu
[n
].data
.val
= km
->keys
[def_key
].bind
.ch
[0];
3944 addr_pu
[n
].label
.style
= lNormal
;
3945 addr_pu
[n
++].label
.string
= km
->keys
[def_key
].label
;
3947 if((i
= menu_clear_binding(km
, '<')) != MC_UNKNOWN
){
3948 menu_add_binding(km
, '<', i
);
3950 if((i
= menu_binding_index(km
, i
)) >= 0){
3951 addr_pu
[n
++].type
= tSeparator
;
3953 addr_pu
[n
].type
= tQueue
;
3954 addr_pu
[n
].data
.val
= km
->keys
[i
].bind
.ch
[0];
3955 addr_pu
[n
].label
.style
= lNormal
;
3956 addr_pu
[n
++].label
.string
= km
->keys
[i
].label
;
3960 addr_pu
[n
].type
= tTail
;
3962 mswin_popup(addr_pu
);
3972 /*------------- Page Up or Down --------*/
3975 if(cmd
== MC_PAGEUP
){
3976 /* find first line on prev page */
3977 new_top_ent
= first_line(as
.top_ent
- as
.l_p_page
);
3978 if(new_top_ent
== NO_LINE
)
3981 /* find first selectable line */
3982 fl
= first_selectable_line(new_top_ent
);
3984 /* If we didn't move, we'd better move now */
3985 if(fl
== as
.top_ent
+as
.cur_row
){
3986 if(!prev_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
)){
3989 /* find first line so everything is displayed */
3990 lineno
= as
.cur_row
+as
.top_ent
;
3991 for(dl
=dlist(lineno
);
3992 dl
->type
!= Beginning
;
3993 dl
= dlist(--lineno
))
3997 * If this new_top_ent is the same as the old_top_ent
3998 * we'll get the warning message.
4000 new_top_ent
= first_line(lineno
);
4001 if(fl
- new_top_ent
>= as
.l_p_page
)
4002 new_top_ent
+= (fl
- new_top_ent
- as
.l_p_page
+ 1);
4011 if(as
.top_ent
== new_top_ent
&& as
.cur_row
== (fl
-as
.top_ent
)){
4012 q_status_message(SM_INFO
, 0, 1, _("Already on first page."));
4016 if(as
.top_ent
== new_top_ent
)
4017 current_changed_flag
++;
4019 as
.top_ent
= new_top_ent
;
4022 /* find first selectable line on next page */
4023 fl
= first_selectable_line(as
.top_ent
+ as
.l_p_page
);
4027 /* if there is another page, scroll */
4028 if(fl
- as
.top_ent
>= as
.l_p_page
){
4029 new_top_ent
= as
.top_ent
+ as
.l_p_page
;
4031 /* on last page already */
4033 new_top_ent
= as
.top_ent
;
4034 if(as
.cur_row
== (fl
- as
.top_ent
)){ /* no change */
4037 /* find last line so everything is displayed */
4038 new_end_line
= as
.cur_row
+as
.top_ent
;
4039 for(dl
=dlist(new_end_line
+1);
4041 dl
= dlist((++new_end_line
)+1))
4044 if(new_end_line
- as
.top_ent
<= as
.l_p_page
- 1 ||
4046 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1)) < 0){
4047 q_status_message(SM_INFO
, 0, 1,
4048 _("Already on last page."));
4052 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
4054 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
4056 ps
->mangled_body
= 1;
4063 if(as
.top_ent
== new_top_ent
)
4064 current_changed_flag
++;
4066 as
.top_ent
= new_top_ent
;
4070 * Stuff in common for up or down.
4072 as
.cur_row
= fl
- as
.top_ent
;
4074 /* if it is still off screen */
4076 as
.top_ent
+= as
.cur_row
;
4079 else if(as
.cur_row
>= as
.l_p_page
){
4080 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4081 as
.cur_row
= as
.l_p_page
- 1;
4084 if(!current_changed_flag
){
4085 ps
->mangled_body
= 1;
4092 /*------------- Delete item from addrbook ---------*/
4094 if(adrbk_check_all_validity_now()){
4095 if(resync_screen(pab
, style
, checkedn
)){
4096 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4097 _("Address book changed. Delete cancelled. Try again."));
4098 ps
->mangled_screen
= 1;
4103 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4104 q_status_message(SM_ORDER
, 0, 4, _("No entries to delete"));
4108 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4109 clickable_warning(as
.top_ent
+as
.cur_row
);
4114 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
4119 empty_warning(as
.top_ent
+as
.cur_row
);
4124 did_delete
= single_entry_delete(pab
->address_book
,
4125 as
.cur_row
+as
.top_ent
,
4127 ps
->mangled_footer
= 1;
4130 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
4131 as
.cur_row
= 0L - as
.top_ent
;
4136 * In case the line we're now at is not a selectable
4139 new_line
= first_selectable_line(as
.cur_row
+as
.top_ent
);
4140 if(new_line
!= NO_LINE
4141 && new_line
!= as
.cur_row
+as
.top_ent
){
4142 as
.cur_row
= new_line
- as
.top_ent
;
4144 as
.top_ent
-= as
.l_p_page
;
4145 as
.cur_row
+= as
.l_p_page
;
4147 else if(as
.cur_row
>= as
.l_p_page
){
4148 as
.top_ent
+= as
.l_p_page
;
4149 as
.cur_row
-= as
.l_p_page
;
4153 start_disp
= MIN(as
.cur_row
, as
.old_cur_row
);
4156 ps
->mangled_body
= 1;
4162 /*------------- Toggle checkbox ---------*/
4165 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4166 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4170 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4171 clickable_warning(as
.top_ent
+as
.cur_row
);
4176 empty_warning(as
.top_ent
+as
.cur_row
);
4180 if(is_addr(as
.top_ent
+as
.cur_row
)){
4181 dl
= dlist(as
.top_ent
+as
.cur_row
);
4183 if(style
== SelectAddrLccCom
&& dl
->type
== ListEnt
)
4184 q_status_message(SM_ORDER
, 0, 4,
4185 _("You may only select whole lists for Lcc"));
4186 else if(style
== SelectAddrLccCom
&& dl
->type
!= ListHead
)
4187 q_status_message(SM_ORDER
, 0, 4,
4188 _("You may only select lists for Lcc, use Bcc for personal entries"));
4189 else if(dl
->type
== ListHead
|| dl
->type
== Simple
){
4190 current_changed_flag
++;
4191 if(entry_is_checked(pab
->address_book
->checks
,
4192 (a_c_arg_t
)dl
->elnum
)){
4193 entry_unset_checked(pab
->address_book
->checks
,
4194 (a_c_arg_t
)dl
->elnum
);
4200 entry_set_checked(pab
->address_book
->checks
,
4201 (a_c_arg_t
)dl
->elnum
);
4209 q_status_message(SM_ORDER
, 0, 4,
4210 _("You may not select list members, only whole lists or personal entries"));
4213 q_status_message(SM_ORDER
, 0, 4,
4214 _("You may only select addresses or lists"));
4219 /*------ Turn all checkboxes on ---------*/
4222 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4223 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4227 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4228 clickable_warning(as
.top_ent
+as
.cur_row
);
4233 empty_warning(as
.top_ent
+as
.cur_row
);
4238 adrbk_cntr_t num
, ab_count
;
4240 ab_count
= adrbk_count(pab
->address_book
);
4242 if(checkedn
){ /* unset All */
4243 for(num
= 0; num
< ab_count
; num
++){
4244 if(entry_is_checked(pab
->address_book
->checks
,
4246 entry_unset_checked(pab
->address_book
->checks
,
4253 for(num
= 0; num
< ab_count
; num
++){
4254 if(!entry_is_checked(pab
->address_book
->checks
,
4256 entry_set_checked(pab
->address_book
->checks
,
4263 ps
->mangled_body
= 1;
4270 /*---------- Turn on ListMode -----------*/
4273 for(i
= 0; i
< as
.n_addrbk
; i
++){
4274 pab
= &as
.adrbks
[i
];
4275 init_disp_form(pab
, ps
->VAR_ABOOK_FORMATS
, i
);
4278 (void)calculate_field_widths();
4279 ps
->mangled_footer
= 1;
4280 ps
->mangled_body
= 1;
4282 q_status_message(SM_ORDER
, 0, 4,
4283 _("Use \"X\" to select addresses or lists"));
4287 /*--------- Compose -----------*/
4289 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 0);
4293 /*--------- Alt Compose -----------*/
4295 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 1);
4300 /*------ Query Directory ------*/
4303 {char *err_mess
= NULL
, **err_mess_p
;
4307 q_status_message(SM_ORDER
, 0, 4,
4308 (style
== SelectAddrLccCom
)
4309 ? "Can't search server for Lcc"
4310 : "Can't search server from here");
4313 else if(as
.checkboxes
){
4314 q_status_message(SM_ORDER
, 0, 4,
4315 "Can't search server when using ListMode");
4320 err_mess_p
= error_message
;
4322 err_mess_p
= &err_mess
;
4325 * The query lines are indexed by their adrbk_num. (Just using
4326 * that because it was handy and not used for addrbooks.)
4328 query_type
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
4330 if((addr
= query_server(ps
, are_selecting
, &quit
, query_type
,
4331 err_mess_p
)) != NULL
){
4335 fs_give((void **)&addr
);
4338 if(err_mess_p
&& *err_mess_p
&& !error_message
){
4339 q_status_message(SM_ORDER
, 0, 4, *err_mess_p
);
4340 fs_give((void **)err_mess_p
);
4345 #endif /* ENABLE_LDAP */
4348 /*----------- Where is (search) ----------------*/
4351 new_top_ent
= ab_whereis(&warped
, command_line
);
4353 if(new_top_ent
!= NO_LINE
){
4354 if(warped
|| new_top_ent
!= as
.top_ent
){
4355 as
.top_ent
= new_top_ent
;
4357 ps
->mangled_body
= 1;
4360 current_changed_flag
++;
4363 ps
->mangled_footer
= 1;
4367 /*----- Select entries to work on --*/
4369 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4370 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4375 if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
4376 q_status_message(SM_ORDER
, 0, 4,
4377 _("Select is only available from within an expanded address book"));
4379 clickable_warning(as
.top_ent
+as
.cur_row
);
4384 dl
= dlist(as
.top_ent
+as
.cur_row
);
4385 if(dl
->type
== Empty
){
4386 empty_warning(as
.top_ent
+as
.cur_row
);
4390 {int were_selections
= as
.selections
;
4392 ab_select(ps
, pab
? pab
->address_book
: NULL
,
4393 as
.top_ent
+as
.cur_row
, command_line
, &start_disp
);
4395 if((!were_selections
&& as
.selections
)
4396 || (were_selections
&& !as
.selections
)){
4397 ps
->mangled_footer
= 1;
4398 for(i
= 0; i
< as
.n_addrbk
; i
++)
4399 init_disp_form(&as
.adrbks
[i
],
4400 ps
->VAR_ABOOK_FORMATS
, i
);
4407 /*----------- Select current entry ----------*/
4409 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4410 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4414 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4415 clickable_warning(as
.top_ent
+as
.cur_row
);
4420 empty_warning(as
.top_ent
+as
.cur_row
);
4424 if(is_addr(as
.top_ent
+as
.cur_row
)){
4425 dl
= dlist(as
.top_ent
+as
.cur_row
);
4427 if(dl
->type
== ListHead
|| dl
->type
== Simple
){
4428 int do_init_disp
= 0;
4431 current_changed_flag
++;
4433 if(entry_is_selected(pab
->address_book
->selects
,
4434 (a_c_arg_t
)dl
->elnum
)){
4435 DL_CACHE_S
*dlc
, dlc_restart
;
4438 if(as
.selections
== 0)
4441 entry_unset_selected(pab
->address_book
->selects
,
4442 (a_c_arg_t
)dl
->elnum
);
4445 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4447 flush_dlc_from_cache(dlc
);
4448 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4449 ps
->mangled_body
= 1;
4450 if(dlc
->type
== DlcEnd
){
4451 r
= prev_selectable_line(as
.cur_row
+
4455 as
.cur_row
= new_line
- as
.top_ent
;
4457 as
.top_ent
+= as
.cur_row
;
4462 start_disp
= as
.cur_row
;
4466 start_disp
= MAX(as
.cur_row
-1,0);
4471 q_status_message(SM_ORDER
, 0, 2,
4472 _("Zoom Mode is now off, no entries selected"));
4474 warp_to_dlc(&dlc_restart
, 0L);
4475 /* put current entry in middle of screen */
4477 first_line(0L - (long)as
.l_p_page
/2L);
4478 as
.cur_row
= 0L - as
.top_ent
;
4479 ps
->mangled_body
= 1;
4483 else if(F_OFF(F_UNSELECT_WONT_ADVANCE
,ps_global
)){
4485 r
= next_selectable_line(as
.cur_row
+as
.top_ent
,
4490 (dl
=dlist(ll
))->type
!= End
;
4492 if(dl
->type
== ListHead
|| dl
->type
== Simple
)
4498 as
.cur_row
= new_line
- as
.top_ent
;
4499 if(as
.cur_row
>= as
.l_p_page
){
4500 /*-- Changed pages --*/
4501 as
.top_ent
+= as
.l_p_page
;
4502 as
.cur_row
-= as
.l_p_page
;
4503 /* if it is still off screen */
4504 if(as
.cur_row
>= as
.l_p_page
){
4505 as
.top_ent
+= (as
.cur_row
-as
.l_p_page
+1);
4506 as
.cur_row
= (as
.l_p_page
- 1);
4510 ps
->mangled_body
= 1;
4516 if(as
.selections
== 0)
4521 entry_set_selected(pab
->address_book
->selects
,
4522 (a_c_arg_t
)dl
->elnum
);
4523 r
= next_selectable_line(as
.cur_row
+as
.top_ent
,
4528 (dl
=dlist(ll
))->type
!= End
;
4530 if(dl
->type
== ListHead
|| dl
->type
== Simple
)
4536 as
.cur_row
= new_line
- as
.top_ent
;
4537 if(as
.cur_row
>= as
.l_p_page
){
4538 /*-- Changed pages --*/
4539 as
.top_ent
+= as
.l_p_page
;
4540 as
.cur_row
-= as
.l_p_page
;
4541 /* if it is still off screen */
4542 if(as
.cur_row
>= as
.l_p_page
){
4543 as
.top_ent
+= (as
.cur_row
-as
.l_p_page
+1);
4544 as
.cur_row
= (as
.l_p_page
- 1);
4548 ps
->mangled_body
= 1;
4554 * If we switch from selected to non-selected or
4555 * vice versa, we have to init_disp_form() for all
4556 * the addrbooks, in case we're using the X instead
4560 ps
->mangled_footer
= 1;
4561 for(i
= 0; i
< as
.n_addrbk
; i
++)
4562 init_disp_form(&as
.adrbks
[i
],
4563 ps
->VAR_ABOOK_FORMATS
, i
);
4567 q_status_message(SM_ORDER
, 0, 4,
4568 _("You may not select list members, only whole lists or personal entries"));
4571 q_status_message(SM_ORDER
, 0, 4,
4572 _("You may only select addresses or lists"));
4577 /*--- Zoom in and look only at selected entries (or zoom out) --*/
4579 as
.zoomed
= (1 - as
.zoomed
);
4581 ab_zoom((pab
&& pab
->address_book
) ? pab
->address_book
->selects
4585 q_status_message(SM_ORDER
, 0, 2, _("Zoom Mode is now off"));
4586 ab_unzoom(&start_disp
);
4592 /*--- Apply a command -----------*/
4595 if(((ab_apply_cmd(ps
, pab
? pab
->address_book
: NULL
,
4596 as
.top_ent
+as
.cur_row
, command_line
) &&
4597 F_ON(F_AUTO_UNZOOM
, ps
)) || !as
.selections
) && as
.zoomed
){
4600 ps_global
->mangled_body
= 1;
4604 * In case the line we're now at is not a selectable
4607 * We set start_disp to zero here but rely on the called
4608 * routine to set mangled_body if appropriate.
4611 new_line
= first_selectable_line(as
.cur_row
+as
.top_ent
);
4612 if(new_line
!= NO_LINE
4613 && new_line
!= as
.cur_row
+as
.top_ent
){
4614 as
.cur_row
= new_line
- as
.top_ent
;
4616 as
.top_ent
+= as
.cur_row
;
4619 else if(as
.cur_row
>= as
.l_p_page
){
4620 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4621 as
.cur_row
= as
.l_p_page
- 1;
4626 q_status_message(SM_ORDER
, 0, 2,
4627 _("No selected entries to apply command to"));
4632 /*--------- QUIT pine -----------*/
4634 dprint((7, "Quitting pine from addrbook\n"));
4635 ps
->next_screen
= quit_screen
;
4639 /*--------- Top of Folder list -----------*/
4640 case MC_COLLECTIONS
:
4641 dprint((7, "Goto folder lister from addrbook\n"));
4642 ps
->next_screen
= folder_screen
;
4646 /*---------- Open specific new folder ----------*/
4648 dprint((7, "Goto from addrbook\n"));
4649 ab_goto_folder(command_line
);
4653 /*--------- Index -----------*/
4655 dprint((7, "Goto message index from addrbook\n"));
4657 && sp_viewing_a_thread(ps
->mail_stream
)
4658 && unview_thread(ps
, ps
->mail_stream
, ps
->msgmap
)){
4659 ps
->next_screen
= mail_index_screen
;
4660 ps
->view_skipped_index
= 0;
4661 ps
->mangled_screen
= 1;
4664 ps
->next_screen
= mail_index_screen
;
4668 /*----------------- Print --------------------*/
4671 ps
->mangled_screen
= 1;
4675 /*------ Copy entries into an abook ----*/
4677 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4678 q_status_message(SM_ORDER
, 0, 4, _("No entries to save"));
4682 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4683 clickable_warning(as
.top_ent
+as
.cur_row
);
4688 empty_warning(as
.top_ent
+as
.cur_row
);
4692 (void)ab_save(ps
, pab
? pab
->address_book
: NULL
,
4693 as
.top_ent
+as
.cur_row
, command_line
, 0);
4697 /*------ Forward an entry in mail -----------*/
4699 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4700 q_status_message(SM_ORDER
, 0, 4, _("No entries to forward"));
4704 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4705 clickable_warning(as
.top_ent
+as
.cur_row
);
4710 empty_warning(as
.top_ent
+as
.cur_row
);
4714 if(!is_addr(as
.top_ent
+as
.cur_row
)){
4715 q_status_message(SM_ORDER
, 0, 4, _("Nothing to forward"));
4719 dl
= dlist(as
.top_ent
+as
.cur_row
);
4720 if(dl
->type
!= ListHead
&& dl
->type
!= Simple
){
4721 q_status_message(SM_ORDER
, 0, 4,
4722 _("Can only forward whole entries"));
4726 (void)ab_forward(ps
, as
.top_ent
+as
.cur_row
, 0);
4727 ps
->mangled_footer
= 1;
4732 /* ^L attempts to resynchronize with changed addrbooks */
4733 if(adrbk_check_all_validity_now())
4734 (void)resync_screen(pab
, style
, checkedn
);
4739 mark_status_dirty();
4740 mark_titlebar_dirty();
4741 mark_keymenu_dirty();
4743 ps
->mangled_screen
= 1;
4751 bogus_utf8_command(utf8str
, F_ON(F_USE_FK
, ps
) ? "F1" : "?");
4755 /*------ Some back compatibility messages -----*/
4757 if(c
== 'e' && !are_selecting
){
4758 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4759 _("Command \"E\" not defined. Use \"View/Update\" to edit an entry"));
4763 && !(are_selecting
|| entry_is_clickable(as
.top_ent
+as
.cur_row
))){
4764 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4765 _("Command \"S\" not defined. Use \"AddNew\" to create a list"));
4768 else if(c
== 'z' && !are_selecting
){
4769 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4770 _("Command \"Z\" not defined. Use \"View/Update\" to add to a list"));
4773 /* else, fall through */
4776 bogus_command(c
, F_ON(F_USE_FK
, ps
) ? "F1" : "?");
4780 if(ps
->next_screen
!= SCREEN_FUN_NULL
)
4790 * Turn on zoom mode and zoom in if applicable.
4792 * Args selecteds -- tells which entries are selected in current abook
4793 * start_disp -- Passed in so we can set it back in the caller
4796 ab_zoom(EXPANDED_S
*selecteds
, int *start_disp
)
4799 DL_CACHE_S
*dlc
, dlc_restart
;
4804 q_status_message(SM_ORDER
, 0, 2, _("Zoom Mode is now on"));
4806 dl
= dlist(as
.top_ent
+as
.cur_row
);
4807 if((dl
->type
== ListHead
||
4808 dl
->type
== Simple
||
4809 dl
->type
== ListEmpty
||
4810 dl
->type
== ListClickHere
||
4811 dl
->type
== ListEnt
) &&
4812 entry_is_selected(selecteds
, (a_c_arg_t
)dl
->elnum
)){
4813 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4815 warp_to_dlc(&dlc_restart
, 0L);
4816 /* put current entry in middle of screen */
4817 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4818 as
.cur_row
= 0L - as
.top_ent
;
4823 warp_to_top_of_abook(as
.cur
);
4825 new_ent
= first_selectable_line(0L);
4826 if(new_ent
== NO_LINE
)
4829 as
.cur_row
= new_ent
;
4831 /* if it is off screen */
4832 if(as
.cur_row
>= as
.l_p_page
){
4833 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4834 as
.cur_row
= (as
.l_p_page
- 1);
4839 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4841 warp_to_dlc(&dlc_restart
, 0L);
4842 /* put current entry in middle of screen */
4843 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4844 as
.cur_row
= 0L - as
.top_ent
;
4847 ps_global
->mangled_body
= 1;
4852 q_status_message(SM_ORDER
, 0, 2, _("No selected entries to zoom on"));
4858 * Turn off zoom mode and zoom out if applicable.
4860 * Args start_disp -- Passed in so we can set it back in the caller
4863 ab_unzoom(int *start_disp
)
4865 DL_CACHE_S
*dlc
, dlc_restart
;
4868 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4869 if(dlc
->type
== DlcZoomEmpty
){
4872 warp_to_beginning();
4874 new_ent
= first_selectable_line(0L);
4875 if(new_ent
== NO_LINE
)
4878 as
.cur_row
= new_ent
;
4880 /* if it is off screen */
4881 if(as
.cur_row
>= as
.l_p_page
){
4882 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4883 as
.cur_row
= (as
.l_p_page
- 1);
4888 warp_to_dlc(&dlc_restart
, 0L);
4889 /* put current entry in middle of screen */
4890 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4891 as
.cur_row
= 0L - as
.top_ent
;
4894 ps_global
->mangled_body
= 1;
4901 * Post an empty addrbook warning.
4903 * Args: cur_line -- The current line position (in global display list)
4907 empty_warning(long int cur_line
)
4909 register AddrScrn_Disp
*dl
;
4911 dl
= dlist(cur_line
);
4912 if(dl
->type
== NoAbooks
)
4913 q_status_message(SM_ORDER
, 0, 4,
4914 _("No address books configured, use Setup"));
4915 else if(dl
->type
== Empty
)
4916 q_status_message(SM_ORDER
, 0, 4, _("Address Book is Empty"));
4918 q_status_message(SM_ORDER
, 0, 4, _("Distribution List is Empty"));
4923 * Tell user to click on this to expand.
4925 * Args: cur_line -- The current line position (in global display list)
4929 clickable_warning(long int cur_line
)
4931 register AddrScrn_Disp
*dl
;
4933 dl
= dlist(cur_line
);
4934 if(dl
->type
== Title
|| dl
->type
== ClickHereCmb
)
4935 q_status_message(SM_ORDER
, 0, 4, _("Address Book not expanded, use \">\" to expand"));
4937 q_status_message(SM_ORDER
, 0, 4, _("Distribution List not expanded, use \">\" to expand"));
4942 * Post a no tabs warning.
4945 no_tabs_warning(void)
4947 q_status_message(SM_ORDER
, 0, 4, "Tabs not allowed in address book");
4952 * Prompt for command to apply to selected entries
4954 * Returns: 1 if the entries are successfully commanded.
4958 ab_apply_cmd(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
)
4961 static ESCKEY_S opts
[] = {
4962 {'c', 'c', "C", "ComposeTo"},
4963 {'d', 'd', "D", "Delete"},
4964 {'%', '%', "%", "Print"},
4965 {'f', 'f', "F", "Forward"},
4966 {'s', 's', "S", "Save"},
4967 {'#', '#', "#", "Role"},
4969 {-1, 0, NULL
, NULL
}};
4970 #define PHANTOM_PRINT 6
4972 dprint((7, "- ab_apply_cmd -\n"));
4975 opts
[PHANTOM_PRINT
].ch
= (F_ON(F_ENABLE_PRYNT
, ps_global
)) ? 'y' : -1;
4977 switch(radio_buttons("APPLY command : ", command_line
, opts
, 'z', 'x',
4980 ret
= ab_compose_to_addr(cur_line
, 1, 0);
4984 ret
= ab_compose_to_addr(cur_line
, 1, 1);
4988 ret
= ab_agg_delete(ps
, 1);
4996 ret
= ab_forward(ps
, cur_line
, 1);
5000 ret
= ab_save(ps
, abook
, cur_line
, command_line
, 1);
5004 cmd_cancelled("Apply command");
5008 q_status_message(SM_INFO
, 0, 2,
5009 "Cancelled, there is no default command");
5013 ps_global
->mangled_footer
= 1;
5020 * Allow user to mark some entries "selected".
5023 ab_select(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
, int *start_disp
)
5025 static ESCKEY_S sel_opts1
[] = {
5026 {'a', 'a', "A", "unselect All"},
5027 { 0 , 'c', "C", NULL
},
5028 {'b', 'b', "B", "Broaden selctn"},
5029 {'n', 'n', "N", "Narrow selctn"},
5030 {'f', 'f', "F", "Flip selected"},
5033 static char *sel_pmt1
= "ALTER selection : ";
5034 static ESCKEY_S sel_opts2
[] = {
5035 {'a', 'a', "A", "select All"},
5036 {'c', 'c', "C", "select Cur"},
5037 {'t', 't', "T", "Text"},
5038 {'s', 's', "S", "Status"},
5041 static char *sel_pmt2
= "SELECT criteria : ";
5043 HelpType help
= NO_HELP
;
5044 adrbk_cntr_t num
, ab_count
;
5045 int q
= 0, rv
= 0, narrow
= 0,
5046 do_flush
= 0, do_warp
= 0, prevsel
,
5047 move_current
= 0, do_beginning
= 0;
5050 DL_CACHE_S
*dlc
, dlc_restart
;
5052 dprint((5, "- ab_select -\n"));
5054 if(cur_is_open()){ /* select applies only to this addrbook */
5056 ps
->mangled_footer
= 1;
5057 prevsel
= as
.selections
;
5058 dl
= dlist(cur_line
);
5059 sel_opts
= sel_opts2
;
5062 * If already some selected, ask how to alter that selection.
5065 sel_opts
+= 2; /* don't offer all or current below */
5066 if(dl
&& (dl
->type
== ListHead
|| dl
->type
== Simple
)){
5067 sel_opts1
[1].label
= entry_is_selected(abook
->selects
,
5068 (a_c_arg_t
)dl
->elnum
)
5071 sel_opts1
[1].ch
= 'c';
5074 sel_opts1
[1].ch
= -2; /* don't offer this choice */
5076 switch(q
= radio_buttons(sel_pmt1
, command_line
, sel_opts1
,
5077 'a', 'x', help
, RB_NORM
)){
5078 case 'n': /* narrow selection */
5080 case 'b': /* broaden selection */
5081 q
= 0; /* but don't offer criteria prompt */
5084 case 'c': /* select or unselect current */
5085 case 'a': /* select or unselect all */
5086 case 'f': /* flip selections */
5087 case 'x': /* cancel */
5091 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5092 "Unsupported Select option");
5098 (dl
->type
== ListHead
|| dl
->type
== Simple
)){
5099 sel_opts1
[1].label
= entry_is_selected(abook
->selects
,
5100 (a_c_arg_t
)dl
->elnum
)
5103 sel_opts1
[1].ch
= 'c';
5106 sel_opts1
[1].ch
= -2; /* don't offer this choice */
5109 q
= radio_buttons(sel_pmt2
, command_line
, sel_opts
,
5110 'c', 'x', help
, RB_NORM
);
5113 dlc
= get_dlc(cur_line
);
5115 ab_count
= adrbk_count(abook
);
5118 case 'x': /* cancel */
5119 cmd_cancelled("Select command");
5122 case 'c': /* select/unselect current */
5123 ps
->mangled_body
= 1;
5124 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
)){
5125 entry_unset_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
);
5128 if(as
.selections
== 0 && as
.zoomed
){
5130 q_status_message(SM_ORDER
, 0, 2,
5131 "Zoom Mode is now off, no entries selected");
5142 entry_set_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
);
5145 if(as
.selections
== 1 && !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5147 as
.top_ent
= dlc_restart
.global_row
;
5158 ps
->mangled_body
= 1;
5159 if(any_selected(abook
->selects
)){ /* unselect all */
5160 for(num
= 0; num
< ab_count
; num
++){
5161 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5163 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5167 if(as
.selections
== 0 && as
.zoomed
){
5169 q_status_message(SM_ORDER
, 0, 2,
5170 "Zoom Mode is now off, all entries UNselected");
5176 snprintf(bb
, sizeof(bb
), "%s entries UNselected%s%s%s",
5177 comatose(prevsel
-as
.selections
),
5178 as
.selections
? ", still " : "",
5179 as
.selections
? comatose(as
.selections
) : "",
5180 as
.selections
? " selected in other addrbooks" : "");
5181 bb
[sizeof(bb
)-1] = '\0';
5182 q_status_message(SM_ORDER
, 0, 2, bb
);
5189 else{ /* select all */
5190 for(num
= 0; num
< ab_count
; num
++){
5191 if(!entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5193 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5197 q_status_message1(SM_ORDER
, 0, 2, "All %s entries selected",
5198 comatose(ab_count
));
5199 if(prevsel
== 0 && as
.selections
> 0 &&
5200 !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5202 as
.top_ent
= dlc_restart
.global_row
- as
.cur_row
;
5205 else if(dlc_restart
.type
== DlcZoomEmpty
&&
5206 as
.selections
> prevsel
)
5216 case 'f': /* flip selections in this abook */
5217 ps
->mangled_body
= 1;
5218 for(num
= 0; num
< ab_count
; num
++){
5219 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5220 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5224 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5234 q_status_message(SM_ORDER
, 0, 2, "Zoom Mode is now off");
5241 q_status_message1(SM_ORDER
, 0, 2, "%s entries now selected",
5242 comatose(as
.selections
));
5250 rv
= ab_select_text(abook
, narrow
);
5254 rv
= ab_select_type(abook
, narrow
);
5259 ps
->mangled_body
= 1;
5260 if(dlc_restart
.type
== DlcZoomEmpty
&&
5261 as
.selections
> prevsel
)
5264 if(as
.selections
== 0){
5266 q_status_message(SM_ORDER
, 0, 2,
5267 "Zoom Mode is now off");
5274 if(prevsel
== 0 && as
.selections
> 0 &&
5275 !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5283 if(prevsel
== as
.selections
&& prevsel
> 0){
5284 if(as
.selections
== 1)
5285 q_status_message(SM_ORDER
, 0, 2,
5286 "No change resulted, 1 entry remains selected");
5288 q_status_message1(SM_ORDER
, 0, 2,
5289 "No change resulted, %s entries remain selected",
5290 comatose(as
.selections
));
5292 else if(prevsel
== 0){
5293 if(as
.selections
== 1)
5294 q_status_message(SM_ORDER
, 0, 2,
5295 "Select matched 1 entry");
5296 else if(as
.selections
> 1)
5297 q_status_message1(SM_ORDER
, 0, 2,
5298 "Select matched %s entries",
5299 comatose(as
.selections
));
5301 q_status_message(SM_ORDER
, 0, 2,
5302 "Select failed! No entries selected");
5304 else if(as
.selections
== 0){
5306 q_status_message(SM_ORDER
, 0, 2,
5307 "The single selected entry is UNselected");
5309 q_status_message1(SM_ORDER
, 0, 2,
5310 "All %s entries UNselected",
5314 if(as
.selections
== 1 && (prevsel
-as
.selections
) == 1)
5315 q_status_message(SM_ORDER
, 0, 2,
5316 "1 entry now selected, 1 entry was UNselected");
5317 else if(as
.selections
== 1)
5318 q_status_message1(SM_ORDER
, 0, 2,
5319 "1 entry now selected, %s entries were UNselected",
5320 comatose(prevsel
-as
.selections
));
5321 else if((prevsel
-as
.selections
) == 1)
5322 q_status_message1(SM_ORDER
, 0, 2,
5323 "%s entries now selected, 1 entry was UNselected",
5324 comatose(as
.selections
));
5326 q_status_message2(SM_ORDER
, 0, 2,
5327 "%s entries now selected, %s entries were UNselected",
5328 comatose(as
.selections
),
5329 comatose(prevsel
-as
.selections
));
5332 if((as
.selections
-prevsel
) == 1)
5333 q_status_message1(SM_ORDER
, 0, 2,
5334 "1 new entry selected, %s entries now selected",
5335 comatose(as
.selections
));
5336 else if(as
.selections
== 1)
5337 q_status_message1(SM_ORDER
, 0, 2,
5338 "%s new entries selected, 1 entry now selected",
5339 comatose(as
.selections
-prevsel
));
5341 q_status_message2(SM_ORDER
, 0, 2,
5342 "%s new entries selected, %s entries now selected",
5343 comatose(as
.selections
-prevsel
),
5344 comatose(as
.selections
));
5351 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5352 "Unsupported Select option");
5357 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
))
5358 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5359 "Select is only available from within an expanded address book");
5361 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5362 "Select is only available when viewing an individual address book");
5371 warp_to_beginning(); /* just go to top */
5373 new_ent
= first_selectable_line(0L);
5374 if(new_ent
== NO_LINE
)
5377 as
.cur_row
= new_ent
;
5379 /* if it is off screen */
5380 if(as
.cur_row
>= as
.l_p_page
){
5381 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
5382 as
.cur_row
= (as
.l_p_page
- 1);
5386 flush_dlc_from_cache(&dlc_restart
);
5388 warp_to_dlc(&dlc_restart
, dlc_restart
.global_row
);
5391 dlc
= get_dlc(cur_line
);
5392 if(dlc
->type
== DlcEnd
){
5395 as
.top_ent
+= as
.cur_row
; /* plus a negative number */
5400 *start_disp
= as
.cur_row
;
5404 *start_disp
= as
.cur_row
;
5410 * Selects based on whether an entry is a list or not.
5412 * Returns: 0 if search went ok
5413 * -1 if there was a problem
5416 ab_select_type(AdrBk
*abook
, int narrow
)
5418 static ESCKEY_S ab_sel_type_opt
[] = {
5419 {'s', 's', "S", "Simple"},
5420 {'l', 'l', "L", "List"},
5423 static char *ab_sel_type
= "Select Lists or Simples (non Lists) ? ";
5425 adrbk_cntr_t num
, ab_count
;
5427 dprint((6, "- ab_select_type -\n"));
5432 switch(type
= radio_buttons(ab_sel_type
, -FOOTER_ROWS(ps_global
),
5433 ab_sel_type_opt
, 'l', 'x', NO_HELP
, RB_NORM
)){
5441 cmd_cancelled("Select");
5445 dprint((1,"\n - BOTCH: ab_select_type unknown option\n"));
5449 ab_count
= adrbk_count(abook
);
5450 for(num
= 0; num
< ab_count
; num
++){
5455 * If it won't possibly change state, don't look at it.
5457 if((narrow
&& !entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)) ||
5458 (!narrow
&& entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)))
5461 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) num
);
5462 matched
= ((type
== 's' && abe
->tag
== Single
) ||
5463 (type
== 'l' && abe
->tag
== List
));
5465 if(narrow
&& !matched
){
5466 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5469 else if(!narrow
&& matched
){
5470 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5480 * Selects based on string matches in various addrbook fields.
5482 * Returns: 0 if search went ok
5483 * -1 if there was a problem
5486 ab_select_text(AdrBk
*abook
, int narrow
)
5488 static ESCKEY_S ab_sel_text_opt
[] = {
5489 {'n', 'n', "N", "Nickname"},
5490 {'a', 'a', "A", "All Text"},
5491 {'f', 'f', "F", "Fullname"},
5492 {'e', 'e', "E", "Email Addrs"},
5493 {'c', 'c', "C", "Comment"},
5494 {'z', 'z', "Z", "Fcc"},
5497 static char *ab_sel_text
=
5498 "Select based on Nickname, All text, Fullname, Addrs, Comment, or Fcc ? ";
5499 HelpType help
= NO_HELP
;
5501 char sstring
[80+1], prompt
[80];
5502 adrbk_cntr_t num
, ab_count
;
5503 char *fmt
= "String in \"%s\" to match : ";
5505 dprint((6, "- ab_select_text -\n"));
5510 switch(type
= radio_buttons(ab_sel_text
, -FOOTER_ROWS(ps_global
),
5511 ab_sel_text_opt
, 'a', 'x', NO_HELP
, RB_NORM
)){
5513 snprintf(prompt
, sizeof(prompt
), fmt
, "Nickname");
5516 snprintf(prompt
, sizeof(prompt
), fmt
, "All Text");
5519 snprintf(prompt
, sizeof(prompt
), fmt
, "Fullname");
5522 snprintf(prompt
, sizeof(prompt
), fmt
, "addresses");
5525 snprintf(prompt
, sizeof(prompt
), fmt
, "Comment");
5528 snprintf(prompt
, sizeof(prompt
), fmt
, "Fcc");
5533 dprint((1,"\n - BOTCH: ab_select_text unknown option\n"));
5537 prompt
[sizeof(prompt
)-1] = '\0';
5541 int flags
= OE_APPEND_CURRENT
;
5543 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
5544 sizeof(sstring
), prompt
, NULL
, help
, &flags
);
5546 case 3: /* BUG, no help */
5554 if(r
== 1 || sstring
[0] == '\0')
5560 if(type
== 'x' || r
== 'x'){
5561 cmd_cancelled("Select");
5565 ab_count
= adrbk_count(abook
);
5566 for(num
= 0; num
< ab_count
; num
++){
5571 * If it won't possibly change state, don't look at it.
5573 if((narrow
&& !entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)) ||
5574 (!narrow
&& entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)))
5577 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) num
);
5578 matched
= match_check(abe
, type
, sstring
);
5580 if(narrow
&& !matched
){
5581 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5584 else if(!narrow
&& matched
){
5585 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5595 * Returns: 1 if a match is found for the entry
5600 match_check(AdrBk_Entry
*abe
, int type
, char *string
)
5602 static int err
= -1;
5603 static int match
= 1;
5604 static int nomatch
= 0;
5605 unsigned int checks
;
5606 #define CK_NICKNAME 0x01
5607 #define CK_FULLNAME 0x02
5608 #define CK_ADDRESSES 0x04
5610 #define CK_COMMENT 0x10
5616 case 'n': /* Nickname */
5617 checks
|= CK_NICKNAME
;
5620 case 'f': /* Fullname */
5621 checks
|= CK_FULLNAME
;
5624 case 'e': /* Addrs */
5625 checks
|= CK_ADDRESSES
;
5628 case 'a': /* All Text */
5636 case 'c': /* Comment */
5637 checks
|= CK_COMMENT
;
5641 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Unknown type");
5645 if(checks
& CK_NICKNAME
){
5646 if(abe
&& abe
->nickname
&& srchstr(abe
->nickname
, string
))
5650 if(checks
& CK_FULLNAME
){
5654 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5655 SIZEOF_20KBUF
, abe
->fullname
),
5660 if(checks
& CK_ADDRESSES
){
5662 abe
->tag
== Single
&&
5664 abe
->addr
.addr
[0] &&
5665 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5666 SIZEOF_20KBUF
, abe
->addr
.addr
),
5674 for(p
= abe
->addr
.list
; p
!= NULL
&& *p
!= NULL
; p
++){
5675 if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5683 if(checks
& CK_FCC
){
5687 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5688 SIZEOF_20KBUF
, abe
->fcc
),
5693 if(checks
& CK_COMMENT
&& abe
&& abe
->extra
&& abe
->extra
[0]){
5695 unsigned char *p
, *tmp
= NULL
;
5698 if((n
= 4*strlen(abe
->extra
)) > SIZEOF_20KBUF
-1){
5700 p
= tmp
= (unsigned char *)fs_get(len
* sizeof(char));
5703 len
= SIZEOF_20KBUF
;
5704 p
= (unsigned char *)tmp_20k_buf
;
5707 if(srchstr((char *)rfc1522_decode_to_utf8(p
, len
, abe
->extra
), string
))
5711 fs_give((void **)&tmp
);
5724 * command_line -- The screen line on which to prompt
5727 ab_goto_folder(int command_line
)
5731 int notrealinbox
= 0;
5733 dprint((2, "- ab_goto_folder -\n"));
5735 tc
= ps_global
->context_current
;
5737 go_folder
= broach_folder(command_line
, 1, ¬realinbox
, &tc
);
5739 if(go_folder
!= NULL
)
5740 visit_folder(ps_global
, go_folder
, tc
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
);
5745 * Execute whereis command.
5747 * Returns value of the new top entry, or NO_LINE if cancelled.
5750 ab_whereis(int *warped
, int command_line
)
5752 int rc
, wrapped
= 0;
5753 long new_top_ent
, new_line
;
5755 dprint((5, "- ab_whereis -\n"));
5757 rc
= search_book(as
.top_ent
+as
.cur_row
, command_line
,
5758 &new_line
, &wrapped
, warped
);
5760 new_top_ent
= NO_LINE
;
5763 q_status_message(SM_INFO
, 0, 2, _("Address book search cancelled"));
5766 q_status_message(SM_ORDER
, 0, 4, _("Word not found"));
5768 else if(rc
== 0){ /* search succeeded */
5771 q_status_message(SM_INFO
, 0, 2, _("Search wrapped to beginning"));
5772 else if(wrapped
== 2)
5773 q_status_message(SM_INFO
, 0, 2,
5774 _("Current line contains the only match"));
5776 /* know match is on the same page */
5778 new_line
>= as
.top_ent
&&
5779 new_line
< as
.top_ent
+as
.l_p_page
)
5780 new_top_ent
= as
.top_ent
;
5781 /* don't know whether it is or not, reset top_ent */
5783 new_top_ent
= first_line(new_line
- as
.l_p_page
/2);
5785 as
.cur_row
= new_line
- new_top_ent
;
5788 return(new_top_ent
);
5793 * recalculate display parameters for window size change
5800 DL_CACHE_S dlc_buf
, *dlc_restart
;
5802 old_l_p_p
= as
.l_p_page
;
5803 as
.l_p_page
= ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)
5804 - HEADER_ROWS(ps_global
);
5806 dprint((9, "- ab_resize -\n l_p_p was %d, now %d\n",
5807 old_l_p_p
, as
.l_p_page
));
5809 if(as
.l_p_page
<= 0){
5814 as
.no_op_possbl
= 0;
5816 new_line
= as
.top_ent
+ as
.cur_row
;
5817 as
.top_ent
= first_line(new_line
- as
.l_p_page
/2);
5818 as
.cur_row
= new_line
- as
.top_ent
;
5819 as
.old_cur_row
= as
.cur_row
;
5821 /* need this to re-initialize Text and Title lines in display */
5822 /* get the old current line (which may be the wrong width) */
5823 dlc_restart
= get_dlc(new_line
);
5824 /* flush it from cache */
5825 flush_dlc_from_cache(dlc_restart
);
5826 /* re-get it (should be right now) */
5827 dlc_restart
= get_dlc(new_line
);
5828 /* copy it to local storage */
5829 dlc_buf
= *dlc_restart
;
5830 dlc_restart
= &dlc_buf
;
5831 /* flush everything from cache and add that one line back in */
5832 warp_to_dlc(dlc_restart
, new_line
);
5837 * Returns 0 if we know for sure that there are no
5838 * addresses available in any of the addressbooks.
5840 * Easiest would be to start at 0 and go through the addrbook, but that will
5841 * be very slow for big addrbooks if we're not close to 0 already. Instead,
5842 * starting_hint is a hint at a good place to start looking.
5845 any_addrs_avail(long int starting_hint
)
5847 register AddrScrn_Disp
*dl
;
5851 * Look from lineno backwards first, in hopes of finding it in cache.
5853 lineno
= starting_hint
;
5854 for(dl
=dlist(lineno
);
5855 dl
->type
!= Beginning
;
5856 dl
= dlist(--lineno
)){
5857 if(dl
->type
== NoAbooks
)
5874 /* search from here forward if we still don't know */
5875 lineno
= starting_hint
;
5876 for(dl
=dlist(lineno
);
5878 dl
= dlist(++lineno
)){
5879 if(dl
->type
== NoAbooks
)
5901 * Returns 1 if this line is a clickable line.
5904 entry_is_clickable(long int lineno
)
5906 register AddrScrn_Disp
*dl
;
5908 if((dl
= dlist(lineno
)) &&
5909 (dl
->type
== Title
|| dl
->type
== ListClickHere
||
5910 dl
->type
== ClickHereCmb
))
5918 * Returns 1 if this line is a clickable Title line.
5921 entry_is_clickable_title(long int lineno
)
5923 register AddrScrn_Disp
*dl
;
5925 if((dl
= dlist(lineno
)) && (dl
->type
== Title
|| dl
->type
== ClickHereCmb
))
5933 * Returns 1 if an address or list is selected.
5936 is_addr(long int lineno
)
5938 register AddrScrn_Disp
*dl
;
5940 if((dl
= dlist(lineno
)) && (dl
->type
== ListHead
||
5941 dl
->type
== ListEnt
||
5942 dl
->type
== Simple
))
5950 * Returns 1 if type of line is Empty.
5953 is_empty(long int lineno
)
5955 register AddrScrn_Disp
*dl
;
5957 if((dl
= dlist(lineno
)) &&
5958 (dl
->type
== Empty
|| dl
->type
== ListEmpty
|| dl
->type
== ZoomEmpty
))
5966 * Returns 1 if lineno is a list entry
5969 entry_is_listent(long int lineno
)
5971 register AddrScrn_Disp
*dl
;
5973 if((dl
= dlist(lineno
)) && dl
->type
== ListEnt
)
5981 * Returns 1 if lineno is a fake addrbook for config screen add
5982 * or if it is an AskServer line for LDAP query
5985 entry_is_addkey(long int lineno
)
5987 register AddrScrn_Disp
*dl
;
5989 if((dl
= dlist(lineno
)) && (dl
->type
== AddFirstPers
||
5990 dl
->type
== AddFirstGlob
||
5991 dl
->type
== AskServer
))
5999 * Returns 1 if lineno is the line to ask for directory server query
6002 entry_is_askserver(long int lineno
)
6004 register AddrScrn_Disp
*dl
;
6006 if((dl
= dlist(lineno
)) && dl
->type
== AskServer
)
6014 * Returns 1 if an add abook here would be global, not personal
6017 add_is_global(long int lineno
)
6019 register AddrScrn_Disp
*dl
;
6024 if(dl
->type
== Title
){
6025 register PerAddrBook
*pab
;
6027 pab
= &as
.adrbks
[as
.cur
];
6028 if(pab
&& pab
->type
& GLOBAL
)
6031 else if(dl
->type
== AddFirstGlob
)
6040 * Find the first line greater than or equal to line. (Any line, not
6041 * necessarily selectable.)
6043 * Returns the line number of the found line or NO_LINE if there is none.
6045 * Warning: This just starts at the passed in line and goes forward until
6046 * it runs into a line that isn't a Beginning line. If the line passed in
6047 * is not in the dlc cache, it will have no way to know when it gets to the
6051 first_line(long int line
)
6054 register PerAddrBook
*pab
;
6058 dlist(lineno
)->type
== Beginning
;
6062 if(dlist(lineno
)->type
!= End
)
6065 for(i
= 0; i
< as
.n_addrbk
; i
++){
6066 pab
= &as
.adrbks
[i
];
6067 if(pab
->ostatus
!= Open
&&
6068 pab
->ostatus
!= HalfOpen
&&
6069 pab
->ostatus
!= ThreeQuartOpen
)
6080 * Find the line number of the next selectable line.
6082 * Args: cur_line -- The current line position (in global display list)
6084 * new_line -- Return value: new line position
6086 * Result: The new line number is set.
6087 * The value 1 is returned if OK or 0 if there is no next line.
6090 next_selectable_line(long int cur_line
, long int *new_line
)
6092 /* skip over non-selectable lines */
6094 !line_is_selectable(cur_line
) && dlist(cur_line
)->type
!= End
;
6098 if(dlist(cur_line
)->type
== End
)
6101 *new_line
= cur_line
;
6107 * Find the line number of the previous selectable line.
6109 * Args: cur_line -- The current line position (in global display list)
6111 * new_line -- Return value: new line position
6113 * Result: The new line number is set.
6114 * The value 1 is returned if OK or 0 if there is no previous line.
6117 prev_selectable_line(long int cur_line
, long int *new_line
)
6119 /* skip backwards over non-selectable lines */
6121 !line_is_selectable(cur_line
) && dlist(cur_line
)->type
!= Beginning
;
6125 if(dlist(cur_line
)->type
== Beginning
)
6128 *new_line
= cur_line
;
6135 * Resync the display with the addrbooks, which were just discovered
6136 * to be out of sync with the display.
6138 * Returns 1 -- current address book had to be resynced
6139 * 0 -- current address book not resynced
6142 resync_screen(PerAddrBook
*pab
, AddrBookArg style
, int checkedn
)
6145 int current_resynced
= 0;
6146 DL_CACHE_S dlc_restart
, *dlc
= NULL
;
6149 * The test below gives conditions under which it is safe to go ahead
6150 * and resync all the addrbooks that are out of sync now, and the
6151 * display won't change. Otherwise, we have to be careful to preserve
6152 * some of our state so that we can attempt to restore the screen to
6153 * a state that is as close as possible to what we have now. If the
6154 * currently opened address book (pab) is out of date we will lose
6155 * the expanded state of its distribution lists, which is no big deal.
6156 * Since resyncing also loses the checked status if we're selecting with
6157 * ListMode, we don't even attempt it in that case.
6159 if((ab_nesting_level
< 2 && !cur_is_open() && checkedn
== 0) ||
6160 (style
== AddrBookScreen
&&
6162 pab
->address_book
&&
6163 !(pab
->address_book
->flags
& FILE_OUTOFDATE
||
6164 (pab
->address_book
->rd
&&
6165 pab
->address_book
->rd
->flags
& REM_OUTOFDATE
)))){
6167 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
6168 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
6172 if(adrbk_check_and_fix_all(1, 0, 1)){
6173 ps_global
->mangled_footer
= 1; /* why? */
6174 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
6175 warp_to_dlc(&dlc_restart
, 0L);
6176 /* put current entry in middle of screen */
6177 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6178 as
.cur_row
= 0L - as
.top_ent
;
6179 ps_global
->mangled_screen
= 1;
6183 else if(style
== AddrBookScreen
){
6184 char *savenick
= NULL
;
6186 adrbk_cntr_t old_entry_num
, new_entry_num
;
6187 long old_global_row
;
6192 * We're going to try to get the nickname of the current
6193 * entry and find it again after the resync.
6195 dl
= dlist(as
.top_ent
+as
.cur_row
);
6196 if(dl
->type
== ListEnt
||
6197 dl
->type
== ListEmpty
||
6198 dl
->type
== ListClickHere
||
6199 dl
->type
== ListHead
||
6200 dl
->type
== Simple
){
6201 abe
= ae(as
.top_ent
+as
.cur_row
);
6202 old_entry_num
= dl
->elnum
;
6203 old_global_row
= as
.top_ent
+as
.cur_row
;
6204 if(abe
&& abe
->nickname
&& abe
->nickname
[0])
6205 savenick
= cpystr(abe
->nickname
);
6208 /* this will close and re-open current addrbook */
6209 (void)adrbk_check_and_fix_all(1, 0, 1);
6213 abe
= adrbk_lookup_by_nick(pab
->address_book
, savenick
,
6215 fs_give((void **)&savenick
);
6218 if(abe
){ /* If we found the same nickname, move to it */
6219 dlc_restart
.adrbk_num
= as
.cur
;
6220 dlc_restart
.dlcelnum
= new_entry_num
;
6221 dlc_restart
.type
= (abe
->tag
== Single
)
6222 ? DlcSimple
: DlcListHead
;
6223 if(old_entry_num
== new_entry_num
)
6224 warp_to_dlc(&dlc_restart
, old_global_row
);
6226 warp_to_dlc(&dlc_restart
, 0L);
6229 warp_to_top_of_abook(as
.cur
);
6231 if(!abe
|| old_entry_num
!= new_entry_num
){
6232 /* put current entry in middle of screen */
6233 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6234 as
.cur_row
= 0L - as
.top_ent
;
6238 return(current_resynced
);
6243 * Erase all the check marks.
6251 for(i
= 0; i
< as
.n_addrbk
; i
++){
6252 pab
= &as
.adrbks
[i
];
6253 if(pab
->address_book
&& pab
->address_book
->checks
)
6254 exp_free(pab
->address_book
->checks
);
6256 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6262 * Erase all the selections.
6265 erase_selections(void)
6270 for(i
= 0; i
< as
.n_addrbk
; i
++){
6271 pab
= &as
.adrbks
[i
];
6272 if(pab
->address_book
&& pab
->address_book
->selects
)
6273 exp_free(pab
->address_book
->selects
);
6276 for(i
= 0; i
< as
.n_addrbk
; i
++){
6277 pab
= &as
.adrbks
[i
];
6278 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6286 * return values of search_in_one_line are or'd combination of these
6288 #define MATCH_NICK 0x1 /* match in field 0 */
6289 #define MATCH_FULL 0x2 /* match in field 1 */
6290 #define MATCH_ADDR 0x4 /* match in field 2 */
6291 #define MATCH_FCC 0x8 /* match in fcc field */
6292 #define MATCH_COMMENT 0x10 /* match in comment field */
6293 #define MATCH_BIGFIELD 0x20 /* match in one of the fields that crosses the
6294 whole screen, like a Title field */
6295 #define MATCH_LISTMEM 0x40 /* match list member */
6297 * Prompt user for search string and call find_in_book.
6299 * Args: cur_line -- The current line position (in global display list)
6301 * command_line -- The screen line to prompt on
6302 * new_line -- Return value: new line position
6303 * wrapped -- Wrapped to beginning of display, tell user
6304 * warped -- Warped to a new location in the addrbook
6306 * Result: The new line number is set if the search is successful.
6307 * Returns 0 if found, -1 if not, -2 if cancelled.
6311 search_book(long int cur_line
, int command_line
, long int *new_line
, int *wrapped
, int *warped
)
6313 int i
=0, find_result
, rc
, flags
, ku
;
6314 static HISTORY_S
*history
= NULL
;
6315 char search_string
[MAX_SEARCH
+ 1];
6316 char prompt
[MAX_SEARCH
+ 50], nsearch_string
[MAX_SEARCH
+1], *p
;
6322 dprint((7, "- search_book -\n"));
6324 init_hist(&history
, HISTSIZE
);
6326 search_string
[0] = '\0';
6327 if((p
= get_prev_hist(history
, "", 0, NULL
)) != NULL
){
6328 strncpy(search_string
, p
, sizeof(search_string
));
6329 search_string
[sizeof(search_string
)-1] = '\0';
6332 snprintf(prompt
, sizeof(prompt
), _("Word to search for [%.*s]: "), MAX_SEARCH
, search_string
);
6333 prompt
[sizeof(prompt
)-1] = '\0';
6335 nsearch_string
[0] = '\0';
6340 ekey
[i
++].label
= "";
6342 ekey
[i
].ch
= ctrl('Y');
6344 ekey
[i
].name
= "^Y";
6345 /* TRANSLATORS: User is searching in address book. One of the options is to
6346 search for the First Address */
6347 ekey
[i
++].label
= _("First Adr");
6349 ekey
[i
].ch
= ctrl('V');
6351 ekey
[i
].name
= "^V";
6352 /* TRANSLATORS: Last Address */
6353 ekey
[i
++].label
= _("Last Adr");
6355 ekey
[i
].ch
= KEY_UP
;
6359 ekey
[i
++].label
= "";
6361 ekey
[i
].ch
= KEY_DOWN
;
6364 ekey
[i
++].label
= "";
6368 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
6372 * 2 is really 1 because there will be one real entry and
6373 * one entry of "" because of the get_prev_hist above.
6375 if(items_in_hist(history
) > 2){
6376 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
6377 ekey
[ku
].label
= HISTORY_KEYLABEL
;
6378 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6379 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
6383 ekey
[ku
].label
= "";
6384 ekey
[ku
+1].name
= "";
6385 ekey
[ku
+1].label
= "";
6388 rc
= optionally_enter(nsearch_string
, command_line
, 0,
6389 sizeof(nsearch_string
),
6390 prompt
, ekey
, help
, &flags
);
6392 help
= help
== NO_HELP
? h_oe_searchab
: NO_HELP
;
6397 warp_to_beginning(); /* go to top of addrbooks */
6398 if((nl
=first_selectable_line(0L)) != NO_LINE
){
6400 q_status_message(SM_INFO
, 0, 2, _("Searched to first entry"));
6404 q_status_message(SM_INFO
, 0, 2, _("No entries"));
6410 warp_to_end(); /* go to bottom */
6411 if((nl
=first_selectable_line(0L)) != NO_LINE
){
6413 q_status_message(SM_INFO
, 0, 2, _("Searched to last entry"));
6417 q_status_message(SM_INFO
, 0, 2, _("No entries"));
6422 if((p
= get_prev_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
6423 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
6424 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
6432 if((p
= get_next_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
6433 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
6434 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
6442 if(rc
!= 4){ /* 4 is redraw */
6443 save_hist(history
, nsearch_string
, 0, NULL
);
6449 if(rc
== 1 || (search_string
[0] == '\0' && nsearch_string
[0] == '\0'))
6452 if(nsearch_string
[0] != '\0'){
6453 strncpy(search_string
, nsearch_string
, sizeof(search_string
)-1);
6454 search_string
[sizeof(search_string
)-1] = '\0';
6457 find_result
= find_in_book(cur_line
, search_string
, new_line
, wrapped
);
6463 int also
= 0, notdisplayed
= 0;
6465 pab
= &as
.adrbks
[adrbk_num_from_lineno(*new_line
)];
6466 if(find_result
& MATCH_NICK
){
6467 if(pab
->nick_is_displayed
)
6473 if(find_result
& MATCH_FULL
){
6474 if(pab
->full_is_displayed
)
6480 if(find_result
& MATCH_ADDR
){
6481 if(pab
->addr_is_displayed
)
6487 if(find_result
& MATCH_FCC
){
6488 if(pab
->fcc_is_displayed
)
6494 if(find_result
& MATCH_COMMENT
){
6495 if(pab
->comment_is_displayed
)
6501 if(find_result
& MATCH_LISTMEM
){
6504 dl
= dlist(*new_line
);
6505 if(F_OFF(F_EXPANDED_DISTLISTS
,ps_global
)
6506 && !exp_is_expanded(pab
->address_book
->exp
, (a_c_arg_t
)dl
->elnum
))
6510 if(notdisplayed
> 1 && *wrapped
== 0){
6512 /* TRANSLATORS: These "matched" messages are advisory messages explaining
6513 how a search command matched an entry */
6514 q_status_message1(SM_ORDER
,0,4, _("Also matched string in %s other fields"),
6515 comatose(notdisplayed
));
6517 q_status_message1(SM_ORDER
,0,4, _("Matched string in %s fields"),
6518 comatose(notdisplayed
));
6520 else if(notdisplayed
== 1 && *wrapped
== 0){
6522 if(find_result
& MATCH_NICK
&& !pab
->nick_is_displayed
)
6523 q_status_message(SM_ORDER
,0,4, _("Also matched string in Nickname field"));
6524 else if(find_result
& MATCH_FULL
&& !pab
->full_is_displayed
)
6525 q_status_message(SM_ORDER
,0,4, _("Also matched string in Fullname field"));
6526 else if(find_result
& MATCH_ADDR
&& !pab
->addr_is_displayed
)
6527 q_status_message(SM_ORDER
,0,4, _("Also matched string in Address field"));
6528 else if(find_result
& MATCH_FCC
&& !pab
->fcc_is_displayed
)
6529 q_status_message(SM_ORDER
,0,4, _("Also matched string in Fcc field"));
6530 else if(find_result
& MATCH_COMMENT
&& !pab
->comment_is_displayed
)
6531 q_status_message(SM_ORDER
,0,4, _("Also matched string in Comment field"));
6532 else if(find_result
& MATCH_LISTMEM
)
6533 q_status_message(SM_ORDER
,0,4, _("Also matched string in list member address"));
6535 q_status_message(SM_ORDER
,0,4, _("Also matched string in ?"));
6538 if(find_result
& MATCH_NICK
&& !pab
->nick_is_displayed
)
6539 q_status_message(SM_ORDER
,0,4, _("Matched string in Nickname field"));
6540 else if(find_result
& MATCH_FULL
&& !pab
->full_is_displayed
)
6541 q_status_message(SM_ORDER
,0,4, _("Matched string in Fullname field"));
6542 else if(find_result
& MATCH_ADDR
&& !pab
->addr_is_displayed
)
6543 q_status_message(SM_ORDER
,0,4, _("Matched string in Address field"));
6544 else if(find_result
& MATCH_FCC
&& !pab
->fcc_is_displayed
)
6545 q_status_message(SM_ORDER
,0,4, _("Matched string in Fcc field"));
6546 else if(find_result
& MATCH_COMMENT
&& !pab
->comment_is_displayed
)
6547 q_status_message(SM_ORDER
,0,4, _("Matched string in Comment field"));
6548 else if(find_result
& MATCH_LISTMEM
)
6549 q_status_message(SM_ORDER
,0,4, _("Matched string in list member address"));
6551 q_status_message(SM_ORDER
,0,4, _("Matched string in ?"));
6556 /* be sure to be on a selectable field */
6557 if(!line_is_selectable(*new_line
))
6558 if((nl
=first_selectable_line(*new_line
+1)) != NO_LINE
)
6562 return(find_result
? 0 : -1);
6567 * Search the display list for the given string.
6569 * Args: cur_line -- The current line position (in global display list)
6571 * string -- String to search for
6572 * new_line -- Return value: new line position
6573 * wrapped -- Wrapped to beginning of display during search
6575 * Result: The new line number is set if the search is successful.
6576 * Returns 0 -- string not found
6577 * Otherwise, a bitmask of which fields the string was found in.
6580 find_in_book(long int cur_line
, char *string
, long int *new_line
, int *wrapped
)
6582 register AddrScrn_Disp
*dl
;
6586 char *listaddr
= NULL
;
6588 dlc_save
; /* a local copy */
6591 dprint((9, "- find_in_book -\n"));
6594 * Save info to allow us to get back to where we were if we can't find
6595 * the string. Also used to stop our search if we wrap back to the
6596 * start and search forward.
6600 dlc
= get_dlc(nl_save
);
6606 /* start with next line and search to the end of the disp_list */
6608 while(dl
->type
!= End
){
6609 if(dl
->type
== Simple
||
6610 dl
->type
== ListHead
||
6611 dl
->type
== ListEnt
||
6612 dl
->type
== ListClickHere
){
6614 if(dl
->type
== ListEnt
)
6615 listaddr
= listmem(nl
);
6618 abe
= (AdrBk_Entry
*)NULL
;
6620 if((fields
=search_in_one_line(dl
, abe
, listaddr
, string
)) != 0)
6628 * Wrap back to the start of the addressbook and search forward
6631 warp_to_beginning(); /* go to top of addrbooks */
6632 nl
= 0L; /* line number is always 0 after warp_to_beginning */
6636 while(!matching_dlcs(&dlc_save
, dlc
) && dlc
->type
!= DlcEnd
){
6638 fill_in_dl_field(dlc
);
6641 if(dl
->type
== Simple
||
6642 dl
->type
== ListHead
||
6643 dl
->type
== ListEnt
||
6644 dl
->type
== ListClickHere
){
6646 if(dl
->type
== ListEnt
)
6647 listaddr
= listmem(nl
);
6650 abe
= (AdrBk_Entry
*)NULL
;
6652 if((fields
=search_in_one_line(dl
, abe
, listaddr
, string
)) != 0)
6655 dlc
= get_dlc(++nl
);
6658 /* see if it is in the current line */
6659 fill_in_dl_field(dlc
);
6662 if(dl
->type
== Simple
||
6663 dl
->type
== ListHead
||
6664 dl
->type
== ListEnt
||
6665 dl
->type
== ListClickHere
){
6667 if(dl
->type
== ListEnt
)
6668 listaddr
= listmem(nl
);
6671 abe
= (AdrBk_Entry
*)NULL
;
6673 fields
= search_in_one_line(dl
, abe
, listaddr
, string
);
6675 (dl
->type
== Text
|| dl
->type
== Title
|| dl
->type
== TitleCmb
))
6676 fs_give((void **)&dl
->usst
);
6678 /* jump cache back to where we started */
6680 warp_to_dlc(&dlc_save
, nl_save
);
6682 *new_line
= nl_save
; /* because it was in current line */
6695 * Look in line dl for string.
6697 * Args: dl -- the display list for this line
6698 * abe -- AdrBk_Entry if it is an address type
6699 * listaddr -- list member if it is of type ListEnt
6700 * string -- look for this string
6702 * Result: 0 -- string not found
6703 * Otherwise, a bitmask of which fields the string was found in.
6708 * MATCH_COMMENT 0x10
6709 * MATCH_BIGFIELD 0x20
6710 * MATCH_LISTMEM 0x40
6713 search_in_one_line(AddrScrn_Disp
*dl
, AdrBk_Entry
*abe
, char *listaddr
, char *string
)
6719 for(c
= 0; c
< 5; c
++){
6725 if(srchstr(abe
->nickname
, string
))
6726 ret_val
|= MATCH_NICK
;
6734 if(srchstr(dl
->usst
, string
))
6735 ret_val
|= MATCH_BIGFIELD
;
6747 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6748 SIZEOF_20KBUF
, abe
->fullname
),
6750 ret_val
|= MATCH_FULL
;
6760 if(srchstr((abe
&& abe
->tag
== Single
) ?
6761 abe
->addr
.addr
: NULL
, string
))
6762 ret_val
|= MATCH_ADDR
;
6768 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6769 SIZEOF_20KBUF
, listaddr
), string
))
6770 ret_val
|= MATCH_LISTMEM
;
6776 for(lm
= abe
->addr
.list
;
6777 !(ret_val
& MATCH_LISTMEM
) && *lm
; lm
++)
6778 if(srchstr(*lm
, string
))
6779 ret_val
|= MATCH_LISTMEM
;
6785 if(srchstr(EMPTY
, string
))
6786 ret_val
|= MATCH_BIGFIELD
;
6791 if(srchstr(ADD_PERSONAL
, string
))
6792 ret_val
|= MATCH_BIGFIELD
;
6797 if(srchstr(ADD_GLOBAL
, string
))
6798 ret_val
|= MATCH_BIGFIELD
;
6803 if(srchstr(NOABOOKS
, string
))
6804 ret_val
|= MATCH_BIGFIELD
;
6817 if(abe
&& srchstr(abe
->fcc
, string
))
6818 ret_val
|= MATCH_FCC
;
6825 case 4: /* comment */
6831 unsigned char *p
, *tmp
= NULL
;
6833 if((n
= 4*strlen(abe
->extra
)) > SIZEOF_20KBUF
-1){
6835 p
= tmp
= (unsigned char *)fs_get(len
* sizeof(char));
6838 len
= SIZEOF_20KBUF
;
6839 p
= (unsigned char *)tmp_20k_buf
;
6842 if(srchstr((char *)rfc1522_decode_to_utf8(p
, len
, abe
->extra
),
6844 ret_val
|= MATCH_COMMENT
;
6847 fs_give((void **)&tmp
);
6863 * These chars in nicknames will mess up parsing.
6865 * Returns 0 if ok, 1 if not.
6866 * Returns an allocated error message on error.
6869 nickname_check(char *nickname
, char **error
)
6874 if((t
= strindex(nickname
, SPACE
)) ||
6875 (t
= strindex(nickname
, ',')) ||
6876 (t
= strindex(nickname
, '"')) ||
6877 (t
= strindex(nickname
, ';')) ||
6878 (t
= strindex(nickname
, ':')) ||
6879 (t
= strindex(nickname
, '@')) ||
6880 (t
= strindex(nickname
, '(')) ||
6881 (t
= strindex(nickname
, ')')) ||
6882 (t
= strindex(nickname
, '\\')) ||
6883 (t
= strindex(nickname
, '[')) ||
6884 (t
= strindex(nickname
, ']')) ||
6885 (t
= strindex(nickname
, '<')) ||
6886 (t
= strindex(nickname
, '>'))){
6894 * TRANSLATORS: this is telling the user that one of the characters
6895 * they have included in a nickname will not work. It will say something like
6896 * Blank spaces not allowed in nicknames or
6900 snprintf(buf
, sizeof(buf
), _("%s not allowed in nicknames"),
6908 buf
[sizeof(buf
)-1] = '\0';
6909 *error
= cpystr(buf
);
6920 abook_select_screen(struct pine
*ps
)
6922 CONF_S
*ctmp
= NULL
, *first_line
= NULL
;
6923 OPT_SCREEN_S screen
;
6930 helptitle
= _("HELP FOR SELECTING AN ADDRESS BOOK");
6931 help
= h_role_abook_select
;
6933 init_ab_if_needed();
6935 for(adrbknum
= 0; adrbknum
< as
.n_addrbk
; adrbknum
++){
6936 new_confline(&ctmp
);
6940 pab
= &as
.adrbks
[adrbknum
];
6942 ctmp
->value
= cpystr((pab
&& pab
->abnick
)
6944 : (pab
&& pab
->filename
)
6948 ctmp
->d
.b
.selected
= &abook
;
6949 ctmp
->d
.b
.abookname
= ctmp
->value
;
6950 ctmp
->keymenu
= &abook_select_km
;
6952 ctmp
->help_title
= helptitle
;
6953 ctmp
->tool
= abook_select_tool
;
6954 ctmp
->flags
= CF_STARTITEM
;
6955 ctmp
->valoffset
= 4;
6959 memset(&screen
, 0, sizeof(screen
));
6960 (void) conf_scroll_screen(ps
, &screen
, first_line
,
6961 /* TRANSLATORS: Print something1 using something2.
6962 abooks is something1 */
6963 _("SELECT ADDRESS BOOK"), _("abooks"), 0, NULL
);
6966 q_status_message(SM_ORDER
|SM_DING
, 3, 3, _("No address books defined!"));
6968 ps
->mangled_screen
= 1;
6974 abook_select_tool(struct pine
*ps
, int cmd
, CONF_S
**cl
, unsigned int flags
)
6980 *((*cl
)->d
.b
.selected
) = cpystr((*cl
)->d
.b
.abookname
);
6981 retval
= simple_exit_cmd(flags
);
6985 retval
= simple_exit_cmd(flags
);
6994 ps
->mangled_body
= 1;
7001 * This isn't actually just a nickname completion anymore. The user types
7002 * in a prefix of either a nickname, or a full address, or the addr@mailbox
7003 * part of an address and we look in all of the address books for matches
7004 * like that. We return the longest unambiguous match in answer.
7006 * Args prefix -- The part of the "nickname" that has been typed so far
7007 * answer -- The answer is returned here.
7008 * tabtab -- If the answer returned from adrbk_list_of_completions is
7009 * ambiguous and tabtab is set, then offer up a
7010 * selector screen for all the possible answers.
7011 * flags -- ANC_AFTERCOMMA -- This means that the passed in
7012 * prefix may be a list of comma
7013 * separated addresses and we're only
7014 * completing the last one.
7016 * Returns 0 -- no matches at all
7017 * 1 -- more than one nickname (or address) begins with
7018 * the answer being returned
7019 * 2 -- the returned answer is a complete answer, either a
7020 * nickname or a complete address, and there are
7021 * no longer matches for the prefix
7023 * Allocated answer is returned in answer argument.
7024 * Caller needs to free the answer.
7027 abook_nickname_complete(char *prefix
, char **answer
, int tabtab
, unsigned flags
)
7030 COMPLETE_S
*completions
, *cp
;
7031 char *saved_beginning
= NULL
;
7032 char *potential_answer
= NULL
;
7034 wp_exit
= wp_nobail
= 0;
7036 /* there shouldn't be a case where answer is NULL */
7041 * If we need to handle case where no prefix is passed in,
7042 * figure that out when we need it.
7044 if(!(prefix
&& prefix
[0]))
7047 if(flags
& ANC_AFTERCOMMA
){
7051 * Find last comma, save the part before that, operate
7052 * only on the last address.
7054 if((lastnick
= strrchr(prefix
? prefix
: "", ',')) != NULL
){
7056 while(!(*lastnick
& 0x80) && isspace((unsigned char) (*lastnick
)))
7059 saved_beginning
= cpystr(prefix
);
7060 saved_beginning
[lastnick
-prefix
] = '\0';
7065 if(!(prefix
&& prefix
[0]))
7068 completions
= adrbk_list_of_completions(prefix
,
7069 ps_global
->cur_uid_stream
, ps_global
->cur_uid
,
7070 ALC_INCLUDE_ADDRS
| ((strlen(prefix
) >= 3) ? ALC_INCLUDE_LDAP
: 0));
7074 else if(completions
&& completions
->next
)
7080 if(completions
->full_address
&& completions
->full_address
[0])
7081 potential_answer
= cpystr(completions
->full_address
);
7082 else if(completions
->nickname
&& completions
->nickname
[0])
7083 potential_answer
= cpystr(completions
->nickname
);
7084 else if(completions
->addr
&& completions
->addr
[0])
7085 potential_answer
= cpystr(completions
->addr
);
7087 potential_answer
= cpystr(prefix
);
7089 /* answer is ambiguous and caller wants a choose list in that case */
7090 else if(ambiguity
== 1 && tabtab
){
7091 potential_answer
= choose_an_address_with_this_prefix(completions
);
7093 if(potential_answer
)
7097 potential_answer
= cpystr(prefix
);
7100 else if(ambiguity
== 1){
7102 char cand1_kth_char
, cand2_kth_char
;
7105 /* find the longest unambiguous prefix */
7106 strncpy(unambig
, prefix
, sizeof(unambig
));
7107 unambig
[sizeof(unambig
)-1] = '\0';
7108 k
= strlen(unambig
);
7111 * First verify that they all match through prefix. LDAP sometimes gives
7112 * weird, inexplicable answers that don't seem to match at all.
7114 for(cp
= completions
; cp
; cp
= cp
->next
)
7115 if(!( /* not a match */
7116 /* no NICK bit OR nickname matches prefix */
7117 (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& !struncmp(unambig
, cp
->nickname
, k
)))
7118 /* AND no ADDR bit OR addr matches prefix */
7119 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& !struncmp(unambig
, cp
->addr
, k
)))
7120 /* AND neither FULL bit is set OR one of the two fulls matches prefix */
7121 && (!(cp
->matches_bitmap
& (ALC_FULL
| ALC_REVFULL
)) || ((cp
->matches_bitmap
& ALC_FULL
&& cp
->full_address
&& strlen(cp
->full_address
) >= k
&& !struncmp(unambig
, cp
->full_address
, k
)) || (cp
->matches_bitmap
& ALC_REVFULL
&& cp
->rev_fullname
&& strlen(cp
->rev_fullname
) >= k
&& !struncmp(unambig
, cp
->rev_fullname
, k
))))
7125 /* if cp that means there was not a universal match up through prefix, stop */
7128 cand1_kth_char
= cand2_kth_char
= '\0';
7129 if(completions
->matches_bitmap
& ALC_NICK
&& completions
->nickname
&& strlen(completions
->nickname
) >= k
)
7130 cand1_kth_char
= completions
->nickname
[k
];
7131 else if(completions
->matches_bitmap
& ALC_ADDR
&& completions
->addr
&& strlen(completions
->addr
) >= k
)
7132 cand1_kth_char
= completions
->addr
[k
];
7134 if(completions
->matches_bitmap
& ALC_FULL
&& completions
->full_address
&& strlen(completions
->full_address
) >= k
)
7135 cand1_kth_char
= completions
->full_address
[k
];
7137 if(completions
->matches_bitmap
& ALC_REVFULL
&& completions
->rev_fullname
&& strlen(completions
->rev_fullname
) >= k
)
7138 cand2_kth_char
= completions
->rev_fullname
[k
];
7142 * You'll want a wide screen to read this. There are two possible
7143 * candidate chars for the next position. One or the other of them
7144 * has to match in all of the possible completions. We consider it
7145 * a match if either of the fullname completions for this entry is
7146 * a match. That may not match what the user expects but it may.
7148 for(cp
= completions
; cp
; cp
= cp
->next
){
7149 if(!( /* candidate 1 is not a match */
7150 /* candidate 1 is defined */
7151 cand1_kth_char
&& cand1_kth_char
!= ','
7152 /* AND no NICK bit OR nickname char is a match */
7153 && (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& cp
->nickname
[k
] == cand1_kth_char
))
7154 /* AND no ADDR bit OR addr char is a match */
7155 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& cp
->addr
[k
] == cand1_kth_char
))
7156 /* AND neither FULL bit is set OR one of the two full chars is a match */
7157 && (!(cp
->matches_bitmap
& (ALC_FULL
| ALC_REVFULL
)) || ((cp
->matches_bitmap
& ALC_FULL
&& cp
->full_address
&& strlen(cp
->full_address
) >= k
&& cp
->full_address
[k
] == cand1_kth_char
) || (cp
->matches_bitmap
& ALC_REVFULL
&& cp
->rev_fullname
&& strlen(cp
->rev_fullname
) >= k
&& cp
->rev_fullname
[k
] == cand1_kth_char
)))
7159 cand1_kth_char
= '\0'; /* mark that it isn't a match */
7161 if(!cand1_kth_char
&& !( /* cand1 is not a match AND cand2 is not a match */
7162 /* candidate 2 is defined */
7163 cand2_kth_char
&& cand2_kth_char
!= ','
7164 /* AND no NICK bit OR nickname char is a match */
7165 && (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& cp
->nickname
[k
] == cand2_kth_char
))
7166 /* AND no ADDR bit OR addr char is a match */
7167 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& cp
->addr
[k
] == cand2_kth_char
))
7168 /* AND neither FULL bit is set OR one of the two full chars is a match */
7169 && (!(cp
->matches_bitmap
& (ALC_FULL
| ALC_REVFULL
)) || ((cp
->matches_bitmap
& ALC_FULL
&& cp
->full_address
&& strlen(cp
->full_address
) >= k
&& cp
->full_address
[k
] == cand2_kth_char
) || (cp
->matches_bitmap
& ALC_REVFULL
&& cp
->rev_fullname
&& strlen(cp
->rev_fullname
) >= k
&& cp
->rev_fullname
[k
] == cand2_kth_char
)))
7171 cand2_kth_char
= '\0'; /* mark that it isn't a match */
7173 if(!cand1_kth_char
&& !cand2_kth_char
)
7174 break; /* no match so break */
7177 if(!cp
) /* they all matched */
7178 unambig
[k
++] = cand1_kth_char
? cand1_kth_char
: cand2_kth_char
;
7180 }while(!cp
&& k
< sizeof(unambig
)-1);
7183 unambig
[sizeof(unambig
)-1] = '\0';
7185 /* don't return answer with trailing space */
7186 while(--k
>= 0 && isspace((unsigned char) unambig
[k
]))
7189 potential_answer
= cpystr(unambig
);
7193 free_complete_s(&completions
);
7195 if(answer
&& ambiguity
!= 0){
7196 if(potential_answer
){
7197 if(saved_beginning
){
7200 l1
= strlen(saved_beginning
);
7201 l2
= strlen(potential_answer
);
7202 *answer
= (char *) fs_get((l1
+l2
+1) * sizeof(char));
7203 strncpy(*answer
, saved_beginning
, l1
+l2
);
7204 strncpy(*answer
+l1
, potential_answer
, l2
);
7205 (*answer
)[l1
+l2
] = '\0';
7208 *answer
= potential_answer
;
7209 potential_answer
= NULL
;
7213 /* this can't happen */
7219 fs_give((void **) &saved_beginning
);
7221 if(potential_answer
)
7222 fs_give((void **) &potential_answer
);
7229 * Returns an allocated nickname choice from user that begins with
7233 choose_an_address_with_this_prefix(COMPLETE_S
*completions
)
7236 char *chosen_address
= NULL
;
7237 char **lp
, **da
, **possible_addrs
= NULL
, **display_addrs
= NULL
;
7240 int show_nick
, show_revfull
;
7243 * Count how many and allocate an array for choose_item_from_list().
7245 for(cnt
= 0, cp
= completions
; cp
; cp
= cp
->next
)
7249 * Copy completions into an array.
7252 lp
= possible_addrs
= (char **) fs_get((cnt
+1) * sizeof(*possible_addrs
));
7253 memset(possible_addrs
, 0, (cnt
+1) * sizeof(*possible_addrs
));
7254 da
= display_addrs
= (char **) fs_get((cnt
+1) * sizeof(*display_addrs
));
7255 memset(display_addrs
, 0, (cnt
+1) * sizeof(*display_addrs
));
7256 for(cp
= completions
; cp
; cp
= cp
->next
){
7257 show_nick
= (cp
->matches_bitmap
& ALC_NICK
) && cp
->nickname
&& cp
->nickname
[0];
7259 if(!show_nick
&& !(cp
->matches_bitmap
& (ALC_NICK
| ALC_ADDR
| ALC_FULL
))
7260 && (cp
->matches_bitmap
& ALC_REVFULL
)
7261 && cp
->rev_fullname
&& cp
->rev_fullname
[0])
7264 snprintf(buf
, sizeof(buf
), "%s%s%s%s%s",
7265 cp
->full_address
? cp
->full_address
: "?",
7266 (show_nick
|| show_revfull
) ? " (" : "",
7267 show_nick
? cp
->nickname
: "",
7268 show_revfull
? cp
->rev_fullname
: "",
7269 (show_nick
|| show_revfull
) ? ")" : "");
7270 *da
++ = cpystr(buf
);
7271 *lp
++ = cpystr(cp
->full_address
? cp
->full_address
: "?");
7276 chosen_address
= choose_item_from_list(possible_addrs
, display_addrs
,
7277 _("SELECT AN ADDRESS"),
7279 h_select_address_screen
,
7280 _("HELP FOR SELECTING AN ADDRESS"),
7282 free_list_array(&possible_addrs
);
7286 free_list_array(&display_addrs
);
7288 return(chosen_address
);
7294 * addr_scroll_up - adjust the global screen state struct such that pine's
7295 * window on the data is shifted DOWN (i.e., the data's
7299 addr_scroll_up(count
)
7305 return(addr_scroll_down(-count
));
7310 as
.cur_row
+= as
.top_ent
;
7311 while(i
&& as
.top_ent
+ 1 < as
.last_ent
){
7312 if(line_is_selectable(as
.top_ent
)){
7313 if(next_selectable_line(as
.top_ent
,&next
)){
7325 as
.cur_row
= as
.cur_row
- as
.top_ent
; /* must always be positive */
7327 as
.old_cur_row
= as
.cur_row
;
7335 * addr_scroll_down - adjust the global screen state struct such that pine's
7336 * window on the data is shifted UP (i.e., the data's
7340 addr_scroll_down(count
)
7344 return(addr_scroll_up(-count
));
7348 for(i
= count
; i
&& as
.top_ent
; i
--, as
.top_ent
--)
7351 while (as
.cur_row
>= as
.l_p_page
){
7352 prev_selectable_line(as
.cur_row
+as
.top_ent
, &as
.cur_row
);
7353 as
.cur_row
= as
.cur_row
- as
.top_ent
;
7356 as
.old_cur_row
= as
.cur_row
;
7364 * addr_scroll_to_pos - scroll the address book data in pine's window such
7365 * tthat the given "line" is at the top of the page.
7368 addr_scroll_to_pos(line
)
7371 return(addr_scroll_up(line
- as
.top_ent
));
7375 /*----------------------------------------------------------------------
7376 MSWin scroll callback. Called during scroll message processing.
7380 Args: cmd - what type of scroll operation.
7381 scroll_pos - parameter for operation.
7382 used as position for SCROLL_TO operation.
7384 Returns: TRUE - did the scroll operation.
7385 FALSE - was not able to do the scroll operation.
7388 addr_scroll_callback (cmd
, scroll_pos
)
7395 case MSWIN_KEY_SCROLLUPLINE
:
7396 paint
= addr_scroll_down (scroll_pos
);
7399 case MSWIN_KEY_SCROLLDOWNLINE
:
7400 paint
= addr_scroll_up (scroll_pos
);
7403 case MSWIN_KEY_SCROLLUPPAGE
:
7404 paint
= addr_scroll_down (as
.l_p_page
);
7407 case MSWIN_KEY_SCROLLDOWNPAGE
:
7408 paint
= addr_scroll_up (as
.l_p_page
);
7411 case MSWIN_KEY_SCROLLTO
:
7412 paint
= addr_scroll_to_pos (scroll_pos
);
7417 display_book(0, as
.cur_row
, -1, 1, (Pos
*)NULL
);
7424 pcpine_help_addrbook(title
)
7428 * Title is size 256. Fix this to pass the titlelen.
7431 strncpy(title
, (as
.config
)
7432 ? _("Alpine CONFIGURING ADDRESS BOOKS Help")
7433 : _("Alpine ADDRESS_BOOK Help"), 256);
7435 return(pcpine_help(gAbookHelp
));
7437 #endif /* _WINDOWS */