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-2015 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 paint_line(old_line
+ HEADER_ROWS(ps_global
), as
.top_ent
+ old_line
,
381 /*--- paint the position with the cursor ---*/
382 paint_line(cur_line
+ HEADER_ROWS(ps_global
), as
.top_ent
+ cur_line
,
389 scroll_setpos(as
.top_ent
);
397 * Paint a line on the screen
399 * Args: line -- Line on screen to paint
400 * global_row -- Row number of line to paint
401 * highlight -- Line should be highlighted
402 * start_pos -- return position where text begins here
404 * Result: Line is painted
406 * The three field widths for the formatting are passed in. There is an
407 * implicit 2 spaces between the fields.
409 * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
412 paint_line(int line
, long int global_row
, int highlight
, Pos
*start_pos
)
414 int scol
, bolden
= 0;
415 char *start_hilite_here
, *end_hilite_here
;
417 char lbuf
[6*MAX_SCREEN_COLS
+ 1];
420 dprint((10, "- paint_line(%d, %d) -\n", line
, highlight
));
422 dl
= dlist(global_row
);
423 start_pos
->row
= line
;
424 start_pos
->col
= 0; /* default */
435 p
= get_abook_display_line(global_row
, line
== HEADER_ROWS(ps_global
),
436 highlight
? &start_hilite_here
: NULL
,
437 highlight
? &end_hilite_here
: NULL
,
438 &scol
, lbuf
, sizeof(lbuf
));
442 (dl
->type
== ListHead
|| dl
->type
== Simple
)){
445 pab
= &as
.adrbks
[adrbk_num_from_lineno(global_row
)];
446 if(entry_is_selected(pab
->address_book
->selects
, (a_c_arg_t
)dl
->elnum
)){
459 * print part before highlight starts
461 if(start_hilite_here
!= NULL
){
462 save_char
= *start_hilite_here
;
463 *start_hilite_here
= '\0';
465 *start_hilite_here
= save_char
;
469 * print highlighted part
472 if(end_hilite_here
!= NULL
){
473 save_char
= *end_hilite_here
;
474 *end_hilite_here
= '\0';
478 Write_to_screen(start_hilite_here
? start_hilite_here
: p
);
482 * print part after highlight ends
484 if(end_hilite_here
!= NULL
){
485 *end_hilite_here
= save_char
;
486 Write_to_screen(end_hilite_here
);
490 PutLine0(line
, 0, p
);
497 start_pos
->col
= scol
;
502 * Assemble a line suitable for displaying on screen
504 * Args: global_row -- Row number of line to assemble.
505 * continuation -- This is the top line of screen display
506 * s_hilite -- Location in the returned line where highlight
507 * should start, if desired
508 * e_hilite -- Location in the returned line where highlight
509 * should end, if desired
510 * retcol -- Return column where text begins here.
511 * lbuf -- Put the output here. Lbuf should be at least
512 * size 6*screen_cols+1.
513 * lbufsize -- Size of lbuf array
515 * Result: Pointer to lbuf is returned.
517 * There is an implicit 2 spaces between the fields.
519 * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
522 get_abook_display_line(long int global_row
, int continuation
, char **s_hilite
,
523 char **e_hilite
, int *retcol
, char *lbuf
, size_t lbufsize
)
525 int fld_col
[NFIELDS
],
528 col
, special_col
= 0,
532 char *string
, *writeptr
;
533 char special
[6*MAX_SCREEN_COLS
-1];
537 #define LSPACE() (lbufsize - (writeptr - lbuf) - 1)
539 dprint((10, "- get_display_line(%d) -\n", global_row
));
541 dl
= dlist(global_row
);
543 *retcol
= 0; /* default */
546 *s_hilite
= NULL
; /* NULL in these means to hilight whole line */
551 memset(writeptr
, 0, lbufsize
);
552 memset(special
, 0, sizeof(special
));
563 screen_width
= ps_global
->ttyo
->screen_cols
;
565 /* the types in this set span all columns */
575 if(dl
->type
== Empty
)
577 else if(dl
->type
== ZoomEmpty
)
579 else if(dl
->type
== NoAbooks
)
581 else if(dl
->type
== ClickHereCmb
)
582 string
= CLICKHERECMB
;
587 col
= (screen_width
- (int) utf8_width(string
))/2;
591 /* col spaces to start */
592 if(col
> 0 && LSPACE() >= col
){
593 memset(writeptr
, ' ', col
);
595 width_consumed
+= col
;
598 if((width
=utf8_width(string
)) <= screen_width
-col
){
599 strncpy(writeptr
, string
, LSPACE());
600 width_consumed
+= width
;
603 utf8_pad_to_width(writeptr
, string
, LSPACE(), screen_width
-col
, 1);
604 width_consumed
= screen_width
;
607 if(s_hilite
&& *s_hilite
== NULL
){
608 *s_hilite
= writeptr
;
609 *e_hilite
= writeptr
+ strlen(writeptr
);
612 writeptr
+= strlen(writeptr
);
613 if(width_consumed
< screen_width
)
614 memset(writeptr
, ' ', screen_width
-width_consumed
);
621 /* left adjust these */
625 if(dl
->type
== AddFirstPers
)
626 string
= ADD_PERSONAL
;
627 else if(dl
->type
== AddFirstGlob
)
635 if((width
=utf8_width(string
)) <= screen_width
){
636 strncpy(writeptr
, string
, LSPACE());
637 width_consumed
+= width
;
640 utf8_pad_to_width(writeptr
, string
, LSPACE(), screen_width
, 1);
641 width_consumed
= screen_width
;
644 if(s_hilite
&& *s_hilite
== NULL
){
645 *s_hilite
= writeptr
;
646 *e_hilite
= writeptr
+ strlen(writeptr
);
649 writeptr
+= strlen(writeptr
);
650 if(width_consumed
< screen_width
)
651 memset(writeptr
, ' ', screen_width
-width_consumed
);
662 pab
= &as
.adrbks
[adrbk_num_from_lineno(global_row
)];
663 for(fld
= 0; fld
< NFIELDS
; fld
++)
664 fld_width
[fld
] = pab
->disp_form
[fld
].width
;
667 for(fld
= 1; fld
< NFIELDS
; fld
++)
668 fld_col
[fld
] = MIN(fld_col
[fld
-1]+fld_width
[fld
-1]+2, screen_width
);
672 /* fill in the fields */
673 for(fld
= 0; fld
< NFIELDS
; fld
++){
674 if(fld_width
[fld
] == 0
675 && !(pab
->disp_form
[fld
].type
== Addr
676 && (dl
->type
== ListClickHere
|| dl
->type
== ListEmpty
)))
679 switch(pab
->disp_form
[fld
].type
){
687 abe
= ae(global_row
);
688 string
= (abe
&& abe
->nickname
) ? abe
->nickname
: "";
692 /* left adjust string in field */
693 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
694 writeptr
+= strlen(writeptr
);
695 width_consumed
+= fld_width
[fld
];
708 abe
= ae(global_row
);
709 string
= (abe
&& abe
->fullname
) ? abe
->fullname
: "";
713 /* left adjust string in field */
714 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
715 writeptr
+= strlen(writeptr
);
716 width_consumed
+= fld_width
[fld
];
720 /* continuation line */
724 width2
= MIN(11, fld_width
[fld
]);
725 width1
= MAX(fld_width
[fld
] - width2
- 1, 0);
727 abe
= ae(global_row
);
728 string
= (abe
&& abe
->fullname
) ? abe
->fullname
: "";
731 utf8_pad_to_width(writeptr
, string
, LSPACE(), width1
, 1);
732 writeptr
+= strlen(writeptr
);
737 /* TRANSLATORS: continuation line meaning there is more on the next page */
738 if(width2
&& LSPACE() >= strlen(_("(continued)"))){
739 strncpy(writeptr
, _("(continued)"), width2
);
740 lbuf
[lbufsize
-1] = '\0';
741 writeptr
+= strlen(writeptr
);
744 width_consumed
+= fld_width
[fld
];
746 lbuf
[lbufsize
-1] = '\0';
761 if(dl
->type
== ListClickHere
)
766 if((width
=utf8_width(string
)) <= fld_width
[fld
]){
768 strncpy(writeptr
, string
, LSPACE());
770 if(s_hilite
&& *s_hilite
== NULL
){
771 *s_hilite
= writeptr
;
772 *e_hilite
= writeptr
+ strlen(writeptr
);
775 writeptr
+= strlen(writeptr
);
776 width_consumed
+= width
;
783 * Place the string in special array and overlay it
784 * onto the right edge of the screen at the end.
786 if(width
<= screen_width
){
787 strncpy(special
, string
, sizeof(special
));
788 special_col
= screen_width
- width
;
791 utf8_pad_to_width(special
, string
, sizeof(special
), screen_width
, 1);
795 special
[sizeof(special
)-1] = '\0';
805 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
806 writeptr
+= strlen(writeptr
);
807 width_consumed
+= fld_width
[fld
];
811 abe
= ae(global_row
);
813 if(abe
&& abe
->addr
.addr
&&
814 !strncmp(abe
->addr
.addr
, QRUN_LDAP
, LEN_QRL
))
818 string
= (abe
&& abe
->tag
== Single
&& abe
->addr
.addr
) ?
823 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
824 writeptr
+= strlen(writeptr
);
825 width_consumed
+= fld_width
[fld
];
829 string
= listmem(global_row
) ? listmem(global_row
) : "";
833 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
835 if(s_hilite
&& *s_hilite
== NULL
){
836 *s_hilite
= writeptr
;
837 *e_hilite
= writeptr
+ strlen(writeptr
);
840 writeptr
+= strlen(writeptr
);
841 width_consumed
+= fld_width
[fld
];
855 abe
= ae(global_row
);
856 if(pab
->disp_form
[fld
].type
== Filecopy
)
857 string
= (abe
&& abe
->fcc
) ? abe
->fcc
: "";
859 string
= (abe
&& abe
->extra
) ? abe
->extra
: "";
864 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
865 writeptr
+= strlen(writeptr
);
866 width_consumed
+= fld_width
[fld
];
879 if(entry_is_checked(pab
->address_book
->checks
,
880 (a_c_arg_t
)dl
->elnum
))
888 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
889 writeptr
+= strlen(writeptr
);
890 width_consumed
+= fld_width
[fld
];
903 if(entry_is_selected(pab
->address_book
->selects
,
904 (a_c_arg_t
)dl
->elnum
))
912 utf8_pad_to_width(writeptr
, string
, LSPACE(), fld_width
[fld
], 1);
913 writeptr
+= strlen(writeptr
);
914 width_consumed
+= fld_width
[fld
];
923 case WhenNoAddrDisplayed
:
928 if(dl
->type
== ListClickHere
)
930 else if(dl
->type
== ListEmpty
)
933 string
= listmem(global_row
) ? listmem(global_row
) : "";
935 if((width
=utf8_width(string
)) <= fld_width
[fld
])
936 strncpy(special
, string
, sizeof(special
));
938 utf8_pad_to_width(special
, string
, sizeof(special
), fld_width
[fld
], 1);
940 special
[sizeof(special
)-1] = '\0';
941 special_col
= screen_width
- fld_width
[fld
];
942 special_col
= MAX(0, special_col
);
956 while(width_consumed
< fld_col
[fld
+1] && LSPACE() > 0){
963 * Right adjust the special string in lbuf, writing over
964 * anything in our way.
969 writeptr
= utf8_count_forw_width(lbuf
, special_col
, &got_width
);
971 strncpy(writeptr
, special
, LSPACE());
976 if(s_hilite
&& *s_hilite
== NULL
){
977 *s_hilite
= writeptr
;
978 *e_hilite
= writeptr
+ strlen(writeptr
);
981 writeptr
+= strlen(writeptr
);
983 width_consumed
= (int) utf8_width(lbuf
);
984 while(width_consumed
++ < screen_width
&& LSPACE() > 0)
991 if(scol
> 0 && retcol
)
994 lbuf
[lbufsize
-1] = '\0';
1000 * Set field widths for the columns of the display. The idea is to
1001 * try to come up with something that works pretty well. Getting it just
1002 * right isn't important.
1004 * Col1 and col2 are arrays which contain some widths useful for
1005 * formatting the screen. The 0th element is the max
1006 * width in that column. The 1st element is the max of the third largest
1007 * width in each addressbook (yup, strange).
1009 * The info above applies to the default case (AllAuto). The newer methods
1010 * where the user specifies the format is the else part of the big if and
1011 * is quite a bit different. It's all sort of ad hoc when we're asked
1012 * to calculate one of the fields for the user.
1014 * Returns non-zero if the widths changed since the last time called.
1017 calculate_field_widths(void)
1019 int space_left
, i
, j
, screen_width
;
1022 WIDTH_INFO_S
*widths
;
1023 int max_nick
, max_full
, max_addr
, third_full
, third_addr
, third_fcc
;
1024 int col1
[5], col2
[5];
1025 int nick
= -1, full
= -1, addr
= -1;
1026 #define SEP 2 /* space between columns */
1028 dprint((9, "- calculate_field_widths -\n"));
1030 screen_width
= ps_global
->ttyo
->screen_cols
;
1032 /* calculate widths for each addrbook independently */
1033 for(j
= 0; j
< as
.n_addrbk
; j
++){
1034 pab
= &as
.adrbks
[j
];
1043 if(pab
->address_book
){
1044 widths
= &pab
->address_book
->widths
;
1045 max_nick
= MIN(MAX(max_nick
, widths
->max_nickname_width
), 25);
1046 max_full
= MAX(max_full
, widths
->max_fullname_width
);
1047 max_addr
= MAX(max_addr
, widths
->max_addrfield_width
);
1048 third_full
= MAX(third_full
, widths
->third_biggest_fullname_width
);
1050 third_full
= MAX(third_full
, 2*max_full
/3);
1052 third_addr
= MAX(third_addr
, widths
->third_biggest_addrfield_width
);
1054 third_addr
= MAX(third_addr
, 2*max_addr
/3);
1056 third_fcc
= MAX(third_fcc
, widths
->third_biggest_fccfield_width
);
1058 third_fcc
= MAX(third_fcc
, 2*widths
->max_fccfield_width
/3);
1061 /* figure out which order they're in and reset widths */
1062 for(i
= 0; i
< NFIELDS
; i
++){
1063 pab
->disp_form
[i
].width
= 0;
1064 switch(pab
->disp_form
[i
].type
){
1082 /* Compute default format */
1083 if(pab
->disp_form
[1].wtype
== AllAuto
){
1087 col1
[1] = third_full
;
1088 col2
[1] = third_addr
;
1096 space_left
= screen_width
;
1098 if(pab
->disp_form
[0].type
== Selected
||
1099 pab
->disp_form
[0].type
== Checkbox
){
1100 pab
->disp_form
[0].width
= MIN(pab
->disp_form
[0].req_width
,space_left
);
1101 space_left
= MAX(space_left
-(pab
->disp_form
[0].width
+ SEP
), 0);
1105 * All of the nickname field should be visible,
1106 * and make it at least 3.
1108 pab
->disp_form
[nick
].width
= MIN(MAX(max_nick
, 3), space_left
);
1111 * The SEP is for two blank columns between nickname and next field.
1112 * Those blank columns are taken automatically in paint_line().
1114 space_left
-= (pab
->disp_form
[nick
].width
+ SEP
);
1117 for(i
= 0; i
< 5; i
++){
1118 /* try fitting most of each field in if possible */
1119 if(col1
[i
] + SEP
+ col2
[i
] <= space_left
){
1122 extra
= space_left
- col1
[i
] - SEP
- col2
[i
];
1124 * try to stabilize nickname column shifts
1125 * so that screen doesn't jump around when we make changes
1127 if(i
== 0 && pab
->disp_form
[nick
].width
< 7 &&
1128 extra
>= (7 - pab
->disp_form
[nick
].width
)){
1129 extra
-= (7 - pab
->disp_form
[nick
].width
);
1130 space_left
-= (7 - pab
->disp_form
[nick
].width
);
1131 pab
->disp_form
[nick
].width
= 7;
1134 pab
->disp_form
[addr
].width
= col2
[i
] + extra
/2;
1135 pab
->disp_form
[full
].width
=
1136 space_left
- SEP
- pab
->disp_form
[addr
].width
;
1142 * None of them would fit. Toss addr field.
1145 pab
->disp_form
[full
].width
= space_left
;
1146 pab
->disp_form
[addr
].width
= 0;
1150 pab
->disp_form
[full
].width
= 0;
1151 pab
->disp_form
[addr
].width
= 0;
1154 dprint((10, "Using %s choice: %d %d %d", enth_string(i
+1),
1155 pab
->disp_form
[nick
].width
, pab
->disp_form
[full
].width
,
1156 pab
->disp_form
[addr
].width
));
1158 else{ /* non-default case */
1159 int some_to_calculate
= 0;
1163 int all_percents
= 1;
1167 * First count how many fields there are.
1168 * Fill in all the Fixed's while we're at it.
1170 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1171 if(pab
->disp_form
[i
].wtype
== Fixed
){
1172 pab
->disp_form
[i
].width
= pab
->disp_form
[i
].req_width
;
1175 else if(pab
->disp_form
[i
].wtype
== WeCalculate
){
1176 pab
->disp_form
[i
].width
= pab
->disp_form
[i
].req_width
; /* for now */
1177 some_to_calculate
++;
1181 if(pab
->disp_form
[i
].wtype
!= Special
){
1182 used
+= pab
->disp_form
[i
].width
;
1187 used
+= ((columns
-1) * SEP
);
1188 avail_screen
= screen_width
- used
;
1191 * Now that we know how much space we've got, we can
1192 * calculate the Percent columns.
1194 if(avail_screen
> 0){
1195 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1196 if(pab
->disp_form
[i
].wtype
== Percent
){
1197 /* The 2, 200, and +100 are because we're rounding */
1198 pab
->disp_form
[i
].width
=
1199 ((2*pab
->disp_form
[i
].req_width
*avail_screen
)+100) / 200;
1200 used
+= pab
->disp_form
[i
].width
;
1205 space_left
= screen_width
- used
;
1209 * If they're all percentages, and the percentages add up to 100,
1210 * then we should fix the rounding problem.
1214 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++)
1215 if(pab
->disp_form
[i
].wtype
== Percent
)
1216 pc_tot
+= pab
->disp_form
[i
].req_width
;
1219 /* fix the rounding problem */
1220 if(all_percents
&& pc_tot
<= 100){
1223 int fix
= used
- screen_width
;
1229 /* find col'th column */
1230 for(i
=0, this_col
=0; i
< NFIELDS
; i
++){
1231 if(pab
->disp_form
[i
].wtype
== Percent
){
1232 if(col
== ++this_col
)
1237 pab
->disp_form
[i
].width
--;
1242 * Assume they meant to have them add up to over 100%, so we
1243 * just truncate the right hand edge.
1246 int this_fix
, space_over
;
1248 /* have to reduce space_over down to zero. */
1249 space_over
= used
- screen_width
;
1250 for(i
=NFIELDS
-1; i
>= 0 && space_over
> 0; i
--){
1251 if(pab
->disp_form
[i
].type
!= Notused
){
1252 this_fix
= MIN(pab
->disp_form
[i
].width
, space_over
);
1253 pab
->disp_form
[i
].width
-= this_fix
;
1254 space_over
-= this_fix
;
1259 else if(space_left
> 0){
1260 if(some_to_calculate
){
1261 /* make nickname big enough to show all nicknames */
1262 if(nick
>= 0 && pab
->disp_form
[nick
].wtype
== WeCalculate
){
1263 --some_to_calculate
;
1264 if(pab
->disp_form
[nick
].width
!= max_nick
){
1267 this_fix
= MIN(max_nick
-pab
->disp_form
[nick
].width
, space_left
);
1268 pab
->disp_form
[nick
].width
+= this_fix
;
1269 space_left
-= this_fix
;
1273 if(!some_to_calculate
&& space_left
> 0)
1274 goto none_to_calculate
;
1280 /* add up total weight */
1281 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1282 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1283 switch(pab
->disp_form
[i
].type
){
1285 weight
+= MAX(third_full
, pab
->disp_form
[i
].width
);
1286 used_wt
+= pab
->disp_form
[i
].width
;
1290 weight
+= MAX(third_addr
, pab
->disp_form
[i
].width
);
1291 used_wt
+= pab
->disp_form
[i
].width
;
1295 weight
+= MAX(third_fcc
, pab
->disp_form
[i
].width
);
1296 used_wt
+= pab
->disp_form
[i
].width
;
1308 if(weight
- used_wt
<= space_left
){
1310 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1312 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1313 switch(pab
->disp_form
[i
].type
){
1315 this_fix
= third_full
- pab
->disp_form
[i
].width
;
1316 space_left
-= this_fix
;
1317 pab
->disp_form
[i
].width
+= this_fix
;
1321 this_fix
= third_addr
- pab
->disp_form
[i
].width
;
1322 space_left
-= this_fix
;
1323 pab
->disp_form
[i
].width
+= this_fix
;
1327 this_fix
= third_fcc
- pab
->disp_form
[i
].width
;
1328 space_left
-= this_fix
;
1329 pab
->disp_form
[i
].width
+= this_fix
;
1338 /* if still space left and a comment field, all to comment */
1341 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1343 if(pab
->disp_form
[i
].type
== Comment
&&
1344 pab
->disp_form
[i
].wtype
== WeCalculate
){
1345 pab
->disp_form
[i
].width
+= space_left
;
1351 else{ /* not enough space, dole out weighted pieces */
1352 int was_sl
= space_left
;
1355 i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
;
1357 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1358 switch(pab
->disp_form
[i
].type
){
1361 this_fix
= (third_full
* was_sl
)/weight
;
1362 space_left
-= this_fix
;
1363 pab
->disp_form
[i
].width
+= this_fix
;
1367 this_fix
= (third_addr
* was_sl
)/weight
;
1368 space_left
-= this_fix
;
1369 pab
->disp_form
[i
].width
+= this_fix
;
1373 this_fix
= (third_fcc
* was_sl
)/weight
;
1374 space_left
-= this_fix
;
1375 pab
->disp_form
[i
].width
+= this_fix
;
1387 while(space_left
> 0){
1388 for(i
=NFIELDS
-1; i
>= 0 && space_left
> 0; i
--){
1389 if(i
!= nick
&& pab
->disp_form
[i
].wtype
== WeCalculate
){
1390 pab
->disp_form
[i
].width
++;
1399 * If they're all percentages, and the percentages add up to 100,
1400 * then we just have to fix a rounding problem. Otherwise, we'll
1401 * assume the user meant to have them add up to less than 100.
1406 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++)
1407 if(pab
->disp_form
[i
].wtype
== Percent
)
1408 pc_tot
+= pab
->disp_form
[i
].req_width
;
1411 if(all_percents
&& pc_tot
>= 100){
1414 int fix
= screen_width
- used
;
1420 /* find col'th column */
1421 for(i
=0, this_col
=0; i
< NFIELDS
; i
++){
1422 if(pab
->disp_form
[i
].wtype
== Percent
){
1423 if(col
== ++this_col
)
1428 pab
->disp_form
[i
].width
++;
1432 /* else, user specified less than 100%, leave it */
1435 /* else space_left == zero, nothing to do */
1438 * Check for special case. If we find it, this is the case where
1439 * we want to display the list entry field even though there is no
1440 * address field displayed. All of the display width is probably
1441 * used up by now, so we just need to pick some arbitrary width
1442 * for these lines. Since these lines are separate from the other
1443 * lines we've been calculating, we don't have to worry about running
1444 * into them, except for list continuation lines which we're not
1445 * going to worry about.
1447 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1448 if(pab
->disp_form
[i
].wtype
== Special
){
1449 pab
->disp_form
[i
].width
= MIN(utf8_width(CLICKHERE
), screen_width
);
1455 /* check for width changes */
1456 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1457 if(pab
->disp_form
[i
].width
!= pab
->disp_form
[i
].old_width
){
1458 ret
++; /* Tell the caller the screen changed */
1459 pab
->disp_form
[i
].old_width
= pab
->disp_form
[i
].width
;
1463 pab
->nick_is_displayed
= 0;
1464 pab
->full_is_displayed
= 0;
1465 pab
->addr_is_displayed
= 0;
1466 pab
->fcc_is_displayed
= 0;
1467 pab
->comment_is_displayed
= 0;
1468 for(i
= 0; i
< NFIELDS
&& pab
->disp_form
[i
].type
!= Notused
; i
++){
1469 if(pab
->disp_form
[i
].width
> 0){
1470 switch(pab
->disp_form
[i
].type
){
1472 pab
->nick_is_displayed
++;
1475 pab
->full_is_displayed
++;
1478 pab
->addr_is_displayed
++;
1481 pab
->fcc_is_displayed
++;
1484 pab
->comment_is_displayed
++;
1494 dprint((9, " some widths changed\n"));
1501 redraw_addr_screen(void)
1503 dprint((7, "- redraw_addr_screen -\n"));
1506 if(as
.l_p_page
<= 0)
1509 (void)calculate_field_widths();
1510 display_book(0, as
.cur_row
, -1, 1, (Pos
*)NULL
);
1515 * Little front end for address book screen so it can be called out
1516 * of the main command loop in alpine.c
1519 addr_book_screen(struct pine
*pine_state
)
1521 dprint((3, "\n\n --- ADDR_BOOK_SCREEN ---\n\n"));
1523 mailcap_free(); /* free resources we won't be using for a while */
1525 if(setjmp(addrbook_changed_unexpectedly
)){
1526 /* TRANSLATORS: a warning message telling the user that the address book
1527 is being reset (re-sychronized, restarted) */
1528 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1529 dprint((1, "RESETTING address book... addr_book_screen!\n"));
1533 ab_nesting_level
= 1; /* come here only from main menu */
1535 /* TRANSLATORS: a screen title for address book screen. Almost all of
1536 the translatable strings which are all upper case are screen titles. */
1537 (void)addr_book(AddrBookScreen
, _("ADDRESS BOOK"), NULL
);
1540 pine_state
->prev_screen
= addr_book_screen
;
1545 addr_book_config(struct pine
*pine_state
, int edit_exceptions
)
1547 if(edit_exceptions
){
1548 q_status_message(SM_ORDER
, 3, 7,
1549 _("Exception Setup not implemented for address books"));
1553 dprint((3, "\n\n --- ADDR_BOOK_CONFIG ---\n\n"));
1555 mailcap_free(); /* free resources we won't be using for a while */
1557 if(setjmp(addrbook_changed_unexpectedly
)){
1558 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1559 dprint((1, "RESETTING address book... addr_book_config!\n"));
1563 ab_nesting_level
= 1;
1565 /* TRANSLATORS: A screen title */
1566 (void)addr_book(AddrBookConfig
, _("SETUP ADDRESS BOOKS"), NULL
);
1569 pine_state
->prev_screen
= addr_book_screen
;
1574 * Return a single address
1576 * Returns: pointer to returned address, or NULL if nothing returned
1579 addr_book_oneaddr(void)
1582 jmp_buf save_jmp_buf
;
1583 int *save_nesting_level
;
1585 dprint((3, "--- addr_book_oneaddr ---\n"));
1587 save_nesting_level
= cpyint(ab_nesting_level
);
1588 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1589 if(setjmp(addrbook_changed_unexpectedly
)){
1590 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1591 dprint((1, "RESETTING address book... addr_book_oneaddr!\n"));
1593 ab_nesting_level
= *save_nesting_level
;
1598 /* TRANSLATORS: a screen title */
1599 p
= addr_book(SelectAddr
, _("SELECT ADDRESS"), NULL
);
1601 if(ab_nesting_level
<= 1)
1606 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1607 if(save_nesting_level
)
1608 fs_give((void **)&save_nesting_level
);
1615 * Return a list of addresses without fullname
1617 * Returns: pointer to returned address, or NULL if nothing returned
1620 addr_book_multaddr_nf(void)
1623 jmp_buf save_jmp_buf
;
1624 int *save_nesting_level
;
1626 dprint((3, "--- addr_book_multaddr_nf ---\n"));
1628 save_nesting_level
= cpyint(ab_nesting_level
);
1629 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1630 if(setjmp(addrbook_changed_unexpectedly
)){
1631 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1632 dprint((1, "RESETTING address book... addr_book_multaddr_nf!\n"));
1634 ab_nesting_level
= *save_nesting_level
;
1639 p
= addr_book(SelectMultNoFull
, _("SELECT ADDRESS"), NULL
);
1641 if(ab_nesting_level
<= 1)
1646 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1647 if(save_nesting_level
)
1648 fs_give((void **)&save_nesting_level
);
1655 * Return a single address without fullname phrase
1657 * Returns: pointer to returned address, or NULL if nothing returned
1660 addr_book_oneaddr_nf(void)
1663 jmp_buf save_jmp_buf
;
1664 int *save_nesting_level
;
1666 dprint((3, "--- addr_book_oneaddr_nf ---\n"));
1668 save_nesting_level
= cpyint(ab_nesting_level
);
1669 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1670 if(setjmp(addrbook_changed_unexpectedly
)){
1671 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1672 dprint((1, "RESETTING address book... addr_book_oneaddr_nf!\n"));
1674 ab_nesting_level
= *save_nesting_level
;
1679 p
= addr_book(SelectAddrNoFull
, _("SELECT ADDRESS"), NULL
);
1681 if(ab_nesting_level
<= 1)
1686 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1687 if(save_nesting_level
)
1688 fs_give((void **)&save_nesting_level
);
1695 * Call address book from message composer
1697 * Args: error_mess -- pointer to return error messages in (unused here)
1699 * Returns: pointer to returned address, or NULL if nothing returned
1702 addr_book_compose(char **error
)
1705 jmp_buf save_jmp_buf
;
1706 int *save_nesting_level
;
1708 dprint((3, "--- addr_book_compose ---\n"));
1710 save_nesting_level
= cpyint(ab_nesting_level
);
1711 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1712 if(setjmp(addrbook_changed_unexpectedly
)){
1713 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1714 dprint((1, "RESETTING address book... addr_book_compose!\n"));
1716 ab_nesting_level
= *save_nesting_level
;
1721 /* TRANSLATORS: a screen title */
1722 p
= addr_book(SelectNicksCom
, _("COMPOSER: SELECT ADDRESS"), error
);
1724 if(ab_nesting_level
<= 1)
1729 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1730 if(save_nesting_level
)
1731 fs_give((void **)&save_nesting_level
);
1738 * Call address book from message composer for Lcc line
1740 * Args: error_mess -- pointer to return error messages in (unused here)
1742 * Returns: pointer to returned address, or NULL if nothing returned
1745 addr_book_compose_lcc(char **error
)
1748 jmp_buf save_jmp_buf
;
1749 int *save_nesting_level
;
1751 dprint((3, "--- addr_book_compose_lcc ---\n"));
1753 save_nesting_level
= cpyint(ab_nesting_level
);
1754 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1755 if(setjmp(addrbook_changed_unexpectedly
)){
1756 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1757 dprint((1, "RESETTING address book... addr_book_compose_lcc!\n"));
1759 ab_nesting_level
= *save_nesting_level
;
1765 * We used to use SelectAddrLccCom here but decided it wasn't necessary
1766 * to restrict the selection to a list.
1768 /* TRANSLATORS: a screen title, user is composing a message and should select
1769 a distribution list from a list of addresses in the address book. */
1770 p
= addr_book(SelectNicksCom
, _("COMPOSER: SELECT LIST"), error
);
1772 if(ab_nesting_level
<= 1)
1777 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1778 if(save_nesting_level
)
1779 fs_give((void **)&save_nesting_level
);
1786 * Call address book from message composer for Lcc line
1788 * Args: error_mess -- pointer to return error messages in (unused here)
1790 * Returns: pointer to returned address, or NULL if nothing returned
1793 addr_book_change_list(char **error
)
1796 jmp_buf save_jmp_buf
;
1797 int *save_nesting_level
;
1799 dprint((3, "--- addr_book_change_list ---\n"));
1801 save_nesting_level
= cpyint(ab_nesting_level
);
1802 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1803 if(setjmp(addrbook_changed_unexpectedly
)){
1804 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1805 dprint((1, "RESETTING address book... addr_book_change_list!\n"));
1807 ab_nesting_level
= *save_nesting_level
;
1812 /* TRANSLATORS: a screen title */
1813 p
= addr_book(SelectNicksCom
, _("ADDRESS BOOK (Update): SELECT ADDRESSES"),
1816 if(ab_nesting_level
<= 1)
1821 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1822 if(save_nesting_level
)
1823 fs_give((void **)&save_nesting_level
);
1830 * Call address book from pine_simple_send
1832 * Returns: pointer to returned address, or NULL if nothing returned
1835 addr_book_bounce(void)
1838 jmp_buf save_jmp_buf
;
1839 int *save_nesting_level
;
1841 dprint((3, "- addr_book_bounce -\n"));
1843 save_nesting_level
= cpyint(ab_nesting_level
);
1844 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1845 if(setjmp(addrbook_changed_unexpectedly
)){
1846 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1847 dprint((1, "RESETTING address book...addr_book_bounce!\n"));
1849 ab_nesting_level
= *save_nesting_level
;
1854 p
= addr_book(SelectManyNicks
, _("SELECT ADDRESSES"), NULL
);
1856 if(ab_nesting_level
<= 1)
1861 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1862 if(save_nesting_level
)
1863 fs_give((void **)&save_nesting_level
);
1870 * Call address book from take address screen
1872 * Returns: pointer to returned nickname, or NULL if nothing returned
1875 addr_book_takeaddr(void)
1878 jmp_buf save_jmp_buf
;
1879 int *save_nesting_level
;
1881 dprint((3, "- addr_book_takeaddr -\n"));
1883 save_nesting_level
= cpyint(ab_nesting_level
);
1884 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1885 if(setjmp(addrbook_changed_unexpectedly
)){
1886 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1887 dprint((1, "RESETTING address book...addr_book_takeaddr!\n"));
1889 ab_nesting_level
= *save_nesting_level
;
1894 /* TRANSLATORS: a screen title */
1895 p
= addr_book(SelectNickTake
, _("TAKEADDR: SELECT NICKNAME"), NULL
);
1897 if(ab_nesting_level
<= 1)
1902 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1903 if(save_nesting_level
)
1904 fs_give((void **)&save_nesting_level
);
1911 * Call address book from editing screen for nickname field.
1913 * Returns: pointer to returned nickname, or NULL if nothing returned
1916 addr_book_nick_for_edit(char **error
)
1919 jmp_buf save_jmp_buf
;
1920 int *save_nesting_level
;
1923 dprint((3, "- addr_book_nick_for_edit -\n"));
1925 save_n_serv
= as
.n_serv
;
1927 save_nesting_level
= cpyint(ab_nesting_level
);
1928 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1929 if(setjmp(addrbook_changed_unexpectedly
)){
1930 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1931 dprint((1, "RESETTING address book...addr_book_nick_for_edit!\n"));
1933 ab_nesting_level
= *save_nesting_level
;
1939 * This is kind of hoaky. We want to prevent the Directory Query
1940 * options from turning on when we're coming in looking for a nickname
1941 * and this seemed to be the easiest way to accomplish that.
1944 /* TRANSLATORS: a screen title, user selecting a nickname from the address book */
1945 p
= addr_book(SelectNickCom
, _("SELECT NICKNAME"), error
);
1946 as
.n_serv
= save_n_serv
;
1948 if(ab_nesting_level
<= 1)
1953 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1954 if(save_nesting_level
)
1955 fs_give((void **)&save_nesting_level
);
1962 * Call address book for generic nickname select
1964 * Returns: pointer to returned nickname, or NULL if nothing returned
1967 addr_book_selnick(void)
1970 jmp_buf save_jmp_buf
;
1971 int *save_nesting_level
;
1973 dprint((3, "- addr_book_selnick -\n"));
1975 save_nesting_level
= cpyint(ab_nesting_level
);
1976 memcpy(save_jmp_buf
, addrbook_changed_unexpectedly
, sizeof(jmp_buf));
1977 if(setjmp(addrbook_changed_unexpectedly
)){
1978 q_status_message(SM_ORDER
, 5, 10, _("Resetting address book..."));
1979 dprint((1, "RESETTING address book...addr_book_selnick!\n"));
1981 ab_nesting_level
= *save_nesting_level
;
1986 p
= addr_book(SelectNick
, _("SELECT NICKNAME"), NULL
);
1988 if(ab_nesting_level
<= 1)
1993 memcpy(addrbook_changed_unexpectedly
, save_jmp_buf
, sizeof(jmp_buf));
1994 if(save_nesting_level
)
1995 fs_give((void **)&save_nesting_level
);
2002 * Main address book screen
2004 * Control loop for address book. Commands are executed out of a big
2005 * switch and screen painting is done.
2006 * The style argument controls whether or not it is called to return an address
2007 * to the composer, a nickname to the TakeAddr screen, or just for address
2010 * Args: style -- how we were called
2012 * Return: might return a string for the composer to edit, or a nickname, ...
2015 addr_book(AddrBookArg style
, char *title
, char **error_message
)
2021 quit
, /* loop control */
2022 km_popped
, /* menu is popped up in blank menu mode */
2023 current_changed_flag
, /* only current row needs update */
2024 was_clickable
, is_clickable
,
2025 was_collapsible_listent
, is_collapsible_listent
,
2026 was_global_config
, is_global_config
,
2027 was_addkey
, is_addkey
,
2028 was_opened
, is_opened
,
2029 was_custom_title
, is_custom_title
,
2031 start_disp
, /* Paint from this line down (0 is top) */
2032 rdonly
, /* cur addrbook read only */
2033 empty
, /* cur addrbook empty */
2034 are_selecting
, /* called as ^T selector */
2036 directory_ok
, /* called from composer, not Lcc */
2038 from_composer
, /* from composer */
2039 listmode_ok
, /* ok to do ListMode with this style */
2041 selecting_mult_nicks
,
2042 checkedn
, /* how many are checked */
2043 def_key
, /* default key */
2044 warped
; /* we warped through hyperspace to a
2045 new location in the display list */
2047 new_top_ent
, /* entry on top of screen after oper */
2048 new_line
; /* new line number after operation */
2052 struct key_menu
*km
;
2054 PerAddrBook
*pab
= NULL
;
2059 dprint((2, "--- addr_book --- (%s)\n",
2060 style
==AddrBookScreen
? "AddrBookScreen" :
2061 style
==SelectAddrLccCom
? "SelectAddrLccCom" :
2062 style
==SelectNicksCom
? "SelectNicksCom" :
2063 style
==SelectNick
? "SelectNick" :
2064 style
==SelectNickTake
? "SelectNickTake" :
2065 style
==SelectNickCom
? "SelectNickCom" :
2066 style
==AddrBookConfig
? "AddrBookConfig" :
2067 style
==SelectManyNicks
? "SelectManyNicks" :
2068 style
==SelectAddr
? "SelectAddr" :
2069 style
==SelectAddrNoFull
? "SelectAddrNoFull" :
2070 style
==SelectMultNoFull
? "SelectMultNoFull":
2075 ps
->next_screen
= SCREEN_FUN_NULL
;
2077 from_composer
= (style
== SelectAddrLccCom
2078 || style
== SelectNicksCom
2079 || style
== SelectNickCom
);
2080 are_selecting
= (style
!= AddrBookScreen
2081 && style
!= AddrBookConfig
);
2082 selecting_one_nick
= (style
== SelectNick
2083 || style
== SelectNickTake
2084 || style
== SelectNickCom
);
2085 selecting_mult_nicks
= (style
== SelectAddrLccCom
2086 || style
== SelectNicksCom
2087 || style
== SelectManyNicks
);
2088 listmode_ok
= (style
== SelectAddrLccCom
2089 || style
== SelectNicksCom
2090 || style
== SelectManyNicks
2091 || style
== SelectMultNoFull
);
2092 as
.config
= (style
== AddrBookConfig
);
2094 directory_ok
= (style
== SelectNicksCom
2095 || style
== AddrBookScreen
2096 || style
== SelectManyNicks
);
2099 /* Coming in from the composer, may need to reset the window */
2103 mark_status_dirty();
2104 mark_titlebar_dirty();
2105 mark_keymenu_dirty();
2108 command_line
= -FOOTER_ROWS(ps
); /* third line from the bottom */
2110 c
= 'x'; /* For display_message the first time through */
2112 if(ps
->remote_abook_validity
> 0)
2113 (void)adrbk_check_and_fix_all(ab_nesting_level
== 1, 0, 0);
2115 if(!init_addrbooks(HalfOpen
, 1, !as
.config
, !are_selecting
)){
2117 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
2118 _("No Address Book Configured"));
2123 else if(!as
.config
){
2124 ps
->next_screen
= main_menu_screen
;
2125 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2126 _("No Address Book Configured, Use SETUP Addressbook screen"));
2127 ps
->mangled_screen
= 1;
2131 else if(style
== AddrBookScreen
&& as
.n_addrbk
== 1 && as
.n_serv
== 0){
2132 if(as
.adrbks
[0].access
== ReadOnly
)
2133 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
2134 else if(as
.adrbks
[0].access
== NoAccess
)
2135 q_status_message(SM_ORDER
, 0, 4,
2136 _("AddressBook not accessible, permission denied"));
2139 if(as
.l_p_page
< 1){
2140 q_status_message(SM_ORDER
, 3, 3,
2141 _("Screen too small to use Address book"));
2149 (void) calculate_field_widths();
2153 ps
->mangled_screen
= 1;
2154 current_changed_flag
= 0;
2158 was_collapsible_listent
= 0;
2159 is_collapsible_listent
= 0;
2160 was_global_config
= 0;
2161 is_global_config
= 0;
2166 was_custom_title
= 0;
2167 is_custom_title
= 0;
2173 ps
->user_says_cancel
= 0;
2179 * Have to repaint from earliest change down, including
2180 * at least the last two body lines.
2182 if(ps
->mangled_body
) /* it was already mangled */
2183 start_disp
= MIN(start_disp
, as
.l_p_page
-2);
2184 else if(current_changed_flag
){
2185 ps
->mangled_body
= 1;
2186 start_disp
= MIN(MIN(as
.cur_row
, as
.l_p_page
-2),
2190 ps
->mangled_body
= 1;
2191 start_disp
= as
.l_p_page
-2;
2196 ps
->redrawer
= redraw_addr_screen
;
2198 if(new_mail(0, NM_TIMING(c
), NM_STATUS_MSG
| NM_DEFER_SORT
) >= 0)
2199 ps
->mangled_header
= 1;
2202 ps
->mangled_header
= 1;
2204 if(ps
->mangled_screen
){
2205 ps
->mangled_header
= 1;
2206 ps
->mangled_body
= 1;
2207 ps
->mangled_footer
= 1;
2209 current_changed_flag
= 0;
2210 ps
->mangled_screen
= 0;
2213 if(ps
->mangled_body
){
2215 if(calculate_field_widths())
2218 display_book(start_disp
,
2224 as
.old_cur_row
= as
.cur_row
;
2225 ps
->mangled_body
= 0;
2227 as
.cur
= cur_addr_book();
2228 pab
= (as
.n_addrbk
&&
2229 as
.cur
>= 0 && as
.cur
< as
.n_addrbk
&&
2230 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
2231 ? &as
.adrbks
[as
.cur
] : NULL
;
2237 for(i
= as
.top_ent
; dlist(i
)->type
!= End
; i
++)
2238 next_selectable_line(i
,&last_sel
);
2241 scroll_setrange(as
.l_p_page
, last_sel
);
2245 /* current entry has been changed */
2246 else if(current_changed_flag
){
2249 need_redraw
= calculate_field_widths();
2251 /*---------- Update the current entry, (move or change) -------*/
2252 display_book(need_redraw
? 0 : as
.cur_row
,
2258 as
.old_cur_row
= as
.cur_row
;
2259 current_changed_flag
= 0;
2260 as
.cur
= cur_addr_book();
2261 pab
= (as
.n_addrbk
&&
2262 as
.cur
>= 0 && as
.cur
< as
.n_addrbk
&&
2263 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
2264 ? &as
.adrbks
[as
.cur
] : NULL
;
2267 is_custom_title
= (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) &&
2268 style
== AddrBookScreen
&&
2273 if(( was_custom_title
&& !is_custom_title
) ||
2274 (!was_custom_title
&& is_custom_title
)){
2275 ps
->mangled_header
= 1;
2276 was_custom_title
= is_custom_title
;
2280 if(ps
->mangled_header
){
2283 if(style
== AddrBookScreen
){
2284 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
2286 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOKS"));
2288 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOK"));
2291 /* TRANSLATORS: a screen title, the %s arguments are for a custom part
2292 of the title. Move them as a group. There are two normal cases.
2293 Either ADDRESS BOOK LIST when a list of names of address books is
2294 displayed, or ADDRESS BOOK <name of address book> when one is open. */
2295 snprintf(buf
, sizeof(buf
), _("ADDRESS BOOK%s%s%s"),
2296 /* TRANSLATORS: This is the LIST that goes with title above */
2297 is_custom_title
? " <" : cur_is_open() ? "" : _(" LIST"),
2298 is_custom_title
? pab
->abnick
: "",
2299 is_custom_title
? ">" : "");
2301 buf
[sizeof(buf
)-1] = '\0';
2308 set_titlebar(bp
, ps
->mail_stream
,
2309 ps
->context_current
, ps
->cur_folder
,
2311 FolderName
, 0, 0, NULL
);
2312 ps
->mangled_header
= 0;
2315 dprint((9, "addr_book: top of loop, addrbk %d top_ent %ld cur_row %d\n", as
.cur
, as
.top_ent
, as
.cur_row
));
2318 * This is a check to catch when a move from one row to another
2319 * should cause the list of commands to change.
2321 is_clickable
= entry_is_clickable(as
.top_ent
+as
.cur_row
);
2322 is_collapsible_listent
= F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2323 entry_is_listent(as
.top_ent
+as
.cur_row
);
2324 is_global_config
= as
.config
&& pab
&& pab
->type
& GLOBAL
;
2325 is_addkey
= entry_is_addkey(as
.top_ent
+as
.cur_row
);
2326 is_opened
= (F_OFF(F_CMBND_ABOOK_DISP
,ps_global
) &&
2328 if(( was_clickable
&& !is_clickable
) ||
2329 (!was_clickable
&& is_clickable
) ||
2330 ( was_collapsible_listent
&& !is_collapsible_listent
) ||
2331 (!was_collapsible_listent
&& is_collapsible_listent
) ||
2332 ( was_global_config
&& !is_global_config
) ||
2333 (!was_global_config
&& is_global_config
) ||
2334 ( was_addkey
&& !is_addkey
) ||
2335 (!was_addkey
&& is_addkey
) ||
2336 ( was_opened
&& !is_opened
) ||
2337 (!was_opened
&& is_opened
) ||
2340 ps
->mangled_footer
= 1;
2341 was_clickable
= is_clickable
;
2342 was_collapsible_listent
= is_collapsible_listent
;
2343 was_global_config
= is_global_config
;
2344 was_addkey
= is_addkey
;
2345 was_opened
= is_opened
;
2350 if(ps
->mangled_footer
){
2353 menu_clear_binding(km
, '>');
2354 menu_clear_binding(km
, '.');
2355 menu_clear_binding(km
, KEY_RIGHT
);
2356 menu_clear_binding(km
, ctrl('M'));
2357 menu_clear_binding(km
, ctrl('J'));
2358 menu_clear_binding(km
, KEY_LEFT
);
2359 menu_clear_binding(km
, '<');
2360 menu_clear_binding(km
, ',');
2361 def_key
= THREE_KEY
; /* default default key */
2365 clrbitn(OTHER_KEY
, bitmap
);
2366 /* TRANSLATORS: a command name for a particular key */
2367 menu_init_binding(km
, 'E', MC_EXIT
, "E", N_("Exit Setup"), TWO_KEY
);
2368 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_EXITMODE
);
2371 * Don't show Delete or Shuffle command if there is nothing
2372 * to delete or shuffle.
2375 clrbitn(DELETE_KEY
, bitmap
);
2376 clrbitn(SENDTO_KEY
, bitmap
);
2377 clrbitn(THREE_KEY
, bitmap
);
2378 menu_init_binding(km
, 'A', MC_ADDABOOK
, "A",
2379 add_is_global(as
.top_ent
+as
.cur_row
)
2380 /* TRANSLATORS: Add Global Address book (one defined by someone else) */
2381 ? "[" N_("Add Glob Abook") "]"
2382 /* TRANSLATORS: Add Personal Address book */
2383 : "[" N_("Add Pers Abook") "]",
2388 /* TRANSLATORS: Delete Address book command */
2389 menu_init_binding(km
, 'D', MC_DELABOOK
, "D", N_("Del Abook"),
2391 /* TRANSLATORS: Shuffle refers to shuffling the order of things,
2392 that is, changing the order */
2393 menu_init_binding(km
, '$', MC_SHUFFLE
, "$", N_("Shuffle"),
2395 /* TRANSLATORS: Change is a command meaning Change some item,
2396 or Edit some item to change it */
2397 menu_init_binding(km
, 'C', MC_EDITABOOK
, "C", "[" N_("Change") "]",
2399 menu_init_binding(km
, 'A', MC_ADDABOOK
, "A",
2400 add_is_global(as
.top_ent
+as
.cur_row
)
2401 ? N_("Add Glob Abook")
2402 : N_("Add Pers Abook"),
2406 else if(are_selecting
){
2410 * The OTHER_KEY is used as the Exit key in selection mode.
2411 * This is because the TWO_KEY is being used for < actions.
2413 /* TRANSLATORS: Command to Exit the Select screen. This would
2414 be a way to make no Selection and go back to where you
2416 menu_init_binding(km
, 'E', MC_EXIT
, "E", N_("ExitSelect"),
2418 KS_OSDATASET(&km
->keys
[OTHER_KEY
], KS_EXITMODE
);
2421 * Use the TWO_KEY for the go back key.
2423 if(cur_is_open() && (as
.n_addrbk
> 1 || as
.n_serv
)){
2424 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2425 entry_is_listent(as
.top_ent
+as
.cur_row
))
2427 else if(F_OFF(F_CMBND_ABOOK_DISP
,ps_global
))
2433 clrbitn(TWO_KEY
, bitmap
);
2435 menu_init_binding(km
, '<', cmd
, "<",
2436 cmd
== MC_POPUP
? N_("AddressBkList")
2437 /* TRANSLATORS: Unexpand is the opposite of Expand.
2438 We might expand an address book distribution
2439 list to see all the members of the list. */
2442 menu_add_binding(km
, ',', cmd
);
2443 if(F_ON(F_ARROW_NAV
,ps
))
2444 menu_add_binding(km
, KEY_LEFT
, cmd
);
2447 else if(as
.checkboxes
&& (as
.n_addrbk
> 1 || as
.n_serv
)){
2449 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2450 menu_init_binding(km
, 'S', MC_CHOICE
, "S",
2451 /* TRANSLATORS: Select something, choose something */
2452 N_("Select"), TWO_KEY
);
2455 menu_init_binding(km
, 'S', MC_CHOICE
, "S",
2456 "[" N_("Select") "]", TWO_KEY
);
2461 menu_init_binding(km
, 'S', MC_CHOICE
, "S", N_("Select"),
2465 clrbitn(TWO_KEY
, bitmap
);
2468 * The THREE_KEY is used as the select key in selection mode,
2469 * but it doesn't show up at the top-level. Instead, the
2470 * key becomes the ViewAbook key.
2472 if(entry_is_askserver(as
.top_ent
+as
.cur_row
) && !as
.checkboxes
){
2473 menu_init_binding(km
, '>', MC_QUERY_SERV
, ">", "[" N_("Search") "]",
2475 menu_add_binding(km
, 's', MC_QUERY_SERV
);
2476 menu_add_binding(km
, '.', MC_QUERY_SERV
);
2477 if(F_ON(F_ARROW_NAV
,ps
))
2478 menu_add_binding(km
, KEY_RIGHT
, MC_QUERY_SERV
);
2480 else if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2481 /* TRANSLATORS: View this address book */
2482 menu_init_binding(km
, '>', MC_OPENABOOK
, ">", "[" N_("ViewAbook") "]",
2484 menu_add_binding(km
, 'v', MC_OPENABOOK
);
2485 menu_add_binding(km
, '.', MC_OPENABOOK
);
2486 if(F_ON(F_ARROW_NAV
,ps
))
2487 menu_add_binding(km
, KEY_RIGHT
, MC_OPENABOOK
);
2489 else if(cur_is_open()){
2490 menu_init_binding(km
, 'S', MC_CHOICE
, "S", "[" N_("Select") "]",
2494 clrbitn(THREE_KEY
, bitmap
);
2496 KS_OSDATASET(&km
->keys
[THREE_KEY
], KS_NONE
);
2499 * The Expand command gets stuck out in right field.
2501 if(entry_is_clickable(as
.top_ent
+as
.cur_row
) &&
2502 !entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
2503 menu_init_binding(km
, '>', MC_EXPAND
, ">", N_("Expand"),
2505 menu_add_binding(km
, '.', MC_EXPAND
);
2506 if(F_ON(F_ARROW_NAV
,ps
))
2507 menu_add_binding(km
, KEY_RIGHT
, MC_EXPAND
);
2510 clrbitn(SENDTO_KEY
, bitmap
);
2512 if(cur_is_open() && as
.checkboxes
){
2513 /* TRANSLATORS: Set/Unset means that this particular command
2514 will toggle between setting something (turning it on) and
2515 unsetting it (turning it off). For example, it might be
2516 a program option that can be turned on or off or it might
2517 be a way to mark which addresses to send a message to. */
2518 menu_init_binding(km
, 'X', MC_TOGGLE
, "X", N_("Set/Unset"),
2522 else if(cur_is_open() && listmode_ok
){
2523 /* TRANSLATORS: List mode is a type of screen in pine that
2524 allows the user to select several of something. This is
2525 the name of the command to go into the List mode style
2527 menu_init_binding(km
, 'L', MC_LISTMODE
, "L", N_("ListMode"),
2531 clrbitn(DELETE_KEY
, bitmap
);
2533 if(cur_is_open() && as
.checkboxes
){
2534 menu_init_binding(km
, 'A', MC_SELALL
, "A",
2535 /* TRANSLATORS: when selecting from a list of items
2536 the unsetall (unset all) means to start over
2537 with nothing selected.
2538 The set all command means select everything
2540 checkedn
? N_("unsetAll") : N_("setAll"),
2544 clrbitn(ADD_KEY
, bitmap
);
2546 KS_OSDATASET(&km
->keys
[DELETE_KEY
], KS_NONE
);
2550 * Reset first Other key. Selection screen may have
2551 * blasted it. Do this by hand because menu_init_binding
2552 * will remove the other two OTHER CMDS bindings.
2553 * Should figure out how to do this correctly with a
2554 * reasonable function call.
2556 km
->keys
[OTHER_KEY
].name
= "O";
2557 /* TRANSLATORS: This is the name of the command that will show
2558 which other commands are available. 12 commands are shown at
2559 the bottom of the screen, this command would show the next set
2561 km
->keys
[OTHER_KEY
].label
= N_("OTHER CMDS");
2562 km
->keys
[OTHER_KEY
].bind
.cmd
= MC_OTHER
;
2563 km
->keys
[OTHER_KEY
].bind
.ch
[0] = 'O';
2564 km
->keys
[OTHER_KEY
].bind
.nch
= 1;
2565 KS_OSDATASET(&km
->keys
[OTHER_KEY
], KS_NONE
);
2569 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
2570 if(F_ON(F_ENABLE_AGG_OPS
, ps
))
2574 * The TWO_KEY is used as the go back key in
2575 * non-selection mode.
2577 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2578 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2580 menu_init_binding(km
, '<', cmd
, "<", N_("Unexpand"),
2582 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2586 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2588 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2593 * Add or delete entries from this address book.
2595 /* TRANSLATORS: Add a new entry (this is a command) */
2596 menu_init_binding(km
, '@', MC_ADD
, "@", N_("AddNew"),
2598 menu_init_binding(km
, 'D', MC_DELETE
, "D", N_("Delete"),
2600 /* TRANSLATORS: Compose a message to be sent to the current address
2602 menu_init_binding(km
, 'C', MC_COMPOSE
, "C", N_("ComposeTo"),
2604 KS_OSDATASET(&km
->keys
[SENDTO_KEY
], KS_COMPOSER
);
2605 menu_init_binding(km
, '#', MC_ROLE
, "#", N_("Role"),
2609 clrbitn(ADD_KEY
, bitmap
);
2610 clrbitn(DELETE_KEY
, bitmap
);
2611 clrbitn(SENDTO_KEY
, bitmap
);
2612 clrbitn(RCOMPOSE_KEY
, bitmap
);
2613 clrbitn(SAVE_KEY
, bitmap
);
2614 clrbitn(TAKE_KEY
, bitmap
);
2615 clrbitn(FORW_KEY
, bitmap
);
2618 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2620 else if(cur_is_open()){
2621 if(F_ON(F_ENABLE_AGG_OPS
, ps
))
2625 * The TWO_KEY is used as the go back key in
2626 * non-selection mode.
2628 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2629 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2631 menu_init_binding(km
, '<', cmd
, "<", N_("Unexpand"),
2633 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2636 if(as
.n_addrbk
> 1 || as
.n_serv
){
2638 menu_init_binding(km
, '<', cmd
, "<",
2639 N_("AddressBkList"), TWO_KEY
);
2640 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_NONE
);
2644 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2646 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2650 if(pab
->access
!= NoAccess
){
2652 * Add or delete entries from this address book.
2654 menu_init_binding(km
, '@', MC_ADD
, "@", N_("AddNew"),
2656 menu_init_binding(km
, 'D', MC_DELETE
, "D", N_("Delete"),
2660 clrbitn(ADD_KEY
, bitmap
);
2661 clrbitn(DELETE_KEY
, bitmap
);
2664 /* Find someplace to put Main Menu command */
2665 if(cmd
== MC_POPUP
){
2666 menu_init_binding(km
, 'M', MC_MAIN
, "M", N_("Main Menu"),
2667 SECONDARY_MAIN_KEY
);
2668 KS_OSDATASET(&km
->keys
[SECONDARY_MAIN_KEY
],KS_MAINMENU
);
2671 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2673 menu_init_binding(km
, 'C', MC_COMPOSE
, "C", N_("ComposeTo"),
2675 KS_OSDATASET(&km
->keys
[SENDTO_KEY
], KS_COMPOSER
);
2676 menu_init_binding(km
, '#', MC_ROLE
, "#", N_("Role"),
2681 * The TWO_KEY is used as the go back key in
2682 * non-selection mode.
2685 menu_init_binding(km
, 'M', cmd
, "<", N_("Main Menu"),
2687 KS_OSDATASET(&km
->keys
[TWO_KEY
], KS_MAINMENU
);
2689 clrbitn(SENDTO_KEY
, bitmap
);
2690 clrbitn(RCOMPOSE_KEY
, bitmap
);
2691 clrbitn(ADD_KEY
, bitmap
);
2692 clrbitn(DELETE_KEY
, bitmap
);
2693 clrbitn(SECONDARY_MAIN_KEY
, bitmap
);
2694 clrbitn(SAVE_KEY
, bitmap
);
2695 clrbitn(TAKE_KEY
, bitmap
);
2696 clrbitn(FORW_KEY
, bitmap
);
2699 /* can't be on third menu if we just reduced to 2 */
2700 if(km
->how_many
== 2 && km
->which
== 2)
2703 menu_add_binding(km
, '<', cmd
);
2704 menu_add_binding(km
, ',', cmd
);
2705 if(F_ON(F_ARROW_NAV
,ps
))
2706 menu_add_binding(km
, KEY_LEFT
, cmd
);
2708 KS_OSDATASET(&km
->keys
[DELETE_KEY
], KS_DELETE
);
2711 * The THREE_KEY is the burrow into the hierarchy key.
2713 if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
2714 cmd
= MC_QUERY_SERV
;
2715 else if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
2716 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
))
2722 cmd
= MC_VIEW_ENTRY
;
2724 menu_init_binding(km
, '>', cmd
, ">",
2725 cmd
== MC_EXPAND
? "[" N_("Expand") "]" :
2726 cmd
== MC_QUERY_SERV
? "[" N_("Search") "]" :
2727 /* TRANSLATORS: In the address book the user can
2728 view a particular address book entry. It is
2729 called View/Update because the way to update
2730 an entry is to first view it and then there
2731 will be an opportunity to update it from there. */
2732 cur_is_open() ? "[" N_("View/Update") "]"
2733 : "[" N_("ViewAbook") "]",
2736 if(cmd
== MC_QUERY_SERV
)
2737 menu_add_binding(km
, 's', cmd
);
2738 else if(cmd
== MC_OPENABOOK
|| cmd
== MC_VIEW_ENTRY
)
2739 menu_add_binding(km
, 'v', cmd
);
2741 menu_add_binding(km
, '.', cmd
);
2742 if(F_ON(F_ARROW_NAV
,ps
))
2743 menu_add_binding(km
, KEY_RIGHT
, cmd
);
2746 menu_add_binding(km
, ctrl('M'), km
->keys
[def_key
].bind
.cmd
);
2747 menu_add_binding(km
, ctrl('J'), km
->keys
[def_key
].bind
.cmd
);
2750 FOOTER_ROWS(ps
) = 3;
2754 draw_keymenu(km
, bitmap
, ps
->ttyo
->screen_cols
,
2755 1-FOOTER_ROWS(ps
), 0, what
);
2756 ps
->mangled_footer
= 0;
2759 FOOTER_ROWS(ps
) = 1;
2760 mark_keymenu_dirty();
2764 rdonly
= (pab
&& pab
->access
== ReadOnly
);
2765 empty
= is_empty(as
.cur_row
+as
.top_ent
);
2767 /*------------ display any status messages ------------------*/
2769 FOOTER_ROWS(ps
) = 3;
2770 mark_status_unknown();
2775 FOOTER_ROWS(ps
) = 1;
2776 mark_status_unknown();
2779 if(F_OFF(F_SHOW_CURSOR
, ps
)){
2780 /* reset each time through to catch screen size changes */
2781 cursor_pos
.row
= ps
->ttyo
->screen_rows
-FOOTER_ROWS(ps
);
2785 MoveCursor(cursor_pos
.row
, cursor_pos
.col
);
2788 /*---------------- Get command and validate -------------------*/
2790 mouse_in_content(KEY_MOUSE
, -1, -1, 0, 0);
2791 register_mfunc(mouse_in_content
, HEADER_ROWS(ps
), 0,
2792 ps
->ttyo
->screen_rows
-(FOOTER_ROWS(ps
)+1),
2793 ps
->ttyo
->screen_cols
);
2796 /* sort out what help needs to be displayed if asked for */
2798 gAbookHelp
= h_abook_config
;
2799 else if(are_selecting
){
2801 if(style
== SelectNickTake
)
2802 gAbookHelp
= h_abook_select_nicks_take
;
2803 else if(selecting_one_nick
)
2804 gAbookHelp
= h_abook_select_nick
;
2805 else if(as
.checkboxes
)
2806 gAbookHelp
= h_abook_select_checks
;
2807 else if(listmode_ok
)
2808 gAbookHelp
= h_abook_select_listmode
;
2810 gAbookHelp
= h_abook_select_addr
;
2813 gAbookHelp
= h_abook_select_top
;
2816 gAbookHelp
= cur_is_open() ? h_abook_opened
: h_abook_top
;
2819 mswin_setscrollcallback(addr_scroll_callback
);
2820 mswin_sethelptextcallback(pcpine_help_addrbook
);
2822 c
= READ_COMMAND(&utf8str
);
2824 clear_mfunc(mouse_in_content
);
2827 mswin_setscrollcallback(NULL
);
2828 mswin_sethelptextcallback(NULL
);
2830 cmd
= menu_command(c
, km
);
2832 dprint((2, "Addrbook command: %d (0x%x %s)\n", cmd
,
2833 c
, pretty_command(c
)));
2835 /* this may be a safe place to update addrbooks */
2836 if(ps
->remote_abook_validity
> 0 &&
2837 adrbk_check_and_fix_all(ab_nesting_level
< 2 &&
2838 !any_ab_open() && checkedn
== 0, 0, 0))
2839 ps
->mangled_footer
= 1;
2855 /*------------- execute command ----------------*/
2858 /*------------ Noop (new mail check) --------------*/
2863 /*----------- Help -------------------*/
2865 if(FOOTER_ROWS(ps
) == 1 && km_popped
== 0){
2867 ps
->mangled_footer
= 1;
2872 helper(gAbookHelp
, _("HELP ON CONFIGURING ADDRESS BOOKS"),
2874 else if(are_selecting
)
2875 helper(gAbookHelp
, _("HELP ON ADDRESS BOOK"),
2876 HLPD_SIMPLE
| HLPD_NEWWIN
);
2877 else /* general maintenance screen */
2878 helper(gAbookHelp
, _("HELP ON ADDRESS BOOK"), HLPD_NONE
);
2881 * Helper() may have a Main Menu key. If user types that
2882 * they'll set next_screen. We don't have to do anything
2883 * special but we want to make sure that that doesn't happen
2884 * when we're selecting, even though it shouldn't be
2885 * possible because HLPD_SIMPLE is set.
2888 ps
->next_screen
= SCREEN_FUN_NULL
; /* probably not needed */
2890 ps
->mangled_screen
= 1;
2894 /*---------- display other key bindings ------*/
2898 ps
->mangled_footer
= 1;
2902 /*------------ Unexpand list -----------------*/
2904 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
) &&
2905 entry_is_listent(as
.top_ent
+as
.cur_row
)){
2906 DL_CACHE_S
*dlc_to_flush
;
2907 long global_row_num
;
2914 * redraw screen starting with first ListEnt
2916 dl
= dlist(as
.top_ent
+as
.cur_row
);
2917 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
2920 * New cur_row should be the line after the ListHead line
2921 * that takes the place of the first address in the list.
2923 as
.cur_row
= as
.cur_row
- dl
->l_offset
;
2926 * If the list header for the new row is off the screen,
2929 if(as
.cur_row
< 0){ /* center it */
2930 global_row_num
= dlc_to_flush
->global_row
-
2931 dlc_to_flush
->dlcoffset
;
2932 as
.top_ent
= first_line(global_row_num
- as
.l_p_page
/2L);
2933 as
.cur_row
= global_row_num
- as
.top_ent
;
2936 else if(as
.cur_row
== 0){ /* just slide up in this case */
2942 start_disp
= as
.cur_row
;
2944 exp_unset_expanded(pab
->address_book
->exp
,
2945 (a_c_arg_t
)dlc_to_flush
->dlcelnum
);
2946 flush_dlc_from_cache(dlc_to_flush
);
2947 ps
->mangled_body
= 1;
2948 ps
->mangled_footer
= 1;
2951 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2952 "Can't happen in MC_UNEXPAND");
2956 /*------ Popup to top level display ----------*/
2958 if(F_ON(F_EXPANDED_DISTLISTS
,ps
) ||
2959 !entry_is_listent(as
.top_ent
+as
.cur_row
)){
2960 DL_CACHE_S dlc_restart
;
2963 (void)init_addrbooks((checkedn
|| as
.selections
)
2964 ? ThreeQuartOpen
: HalfOpen
,
2965 0, 0, !are_selecting
);
2966 dlc_restart
.adrbk_num
= as
.cur
;
2967 dlc_restart
.type
= DlcTitle
;
2968 warp_to_dlc(&dlc_restart
, 0L);
2970 * Put the current entry in a nice spot on the screen.
2971 * Will everything above fit and still leave ours on screen?
2973 new_row
= LINES_PER_ABOOK
* as
.cur
+
2974 (((pab
->type
& GLOBAL
) &&
2975 (as
.how_many_personals
> 0 || as
.config
))
2976 ? XTRA_LINES_BETWEEN
: 0) +
2977 ((as
.how_many_personals
== 0 && as
.config
)
2978 ? LINES_PER_ADD_LINE
: 0);
2979 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
2981 as
.cur_row
= new_row
;
2982 as
.top_ent
= 0L - as
.cur_row
;
2984 ps
->mangled_screen
= 1;
2987 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
2988 "Can't happen in MC_POPUP");
2993 /*------------- Back to main menu or exit to caller -------*/
2997 ps
->next_screen
= main_menu_screen
;
2999 if(!(are_selecting
&& as
.checkboxes
&& checkedn
> 0)
3000 /* TRANSLATORS: we are asking for confirmation about abandonding selections
3001 in the address book. */
3002 || want_to(_("Really abandon your selections "),
3003 'y', 'x', NO_HELP
, WT_NORM
) == 'y')
3006 ps
->next_screen
= SCREEN_FUN_NULL
;
3011 /*------- Open an address book ----------*/
3014 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
3015 DL_CACHE_S
*dlc_to_flush
;
3017 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3018 if(dlc_to_flush
->type
== DlcTitle
||
3019 dlc_to_flush
->type
== DlcClickHereCmb
){
3022 * open this addrbook and fill in display list
3025 init_abook(pab
, Open
);
3027 if(pab
->access
== ReadWrite
|| pab
->access
== ReadOnly
){
3028 if(!are_selecting
&& pab
->access
== ReadOnly
)
3029 q_status_message1(SM_ORDER
, 0, 4, _("AddressBook %s is Read Only"), pab
->abnick
);
3032 * successful open, burrow into addrbook
3034 if(F_OFF(F_CMBND_ABOOK_DISP
,ps_global
)){
3035 warp_to_top_of_abook(as
.cur
);
3039 ps
->mangled_screen
= 1;
3042 flush_dlc_from_cache(dlc_to_flush
);
3043 start_disp
= as
.cur_row
;
3044 ps
->mangled_footer
= 1;
3045 ps
->mangled_body
= 1;
3048 else{ /* open failed */
3050 * Flush the title line so that it will change into
3051 * a permission denied line,
3053 flush_dlc_from_cache(dlc_to_flush
);
3054 start_disp
= as
.cur_row
;
3055 ps
->mangled_footer
= 1;
3056 ps
->mangled_body
= 1;
3059 else if(dlc_to_flush
->type
== DlcTitleNoPerm
)
3060 q_status_message(SM_ORDER
, 0, 4,
3061 _("Cannot access address book."));
3064 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3065 "Can't happen in MC_OPENABOOK");
3070 /*------- Expand addresses in List ------*/
3073 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
3074 DL_CACHE_S
*dlc_to_flush
;
3076 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3077 if(dlc_to_flush
->type
== DlcListClickHere
){
3078 start_disp
= as
.cur_row
; /* will redraw from here down */
3081 * Mark this list expanded, then flush the
3082 * current line from dlc cache. When we get the
3083 * line again it will notice the expanded flag and change
3084 * the type to DlcListEnt (if any entries).
3087 if(F_OFF(F_EXPANDED_DISTLISTS
,ps
))
3088 exp_set_expanded(pab
->address_book
->exp
,
3089 (a_c_arg_t
)dlc_to_flush
->dlcelnum
);
3091 flush_dlc_from_cache(dlc_to_flush
);
3092 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3094 * If list is empty, back cursor up one.
3096 if(dlc_to_flush
->type
== DlcListEmpty
){
3106 ps
->mangled_body
= 1;
3110 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3111 "Can't happen in MC_EXPAND");
3116 /*------- Select ---------------*/
3120 /* Select an entry to mail to or a nickname to add to */
3121 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
3122 q_status_message(SM_ORDER
| SM_DING
, 0, 4,
3123 _("No entries in address book. Use ExitSelect to leave address books"));
3127 if(as
.checkboxes
|| is_addr(as
.top_ent
+as
.cur_row
)){
3133 dl
= dlist(as
.top_ent
+as
.cur_row
);
3135 if(selecting_one_nick
){
3136 char nickbuf
[MAX_NICKNAME
+ 1];
3139 ae(as
.top_ent
+as
.cur_row
)->nickname
,
3141 nickbuf
[sizeof(nickbuf
)-1] = '\0';
3142 return(cpystr(nickbuf
));
3144 else if(as
.checkboxes
&& checkedn
<= 0){
3145 q_status_message(SM_ORDER
, 0, 1,
3146 _("Use \"X\" to mark addresses or lists"));
3149 else if(as
.checkboxes
){
3150 size_t incr
= 100, avail
, alloced
;
3153 * Have to run through all of the checked entries
3154 * in all of the address books.
3155 * Put the nicknames together into one long
3156 * string with comma separators and let
3157 * our_build_address handle the parsing.
3159 to
= (char *)fs_get(incr
);
3164 for(i
= 0; i
< as
.n_addrbk
; i
++){
3165 EXPANDED_S
*next_one
;
3167 AddrScrn_Disp fake_dl
;
3170 pab
= &as
.adrbks
[i
];
3171 if(pab
->address_book
)
3172 next_one
= pab
->address_book
->checks
;
3176 while((num
= entry_get_next(&next_one
)) != NO_NEXT
){
3177 abe
= adrbk_get_ae(pab
->address_book
,
3180 * Since we're picking up address book entries
3181 * directly from the address books and have
3182 * no knowledge of the display lines they came
3183 * from, we don't know the dl's that go with
3184 * them. We need to pass a dl to abe_to_nick
3185 * but it really is only going to use the
3186 * type in this case.
3189 dl
->type
= (abe
->tag
== Single
) ? Simple
3191 a_string
= abe_to_nick_or_addr_string(abe
, dl
, i
);
3193 while(abe
&& avail
< (size_t)strlen(a_string
)+1){
3196 fs_resize((void **)&to
, alloced
);
3200 strncpy(to
, a_string
, alloced
);
3201 to
[alloced
-1] = '\0';
3204 strncat(to
, ",", alloced
-strlen(to
)-1);
3205 strncat(to
, a_string
, alloced
-strlen(to
)-1);
3208 avail
-= (strlen(a_string
) + 1);
3213 * Return the nickname list for lcc so that the
3214 * correct fullname can make it to the To line.
3215 * If we expand it ahead of time, the list name
3216 * and first user's fullname will get mushed together.
3217 * If an entry doesn't have a nickname then we're
3218 * out of luck as far as getting the right entry
3219 * in the To line goes.
3221 if(selecting_mult_nicks
)
3228 /* Select an address, but not using checkboxes */
3229 if(selecting_mult_nicks
){
3230 if(dl
->type
!= ListHead
&& style
== SelectAddrLccCom
){
3231 q_status_message(SM_ORDER
, 0, 4,
3232 _("You may only select lists for Lcc, use Bcc for other addresses"));
3237 * Even though we're supposedly selecting
3238 * nicknames, we have a special case here to
3239 * select a single member of a distribution
3240 * list. This happens with style SelectNicksCom
3241 * which is the regular ^T entry from the
3242 * composer, and it allows somebody to mail to
3243 * a single member of a distribution list.
3245 abe
= ae(as
.top_ent
+as
.cur_row
);
3246 return(abe_to_nick_or_addr_string(abe
, dl
, as
.cur
));
3250 if(dl
->type
== ListEnt
){
3253 listmem_from_dl(pab
->address_book
, dl
);
3257 bldto
.arg
.abe
= ae(as
.top_ent
+as
.cur_row
);
3262 (void)our_build_address(bldto
, &addr
, &error
, NULL
, save_and_restore
);
3263 /* Have to rfc1522_decode the addr */
3265 char *tmp_a_string
, *p
;
3268 if(style
== SelectAddrNoFull
){
3269 tmp_a_string
= cpystr(addr
);
3270 rfc822_parse_adrlist(&a
,tmp_a_string
,ps
->maildomain
);
3271 fs_give((void **)&tmp_a_string
);
3273 fs_give((void **)&addr
);
3274 addr
= cpystr(simple_addr_string(a
, tmp_20k_buf
,
3276 mail_free_address(&a
);
3279 else if(style
== SelectMultNoFull
){
3280 tmp_a_string
= cpystr(addr
);
3281 rfc822_parse_adrlist(&a
,tmp_a_string
,ps
->maildomain
);
3282 fs_give((void **)&tmp_a_string
);
3284 fs_give((void **)&addr
);
3285 addr
= cpystr(simple_mult_addr_string(a
,
3289 mail_free_address(&a
);
3294 len
= 4*strlen(addr
)+1;
3295 p
= (char *)fs_get(len
* sizeof(char));
3296 if(rfc1522_decode_to_utf8((unsigned char *)p
, len
, addr
) == (unsigned char *)p
){
3297 fs_give((void **)&addr
);
3301 fs_give((void **)&p
);
3306 fs_give((void **)&to
);
3309 q_status_message1(SM_ORDER
, 3, 4, "%s", error
);
3310 fs_give((void **)&error
);
3313 return(addr
); /* Caller frees this */
3316 if(entry_is_clickable(as
.top_ent
+as
.cur_row
))
3317 clickable_warning(as
.top_ent
+as
.cur_row
);
3318 else if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
3319 q_status_message(SM_ORDER
, 3, 4, _("Use select to select an address or addresses from address books"));
3321 q_status_message(SM_ORDER
, 3, 4, _("No address selected"));
3327 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3328 "Can't happen in MC_CHOICE");
3333 /*----- View an entry --------------------*/
3337 empty_warning(as
.top_ent
+as
.cur_row
);
3341 view_abook_entry(ps
, as
.top_ent
+as
.cur_row
);
3345 /*----- Add new ---------*/
3347 {long old_l_p_p
, old_top_ent
, old_cur_row
;
3349 if(adrbk_check_all_validity_now()){
3350 if(resync_screen(pab
, style
, checkedn
)){
3351 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
3352 _("Address book changed. AddNew cancelled. Try again."));
3353 ps
->mangled_screen
= 1;
3359 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
3365 "Calling edit_entry to add entry manually\n"));
3366 /* TRANSLATORS: add as in add a new entry to something */
3367 edit_entry(pab
->address_book
, (AdrBk_Entry
*)NULL
, NO_NEXT
,
3368 NotSet
, 0, &warped
, _("add"));
3371 * Warped means we got plopped down somewhere in the display
3372 * list so that we don't know where we are relative to where
3373 * we were before we warped. The current line number will
3374 * be zero, since that is what the warp would have set.
3377 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
3378 as
.cur_row
= 0L - as
.top_ent
;
3382 * If we didn't warp, that means we didn't change at all,
3383 * so keep old screen.
3385 old_l_p_p
= as
.l_p_page
;
3386 old_top_ent
= as
.top_ent
;
3387 old_cur_row
= as
.cur_row
;
3390 /* Window size may have changed while in pico. */
3393 /* fix up what ab_resize messed up */
3394 if(!warped
&& old_l_p_p
== as
.l_p_page
){
3395 as
.top_ent
= old_top_ent
;
3396 as
.cur_row
= old_cur_row
;
3397 as
.old_cur_row
= old_cur_row
;
3401 ps
->mangled_screen
= 1;
3405 /*---------- Add a new address book -------------------*/
3407 {int old_abook_num
, new_abook_num
, new_row
, global
;
3408 int stay_put
, old_cur_row
;
3411 stay_put
= (dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstPers
||
3412 dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstGlob
);
3413 old_cur_row
= as
.cur_row
;
3414 old_abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3415 global
= (dlist(as
.top_ent
+as
.cur_row
)->type
== AddFirstGlob
||
3416 (dlist(as
.top_ent
+as
.cur_row
)->type
!= AddFirstPers
&&
3417 (old_abook_num
>= as
.how_many_personals
) &&
3420 ab_add_abook(global
,
3422 : adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
)))
3424 DL_CACHE_S dlc_restart
;
3426 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3431 dlc_restart
.adrbk_num
= new_abook_num
;
3432 dlc_restart
.type
= DlcTitle
;
3433 warp_to_dlc(&dlc_restart
, 0L);
3436 * Put the current entry in a nice spot on the screen.
3438 new_row
= old_cur_row
+
3441 : LINES_PER_ABOOK
*(new_abook_num
- old_abook_num
));
3444 new_row
<= (as
.l_p_page
-VIS_LINES_PER_ABOOK
))/* ok, use it */
3445 as
.cur_row
= new_row
;
3448 * Will everything above fit and still leave ours on screen?
3450 new_row
= LINES_PER_ABOOK
* new_abook_num
+
3452 (as
.how_many_personals
> 0 || as
.config
))
3453 ? XTRA_LINES_BETWEEN
: 0) +
3454 ((as
.how_many_personals
== 0 && as
.config
)
3455 ? LINES_PER_ADD_LINE
: 0);
3456 new_row
= MAX(MIN(new_row
,
3457 as
.l_p_page
- VIS_LINES_PER_ABOOK
), 0);
3458 as
.cur_row
= new_row
;
3461 as
.top_ent
= 0L - as
.cur_row
;
3462 dprint((5, "addrbook added: %s\n",
3463 as
.adrbks
[new_abook_num
].filename
3464 ? as
.adrbks
[new_abook_num
].filename
: "?"));
3467 ps
->mangled_screen
= 1;
3474 /*---------- Change address book config -------------------*/
3477 if(entry_is_clickable_title(as
.top_ent
+as
.cur_row
)){
3478 int abook_num
, old_cur_row
, global
;
3480 DL_CACHE_S dlc_restart
, *dlc
;
3481 char *serv
= NULL
, *folder
= NULL
, *p
, *q
;
3482 char *nick
= NULL
, *file
= NULL
;
3484 abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3485 global
= (abook_num
>= as
.how_many_personals
);
3486 old_cur_row
= as
.cur_row
;
3487 old_top_ent
= as
.top_ent
;
3488 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
3492 q
= ps_global
->VAR_GLOB_ADDRBOOK
[abook_num
-
3493 as
.how_many_personals
];
3495 q
= ps_global
->VAR_ADDRESSBOOK
[abook_num
];
3497 get_pair(q
, &nick
, &file
, 0, 0);
3500 fs_give((void **)&nick
);
3502 if(file
&& *file
== '{'){
3504 if((p
= strindex(file
, '}'))){
3510 q_status_message1(SM_ORDER
|SM_DING
, 0, 4,
3511 _("Missing \"}\" in config: %s"), q
);
3513 fs_give((void **)&nick
);
3515 fs_give((void **)&file
);
3523 if(ab_edit_abook(global
, abook_num
, serv
, folder
, nick
) >= 0){
3524 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3529 dlc_restart
.type
= DlcTitle
;
3530 warp_to_dlc(&dlc_restart
, old_top_ent
+(long)old_cur_row
);
3531 as
.cur_row
= old_cur_row
;
3532 as
.top_ent
= old_top_ent
;
3534 dprint((5, "addrbook config edited: %s\n",
3535 as
.adrbks
[dlc_restart
.adrbk_num
].filename
3536 ? as
.adrbks
[dlc_restart
.adrbk_num
].filename
: "?"));
3540 fs_give((void **)&nick
);
3542 fs_give((void **)&file
);
3544 ps
->mangled_screen
= 1;
3547 /* TRANSLATORS: the user tried to change the current line of the address
3548 book but the line could not be changed */
3549 q_status_message(SM_ORDER
, 0, 4, _("Not a changeable line"));
3554 /*---------- Delete an address book -------------------*/
3556 if(as
.n_addrbk
== 0){
3557 q_status_message(SM_ORDER
, 0, 4, _("Nothing to delete"));
3563 if(ab_del_abook(as
.top_ent
+as
.cur_row
, command_line
, &err
) >= 0){
3564 DL_CACHE_S dlc_restart
;
3565 int new_abook_num
, old_abook_num
, old_pers
, old_glob
, new_row
;
3568 * Careful, these are only ok because ab_del_abook didn't
3569 * mess with the as globals, like as.how_many_personals.
3570 * The addrbook_reset does reset them, of course.
3572 old_abook_num
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
3573 old_pers
= as
.how_many_personals
;
3574 old_glob
= as
.n_addrbk
- as
.how_many_personals
;
3576 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3581 if(old_abook_num
>= as
.n_addrbk
) /* we deleted last addrbook */
3582 new_abook_num
= as
.n_addrbk
- 1;
3584 new_abook_num
= old_abook_num
;
3587 * Pick a line to highlight and center
3589 if(as
.how_many_personals
== 0 && old_pers
== 1)
3590 dlc_restart
.type
= DlcPersAdd
;
3591 else if((as
.n_addrbk
- as
.how_many_personals
) == 0 &&
3593 dlc_restart
.type
= DlcGlobAdd
;
3594 else if(as
.n_addrbk
== 0)
3595 dlc_restart
.type
= DlcPersAdd
;
3597 dlc_restart
.adrbk_num
= new_abook_num
;
3598 dlc_restart
.type
= DlcTitle
;
3601 warp_to_dlc(&dlc_restart
, 0L);
3604 * Will everything above fit and still leave ours on screen?
3606 if(dlc_restart
.type
== DlcTitle
)
3607 new_row
= LINES_PER_ABOOK
* new_abook_num
+
3608 (((as
.adrbks
[new_abook_num
].type
& GLOBAL
) &&
3609 (as
.how_many_personals
> 0 || as
.config
))
3610 ? XTRA_LINES_BETWEEN
: 0) +
3611 ((as
.how_many_personals
== 0 && as
.config
)
3612 ? LINES_PER_ADD_LINE
: 0);
3613 else if(dlc_restart
.type
== DlcGlobAdd
)
3614 new_row
= LINES_PER_ABOOK
* as
.n_addrbk
+
3615 ((as
.how_many_personals
> 0 || as
.config
)
3616 ? XTRA_LINES_BETWEEN
: 0) +
3617 ((as
.how_many_personals
== 0 && as
.config
)
3618 ? LINES_PER_ADD_LINE
: 0);
3622 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
3623 as
.cur_row
= new_row
;
3624 as
.top_ent
= 0L - as
.cur_row
;
3626 ps
->mangled_body
= 1;
3627 ps
->mangled_footer
= 1;
3628 /* TRANSLATORS: This is just comforting confirmation that the
3629 address book being deleted was successfully deleted */
3630 q_status_message(SM_ORDER
, 0, 3, _("Address book deleted"));
3634 q_status_message(SM_ORDER
, 0, 4, err
);
3635 dprint((5, "addrbook delete failed: %s\n",
3645 /*---- Reorder an addressbook list ---------*/
3647 if(entry_is_addkey(as
.top_ent
+as
.cur_row
)){
3648 q_status_message(SM_ORDER
, 0, 4,
3649 _("Highlight entry you wish to shuffle"));
3657 if((ret
= ab_shuffle(pab
, &slide
, command_line
, &msg
)) > 0){
3658 DL_CACHE_S dlc_restart
;
3661 new_anum
= ret
- 1; /* see ab_shuffle return value */
3663 (void)init_addrbooks(HalfOpen
, 0, 0, !are_selecting
);
3668 /* put cursor on new_anum */
3669 dlc_restart
.adrbk_num
= new_anum
;
3670 dlc_restart
.type
= DlcTitle
;
3671 warp_to_dlc(&dlc_restart
, 0L);
3673 * Will everything above fit and still leave ours on screen?
3675 new_row
= LINES_PER_ABOOK
* new_anum
+
3676 (((as
.adrbks
[new_anum
].type
& GLOBAL
) &&
3677 (as
.how_many_personals
> 0 || as
.config
))
3678 ? XTRA_LINES_BETWEEN
: 0) +
3679 ((as
.how_many_personals
== 0 && as
.config
)
3680 ? LINES_PER_ADD_LINE
: 0);
3681 new_row
= MAX(MIN(new_row
, as
.l_p_page
-VIS_LINES_PER_ABOOK
), 0);
3682 as
.cur_row
= new_row
;
3683 as
.top_ent
= 0L - as
.cur_row
;
3685 ps
->mangled_body
= 1;
3688 DL_CACHE_S
*dlc_to_flush
;
3690 if(slide
< 0){ /* moved it up */
3691 as
.cur_row
+= slide
;
3692 start_disp
= MAX(as
.cur_row
- 1, 0);
3693 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3695 * If we backed off the top of the screen, just
3696 * inch the display up so we can see it.
3699 as
.top_ent
+= as
.cur_row
; /* cur_row is negative */
3704 else{ /* moved it down */
3705 start_disp
= as
.cur_row
;
3706 dlc_to_flush
= get_dlc(as
.top_ent
+as
.cur_row
);
3707 as
.cur_row
+= slide
;
3708 if(as
.cur_row
> as
.l_p_page
- VIS_LINES_PER_ABOOK
){
3709 as
.top_ent
+= (as
.cur_row
-
3710 (as
.l_p_page
- VIS_LINES_PER_ABOOK
));
3711 as
.cur_row
= MAX(as
.l_p_page
-VIS_LINES_PER_ABOOK
, 0);
3716 flush_dlc_from_cache(dlc_to_flush
);
3717 ps
->mangled_body
= 1;
3720 q_status_message(SM_ORDER
, 0, 3,
3722 (ret
< 0) ? _("Shuffle failed") :
3723 _("Address books shuffled"));
3725 dprint((5, "addrbook shuffle failed: %s\n",
3729 fs_give((void **)&msg
);
3736 /*----------------------- Move Up ---------------------*/
3739 r
= prev_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
);
3741 /* find first line so everything is displayed */
3742 new_top_ent
= as
.cur_row
+as
.top_ent
;
3743 for(dl
=dlist(new_top_ent
-1);
3744 dl
->type
!= Beginning
;
3745 dl
= dlist((--new_top_ent
) - 1))
3748 if(new_top_ent
== as
.top_ent
||
3749 (as
.cur_row
+ (as
.top_ent
-new_top_ent
) > as
.l_p_page
- 1)){
3750 q_status_message(SM_INFO
, 0, 1, _("Already on first line."));
3753 as
.cur_row
+= (as
.top_ent
- new_top_ent
);
3754 as
.top_ent
= new_top_ent
;
3756 ps
->mangled_body
= 1;
3762 i
= ((cmd
== MC_CHARUP
)
3763 && dlist(as
.top_ent
- 1L)->type
!= Beginning
)
3764 ? MIN(HS_MARGIN(ps
), as
.cur_row
) : 0;
3765 as
.cur_row
= new_line
- as
.top_ent
;
3766 if(as
.cur_row
- i
< 0){
3767 if(cmd
== MC_CHARUP
){
3768 /*-- Past top of page --*/
3769 as
.top_ent
+= (as
.cur_row
- i
);
3773 new_top_ent
= first_line(as
.top_ent
- as
.l_p_page
);
3774 as
.cur_row
+= (as
.top_ent
- new_top_ent
);
3775 as
.top_ent
= new_top_ent
;
3776 /* if it is still off screen */
3777 if(as
.cur_row
- i
< 0){
3778 as
.top_ent
+= (as
.cur_row
- i
);
3784 ps
->mangled_body
= 1;
3787 current_changed_flag
++;
3792 /*------------------- Move Down -------------------*/
3795 r
= next_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
);
3799 /* find last line so everything is displayed */
3800 new_end_line
= as
.cur_row
+as
.top_ent
;
3801 for(dl
=dlist(new_end_line
+1);
3803 dl
= dlist((++new_end_line
)+1))
3806 if(new_end_line
- as
.top_ent
<= as
.l_p_page
- 1 ||
3807 as
.cur_row
- (new_end_line
-as
.top_ent
-(as
.l_p_page
-1)) < 0){
3808 q_status_message(SM_INFO
, 0, 1, _("Already on last line."));
3811 as
.cur_row
-= (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
3812 as
.top_ent
+= (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
3814 ps
->mangled_body
= 1;
3820 /* adjust for scrolling margin */
3821 i
= (cmd
== MC_CHARDOWN
) ? HS_MARGIN(ps
) : 0;
3822 as
.cur_row
= new_line
- as
.top_ent
;
3823 if(as
.cur_row
>= as
.l_p_page
- i
){
3824 if(cmd
== MC_CHARDOWN
){
3825 /*-- Past bottom of page --*/
3826 as
.top_ent
+= (as
.cur_row
- (as
.l_p_page
- i
) + 1);
3827 as
.cur_row
= (as
.l_p_page
- i
) - 1;
3830 /*-- Changed pages --*/
3831 as
.top_ent
+= as
.l_p_page
;
3832 as
.cur_row
-= as
.l_p_page
;
3833 /* if it is still off screen */
3834 if(as
.cur_row
>= as
.l_p_page
- i
){
3835 as
.top_ent
+= (as
.cur_row
- (as
.l_p_page
- i
) + 1);
3836 as
.cur_row
= (as
.l_p_page
- i
) - 1;
3841 ps
->mangled_body
= 1;
3844 current_changed_flag
++;
3855 * Get the mouse down. Convert to content row number.
3856 * If the row is selectable, do the single or double click
3859 mouse_get_last(NULL
, &mp
);
3860 mp
.row
-= HEADER_ROWS(ps
);
3861 if(line_is_selectable(as
.top_ent
+ mp
.row
)){
3862 if(mp
.button
== M_BUTTON_LEFT
){
3865 * A mouse double click does the default action to
3866 * the selected line. Since we need to do a goto
3867 * to get there, we have this ugly bit of code.
3869 * On the first mouse click we go around the loop
3870 * once and set def_key appropriately for the
3871 * line as.top_ent+mp.row, which will then be
3872 * the same as as.top_ent+as.cur_row.
3874 switch(km
->keys
[def_key
].bind
.cmd
){
3876 if (as
.checkboxes
) goto togglex
;
3897 q_status_message(SM_INFO
, 0, 1,
3898 "Can't happen in MC_MOUSE");
3903 as
.cur_row
= mp
.row
;
3904 current_changed_flag
++;
3906 else if(mp
.button
== M_BUTTON_RIGHT
){
3910 as
.cur_row
= mp
.row
;
3911 current_changed_flag
++;
3915 need_redraw
= calculate_field_widths();
3917 /*---------- Update the current entry, (move or change) -------*/
3918 display_book(need_redraw
? 0 : as
.cur_row
,
3924 as
.old_cur_row
= as
.cur_row
;
3925 current_changed_flag
= 0;
3926 as
.cur
= cur_addr_book();
3927 pab
= (as
.n_addrbk
&&
3928 !entry_is_askserver(as
.top_ent
+as
.cur_row
))
3929 ? &as
.adrbks
[as
.cur
] : NULL
;
3931 if(!mp
.doubleclick
){
3935 /* Make what they clicked "current" */
3936 memset(addr_pu
, 0, 20 * sizeof(MPopup
));
3938 addr_pu
[n
= 0].type
= tQueue
;
3939 addr_pu
[n
].data
.val
= km
->keys
[def_key
].bind
.ch
[0];
3940 addr_pu
[n
].label
.style
= lNormal
;
3941 addr_pu
[n
++].label
.string
= km
->keys
[def_key
].label
;
3943 if((i
= menu_clear_binding(km
, '<')) != MC_UNKNOWN
){
3944 menu_add_binding(km
, '<', i
);
3946 if((i
= menu_binding_index(km
, i
)) >= 0){
3947 addr_pu
[n
++].type
= tSeparator
;
3949 addr_pu
[n
].type
= tQueue
;
3950 addr_pu
[n
].data
.val
= km
->keys
[i
].bind
.ch
[0];
3951 addr_pu
[n
].label
.style
= lNormal
;
3952 addr_pu
[n
++].label
.string
= km
->keys
[i
].label
;
3956 addr_pu
[n
].type
= tTail
;
3958 mswin_popup(addr_pu
);
3968 /*------------- Page Up or Down --------*/
3971 if(cmd
== MC_PAGEUP
){
3972 /* find first line on prev page */
3973 new_top_ent
= first_line(as
.top_ent
- as
.l_p_page
);
3974 if(new_top_ent
== NO_LINE
)
3977 /* find first selectable line */
3978 fl
= first_selectable_line(new_top_ent
);
3980 /* If we didn't move, we'd better move now */
3981 if(fl
== as
.top_ent
+as
.cur_row
){
3982 if(!prev_selectable_line(as
.cur_row
+as
.top_ent
, &new_line
)){
3985 /* find first line so everything is displayed */
3986 lineno
= as
.cur_row
+as
.top_ent
;
3987 for(dl
=dlist(lineno
);
3988 dl
->type
!= Beginning
;
3989 dl
= dlist(--lineno
))
3993 * If this new_top_ent is the same as the old_top_ent
3994 * we'll get the warning message.
3996 new_top_ent
= first_line(lineno
);
3997 if(fl
- new_top_ent
>= as
.l_p_page
)
3998 new_top_ent
+= (fl
- new_top_ent
- as
.l_p_page
+ 1);
4007 if(as
.top_ent
== new_top_ent
&& as
.cur_row
== (fl
-as
.top_ent
)){
4008 q_status_message(SM_INFO
, 0, 1, _("Already on first page."));
4012 if(as
.top_ent
== new_top_ent
)
4013 current_changed_flag
++;
4015 as
.top_ent
= new_top_ent
;
4018 /* find first selectable line on next page */
4019 fl
= first_selectable_line(as
.top_ent
+ as
.l_p_page
);
4023 /* if there is another page, scroll */
4024 if(fl
- as
.top_ent
>= as
.l_p_page
){
4025 new_top_ent
= as
.top_ent
+ as
.l_p_page
;
4027 /* on last page already */
4029 new_top_ent
= as
.top_ent
;
4030 if(as
.cur_row
== (fl
- as
.top_ent
)){ /* no change */
4033 /* find last line so everything is displayed */
4034 new_end_line
= as
.cur_row
+as
.top_ent
;
4035 for(dl
=dlist(new_end_line
+1);
4037 dl
= dlist((++new_end_line
)+1))
4040 if(new_end_line
- as
.top_ent
<= as
.l_p_page
- 1 ||
4042 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1)) < 0){
4043 q_status_message(SM_INFO
, 0, 1,
4044 _("Already on last page."));
4048 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
4050 (new_end_line
-as
.top_ent
-(as
.l_p_page
-1));
4052 ps
->mangled_body
= 1;
4059 if(as
.top_ent
== new_top_ent
)
4060 current_changed_flag
++;
4062 as
.top_ent
= new_top_ent
;
4066 * Stuff in common for up or down.
4068 as
.cur_row
= fl
- as
.top_ent
;
4070 /* if it is still off screen */
4072 as
.top_ent
+= as
.cur_row
;
4075 else if(as
.cur_row
>= as
.l_p_page
){
4076 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4077 as
.cur_row
= as
.l_p_page
- 1;
4080 if(!current_changed_flag
){
4081 ps
->mangled_body
= 1;
4088 /*------------- Delete item from addrbook ---------*/
4090 if(adrbk_check_all_validity_now()){
4091 if(resync_screen(pab
, style
, checkedn
)){
4092 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
4093 _("Address book changed. Delete cancelled. Try again."));
4094 ps
->mangled_screen
= 1;
4099 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4100 q_status_message(SM_ORDER
, 0, 4, _("No entries to delete"));
4104 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4105 clickable_warning(as
.top_ent
+as
.cur_row
);
4110 q_status_message(SM_ORDER
, 0, 4, _("AddressBook is Read Only"));
4115 empty_warning(as
.top_ent
+as
.cur_row
);
4120 did_delete
= single_entry_delete(pab
->address_book
,
4121 as
.cur_row
+as
.top_ent
,
4123 ps
->mangled_footer
= 1;
4126 as
.top_ent
= first_line(0L - as
.l_p_page
/2L);
4127 as
.cur_row
= 0L - as
.top_ent
;
4132 * In case the line we're now at is not a selectable
4135 new_line
= first_selectable_line(as
.cur_row
+as
.top_ent
);
4136 if(new_line
!= NO_LINE
4137 && new_line
!= as
.cur_row
+as
.top_ent
){
4138 as
.cur_row
= new_line
- as
.top_ent
;
4140 as
.top_ent
-= as
.l_p_page
;
4141 as
.cur_row
+= as
.l_p_page
;
4143 else if(as
.cur_row
>= as
.l_p_page
){
4144 as
.top_ent
+= as
.l_p_page
;
4145 as
.cur_row
-= as
.l_p_page
;
4149 start_disp
= MIN(as
.cur_row
, as
.old_cur_row
);
4152 ps
->mangled_body
= 1;
4158 /*------------- Toggle checkbox ---------*/
4161 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4162 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4166 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4167 clickable_warning(as
.top_ent
+as
.cur_row
);
4172 empty_warning(as
.top_ent
+as
.cur_row
);
4176 if(is_addr(as
.top_ent
+as
.cur_row
)){
4177 dl
= dlist(as
.top_ent
+as
.cur_row
);
4179 if(style
== SelectAddrLccCom
&& dl
->type
== ListEnt
)
4180 q_status_message(SM_ORDER
, 0, 4,
4181 _("You may only select whole lists for Lcc"));
4182 else if(style
== SelectAddrLccCom
&& dl
->type
!= ListHead
)
4183 q_status_message(SM_ORDER
, 0, 4,
4184 _("You may only select lists for Lcc, use Bcc for personal entries"));
4185 else if(dl
->type
== ListHead
|| dl
->type
== Simple
){
4186 current_changed_flag
++;
4187 if(entry_is_checked(pab
->address_book
->checks
,
4188 (a_c_arg_t
)dl
->elnum
)){
4189 entry_unset_checked(pab
->address_book
->checks
,
4190 (a_c_arg_t
)dl
->elnum
);
4196 entry_set_checked(pab
->address_book
->checks
,
4197 (a_c_arg_t
)dl
->elnum
);
4205 q_status_message(SM_ORDER
, 0, 4,
4206 _("You may not select list members, only whole lists or personal entries"));
4209 q_status_message(SM_ORDER
, 0, 4,
4210 _("You may only select addresses or lists"));
4215 /*------ Turn all checkboxes on ---------*/
4218 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4219 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4223 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4224 clickable_warning(as
.top_ent
+as
.cur_row
);
4229 empty_warning(as
.top_ent
+as
.cur_row
);
4234 adrbk_cntr_t num
, ab_count
;
4236 ab_count
= adrbk_count(pab
->address_book
);
4238 if(checkedn
){ /* unset All */
4239 for(num
= 0; num
< ab_count
; num
++){
4240 if(entry_is_checked(pab
->address_book
->checks
,
4242 entry_unset_checked(pab
->address_book
->checks
,
4249 for(num
= 0; num
< ab_count
; num
++){
4250 if(!entry_is_checked(pab
->address_book
->checks
,
4252 entry_set_checked(pab
->address_book
->checks
,
4259 ps
->mangled_body
= 1;
4266 /*---------- Turn on ListMode -----------*/
4269 for(i
= 0; i
< as
.n_addrbk
; i
++){
4270 pab
= &as
.adrbks
[i
];
4271 init_disp_form(pab
, ps
->VAR_ABOOK_FORMATS
, i
);
4274 (void)calculate_field_widths();
4275 ps
->mangled_footer
= 1;
4276 ps
->mangled_body
= 1;
4278 q_status_message(SM_ORDER
, 0, 4,
4279 _("Use \"X\" to select addresses or lists"));
4283 /*--------- Compose -----------*/
4285 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 0);
4289 /*--------- Alt Compose -----------*/
4291 (void)ab_compose_to_addr(as
.top_ent
+as
.cur_row
, 0, 1);
4296 /*------ Query Directory ------*/
4299 {char *err_mess
= NULL
, **err_mess_p
;
4303 q_status_message(SM_ORDER
, 0, 4,
4304 (style
== SelectAddrLccCom
)
4305 ? "Can't search server for Lcc"
4306 : "Can't search server from here");
4309 else if(as
.checkboxes
){
4310 q_status_message(SM_ORDER
, 0, 4,
4311 "Can't search server when using ListMode");
4316 err_mess_p
= error_message
;
4318 err_mess_p
= &err_mess
;
4321 * The query lines are indexed by their adrbk_num. (Just using
4322 * that because it was handy and not used for addrbooks.)
4324 query_type
= adrbk_num_from_lineno(as
.top_ent
+as
.cur_row
);
4326 if((addr
= query_server(ps
, are_selecting
, &quit
, query_type
,
4327 err_mess_p
)) != NULL
){
4331 fs_give((void **)&addr
);
4334 if(err_mess_p
&& *err_mess_p
&& !error_message
){
4335 q_status_message(SM_ORDER
, 0, 4, *err_mess_p
);
4336 fs_give((void **)err_mess_p
);
4341 #endif /* ENABLE_LDAP */
4344 /*----------- Where is (search) ----------------*/
4347 new_top_ent
= ab_whereis(&warped
, command_line
);
4349 if(new_top_ent
!= NO_LINE
){
4350 if(warped
|| new_top_ent
!= as
.top_ent
){
4351 as
.top_ent
= new_top_ent
;
4353 ps
->mangled_body
= 1;
4356 current_changed_flag
++;
4359 ps
->mangled_footer
= 1;
4363 /*----- Select entries to work on --*/
4365 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4366 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4371 if(entry_is_askserver(as
.top_ent
+as
.cur_row
))
4372 q_status_message(SM_ORDER
, 0, 4,
4373 _("Select is only available from within an expanded address book"));
4375 clickable_warning(as
.top_ent
+as
.cur_row
);
4380 dl
= dlist(as
.top_ent
+as
.cur_row
);
4381 if(dl
->type
== Empty
){
4382 empty_warning(as
.top_ent
+as
.cur_row
);
4386 {int were_selections
= as
.selections
;
4388 ab_select(ps
, pab
? pab
->address_book
: NULL
,
4389 as
.top_ent
+as
.cur_row
, command_line
, &start_disp
);
4391 if((!were_selections
&& as
.selections
)
4392 || (were_selections
&& !as
.selections
)){
4393 ps
->mangled_footer
= 1;
4394 for(i
= 0; i
< as
.n_addrbk
; i
++)
4395 init_disp_form(&as
.adrbks
[i
],
4396 ps
->VAR_ABOOK_FORMATS
, i
);
4403 /*----------- Select current entry ----------*/
4405 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4406 q_status_message(SM_ORDER
, 0, 4, _("No entries to select"));
4410 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4411 clickable_warning(as
.top_ent
+as
.cur_row
);
4416 empty_warning(as
.top_ent
+as
.cur_row
);
4420 if(is_addr(as
.top_ent
+as
.cur_row
)){
4421 dl
= dlist(as
.top_ent
+as
.cur_row
);
4423 if(dl
->type
== ListHead
|| dl
->type
== Simple
){
4424 int do_init_disp
= 0;
4427 current_changed_flag
++;
4429 if(entry_is_selected(pab
->address_book
->selects
,
4430 (a_c_arg_t
)dl
->elnum
)){
4431 DL_CACHE_S
*dlc
, dlc_restart
;
4434 if(as
.selections
== 0)
4437 entry_unset_selected(pab
->address_book
->selects
,
4438 (a_c_arg_t
)dl
->elnum
);
4441 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4443 flush_dlc_from_cache(dlc
);
4444 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4445 ps
->mangled_body
= 1;
4446 if(dlc
->type
== DlcEnd
){
4447 r
= prev_selectable_line(as
.cur_row
+
4451 as
.cur_row
= new_line
- as
.top_ent
;
4453 as
.top_ent
+= as
.cur_row
;
4458 start_disp
= as
.cur_row
;
4462 start_disp
= MAX(as
.cur_row
-1,0);
4467 q_status_message(SM_ORDER
, 0, 2,
4468 _("Zoom Mode is now off, no entries selected"));
4470 warp_to_dlc(&dlc_restart
, 0L);
4471 /* put current entry in middle of screen */
4473 first_line(0L - (long)as
.l_p_page
/2L);
4474 as
.cur_row
= 0L - as
.top_ent
;
4475 ps
->mangled_body
= 1;
4479 else if(F_OFF(F_UNSELECT_WONT_ADVANCE
,ps_global
)){
4481 r
= next_selectable_line(as
.cur_row
+as
.top_ent
,
4486 (dl
=dlist(ll
))->type
!= End
;
4488 if(dl
->type
== ListHead
|| dl
->type
== Simple
)
4494 as
.cur_row
= new_line
- as
.top_ent
;
4495 if(as
.cur_row
>= as
.l_p_page
){
4496 /*-- Changed pages --*/
4497 as
.top_ent
+= as
.l_p_page
;
4498 as
.cur_row
-= as
.l_p_page
;
4499 /* if it is still off screen */
4500 if(as
.cur_row
>= as
.l_p_page
){
4501 as
.top_ent
+= (as
.cur_row
-as
.l_p_page
+1);
4502 as
.cur_row
= (as
.l_p_page
- 1);
4506 ps
->mangled_body
= 1;
4512 if(as
.selections
== 0)
4517 entry_set_selected(pab
->address_book
->selects
,
4518 (a_c_arg_t
)dl
->elnum
);
4519 r
= next_selectable_line(as
.cur_row
+as
.top_ent
,
4524 (dl
=dlist(ll
))->type
!= End
;
4526 if(dl
->type
== ListHead
|| dl
->type
== Simple
)
4532 as
.cur_row
= new_line
- as
.top_ent
;
4533 if(as
.cur_row
>= as
.l_p_page
){
4534 /*-- Changed pages --*/
4535 as
.top_ent
+= as
.l_p_page
;
4536 as
.cur_row
-= as
.l_p_page
;
4537 /* if it is still off screen */
4538 if(as
.cur_row
>= as
.l_p_page
){
4539 as
.top_ent
+= (as
.cur_row
-as
.l_p_page
+1);
4540 as
.cur_row
= (as
.l_p_page
- 1);
4544 ps
->mangled_body
= 1;
4550 * If we switch from selected to non-selected or
4551 * vice versa, we have to init_disp_form() for all
4552 * the addrbooks, in case we're using the X instead
4556 ps
->mangled_footer
= 1;
4557 for(i
= 0; i
< as
.n_addrbk
; i
++)
4558 init_disp_form(&as
.adrbks
[i
],
4559 ps
->VAR_ABOOK_FORMATS
, i
);
4563 q_status_message(SM_ORDER
, 0, 4,
4564 _("You may not select list members, only whole lists or personal entries"));
4567 q_status_message(SM_ORDER
, 0, 4,
4568 _("You may only select addresses or lists"));
4573 /*--- Zoom in and look only at selected entries (or zoom out) --*/
4575 as
.zoomed
= (1 - as
.zoomed
);
4577 ab_zoom((pab
&& pab
->address_book
) ? pab
->address_book
->selects
4581 q_status_message(SM_ORDER
, 0, 2, _("Zoom Mode is now off"));
4582 ab_unzoom(&start_disp
);
4588 /*--- Apply a command -----------*/
4591 if(((ab_apply_cmd(ps
, pab
? pab
->address_book
: NULL
,
4592 as
.top_ent
+as
.cur_row
, command_line
) &&
4593 F_ON(F_AUTO_UNZOOM
, ps
)) || !as
.selections
) && as
.zoomed
){
4596 ps_global
->mangled_body
= 1;
4600 * In case the line we're now at is not a selectable
4603 * We set start_disp to zero here but rely on the called
4604 * routine to set mangled_body if appropriate.
4607 new_line
= first_selectable_line(as
.cur_row
+as
.top_ent
);
4608 if(new_line
!= NO_LINE
4609 && new_line
!= as
.cur_row
+as
.top_ent
){
4610 as
.cur_row
= new_line
- as
.top_ent
;
4612 as
.top_ent
+= as
.cur_row
;
4615 else if(as
.cur_row
>= as
.l_p_page
){
4616 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4617 as
.cur_row
= as
.l_p_page
- 1;
4622 q_status_message(SM_ORDER
, 0, 2,
4623 _("No selected entries to apply command to"));
4628 /*--------- QUIT pine -----------*/
4630 dprint((7, "Quitting pine from addrbook\n"));
4631 ps
->next_screen
= quit_screen
;
4635 /*--------- Top of Folder list -----------*/
4636 case MC_COLLECTIONS
:
4637 dprint((7, "Goto folder lister from addrbook\n"));
4638 ps
->next_screen
= folder_screen
;
4642 /*---------- Open specific new folder ----------*/
4644 dprint((7, "Goto from addrbook\n"));
4645 ab_goto_folder(command_line
);
4649 /*--------- Index -----------*/
4651 dprint((7, "Goto message index from addrbook\n"));
4653 && sp_viewing_a_thread(ps
->mail_stream
)
4654 && unview_thread(ps
, ps
->mail_stream
, ps
->msgmap
)){
4655 ps
->next_screen
= mail_index_screen
;
4656 ps
->view_skipped_index
= 0;
4657 ps
->mangled_screen
= 1;
4660 ps
->next_screen
= mail_index_screen
;
4664 /*----------------- Print --------------------*/
4667 ps
->mangled_screen
= 1;
4671 /*------ Copy entries into an abook ----*/
4673 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4674 q_status_message(SM_ORDER
, 0, 4, _("No entries to save"));
4678 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4679 clickable_warning(as
.top_ent
+as
.cur_row
);
4684 empty_warning(as
.top_ent
+as
.cur_row
);
4688 (void)ab_save(ps
, pab
? pab
->address_book
: NULL
,
4689 as
.top_ent
+as
.cur_row
, command_line
, 0);
4693 /*------ Forward an entry in mail -----------*/
4695 if(!any_addrs_avail(as
.top_ent
+as
.cur_row
)){
4696 q_status_message(SM_ORDER
, 0, 4, _("No entries to forward"));
4700 if(entry_is_clickable(as
.top_ent
+as
.cur_row
)){
4701 clickable_warning(as
.top_ent
+as
.cur_row
);
4706 empty_warning(as
.top_ent
+as
.cur_row
);
4710 if(!is_addr(as
.top_ent
+as
.cur_row
)){
4711 q_status_message(SM_ORDER
, 0, 4, _("Nothing to forward"));
4715 dl
= dlist(as
.top_ent
+as
.cur_row
);
4716 if(dl
->type
!= ListHead
&& dl
->type
!= Simple
){
4717 q_status_message(SM_ORDER
, 0, 4,
4718 _("Can only forward whole entries"));
4722 (void)ab_forward(ps
, as
.top_ent
+as
.cur_row
, 0);
4723 ps
->mangled_footer
= 1;
4728 /* ^L attempts to resynchronize with changed addrbooks */
4729 if(adrbk_check_all_validity_now())
4730 (void)resync_screen(pab
, style
, checkedn
);
4735 mark_status_dirty();
4736 mark_titlebar_dirty();
4737 mark_keymenu_dirty();
4739 ps
->mangled_screen
= 1;
4747 bogus_utf8_command(utf8str
, F_ON(F_USE_FK
, ps
) ? "F1" : "?");
4751 /*------ Some back compatibility messages -----*/
4753 if(c
== 'e' && !are_selecting
){
4754 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4755 _("Command \"E\" not defined. Use \"View/Update\" to edit an entry"));
4759 && !(are_selecting
|| entry_is_clickable(as
.top_ent
+as
.cur_row
))){
4760 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4761 _("Command \"S\" not defined. Use \"AddNew\" to create a list"));
4764 else if(c
== 'z' && !are_selecting
){
4765 q_status_message(SM_ORDER
| SM_DING
, 0, 2,
4766 _("Command \"Z\" not defined. Use \"View/Update\" to add to a list"));
4769 /* else, fall through */
4772 bogus_command(c
, F_ON(F_USE_FK
, ps
) ? "F1" : "?");
4776 if(ps
->next_screen
!= SCREEN_FUN_NULL
)
4786 * Turn on zoom mode and zoom in if applicable.
4788 * Args selecteds -- tells which entries are selected in current abook
4789 * start_disp -- Passed in so we can set it back in the caller
4792 ab_zoom(EXPANDED_S
*selecteds
, int *start_disp
)
4795 DL_CACHE_S
*dlc
, dlc_restart
;
4800 q_status_message(SM_ORDER
, 0, 2, _("Zoom Mode is now on"));
4802 dl
= dlist(as
.top_ent
+as
.cur_row
);
4803 if((dl
->type
== ListHead
||
4804 dl
->type
== Simple
||
4805 dl
->type
== ListEmpty
||
4806 dl
->type
== ListClickHere
||
4807 dl
->type
== ListEnt
) &&
4808 entry_is_selected(selecteds
, (a_c_arg_t
)dl
->elnum
)){
4809 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4811 warp_to_dlc(&dlc_restart
, 0L);
4812 /* put current entry in middle of screen */
4813 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4814 as
.cur_row
= 0L - as
.top_ent
;
4819 warp_to_top_of_abook(as
.cur
);
4821 new_ent
= first_selectable_line(0L);
4822 if(new_ent
== NO_LINE
)
4825 as
.cur_row
= new_ent
;
4827 /* if it is off screen */
4828 if(as
.cur_row
>= as
.l_p_page
){
4829 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4830 as
.cur_row
= (as
.l_p_page
- 1);
4835 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4837 warp_to_dlc(&dlc_restart
, 0L);
4838 /* put current entry in middle of screen */
4839 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4840 as
.cur_row
= 0L - as
.top_ent
;
4843 ps_global
->mangled_body
= 1;
4848 q_status_message(SM_ORDER
, 0, 2, _("No selected entries to zoom on"));
4854 * Turn off zoom mode and zoom out if applicable.
4856 * Args start_disp -- Passed in so we can set it back in the caller
4859 ab_unzoom(int *start_disp
)
4861 DL_CACHE_S
*dlc
, dlc_restart
;
4864 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
4865 if(dlc
->type
== DlcZoomEmpty
){
4868 warp_to_beginning();
4870 new_ent
= first_selectable_line(0L);
4871 if(new_ent
== NO_LINE
)
4874 as
.cur_row
= new_ent
;
4876 /* if it is off screen */
4877 if(as
.cur_row
>= as
.l_p_page
){
4878 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
4879 as
.cur_row
= (as
.l_p_page
- 1);
4884 warp_to_dlc(&dlc_restart
, 0L);
4885 /* put current entry in middle of screen */
4886 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
4887 as
.cur_row
= 0L - as
.top_ent
;
4890 ps_global
->mangled_body
= 1;
4897 * Post an empty addrbook warning.
4899 * Args: cur_line -- The current line position (in global display list)
4903 empty_warning(long int cur_line
)
4905 register AddrScrn_Disp
*dl
;
4907 dl
= dlist(cur_line
);
4908 if(dl
->type
== NoAbooks
)
4909 q_status_message(SM_ORDER
, 0, 4,
4910 _("No address books configured, use Setup"));
4911 else if(dl
->type
== Empty
)
4912 q_status_message(SM_ORDER
, 0, 4, _("Address Book is Empty"));
4914 q_status_message(SM_ORDER
, 0, 4, _("Distribution List is Empty"));
4919 * Tell user to click on this to expand.
4921 * Args: cur_line -- The current line position (in global display list)
4925 clickable_warning(long int cur_line
)
4927 register AddrScrn_Disp
*dl
;
4929 dl
= dlist(cur_line
);
4930 if(dl
->type
== Title
|| dl
->type
== ClickHereCmb
)
4931 q_status_message(SM_ORDER
, 0, 4, _("Address Book not expanded, use \">\" to expand"));
4933 q_status_message(SM_ORDER
, 0, 4, _("Distribution List not expanded, use \">\" to expand"));
4938 * Post a no tabs warning.
4941 no_tabs_warning(void)
4943 q_status_message(SM_ORDER
, 0, 4, "Tabs not allowed in address book");
4948 * Prompt for command to apply to selected entries
4950 * Returns: 1 if the entries are successfully commanded.
4954 ab_apply_cmd(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
)
4957 static ESCKEY_S opts
[] = {
4958 {'c', 'c', "C", "ComposeTo"},
4959 {'d', 'd', "D", "Delete"},
4960 {'%', '%', "%", "Print"},
4961 {'f', 'f', "F", "Forward"},
4962 {'s', 's', "S", "Save"},
4963 {'#', '#', "#", "Role"},
4965 {-1, 0, NULL
, NULL
}};
4966 #define PHANTOM_PRINT 6
4968 dprint((7, "- ab_apply_cmd -\n"));
4971 opts
[PHANTOM_PRINT
].ch
= (F_ON(F_ENABLE_PRYNT
, ps_global
)) ? 'y' : -1;
4973 switch(radio_buttons("APPLY command : ", command_line
, opts
, 'z', 'x',
4976 ret
= ab_compose_to_addr(cur_line
, 1, 0);
4980 ret
= ab_compose_to_addr(cur_line
, 1, 1);
4984 ret
= ab_agg_delete(ps
, 1);
4992 ret
= ab_forward(ps
, cur_line
, 1);
4996 ret
= ab_save(ps
, abook
, cur_line
, command_line
, 1);
5000 cmd_cancelled("Apply command");
5004 q_status_message(SM_INFO
, 0, 2,
5005 "Cancelled, there is no default command");
5009 ps_global
->mangled_footer
= 1;
5016 * Allow user to mark some entries "selected".
5019 ab_select(struct pine
*ps
, AdrBk
*abook
, long int cur_line
, int command_line
, int *start_disp
)
5021 static ESCKEY_S sel_opts1
[] = {
5022 {'a', 'a', "A", "unselect All"},
5023 { 0 , 'c', "C", NULL
},
5024 {'b', 'b', "B", "Broaden selctn"},
5025 {'n', 'n', "N", "Narrow selctn"},
5026 {'f', 'f', "F", "Flip selected"},
5029 static char *sel_pmt1
= "ALTER selection : ";
5030 static ESCKEY_S sel_opts2
[] = {
5031 {'a', 'a', "A", "select All"},
5032 {'c', 'c', "C", "select Cur"},
5033 {'t', 't', "T", "Text"},
5034 {'s', 's', "S", "Status"},
5037 static char *sel_pmt2
= "SELECT criteria : ";
5039 HelpType help
= NO_HELP
;
5040 adrbk_cntr_t num
, ab_count
;
5041 int q
= 0, rv
= 0, narrow
= 0,
5042 do_flush
= 0, do_warp
= 0, prevsel
,
5043 move_current
= 0, do_beginning
= 0;
5046 DL_CACHE_S
*dlc
, dlc_restart
;
5048 dprint((5, "- ab_select -\n"));
5050 if(cur_is_open()){ /* select applies only to this addrbook */
5052 ps
->mangled_footer
= 1;
5053 prevsel
= as
.selections
;
5054 dl
= dlist(cur_line
);
5055 sel_opts
= sel_opts2
;
5058 * If already some selected, ask how to alter that selection.
5061 sel_opts
+= 2; /* don't offer all or current below */
5062 if(dl
&& (dl
->type
== ListHead
|| dl
->type
== Simple
)){
5063 sel_opts1
[1].label
= entry_is_selected(abook
->selects
,
5064 (a_c_arg_t
)dl
->elnum
)
5067 sel_opts1
[1].ch
= 'c';
5070 sel_opts1
[1].ch
= -2; /* don't offer this choice */
5072 switch(q
= radio_buttons(sel_pmt1
, command_line
, sel_opts1
,
5073 'a', 'x', help
, RB_NORM
)){
5074 case 'n': /* narrow selection */
5076 case 'b': /* broaden selection */
5077 q
= 0; /* but don't offer criteria prompt */
5080 case 'c': /* select or unselect current */
5081 case 'a': /* select or unselect all */
5082 case 'f': /* flip selections */
5083 case 'x': /* cancel */
5087 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5088 "Unsupported Select option");
5094 (dl
->type
== ListHead
|| dl
->type
== Simple
)){
5095 sel_opts1
[1].label
= entry_is_selected(abook
->selects
,
5096 (a_c_arg_t
)dl
->elnum
)
5099 sel_opts1
[1].ch
= 'c';
5102 sel_opts1
[1].ch
= -2; /* don't offer this choice */
5105 q
= radio_buttons(sel_pmt2
, command_line
, sel_opts
,
5106 'c', 'x', help
, RB_NORM
);
5109 dlc
= get_dlc(cur_line
);
5111 ab_count
= adrbk_count(abook
);
5114 case 'x': /* cancel */
5115 cmd_cancelled("Select command");
5118 case 'c': /* select/unselect current */
5119 ps
->mangled_body
= 1;
5120 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
)){
5121 entry_unset_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
);
5124 if(as
.selections
== 0 && as
.zoomed
){
5126 q_status_message(SM_ORDER
, 0, 2,
5127 "Zoom Mode is now off, no entries selected");
5138 entry_set_selected(abook
->selects
, (a_c_arg_t
)dl
->elnum
);
5141 if(as
.selections
== 1 && !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5143 as
.top_ent
= dlc_restart
.global_row
;
5154 ps
->mangled_body
= 1;
5155 if(any_selected(abook
->selects
)){ /* unselect all */
5156 for(num
= 0; num
< ab_count
; num
++){
5157 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5159 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5163 if(as
.selections
== 0 && as
.zoomed
){
5165 q_status_message(SM_ORDER
, 0, 2,
5166 "Zoom Mode is now off, all entries UNselected");
5172 snprintf(bb
, sizeof(bb
), "%s entries UNselected%s%s%s",
5173 comatose(prevsel
-as
.selections
),
5174 as
.selections
? ", still " : "",
5175 as
.selections
? comatose(as
.selections
) : "",
5176 as
.selections
? " selected in other addrbooks" : "");
5177 bb
[sizeof(bb
)-1] = '\0';
5178 q_status_message(SM_ORDER
, 0, 2, bb
);
5185 else{ /* select all */
5186 for(num
= 0; num
< ab_count
; num
++){
5187 if(!entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5189 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5193 q_status_message1(SM_ORDER
, 0, 2, "All %s entries selected",
5194 comatose(ab_count
));
5195 if(prevsel
== 0 && as
.selections
> 0 &&
5196 !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5198 as
.top_ent
= dlc_restart
.global_row
- as
.cur_row
;
5201 else if(dlc_restart
.type
== DlcZoomEmpty
&&
5202 as
.selections
> prevsel
)
5212 case 'f': /* flip selections in this abook */
5213 ps
->mangled_body
= 1;
5214 for(num
= 0; num
< ab_count
; num
++){
5215 if(entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)){
5216 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5220 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5230 q_status_message(SM_ORDER
, 0, 2, "Zoom Mode is now off");
5237 q_status_message1(SM_ORDER
, 0, 2, "%s entries now selected",
5238 comatose(as
.selections
));
5246 rv
= ab_select_text(abook
, narrow
);
5250 rv
= ab_select_type(abook
, narrow
);
5255 ps
->mangled_body
= 1;
5256 if(dlc_restart
.type
== DlcZoomEmpty
&&
5257 as
.selections
> prevsel
)
5260 if(as
.selections
== 0){
5262 q_status_message(SM_ORDER
, 0, 2,
5263 "Zoom Mode is now off");
5270 if(prevsel
== 0 && as
.selections
> 0 &&
5271 !as
.zoomed
&& F_ON(F_AUTO_ZOOM
, ps
)){
5279 if(prevsel
== as
.selections
&& prevsel
> 0){
5280 if(as
.selections
== 1)
5281 q_status_message(SM_ORDER
, 0, 2,
5282 "No change resulted, 1 entry remains selected");
5284 q_status_message1(SM_ORDER
, 0, 2,
5285 "No change resulted, %s entries remain selected",
5286 comatose(as
.selections
));
5288 else if(prevsel
== 0){
5289 if(as
.selections
== 1)
5290 q_status_message(SM_ORDER
, 0, 2,
5291 "Select matched 1 entry");
5292 else if(as
.selections
> 1)
5293 q_status_message1(SM_ORDER
, 0, 2,
5294 "Select matched %s entries",
5295 comatose(as
.selections
));
5297 q_status_message(SM_ORDER
, 0, 2,
5298 "Select failed! No entries selected");
5300 else if(as
.selections
== 0){
5302 q_status_message(SM_ORDER
, 0, 2,
5303 "The single selected entry is UNselected");
5305 q_status_message1(SM_ORDER
, 0, 2,
5306 "All %s entries UNselected",
5310 if(as
.selections
== 1 && (prevsel
-as
.selections
) == 1)
5311 q_status_message(SM_ORDER
, 0, 2,
5312 "1 entry now selected, 1 entry was UNselected");
5313 else if(as
.selections
== 1)
5314 q_status_message1(SM_ORDER
, 0, 2,
5315 "1 entry now selected, %s entries were UNselected",
5316 comatose(prevsel
-as
.selections
));
5317 else if((prevsel
-as
.selections
) == 1)
5318 q_status_message1(SM_ORDER
, 0, 2,
5319 "%s entries now selected, 1 entry was UNselected",
5320 comatose(as
.selections
));
5322 q_status_message2(SM_ORDER
, 0, 2,
5323 "%s entries now selected, %s entries were UNselected",
5324 comatose(as
.selections
),
5325 comatose(prevsel
-as
.selections
));
5328 if((as
.selections
-prevsel
) == 1)
5329 q_status_message1(SM_ORDER
, 0, 2,
5330 "1 new entry selected, %s entries now selected",
5331 comatose(as
.selections
));
5332 else if(as
.selections
== 1)
5333 q_status_message1(SM_ORDER
, 0, 2,
5334 "%s new entries selected, 1 entry now selected",
5335 comatose(as
.selections
-prevsel
));
5337 q_status_message2(SM_ORDER
, 0, 2,
5338 "%s new entries selected, %s entries now selected",
5339 comatose(as
.selections
-prevsel
),
5340 comatose(as
.selections
));
5347 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5348 "Unsupported Select option");
5353 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
))
5354 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5355 "Select is only available from within an expanded address book");
5357 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5358 "Select is only available when viewing an individual address book");
5367 warp_to_beginning(); /* just go to top */
5369 new_ent
= first_selectable_line(0L);
5370 if(new_ent
== NO_LINE
)
5373 as
.cur_row
= new_ent
;
5375 /* if it is off screen */
5376 if(as
.cur_row
>= as
.l_p_page
){
5377 as
.top_ent
+= (as
.cur_row
- as
.l_p_page
+ 1);
5378 as
.cur_row
= (as
.l_p_page
- 1);
5382 flush_dlc_from_cache(&dlc_restart
);
5384 warp_to_dlc(&dlc_restart
, dlc_restart
.global_row
);
5387 dlc
= get_dlc(cur_line
);
5388 if(dlc
->type
== DlcEnd
){
5391 as
.top_ent
+= as
.cur_row
; /* plus a negative number */
5396 *start_disp
= as
.cur_row
;
5400 *start_disp
= as
.cur_row
;
5406 * Selects based on whether an entry is a list or not.
5408 * Returns: 0 if search went ok
5409 * -1 if there was a problem
5412 ab_select_type(AdrBk
*abook
, int narrow
)
5414 static ESCKEY_S ab_sel_type_opt
[] = {
5415 {'s', 's', "S", "Simple"},
5416 {'l', 'l', "L", "List"},
5419 static char *ab_sel_type
= "Select Lists or Simples (non Lists) ? ";
5421 adrbk_cntr_t num
, ab_count
;
5423 dprint((6, "- ab_select_type -\n"));
5428 switch(type
= radio_buttons(ab_sel_type
, -FOOTER_ROWS(ps_global
),
5429 ab_sel_type_opt
, 'l', 'x', NO_HELP
, RB_NORM
)){
5437 cmd_cancelled("Select");
5441 dprint((1,"\n - BOTCH: ab_select_type unknown option\n"));
5445 ab_count
= adrbk_count(abook
);
5446 for(num
= 0; num
< ab_count
; num
++){
5451 * If it won't possibly change state, don't look at it.
5453 if((narrow
&& !entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)) ||
5454 (!narrow
&& entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)))
5457 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) num
);
5458 matched
= ((type
== 's' && abe
->tag
== Single
) ||
5459 (type
== 'l' && abe
->tag
== List
));
5461 if(narrow
&& !matched
){
5462 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5465 else if(!narrow
&& matched
){
5466 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5476 * Selects based on string matches in various addrbook fields.
5478 * Returns: 0 if search went ok
5479 * -1 if there was a problem
5482 ab_select_text(AdrBk
*abook
, int narrow
)
5484 static ESCKEY_S ab_sel_text_opt
[] = {
5485 {'n', 'n', "N", "Nickname"},
5486 {'a', 'a', "A", "All Text"},
5487 {'f', 'f', "F", "Fullname"},
5488 {'e', 'e', "E", "Email Addrs"},
5489 {'c', 'c', "C", "Comment"},
5490 {'z', 'z', "Z", "Fcc"},
5493 static char *ab_sel_text
=
5494 "Select based on Nickname, All text, Fullname, Addrs, Comment, or Fcc ? ";
5495 HelpType help
= NO_HELP
;
5497 char sstring
[80+1], prompt
[80];
5498 adrbk_cntr_t num
, ab_count
;
5499 char *fmt
= "String in \"%s\" to match : ";
5501 dprint((6, "- ab_select_text -\n"));
5506 switch(type
= radio_buttons(ab_sel_text
, -FOOTER_ROWS(ps_global
),
5507 ab_sel_text_opt
, 'a', 'x', NO_HELP
, RB_NORM
)){
5509 snprintf(prompt
, sizeof(prompt
), fmt
, "Nickname");
5512 snprintf(prompt
, sizeof(prompt
), fmt
, "All Text");
5515 snprintf(prompt
, sizeof(prompt
), fmt
, "Fullname");
5518 snprintf(prompt
, sizeof(prompt
), fmt
, "addresses");
5521 snprintf(prompt
, sizeof(prompt
), fmt
, "Comment");
5524 snprintf(prompt
, sizeof(prompt
), fmt
, "Fcc");
5529 dprint((1,"\n - BOTCH: ab_select_text unknown option\n"));
5533 prompt
[sizeof(prompt
)-1] = '\0';
5537 int flags
= OE_APPEND_CURRENT
;
5539 r
= optionally_enter(sstring
, -FOOTER_ROWS(ps_global
), 0,
5540 sizeof(sstring
), prompt
, NULL
, help
, &flags
);
5542 case 3: /* BUG, no help */
5550 if(r
== 1 || sstring
[0] == '\0')
5556 if(type
== 'x' || r
== 'x'){
5557 cmd_cancelled("Select");
5561 ab_count
= adrbk_count(abook
);
5562 for(num
= 0; num
< ab_count
; num
++){
5567 * If it won't possibly change state, don't look at it.
5569 if((narrow
&& !entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)) ||
5570 (!narrow
&& entry_is_selected(abook
->selects
, (a_c_arg_t
)num
)))
5573 abe
= adrbk_get_ae(abook
, (a_c_arg_t
) num
);
5574 matched
= match_check(abe
, type
, sstring
);
5576 if(narrow
&& !matched
){
5577 entry_unset_selected(abook
->selects
, (a_c_arg_t
)num
);
5580 else if(!narrow
&& matched
){
5581 entry_set_selected(abook
->selects
, (a_c_arg_t
)num
);
5591 * Returns: 1 if a match is found for the entry
5596 match_check(AdrBk_Entry
*abe
, int type
, char *string
)
5598 static int err
= -1;
5599 static int match
= 1;
5600 static int nomatch
= 0;
5601 unsigned int checks
;
5602 #define CK_NICKNAME 0x01
5603 #define CK_FULLNAME 0x02
5604 #define CK_ADDRESSES 0x04
5606 #define CK_COMMENT 0x10
5612 case 'n': /* Nickname */
5613 checks
|= CK_NICKNAME
;
5616 case 'f': /* Fullname */
5617 checks
|= CK_FULLNAME
;
5620 case 'e': /* Addrs */
5621 checks
|= CK_ADDRESSES
;
5624 case 'a': /* All Text */
5632 case 'c': /* Comment */
5633 checks
|= CK_COMMENT
;
5637 q_status_message(SM_ORDER
| SM_DING
, 3, 3, "Unknown type");
5641 if(checks
& CK_NICKNAME
){
5642 if(abe
&& abe
->nickname
&& srchstr(abe
->nickname
, string
))
5646 if(checks
& CK_FULLNAME
){
5650 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5651 SIZEOF_20KBUF
, abe
->fullname
),
5656 if(checks
& CK_ADDRESSES
){
5658 abe
->tag
== Single
&&
5660 abe
->addr
.addr
[0] &&
5661 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5662 SIZEOF_20KBUF
, abe
->addr
.addr
),
5670 for(p
= abe
->addr
.list
; p
!= NULL
&& *p
!= NULL
; p
++){
5671 if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5679 if(checks
& CK_FCC
){
5683 srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
5684 SIZEOF_20KBUF
, abe
->fcc
),
5689 if(checks
& CK_COMMENT
&& abe
&& abe
->extra
&& abe
->extra
[0]){
5691 unsigned char *p
, *tmp
= NULL
;
5694 if((n
= 4*strlen(abe
->extra
)) > SIZEOF_20KBUF
-1){
5696 p
= tmp
= (unsigned char *)fs_get(len
* sizeof(char));
5699 len
= SIZEOF_20KBUF
;
5700 p
= (unsigned char *)tmp_20k_buf
;
5703 if(srchstr((char *)rfc1522_decode_to_utf8(p
, len
, abe
->extra
), string
))
5707 fs_give((void **)&tmp
);
5720 * command_line -- The screen line on which to prompt
5723 ab_goto_folder(int command_line
)
5727 int notrealinbox
= 0;
5729 dprint((2, "- ab_goto_folder -\n"));
5731 tc
= ps_global
->context_current
;
5733 go_folder
= broach_folder(command_line
, 1, ¬realinbox
, &tc
);
5735 if(go_folder
!= NULL
)
5736 visit_folder(ps_global
, go_folder
, tc
, NULL
, notrealinbox
? 0L : DB_INBOXWOCNTXT
);
5741 * Execute whereis command.
5743 * Returns value of the new top entry, or NO_LINE if cancelled.
5746 ab_whereis(int *warped
, int command_line
)
5748 int rc
, wrapped
= 0;
5749 long new_top_ent
, new_line
;
5751 dprint((5, "- ab_whereis -\n"));
5753 rc
= search_book(as
.top_ent
+as
.cur_row
, command_line
,
5754 &new_line
, &wrapped
, warped
);
5756 new_top_ent
= NO_LINE
;
5759 q_status_message(SM_INFO
, 0, 2, _("Address book search cancelled"));
5762 q_status_message(SM_ORDER
, 0, 4, _("Word not found"));
5764 else if(rc
== 0){ /* search succeeded */
5767 q_status_message(SM_INFO
, 0, 2, _("Search wrapped to beginning"));
5768 else if(wrapped
== 2)
5769 q_status_message(SM_INFO
, 0, 2,
5770 _("Current line contains the only match"));
5772 /* know match is on the same page */
5774 new_line
>= as
.top_ent
&&
5775 new_line
< as
.top_ent
+as
.l_p_page
)
5776 new_top_ent
= as
.top_ent
;
5777 /* don't know whether it is or not, reset top_ent */
5779 new_top_ent
= first_line(new_line
- as
.l_p_page
/2);
5781 as
.cur_row
= new_line
- new_top_ent
;
5784 return(new_top_ent
);
5789 * recalculate display parameters for window size change
5796 DL_CACHE_S dlc_buf
, *dlc_restart
;
5798 old_l_p_p
= as
.l_p_page
;
5799 as
.l_p_page
= ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)
5800 - HEADER_ROWS(ps_global
);
5802 dprint((9, "- ab_resize -\n l_p_p was %d, now %d\n",
5803 old_l_p_p
, as
.l_p_page
));
5805 if(as
.l_p_page
<= 0){
5810 as
.no_op_possbl
= 0;
5812 new_line
= as
.top_ent
+ as
.cur_row
;
5813 as
.top_ent
= first_line(new_line
- as
.l_p_page
/2);
5814 as
.cur_row
= new_line
- as
.top_ent
;
5815 as
.old_cur_row
= as
.cur_row
;
5817 /* need this to re-initialize Text and Title lines in display */
5818 /* get the old current line (which may be the wrong width) */
5819 dlc_restart
= get_dlc(new_line
);
5820 /* flush it from cache */
5821 flush_dlc_from_cache(dlc_restart
);
5822 /* re-get it (should be right now) */
5823 dlc_restart
= get_dlc(new_line
);
5824 /* copy it to local storage */
5825 dlc_buf
= *dlc_restart
;
5826 dlc_restart
= &dlc_buf
;
5827 /* flush everything from cache and add that one line back in */
5828 warp_to_dlc(dlc_restart
, new_line
);
5833 * Returns 0 if we know for sure that there are no
5834 * addresses available in any of the addressbooks.
5836 * Easiest would be to start at 0 and go through the addrbook, but that will
5837 * be very slow for big addrbooks if we're not close to 0 already. Instead,
5838 * starting_hint is a hint at a good place to start looking.
5841 any_addrs_avail(long int starting_hint
)
5843 register AddrScrn_Disp
*dl
;
5847 * Look from lineno backwards first, in hopes of finding it in cache.
5849 lineno
= starting_hint
;
5850 for(dl
=dlist(lineno
);
5851 dl
->type
!= Beginning
;
5852 dl
= dlist(--lineno
)){
5853 if(dl
->type
== NoAbooks
)
5870 /* search from here forward if we still don't know */
5871 lineno
= starting_hint
;
5872 for(dl
=dlist(lineno
);
5874 dl
= dlist(++lineno
)){
5875 if(dl
->type
== NoAbooks
)
5897 * Returns 1 if this line is a clickable line.
5900 entry_is_clickable(long int lineno
)
5902 register AddrScrn_Disp
*dl
;
5904 if((dl
= dlist(lineno
)) &&
5905 (dl
->type
== Title
|| dl
->type
== ListClickHere
||
5906 dl
->type
== ClickHereCmb
))
5914 * Returns 1 if this line is a clickable Title line.
5917 entry_is_clickable_title(long int lineno
)
5919 register AddrScrn_Disp
*dl
;
5921 if((dl
= dlist(lineno
)) && (dl
->type
== Title
|| dl
->type
== ClickHereCmb
))
5929 * Returns 1 if an address or list is selected.
5932 is_addr(long int lineno
)
5934 register AddrScrn_Disp
*dl
;
5936 if((dl
= dlist(lineno
)) && (dl
->type
== ListHead
||
5937 dl
->type
== ListEnt
||
5938 dl
->type
== Simple
))
5946 * Returns 1 if type of line is Empty.
5949 is_empty(long int lineno
)
5951 register AddrScrn_Disp
*dl
;
5953 if((dl
= dlist(lineno
)) &&
5954 (dl
->type
== Empty
|| dl
->type
== ListEmpty
|| dl
->type
== ZoomEmpty
))
5962 * Returns 1 if lineno is a list entry
5965 entry_is_listent(long int lineno
)
5967 register AddrScrn_Disp
*dl
;
5969 if((dl
= dlist(lineno
)) && dl
->type
== ListEnt
)
5977 * Returns 1 if lineno is a fake addrbook for config screen add
5978 * or if it is an AskServer line for LDAP query
5981 entry_is_addkey(long int lineno
)
5983 register AddrScrn_Disp
*dl
;
5985 if((dl
= dlist(lineno
)) && (dl
->type
== AddFirstPers
||
5986 dl
->type
== AddFirstGlob
||
5987 dl
->type
== AskServer
))
5995 * Returns 1 if lineno is the line to ask for directory server query
5998 entry_is_askserver(long int lineno
)
6000 register AddrScrn_Disp
*dl
;
6002 if((dl
= dlist(lineno
)) && dl
->type
== AskServer
)
6010 * Returns 1 if an add abook here would be global, not personal
6013 add_is_global(long int lineno
)
6015 register AddrScrn_Disp
*dl
;
6020 if(dl
->type
== Title
){
6021 register PerAddrBook
*pab
;
6023 pab
= &as
.adrbks
[as
.cur
];
6024 if(pab
&& pab
->type
& GLOBAL
)
6027 else if(dl
->type
== AddFirstGlob
)
6036 * Find the first line greater than or equal to line. (Any line, not
6037 * necessarily selectable.)
6039 * Returns the line number of the found line or NO_LINE if there is none.
6041 * Warning: This just starts at the passed in line and goes forward until
6042 * it runs into a line that isn't a Beginning line. If the line passed in
6043 * is not in the dlc cache, it will have no way to know when it gets to the
6047 first_line(long int line
)
6050 register PerAddrBook
*pab
;
6054 dlist(lineno
)->type
== Beginning
;
6058 if(dlist(lineno
)->type
!= End
)
6061 for(i
= 0; i
< as
.n_addrbk
; i
++){
6062 pab
= &as
.adrbks
[i
];
6063 if(pab
->ostatus
!= Open
&&
6064 pab
->ostatus
!= HalfOpen
&&
6065 pab
->ostatus
!= ThreeQuartOpen
)
6076 * Find the line number of the next selectable line.
6078 * Args: cur_line -- The current line position (in global display list)
6080 * new_line -- Return value: new line position
6082 * Result: The new line number is set.
6083 * The value 1 is returned if OK or 0 if there is no next line.
6086 next_selectable_line(long int cur_line
, long int *new_line
)
6088 /* skip over non-selectable lines */
6090 !line_is_selectable(cur_line
) && dlist(cur_line
)->type
!= End
;
6094 if(dlist(cur_line
)->type
== End
)
6097 *new_line
= cur_line
;
6103 * Find the line number of the previous selectable line.
6105 * Args: cur_line -- The current line position (in global display list)
6107 * new_line -- Return value: new line position
6109 * Result: The new line number is set.
6110 * The value 1 is returned if OK or 0 if there is no previous line.
6113 prev_selectable_line(long int cur_line
, long int *new_line
)
6115 /* skip backwards over non-selectable lines */
6117 !line_is_selectable(cur_line
) && dlist(cur_line
)->type
!= Beginning
;
6121 if(dlist(cur_line
)->type
== Beginning
)
6124 *new_line
= cur_line
;
6131 * Resync the display with the addrbooks, which were just discovered
6132 * to be out of sync with the display.
6134 * Returns 1 -- current address book had to be resynced
6135 * 0 -- current address book not resynced
6138 resync_screen(PerAddrBook
*pab
, AddrBookArg style
, int checkedn
)
6141 int current_resynced
= 0;
6142 DL_CACHE_S dlc_restart
, *dlc
= NULL
;
6145 * The test below gives conditions under which it is safe to go ahead
6146 * and resync all the addrbooks that are out of sync now, and the
6147 * display won't change. Otherwise, we have to be careful to preserve
6148 * some of our state so that we can attempt to restore the screen to
6149 * a state that is as close as possible to what we have now. If the
6150 * currently opened address book (pab) is out of date we will lose
6151 * the expanded state of its distribution lists, which is no big deal.
6152 * Since resyncing also loses the checked status if we're selecting with
6153 * ListMode, we don't even attempt it in that case.
6155 if((ab_nesting_level
< 2 && !cur_is_open() && checkedn
== 0) ||
6156 (style
== AddrBookScreen
&&
6158 pab
->address_book
&&
6159 !(pab
->address_book
->flags
& FILE_OUTOFDATE
||
6160 (pab
->address_book
->rd
&&
6161 pab
->address_book
->rd
->flags
& REM_OUTOFDATE
)))){
6163 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
6164 dlc
= get_dlc(as
.top_ent
+as
.cur_row
);
6168 if(adrbk_check_and_fix_all(1, 0, 1)){
6169 ps_global
->mangled_footer
= 1; /* why? */
6170 if(F_ON(F_CMBND_ABOOK_DISP
,ps_global
)){
6171 warp_to_dlc(&dlc_restart
, 0L);
6172 /* put current entry in middle of screen */
6173 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6174 as
.cur_row
= 0L - as
.top_ent
;
6175 ps_global
->mangled_screen
= 1;
6179 else if(style
== AddrBookScreen
){
6180 char *savenick
= NULL
;
6182 adrbk_cntr_t old_entry_num
, new_entry_num
;
6183 long old_global_row
;
6188 * We're going to try to get the nickname of the current
6189 * entry and find it again after the resync.
6191 dl
= dlist(as
.top_ent
+as
.cur_row
);
6192 if(dl
->type
== ListEnt
||
6193 dl
->type
== ListEmpty
||
6194 dl
->type
== ListClickHere
||
6195 dl
->type
== ListHead
||
6196 dl
->type
== Simple
){
6197 abe
= ae(as
.top_ent
+as
.cur_row
);
6198 old_entry_num
= dl
->elnum
;
6199 old_global_row
= as
.top_ent
+as
.cur_row
;
6200 if(abe
&& abe
->nickname
&& abe
->nickname
[0])
6201 savenick
= cpystr(abe
->nickname
);
6204 /* this will close and re-open current addrbook */
6205 (void)adrbk_check_and_fix_all(1, 0, 1);
6209 abe
= adrbk_lookup_by_nick(pab
->address_book
, savenick
,
6211 fs_give((void **)&savenick
);
6214 if(abe
){ /* If we found the same nickname, move to it */
6215 dlc_restart
.adrbk_num
= as
.cur
;
6216 dlc_restart
.dlcelnum
= new_entry_num
;
6217 dlc_restart
.type
= (abe
->tag
== Single
)
6218 ? DlcSimple
: DlcListHead
;
6219 if(old_entry_num
== new_entry_num
)
6220 warp_to_dlc(&dlc_restart
, old_global_row
);
6222 warp_to_dlc(&dlc_restart
, 0L);
6225 warp_to_top_of_abook(as
.cur
);
6227 if(!abe
|| old_entry_num
!= new_entry_num
){
6228 /* put current entry in middle of screen */
6229 as
.top_ent
= first_line(0L - (long)as
.l_p_page
/2L);
6230 as
.cur_row
= 0L - as
.top_ent
;
6234 return(current_resynced
);
6239 * Erase all the check marks.
6247 for(i
= 0; i
< as
.n_addrbk
; i
++){
6248 pab
= &as
.adrbks
[i
];
6249 if(pab
->address_book
&& pab
->address_book
->checks
)
6250 exp_free(pab
->address_book
->checks
);
6252 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6258 * Erase all the selections.
6261 erase_selections(void)
6266 for(i
= 0; i
< as
.n_addrbk
; i
++){
6267 pab
= &as
.adrbks
[i
];
6268 if(pab
->address_book
&& pab
->address_book
->selects
)
6269 exp_free(pab
->address_book
->selects
);
6272 for(i
= 0; i
< as
.n_addrbk
; i
++){
6273 pab
= &as
.adrbks
[i
];
6274 init_disp_form(pab
, ps_global
->VAR_ABOOK_FORMATS
, i
);
6282 * return values of search_in_one_line are or'd combination of these
6284 #define MATCH_NICK 0x1 /* match in field 0 */
6285 #define MATCH_FULL 0x2 /* match in field 1 */
6286 #define MATCH_ADDR 0x4 /* match in field 2 */
6287 #define MATCH_FCC 0x8 /* match in fcc field */
6288 #define MATCH_COMMENT 0x10 /* match in comment field */
6289 #define MATCH_BIGFIELD 0x20 /* match in one of the fields that crosses the
6290 whole screen, like a Title field */
6291 #define MATCH_LISTMEM 0x40 /* match list member */
6293 * Prompt user for search string and call find_in_book.
6295 * Args: cur_line -- The current line position (in global display list)
6297 * command_line -- The screen line to prompt on
6298 * new_line -- Return value: new line position
6299 * wrapped -- Wrapped to beginning of display, tell user
6300 * warped -- Warped to a new location in the addrbook
6302 * Result: The new line number is set if the search is successful.
6303 * Returns 0 if found, -1 if not, -2 if cancelled.
6307 search_book(long int cur_line
, int command_line
, long int *new_line
, int *wrapped
, int *warped
)
6309 int i
=0, find_result
, rc
, flags
, ku
;
6310 static HISTORY_S
*history
= NULL
;
6311 char search_string
[MAX_SEARCH
+ 1];
6312 char prompt
[MAX_SEARCH
+ 50], nsearch_string
[MAX_SEARCH
+1], *p
;
6318 dprint((7, "- search_book -\n"));
6320 init_hist(&history
, HISTSIZE
);
6322 search_string
[0] = '\0';
6323 if((p
= get_prev_hist(history
, "", 0, NULL
)) != NULL
){
6324 strncpy(search_string
, p
, sizeof(search_string
));
6325 search_string
[sizeof(search_string
)-1] = '\0';
6328 snprintf(prompt
, sizeof(prompt
), _("Word to search for [%.*s]: "), MAX_SEARCH
, search_string
);
6329 prompt
[sizeof(prompt
)-1] = '\0';
6331 nsearch_string
[0] = '\0';
6336 ekey
[i
++].label
= "";
6338 ekey
[i
].ch
= ctrl('Y');
6340 ekey
[i
].name
= "^Y";
6341 /* TRANSLATORS: User is searching in address book. One of the options is to
6342 search for the First Address */
6343 ekey
[i
++].label
= _("First Adr");
6345 ekey
[i
].ch
= ctrl('V');
6347 ekey
[i
].name
= "^V";
6348 /* TRANSLATORS: Last Address */
6349 ekey
[i
++].label
= _("Last Adr");
6351 ekey
[i
].ch
= KEY_UP
;
6355 ekey
[i
++].label
= "";
6357 ekey
[i
].ch
= KEY_DOWN
;
6360 ekey
[i
++].label
= "";
6364 flags
= OE_APPEND_CURRENT
| OE_KEEP_TRAILING_SPACE
;
6368 * 2 is really 1 because there will be one real entry and
6369 * one entry of "" because of the get_prev_hist above.
6371 if(items_in_hist(history
) > 2){
6372 ekey
[ku
].name
= HISTORY_UP_KEYNAME
;
6373 ekey
[ku
].label
= HISTORY_KEYLABEL
;
6374 ekey
[ku
+1].name
= HISTORY_DOWN_KEYNAME
;
6375 ekey
[ku
+1].label
= HISTORY_KEYLABEL
;
6379 ekey
[ku
].label
= "";
6380 ekey
[ku
+1].name
= "";
6381 ekey
[ku
+1].label
= "";
6384 rc
= optionally_enter(nsearch_string
, command_line
, 0,
6385 sizeof(nsearch_string
),
6386 prompt
, ekey
, help
, &flags
);
6388 help
= help
== NO_HELP
? h_oe_searchab
: NO_HELP
;
6393 warp_to_beginning(); /* go to top of addrbooks */
6394 if((nl
=first_selectable_line(0L)) != NO_LINE
){
6396 q_status_message(SM_INFO
, 0, 2, _("Searched to first entry"));
6400 q_status_message(SM_INFO
, 0, 2, _("No entries"));
6406 warp_to_end(); /* go to bottom */
6407 if((nl
=first_selectable_line(0L)) != NO_LINE
){
6409 q_status_message(SM_INFO
, 0, 2, _("Searched to last entry"));
6413 q_status_message(SM_INFO
, 0, 2, _("No entries"));
6418 if((p
= get_prev_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
6419 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
6420 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
6428 if((p
= get_next_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
6429 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
6430 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
6438 if(rc
!= 4){ /* 4 is redraw */
6439 save_hist(history
, nsearch_string
, 0, NULL
);
6445 if(rc
== 1 || (search_string
[0] == '\0' && nsearch_string
[0] == '\0'))
6448 if(nsearch_string
[0] != '\0'){
6449 strncpy(search_string
, nsearch_string
, sizeof(search_string
)-1);
6450 search_string
[sizeof(search_string
)-1] = '\0';
6453 find_result
= find_in_book(cur_line
, search_string
, new_line
, wrapped
);
6459 int also
= 0, notdisplayed
= 0;
6461 pab
= &as
.adrbks
[adrbk_num_from_lineno(*new_line
)];
6462 if(find_result
& MATCH_NICK
){
6463 if(pab
->nick_is_displayed
)
6469 if(find_result
& MATCH_FULL
){
6470 if(pab
->full_is_displayed
)
6476 if(find_result
& MATCH_ADDR
){
6477 if(pab
->addr_is_displayed
)
6483 if(find_result
& MATCH_FCC
){
6484 if(pab
->fcc_is_displayed
)
6490 if(find_result
& MATCH_COMMENT
){
6491 if(pab
->comment_is_displayed
)
6497 if(find_result
& MATCH_LISTMEM
){
6500 dl
= dlist(*new_line
);
6501 if(F_OFF(F_EXPANDED_DISTLISTS
,ps_global
)
6502 && !exp_is_expanded(pab
->address_book
->exp
, (a_c_arg_t
)dl
->elnum
))
6506 if(notdisplayed
> 1 && *wrapped
== 0){
6508 /* TRANSLATORS: These "matched" messages are advisory messages explaining
6509 how a search command matched an entry */
6510 q_status_message1(SM_ORDER
,0,4, _("Also matched string in %s other fields"),
6511 comatose(notdisplayed
));
6513 q_status_message1(SM_ORDER
,0,4, _("Matched string in %s fields"),
6514 comatose(notdisplayed
));
6516 else if(notdisplayed
== 1 && *wrapped
== 0){
6518 if(find_result
& MATCH_NICK
&& !pab
->nick_is_displayed
)
6519 q_status_message(SM_ORDER
,0,4, _("Also matched string in Nickname field"));
6520 else if(find_result
& MATCH_FULL
&& !pab
->full_is_displayed
)
6521 q_status_message(SM_ORDER
,0,4, _("Also matched string in Fullname field"));
6522 else if(find_result
& MATCH_ADDR
&& !pab
->addr_is_displayed
)
6523 q_status_message(SM_ORDER
,0,4, _("Also matched string in Address field"));
6524 else if(find_result
& MATCH_FCC
&& !pab
->fcc_is_displayed
)
6525 q_status_message(SM_ORDER
,0,4, _("Also matched string in Fcc field"));
6526 else if(find_result
& MATCH_COMMENT
&& !pab
->comment_is_displayed
)
6527 q_status_message(SM_ORDER
,0,4, _("Also matched string in Comment field"));
6528 else if(find_result
& MATCH_LISTMEM
)
6529 q_status_message(SM_ORDER
,0,4, _("Also matched string in list member address"));
6531 q_status_message(SM_ORDER
,0,4, _("Also matched string in ?"));
6534 if(find_result
& MATCH_NICK
&& !pab
->nick_is_displayed
)
6535 q_status_message(SM_ORDER
,0,4, _("Matched string in Nickname field"));
6536 else if(find_result
& MATCH_FULL
&& !pab
->full_is_displayed
)
6537 q_status_message(SM_ORDER
,0,4, _("Matched string in Fullname field"));
6538 else if(find_result
& MATCH_ADDR
&& !pab
->addr_is_displayed
)
6539 q_status_message(SM_ORDER
,0,4, _("Matched string in Address field"));
6540 else if(find_result
& MATCH_FCC
&& !pab
->fcc_is_displayed
)
6541 q_status_message(SM_ORDER
,0,4, _("Matched string in Fcc field"));
6542 else if(find_result
& MATCH_COMMENT
&& !pab
->comment_is_displayed
)
6543 q_status_message(SM_ORDER
,0,4, _("Matched string in Comment field"));
6544 else if(find_result
& MATCH_LISTMEM
)
6545 q_status_message(SM_ORDER
,0,4, _("Matched string in list member address"));
6547 q_status_message(SM_ORDER
,0,4, _("Matched string in ?"));
6552 /* be sure to be on a selectable field */
6553 if(!line_is_selectable(*new_line
))
6554 if((nl
=first_selectable_line(*new_line
+1)) != NO_LINE
)
6558 return(find_result
? 0 : -1);
6563 * Search the display list for the given string.
6565 * Args: cur_line -- The current line position (in global display list)
6567 * string -- String to search for
6568 * new_line -- Return value: new line position
6569 * wrapped -- Wrapped to beginning of display during search
6571 * Result: The new line number is set if the search is successful.
6572 * Returns 0 -- string not found
6573 * Otherwise, a bitmask of which fields the string was found in.
6576 find_in_book(long int cur_line
, char *string
, long int *new_line
, int *wrapped
)
6578 register AddrScrn_Disp
*dl
;
6582 char *listaddr
= NULL
;
6584 dlc_save
; /* a local copy */
6587 dprint((9, "- find_in_book -\n"));
6590 * Save info to allow us to get back to where we were if we can't find
6591 * the string. Also used to stop our search if we wrap back to the
6592 * start and search forward.
6596 dlc
= get_dlc(nl_save
);
6602 /* start with next line and search to the end of the disp_list */
6604 while(dl
->type
!= End
){
6605 if(dl
->type
== Simple
||
6606 dl
->type
== ListHead
||
6607 dl
->type
== ListEnt
||
6608 dl
->type
== ListClickHere
){
6610 if(dl
->type
== ListEnt
)
6611 listaddr
= listmem(nl
);
6614 abe
= (AdrBk_Entry
*)NULL
;
6616 if((fields
=search_in_one_line(dl
, abe
, listaddr
, string
)) != 0)
6624 * Wrap back to the start of the addressbook and search forward
6627 warp_to_beginning(); /* go to top of addrbooks */
6628 nl
= 0L; /* line number is always 0 after warp_to_beginning */
6632 while(!matching_dlcs(&dlc_save
, dlc
) && dlc
->type
!= DlcEnd
){
6634 fill_in_dl_field(dlc
);
6637 if(dl
->type
== Simple
||
6638 dl
->type
== ListHead
||
6639 dl
->type
== ListEnt
||
6640 dl
->type
== ListClickHere
){
6642 if(dl
->type
== ListEnt
)
6643 listaddr
= listmem(nl
);
6646 abe
= (AdrBk_Entry
*)NULL
;
6648 if((fields
=search_in_one_line(dl
, abe
, listaddr
, string
)) != 0)
6651 dlc
= get_dlc(++nl
);
6654 /* see if it is in the current line */
6655 fill_in_dl_field(dlc
);
6658 if(dl
->type
== Simple
||
6659 dl
->type
== ListHead
||
6660 dl
->type
== ListEnt
||
6661 dl
->type
== ListClickHere
){
6663 if(dl
->type
== ListEnt
)
6664 listaddr
= listmem(nl
);
6667 abe
= (AdrBk_Entry
*)NULL
;
6669 fields
= search_in_one_line(dl
, abe
, listaddr
, string
);
6671 (dl
->type
== Text
|| dl
->type
== Title
|| dl
->type
== TitleCmb
))
6672 fs_give((void **)&dl
->usst
);
6674 /* jump cache back to where we started */
6676 warp_to_dlc(&dlc_save
, nl_save
);
6678 *new_line
= nl_save
; /* because it was in current line */
6691 * Look in line dl for string.
6693 * Args: dl -- the display list for this line
6694 * abe -- AdrBk_Entry if it is an address type
6695 * listaddr -- list member if it is of type ListEnt
6696 * string -- look for this string
6698 * Result: 0 -- string not found
6699 * Otherwise, a bitmask of which fields the string was found in.
6704 * MATCH_COMMENT 0x10
6705 * MATCH_BIGFIELD 0x20
6706 * MATCH_LISTMEM 0x40
6709 search_in_one_line(AddrScrn_Disp
*dl
, AdrBk_Entry
*abe
, char *listaddr
, char *string
)
6715 for(c
= 0; c
< 5; c
++){
6721 if(srchstr(abe
->nickname
, string
))
6722 ret_val
|= MATCH_NICK
;
6730 if(srchstr(dl
->usst
, string
))
6731 ret_val
|= MATCH_BIGFIELD
;
6743 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6744 SIZEOF_20KBUF
, abe
->fullname
),
6746 ret_val
|= MATCH_FULL
;
6756 if(srchstr((abe
&& abe
->tag
== Single
) ?
6757 abe
->addr
.addr
: NULL
, string
))
6758 ret_val
|= MATCH_ADDR
;
6764 (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
,
6765 SIZEOF_20KBUF
, listaddr
), string
))
6766 ret_val
|= MATCH_LISTMEM
;
6772 for(lm
= abe
->addr
.list
;
6773 !(ret_val
& MATCH_LISTMEM
) && *lm
; lm
++)
6774 if(srchstr(*lm
, string
))
6775 ret_val
|= MATCH_LISTMEM
;
6781 if(srchstr(EMPTY
, string
))
6782 ret_val
|= MATCH_BIGFIELD
;
6787 if(srchstr(ADD_PERSONAL
, string
))
6788 ret_val
|= MATCH_BIGFIELD
;
6793 if(srchstr(ADD_GLOBAL
, string
))
6794 ret_val
|= MATCH_BIGFIELD
;
6799 if(srchstr(NOABOOKS
, string
))
6800 ret_val
|= MATCH_BIGFIELD
;
6813 if(abe
&& srchstr(abe
->fcc
, string
))
6814 ret_val
|= MATCH_FCC
;
6821 case 4: /* comment */
6827 unsigned char *p
, *tmp
= NULL
;
6829 if((n
= 4*strlen(abe
->extra
)) > SIZEOF_20KBUF
-1){
6831 p
= tmp
= (unsigned char *)fs_get(len
* sizeof(char));
6834 len
= SIZEOF_20KBUF
;
6835 p
= (unsigned char *)tmp_20k_buf
;
6838 if(srchstr((char *)rfc1522_decode_to_utf8(p
, len
, abe
->extra
),
6840 ret_val
|= MATCH_COMMENT
;
6843 fs_give((void **)&tmp
);
6859 * These chars in nicknames will mess up parsing.
6861 * Returns 0 if ok, 1 if not.
6862 * Returns an allocated error message on error.
6865 nickname_check(char *nickname
, char **error
)
6870 if((t
= strindex(nickname
, SPACE
)) ||
6871 (t
= strindex(nickname
, ',')) ||
6872 (t
= strindex(nickname
, '"')) ||
6873 (t
= strindex(nickname
, ';')) ||
6874 (t
= strindex(nickname
, ':')) ||
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
, '>'))){
6890 * TRANSLATORS: this is telling the user that one of the characters
6891 * they have included in a nickname will not work. It will say something like
6892 * Blank spaces not allowed in nicknames or
6896 snprintf(buf
, sizeof(buf
), _("%s not allowed in nicknames"),
6904 buf
[sizeof(buf
)-1] = '\0';
6905 *error
= cpystr(buf
);
6916 abook_select_screen(struct pine
*ps
)
6918 CONF_S
*ctmp
= NULL
, *first_line
= NULL
;
6919 OPT_SCREEN_S screen
;
6926 helptitle
= _("HELP FOR SELECTING AN ADDRESS BOOK");
6927 help
= h_role_abook_select
;
6929 init_ab_if_needed();
6931 for(adrbknum
= 0; adrbknum
< as
.n_addrbk
; adrbknum
++){
6932 new_confline(&ctmp
);
6936 pab
= &as
.adrbks
[adrbknum
];
6938 ctmp
->value
= cpystr((pab
&& pab
->abnick
)
6940 : (pab
&& pab
->filename
)
6944 ctmp
->d
.b
.selected
= &abook
;
6945 ctmp
->d
.b
.abookname
= ctmp
->value
;
6946 ctmp
->keymenu
= &abook_select_km
;
6948 ctmp
->help_title
= helptitle
;
6949 ctmp
->tool
= abook_select_tool
;
6950 ctmp
->flags
= CF_STARTITEM
;
6951 ctmp
->valoffset
= 4;
6955 memset(&screen
, 0, sizeof(screen
));
6956 (void) conf_scroll_screen(ps
, &screen
, first_line
,
6957 /* TRANSLATORS: Print something1 using something2.
6958 abooks is something1 */
6959 _("SELECT ADDRESS BOOK"), _("abooks"), 0);
6962 q_status_message(SM_ORDER
|SM_DING
, 3, 3, _("No address books defined!"));
6964 ps
->mangled_screen
= 1;
6970 abook_select_tool(struct pine
*ps
, int cmd
, CONF_S
**cl
, unsigned int flags
)
6976 *((*cl
)->d
.b
.selected
) = cpystr((*cl
)->d
.b
.abookname
);
6977 retval
= simple_exit_cmd(flags
);
6981 retval
= simple_exit_cmd(flags
);
6990 ps
->mangled_body
= 1;
6997 * This isn't actually just a nickname completion anymore. The user types
6998 * in a prefix of either a nickname, or a full address, or the addr@mailbox
6999 * part of an address and we look in all of the address books for matches
7000 * like that. We return the longest unambiguous match in answer.
7002 * Args prefix -- The part of the "nickname" that has been typed so far
7003 * answer -- The answer is returned here.
7004 * tabtab -- If the answer returned from adrbk_list_of_completions is
7005 * ambiguous and tabtab is set, then offer up a
7006 * selector screen for all the possible answers.
7007 * flags -- ANC_AFTERCOMMA -- This means that the passed in
7008 * prefix may be a list of comma
7009 * separated addresses and we're only
7010 * completing the last one.
7012 * Returns 0 -- no matches at all
7013 * 1 -- more than one nickname (or address) begins with
7014 * the answer being returned
7015 * 2 -- the returned answer is a complete answer, either a
7016 * nickname or a complete address, and there are
7017 * no longer matches for the prefix
7019 * Allocated answer is returned in answer argument.
7020 * Caller needs to free the answer.
7023 abook_nickname_complete(char *prefix
, char **answer
, int tabtab
, unsigned flags
)
7026 COMPLETE_S
*completions
, *cp
;
7027 char *saved_beginning
= NULL
;
7028 char *potential_answer
= NULL
;
7030 wp_exit
= wp_nobail
= 0;
7032 /* there shouldn't be a case where answer is NULL */
7037 * If we need to handle case where no prefix is passed in,
7038 * figure that out when we need it.
7040 if(!(prefix
&& prefix
[0]))
7043 if(flags
& ANC_AFTERCOMMA
){
7047 * Find last comma, save the part before that, operate
7048 * only on the last address.
7050 if((lastnick
= strrchr(prefix
? prefix
: "", ',')) != NULL
){
7052 while(!(*lastnick
& 0x80) && isspace((unsigned char) (*lastnick
)))
7055 saved_beginning
= cpystr(prefix
);
7056 saved_beginning
[lastnick
-prefix
] = '\0';
7061 if(!(prefix
&& prefix
[0]))
7064 completions
= adrbk_list_of_completions(prefix
,
7065 ps_global
->cur_uid_stream
, ps_global
->cur_uid
,
7066 ALC_INCLUDE_ADDRS
| ((strlen(prefix
) >= 3) ? ALC_INCLUDE_LDAP
: 0));
7070 else if(completions
&& completions
->next
)
7076 if(completions
->full_address
&& completions
->full_address
[0])
7077 potential_answer
= cpystr(completions
->full_address
);
7078 else if(completions
->nickname
&& completions
->nickname
[0])
7079 potential_answer
= cpystr(completions
->nickname
);
7080 else if(completions
->addr
&& completions
->addr
[0])
7081 potential_answer
= cpystr(completions
->addr
);
7083 potential_answer
= cpystr(prefix
);
7085 /* answer is ambiguous and caller wants a choose list in that case */
7086 else if(ambiguity
== 1 && tabtab
){
7087 potential_answer
= choose_an_address_with_this_prefix(completions
);
7089 if(potential_answer
)
7093 potential_answer
= cpystr(prefix
);
7096 else if(ambiguity
== 1){
7098 char cand1_kth_char
, cand2_kth_char
;
7101 /* find the longest unambiguous prefix */
7102 strncpy(unambig
, prefix
, sizeof(unambig
));
7103 unambig
[sizeof(unambig
)-1] = '\0';
7104 k
= strlen(unambig
);
7107 * First verify that they all match through prefix. LDAP sometimes gives
7108 * weird, inexplicable answers that don't seem to match at all.
7110 for(cp
= completions
; cp
; cp
= cp
->next
)
7111 if(!( /* not a match */
7112 /* no NICK bit OR nickname matches prefix */
7113 (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& !struncmp(unambig
, cp
->nickname
, k
)))
7114 /* AND no ADDR bit OR addr matches prefix */
7115 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& !struncmp(unambig
, cp
->addr
, k
)))
7116 /* AND neither FULL bit is set OR one of the two fulls matches prefix */
7117 && (!(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
))))
7121 /* if cp that means there was not a universal match up through prefix, stop */
7124 cand1_kth_char
= cand2_kth_char
= '\0';
7125 if(completions
->matches_bitmap
& ALC_NICK
&& completions
->nickname
&& strlen(completions
->nickname
) >= k
)
7126 cand1_kth_char
= completions
->nickname
[k
];
7127 else if(completions
->matches_bitmap
& ALC_ADDR
&& completions
->addr
&& strlen(completions
->addr
) >= k
)
7128 cand1_kth_char
= completions
->addr
[k
];
7130 if(completions
->matches_bitmap
& ALC_FULL
&& completions
->full_address
&& strlen(completions
->full_address
) >= k
)
7131 cand1_kth_char
= completions
->full_address
[k
];
7133 if(completions
->matches_bitmap
& ALC_REVFULL
&& completions
->rev_fullname
&& strlen(completions
->rev_fullname
) >= k
)
7134 cand2_kth_char
= completions
->rev_fullname
[k
];
7138 * You'll want a wide screen to read this. There are two possible
7139 * candidate chars for the next position. One or the other of them
7140 * has to match in all of the possible completions. We consider it
7141 * a match if either of the fullname completions for this entry is
7142 * a match. That may not match what the user expects but it may.
7144 for(cp
= completions
; cp
; cp
= cp
->next
){
7145 if(!( /* candidate 1 is not a match */
7146 /* candidate 1 is defined */
7147 cand1_kth_char
&& cand1_kth_char
!= ','
7148 /* AND no NICK bit OR nickname char is a match */
7149 && (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& cp
->nickname
[k
] == cand1_kth_char
))
7150 /* AND no ADDR bit OR addr char is a match */
7151 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& cp
->addr
[k
] == cand1_kth_char
))
7152 /* AND neither FULL bit is set OR one of the two full chars is a match */
7153 && (!(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
)))
7155 cand1_kth_char
= '\0'; /* mark that it isn't a match */
7157 if(!cand1_kth_char
&& !( /* cand1 is not a match AND cand2 is not a match */
7158 /* candidate 2 is defined */
7159 cand2_kth_char
&& cand2_kth_char
!= ','
7160 /* AND no NICK bit OR nickname char is a match */
7161 && (!(cp
->matches_bitmap
& ALC_NICK
) || (cp
->nickname
&& strlen(cp
->nickname
) >= k
&& cp
->nickname
[k
] == cand2_kth_char
))
7162 /* AND no ADDR bit OR addr char is a match */
7163 && (!(cp
->matches_bitmap
& ALC_ADDR
) || (cp
->addr
&& strlen(cp
->addr
) >= k
&& cp
->addr
[k
] == cand2_kth_char
))
7164 /* AND neither FULL bit is set OR one of the two full chars is a match */
7165 && (!(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
)))
7167 cand2_kth_char
= '\0'; /* mark that it isn't a match */
7169 if(!cand1_kth_char
&& !cand2_kth_char
)
7170 break; /* no match so break */
7173 if(!cp
) /* they all matched */
7174 unambig
[k
++] = cand1_kth_char
? cand1_kth_char
: cand2_kth_char
;
7176 }while(!cp
&& k
< sizeof(unambig
)-1);
7179 unambig
[sizeof(unambig
)-1] = '\0';
7181 /* don't return answer with trailing space */
7182 while(--k
>= 0 && isspace((unsigned char) unambig
[k
]))
7185 potential_answer
= cpystr(unambig
);
7189 free_complete_s(&completions
);
7191 if(answer
&& ambiguity
!= 0){
7192 if(potential_answer
){
7193 if(saved_beginning
){
7196 l1
= strlen(saved_beginning
);
7197 l2
= strlen(potential_answer
);
7198 *answer
= (char *) fs_get((l1
+l2
+1) * sizeof(char));
7199 strncpy(*answer
, saved_beginning
, l1
+l2
);
7200 strncpy(*answer
+l1
, potential_answer
, l2
);
7201 (*answer
)[l1
+l2
] = '\0';
7204 *answer
= potential_answer
;
7205 potential_answer
= NULL
;
7209 /* this can't happen */
7215 fs_give((void **) &saved_beginning
);
7217 if(potential_answer
)
7218 fs_give((void **) &potential_answer
);
7225 * Returns an allocated nickname choice from user that begins with
7229 choose_an_address_with_this_prefix(COMPLETE_S
*completions
)
7232 char *chosen_address
= NULL
;
7233 char **lp
, **da
, **possible_addrs
= NULL
, **display_addrs
= NULL
;
7236 int show_nick
, show_revfull
;
7239 * Count how many and allocate an array for choose_item_from_list().
7241 for(cnt
= 0, cp
= completions
; cp
; cp
= cp
->next
)
7245 * Copy completions into an array.
7248 lp
= possible_addrs
= (char **) fs_get((cnt
+1) * sizeof(*possible_addrs
));
7249 memset(possible_addrs
, 0, (cnt
+1) * sizeof(*possible_addrs
));
7250 da
= display_addrs
= (char **) fs_get((cnt
+1) * sizeof(*display_addrs
));
7251 memset(display_addrs
, 0, (cnt
+1) * sizeof(*display_addrs
));
7252 for(cp
= completions
; cp
; cp
= cp
->next
){
7253 show_nick
= (cp
->matches_bitmap
& ALC_NICK
) && cp
->nickname
&& cp
->nickname
[0];
7255 if(!show_nick
&& !(cp
->matches_bitmap
& (ALC_NICK
| ALC_ADDR
| ALC_FULL
))
7256 && (cp
->matches_bitmap
& ALC_REVFULL
)
7257 && cp
->rev_fullname
&& cp
->rev_fullname
[0])
7260 snprintf(buf
, sizeof(buf
), "%s%s%s%s%s",
7261 cp
->full_address
? cp
->full_address
: "?",
7262 (show_nick
|| show_revfull
) ? " (" : "",
7263 show_nick
? cp
->nickname
: "",
7264 show_revfull
? cp
->rev_fullname
: "",
7265 (show_nick
|| show_revfull
) ? ")" : "");
7266 *da
++ = cpystr(buf
);
7267 *lp
++ = cpystr(cp
->full_address
? cp
->full_address
: "?");
7272 chosen_address
= choose_item_from_list(possible_addrs
, display_addrs
,
7273 _("SELECT AN ADDRESS"),
7275 h_select_address_screen
,
7276 _("HELP FOR SELECTING AN ADDRESS"),
7278 free_list_array(&possible_addrs
);
7282 free_list_array(&display_addrs
);
7284 return(chosen_address
);
7290 * addr_scroll_up - adjust the global screen state struct such that pine's
7291 * window on the data is shifted DOWN (i.e., the data's
7295 addr_scroll_up(count
)
7301 return(addr_scroll_down(-count
));
7306 as
.cur_row
+= as
.top_ent
;
7307 while(i
&& as
.top_ent
+ 1 < as
.last_ent
){
7308 if(line_is_selectable(as
.top_ent
)){
7309 if(next_selectable_line(as
.top_ent
,&next
)){
7321 as
.cur_row
= as
.cur_row
- as
.top_ent
; /* must always be positive */
7323 as
.old_cur_row
= as
.cur_row
;
7331 * addr_scroll_down - adjust the global screen state struct such that pine's
7332 * window on the data is shifted UP (i.e., the data's
7336 addr_scroll_down(count
)
7340 return(addr_scroll_up(-count
));
7344 for(i
= count
; i
&& as
.top_ent
; i
--, as
.top_ent
--)
7347 while (as
.cur_row
>= as
.l_p_page
){
7348 prev_selectable_line(as
.cur_row
+as
.top_ent
, &as
.cur_row
);
7349 as
.cur_row
= as
.cur_row
- as
.top_ent
;
7352 as
.old_cur_row
= as
.cur_row
;
7360 * addr_scroll_to_pos - scroll the address book data in pine's window such
7361 * tthat the given "line" is at the top of the page.
7364 addr_scroll_to_pos(line
)
7367 return(addr_scroll_up(line
- as
.top_ent
));
7371 /*----------------------------------------------------------------------
7372 MSWin scroll callback. Called during scroll message processing.
7376 Args: cmd - what type of scroll operation.
7377 scroll_pos - parameter for operation.
7378 used as position for SCROLL_TO operation.
7380 Returns: TRUE - did the scroll operation.
7381 FALSE - was not able to do the scroll operation.
7384 addr_scroll_callback (cmd
, scroll_pos
)
7391 case MSWIN_KEY_SCROLLUPLINE
:
7392 paint
= addr_scroll_down (scroll_pos
);
7395 case MSWIN_KEY_SCROLLDOWNLINE
:
7396 paint
= addr_scroll_up (scroll_pos
);
7399 case MSWIN_KEY_SCROLLUPPAGE
:
7400 paint
= addr_scroll_down (as
.l_p_page
);
7403 case MSWIN_KEY_SCROLLDOWNPAGE
:
7404 paint
= addr_scroll_up (as
.l_p_page
);
7407 case MSWIN_KEY_SCROLLTO
:
7408 paint
= addr_scroll_to_pos (scroll_pos
);
7413 display_book(0, as
.cur_row
, -1, 1, (Pos
*)NULL
);
7420 pcpine_help_addrbook(title
)
7424 * Title is size 256. Fix this to pass the titlelen.
7427 strncpy(title
, (as
.config
)
7428 ? _("Alpine CONFIGURING ADDRESS BOOKS Help")
7429 : _("Alpine ADDRESS_BOOK Help"), 256);
7431 return(pcpine_help(gAbookHelp
));
7433 #endif /* _WINDOWS */