Add support for tab-completion when selecting by rule
[alpine.git] / pith / addrbook.c
blob3aad654dde6c82a861439ad2e8c9c12b6eae1500
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 addrbook.c
17 display format/support routines
18 ====*/
20 #include "../c-client/c-client.h"
22 #include <system.h>
23 #include <general.h>
25 #include "addrbook.h"
26 #include "state.h"
27 #include "adrbklib.h"
28 #include "abdlc.h"
29 #include "conf.h"
30 #include "conftype.h"
31 #include "status.h"
32 #include "debug.h"
33 #include "osdep/collate.h"
36 /* internal prototypes */
37 void parse_format(char *, COL_S *);
43 * We have to call this to set up the format of the columns. There is a
44 * separate format for each addrbook, so we need to call this for each
45 * addrbook. We call it when the pab's are built. It also depends on
46 * whether or not as.checkboxes is set, so if we go into a Select mode
47 * from the address book maintenance screen we need to re-call this. Since
48 * we can't go back out of ListMode we don't have that problem. Restore_state
49 * has to call it because of the as.checkboxes possibly being different in
50 * the two states.
52 void
53 addrbook_new_disp_form(PerAddrBook *pab, char **list, int addrbook_num,
54 int (*prefix_f)(PerAddrBook *, int *))
56 char *last_one;
57 int column = 0;
59 dprint((9, "- init_disp_form(%s) -\n",
60 (pab && pab->abnick) ? pab->abnick : "?"));
62 memset((void *)pab->disp_form, 0, NFIELDS*sizeof(COL_S));
63 pab->disp_form[1].wtype = WeCalculate; /* so we don't get false AllAuto */
65 if(prefix_f)
66 as.do_bold = (*prefix_f)(pab, &column);
68 /* if custom format is specified */
69 if(list && list[0] && list[0][0]){
70 /* find the one for addrbook_num */
71 for(last_one = *list;
72 *list != NULL && addrbook_num;
73 addrbook_num--,list++)
74 last_one = *list;
76 /* If not enough to go around, last one repeats */
77 if(*list == NULL)
78 parse_format(last_one, &(pab->disp_form[column]));
79 else
80 parse_format(*list, &(pab->disp_form[column]));
82 else{ /* default */
83 /* If 2nd wtype is AllAuto, the widths are calculated old way */
84 pab->disp_form[1].wtype = AllAuto;
86 pab->disp_form[column++].type = Nickname;
87 pab->disp_form[column++].type = Fullname;
88 pab->disp_form[column++].type = Addr;
89 /* Fill in rest */
90 while(column < NFIELDS)
91 pab->disp_form[column++].type = Notused;
96 struct parse_tokens {
97 char *name;
98 ColumnType ctype;
101 struct parse_tokens ptokens[] = {
102 {"NICKNAME", Nickname},
103 {"FULLNAME", Fullname},
104 {"ADDRESS", Addr},
105 {"FCC", Filecopy},
106 {"COMMENT", Comment},
107 {"DEFAULT", Def},
108 {NULL, Notused}
112 * Parse format_str and fill in disp_form structure based on what's there.
114 * Args: format_str -- The format string from pinerc.
115 * disp_form -- This is where we fill in the answer.
117 * The format string consists of special tokens which give the order of
118 * the columns to be displayed. The possible tokens are NICKNAME,
119 * FULLNAME, ADDRESS, FCC, COMMENT. If a token is followed by
120 * parens with an integer inside (FULLNAME(16)) then that means we
121 * make that variable that many characters wide. If it is a percentage, we
122 * allocate that percentage of the columns to that variable. If no
123 * parens, that means we calculate it for the user. The tokens are
124 * delimited by white space. A token of DEFAULT means to calculate the
125 * whole thing as we would if no spec was given. This makes it possible
126 * to specify default for one addrbook and something special for another.
128 void
129 parse_format(char *format_str, COL_S *disp_form)
131 int column = 0;
132 char *p, *q;
133 struct parse_tokens *pt;
134 int nicknames, fullnames, addresses, not_allauto;
135 int warnings = 0;
137 p = format_str;
138 while(p && *p && column < NFIELDS){
139 p = skip_white_space(p); /* space for next word */
141 /* look for the ptoken this word matches */
142 for(pt = ptokens; pt->name; pt++)
143 if(!struncmp(pt->name, p, strlen(pt->name)))
144 break;
146 /* ignore unrecognized word */
147 if(!pt->name){
148 char *r;
150 if((r=strindex(p, SPACE)) != NULL)
151 *r = '\0';
153 dprint((2, "parse_format: ignoring unrecognized word \"%s\" in address-book-formats\n", p ? p : "?"));
154 q_status_message1(SM_ORDER, warnings++==0 ? 1 : 0, 4,
155 /* TRANSLATORS: an informative error message */
156 _("Ignoring unrecognized word \"%s\" in Address-Book-Formats"), p);
157 /* put back space */
158 if(r)
159 *r = SPACE;
161 /* skip unrecognized word */
162 while(p && *p && !isspace((unsigned char)(*p)))
163 p++;
165 continue;
168 disp_form[column].type = pt->ctype;
170 /* skip over name and look for parens */
171 p += strlen(pt->name);
172 if(*p == '('){
173 p++;
174 q = p;
175 while(p && *p && isdigit((unsigned char)*p))
176 p++;
178 if(p && *p && *p == ')' && p > q){
179 disp_form[column].wtype = Fixed;
180 disp_form[column].req_width = atoi(q);
182 else if(p && *p && *p == '%' && p > q){
183 disp_form[column].wtype = Percent;
184 disp_form[column].req_width = atoi(q);
186 else{
187 disp_form[column].wtype = WeCalculate;
188 if(disp_form[column].type == Nickname)
189 disp_form[column].req_width = 8;
190 else
191 disp_form[column].req_width = 3;
194 else{
195 disp_form[column].wtype = WeCalculate;
196 if(disp_form[column].type == Nickname)
197 disp_form[column].req_width = 8;
198 else
199 disp_form[column].req_width = 3;
202 if(disp_form[column].type == Def){
203 /* If any type is DEFAULT, the widths are calculated old way */
204 assign_default:
205 column = 0;
207 disp_form[column].wtype = AllAuto;
208 disp_form[column++].type = Nickname;
209 disp_form[column].wtype = AllAuto;
210 disp_form[column++].type = Fullname;
211 disp_form[column].wtype = AllAuto;
212 disp_form[column++].type = Addr;
213 /* Fill in rest */
214 while(column < NFIELDS)
215 disp_form[column++].type = Notused;
217 return;
220 column++;
221 /* skip text at end of word */
222 while(p && *p && !isspace((unsigned char)(*p)))
223 p++;
226 if(column == 0){
227 q_status_message(SM_ORDER, 0, 4,
228 _("Address-Book-Formats has no recognizable words, using default format"));
229 goto assign_default;
232 /* Fill in rest */
233 while(column < NFIELDS)
234 disp_form[column++].type = Notused;
236 /* check to see if user is just re-ordering default fields */
237 nicknames = 0;
238 fullnames = 0;
239 addresses = 0;
240 not_allauto = 0;
241 for(column = 0; column < NFIELDS; column++){
242 if(disp_form[column].type != Notused
243 && disp_form[column].wtype != WeCalculate)
244 not_allauto++;
246 switch(disp_form[column].type){
247 case Nickname:
248 nicknames++;
249 break;
251 case Fullname:
252 fullnames++;
253 break;
255 case Addr:
256 addresses++;
257 break;
259 case Filecopy:
260 case Comment:
261 not_allauto++;
262 break;
264 default:
265 break;
270 * Special case: if there is no address field specified, we put in
271 * a special field called WhenNoAddrDisplayed, which causes list
272 * entries to be displayable in all cases.
274 if(!addresses){
275 for(column = 0; column < NFIELDS; column++)
276 if(disp_form[column].type == Notused)
277 break;
279 if(column < NFIELDS){
280 disp_form[column].type = WhenNoAddrDisplayed;
281 disp_form[column].wtype = Special;
285 if(nicknames == 1 && fullnames == 1 && addresses == 1 && not_allauto == 0)
286 disp_form[0].wtype = AllAuto; /* set to do default widths */
291 * Find the first selectable line greater than or equal to line. That is,
292 * the first line the cursor is allowed to start on.
293 * (If there are none >= line, it will find the highest one.)
295 * Returns the line number of the found line or NO_LINE if there isn't one.
297 long
298 first_selectable_line(long int line)
300 long lineno;
301 register PerAddrBook *pab;
302 int i;
304 /* skip past non-selectable lines */
305 for(lineno=line;
306 !line_is_selectable(lineno) && dlist(lineno)->type != End;
307 lineno++)
308 ;/* do nothing */
310 if(line_is_selectable(lineno))
311 return(lineno);
314 * There were no selectable lines from lineno on down. Trying looking
315 * back up the list.
317 for(lineno=line-1;
318 !line_is_selectable(lineno) && dlist(lineno)->type != Beginning;
319 lineno--)
320 ;/* do nothing */
322 if(line_is_selectable(lineno))
323 return(lineno);
326 * No selectable lines at all.
327 * If some of the addrbooks are still not displayed, it is too
328 * early to set the no_op_possbl flag. Or, if some of the addrbooks
329 * are empty but writable, then we should not set it either.
331 for(i = 0; i < as.n_addrbk; i++){
332 pab = &as.adrbks[i];
333 if(pab->ostatus != Open &&
334 pab->ostatus != HalfOpen &&
335 pab->ostatus != ThreeQuartOpen)
336 return NO_LINE;
338 if(pab->access == ReadWrite && adrbk_count(pab->address_book) == 0)
339 return NO_LINE;
342 as.no_op_possbl++;
343 return NO_LINE;
348 * Returns 1 if this line is of a type that can have a cursor on it.
351 line_is_selectable(long int lineno)
353 register AddrScrn_Disp *dl;
355 if((dl = dlist(lineno)) && (dl->type == Text ||
356 dl->type == ListEmpty ||
357 dl->type == TitleCmb ||
358 dl->type == Beginning ||
359 dl->type == End)){
361 return 0;
364 return 1;