]> git.deb.at Git - pkg/abook.git/blob - list.c
ldif: support parsing from stdin
[pkg/abook.git] / list.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <stdio.h>
11 #include <string.h>
12 #include "abook.h"
13 #include <assert.h>
14 #include "ui.h"
15 #include "database.h"
16 #include "edit.h"
17 #include "gettext.h"
18 #include "list.h"
19 #include "misc.h"
20 #include "options.h"
21 #include "xmalloc.h"
22 #include "color.h"
23
24
25 int curitem = -1;
26 int first_list_item = -1;
27 char *selected = NULL;
28
29 extern abook_field_list *fields_list;
30 struct index_elem *index_elements = NULL;
31
32 static WINDOW *list = NULL;
33
34
35 static void
36 index_elem_add(int type, char *a, char *b)
37 {
38         struct index_elem *tmp = NULL, *cur, *cur2;
39         int field, len = 0;
40
41         if(!a || !*a)
42                 return;
43
44         switch(type) {
45                 case INDEX_TEXT:
46                         tmp = xmalloc(sizeof(struct index_elem));
47                         tmp->d.text = xstrdup(a);
48                         break;
49                 case INDEX_FIELD: /* fall through */
50                 case INDEX_ALT_FIELD:
51                         find_field_number(a, &field);
52                         if(field == -1)
53                                 return;
54                         len = (b && *b && is_number(b)) ? atoi(b) : 0;
55                         tmp = xmalloc(sizeof(struct index_elem));
56                         tmp->d.field.id = field;
57                         tmp->d.field.len = len;
58                         break;
59                 default:
60                         assert(0);
61         }
62         tmp->type = type;
63         tmp->next = NULL;
64         tmp->d.field.next = NULL;
65
66         if(!index_elements) { /* first element */
67                 index_elements = tmp;
68                 return;
69         }
70
71         for(cur = index_elements; cur->next; cur = cur->next)
72                 ;
73         if(type != INDEX_ALT_FIELD)
74                 cur->next = tmp;
75         else { /* add as an alternate field */
76                 tmp->d.field.len = cur->d.field.len;
77                 for(cur2 = cur; cur2->d.field.next; cur2 = cur2->d.field.next)
78                         ;
79                 cur2->d.field.next = tmp;
80         }
81 }
82
83 static void
84 parse_index_format(char *s)
85 {
86         char *p, *start, *lstart = NULL;
87         int in_field = 0, in_alternate = 0, in_length = 0, type;
88
89         p = start = s;
90
91         while(*p) {
92                 if(*p == '{' && !in_field) {
93                         *p = 0;
94                         index_elem_add(INDEX_TEXT, start, NULL);
95                         start = ++p;
96                         in_field = 1;
97                 } else if(*p == ':' && in_field && !in_alternate) {
98                         *p = 0;
99                         lstart = ++p;
100                         in_length = 1;
101                 } else if(*p == '|' && in_field) {
102                         *p = 0;
103                         type = in_alternate ? INDEX_ALT_FIELD : INDEX_FIELD;
104                         index_elem_add(type, start, in_length ? lstart : NULL);
105                         start = ++p;
106                         in_length = 0;
107                         in_alternate = 1;
108                 } else if(*p == '}' && in_field) {
109                         *p = 0;
110                         type = in_alternate ? INDEX_ALT_FIELD : INDEX_FIELD;
111                         index_elem_add(type, start, in_length ? lstart : NULL);
112                         start = ++p;
113                         in_field = in_alternate = in_length = 0;
114                 } else
115                         p++;
116         }
117         if(!in_field)
118                 index_elem_add(INDEX_TEXT, start, NULL);
119 }
120
121 void
122 init_index()
123 {
124         assert(!index_elements);
125         parse_index_format(opt_get_str(STR_INDEX_FORMAT));
126 }
127
128 void
129 init_list()
130 {
131         list = newwin(LIST_LINES, LIST_COLS, LIST_TOP, 0);
132         scrollok(list, TRUE);
133 }
134
135 void
136 close_list()
137 {
138         delwin(list);
139         list = NULL;
140 }
141
142 void
143 get_list_field(int item, struct index_elem *e, struct list_field *res)
144 {
145         char *s;
146
147         res->data = s = NULL;
148
149         do { /* find first non-empty field data in the alternate fields list */
150                 s = db_fget_byid(item, e->d.field.id);
151         } while(!(s && *s) && ((e = e->d.field.next) != NULL));
152
153         if(!e || !s || !*s)
154                 return;
155
156         res->data = s;
157         get_field_info(e->d.field.id, NULL, NULL, &res->type);
158 }
159
160 static void
161 print_list_field(int item, int line, int *x_pos, struct index_elem *e)
162 {
163         char *s, *p;
164         int width, x_start, mustfree = FALSE, len = abs(e->d.field.len);
165         struct list_field f;
166
167         get_list_field(item, e, &f);
168         s = f.data;
169
170         if(!s || !*s) {
171                 *x_pos += len;
172                 return;
173         }
174         
175         if(f.type == FIELD_EMAILS && !opt_get_bool(BOOL_SHOW_ALL_EMAILS))
176                 if((p = strchr(s, ',')) != NULL) {
177                         s = xstrndup(s, p - s);
178                         mustfree = TRUE;
179                 }
180
181         width = len ? bytes2width(s, len) : strwidth(s);
182         x_start = *x_pos + ((e->d.field.len < 0) ? len - width : 0);
183         if(width + x_start >= COLS)
184                 width = bytes2width(s, COLS - x_start);
185
186         if(width)
187                 mvwaddnstr(list, line, x_start, s, width);
188
189         if(mustfree)
190                 free(s);
191                 
192         *x_pos += len ? len : width;
193 }
194
195 static void
196 highlight_line(WINDOW *win, int line)
197 {
198         wattrset(win, COLOR_PAIR(CP_LIST_HIGHLIGHT));
199         if(!opt_get_bool(BOOL_USE_COLORS)) {
200                 wstandout(win);
201         }
202
203         /*
204          * this is a tricky one
205          */
206 #if 0
207 /*#ifdef mvwchgat*/
208         mvwchgat(win, line, 0, -1,  A_STANDOUT, 0, NULL);
209 #else
210         /*
211          * buggy function: FIXME
212          */
213         scrollok(win, FALSE);
214         {
215                 int i;
216                 wmove(win, line, 0);
217                 for(i = 0; i < COLS; i++)
218                         waddch(win, ' ');
219         /*wattrset(win, 0);*/
220         }
221         scrollok(win, TRUE);
222 #endif
223 }
224
225 static void
226 print_list_line(int item, int line, int highlight)
227 {
228         struct index_elem *cur;
229         int x_pos = 1;
230
231         if(item % 2 == 0)
232                 wattrset(list, COLOR_PAIR(CP_LIST_EVEN));
233         else
234                 wattrset(list, COLOR_PAIR(CP_LIST_ODD));
235         scrollok(list, FALSE);
236         if(highlight)
237                 highlight_line(list, line);
238
239         if(selected[item])
240                 mvwaddch(list, line, 0, '*' );
241
242         for(cur = index_elements; cur; cur = cur->next)
243                 switch(cur->type) {
244                         case INDEX_TEXT:
245                                 mvwaddstr(list, line, x_pos, cur->d.text);
246                                 x_pos += strwidth(cur->d.text);
247                                 break;
248                         case INDEX_FIELD:
249                                 print_list_field(item, line, &x_pos, cur);
250                                 break;
251                         default:
252                                 assert(0);
253                 }
254
255         scrollok(list, TRUE);
256         if(highlight)
257                 wstandend(list);
258 }
259
260 void
261 refresh_list()
262 {
263         int i, line;
264
265         werase(list);
266
267         ui_print_number_of_items();
268
269         if(list_is_empty()) {
270                 refresh();
271                 wrefresh(list);
272                 return;
273         }
274
275         if(curitem < 0)
276                 curitem = 0;
277
278         if(first_list_item < 0)
279                 first_list_item = 0;
280
281         if(curitem < first_list_item)
282                 first_list_item = curitem;
283         else if(curitem > LAST_LIST_ITEM)
284                 first_list_item = max(curitem - LIST_LINES + 1, 0);
285
286         for(line = 0, i = first_list_item;
287                         i <= LAST_LIST_ITEM && i < db_n_items();
288                         line++, i++) {
289
290                 print_list_line(i, line, i == curitem);
291         }
292
293         if(opt_get_bool(BOOL_SHOW_CURSOR)) {
294                 wmove(list, curitem - first_list_item, 0);
295                 /* need to call refresh() to update the cursor positions */
296                 refresh();
297         }
298         wrefresh(list);
299 }
300
301 void
302 list_headerline()
303 {
304         struct index_elem *e;
305         int x_pos = 1, width;
306         char *str = NULL;
307
308 #if defined(A_BOLD) && defined(A_NORMAL)
309         attrset(A_BOLD);
310 #endif
311         attrset(COLOR_PAIR(CP_LIST_HEADER));
312         mvhline(2, 0, ' ', COLS);
313
314         for(e = index_elements; e; e = e->next)
315                 if(e->type == INDEX_TEXT)
316                         x_pos += strwidth(e->d.text);
317                 else if(e->type == INDEX_FIELD) {
318                         get_field_info(e->d.field.id, NULL, &str, NULL);
319                         width = e->d.field.len ?
320                                 abs(e->d.field.len) : strwidth(str);
321                         if(width + x_pos > COLS)
322                                 width = bytes2width(str, COLS - x_pos);
323                         mvaddnstr(2, x_pos, str, width);
324                         x_pos += width;
325                 } else
326                         assert(0);
327
328 #if defined(A_BOLD) && defined(A_NORMAL)
329         attrset(A_NORMAL);
330 #endif
331 }
332
333 void
334 scroll_up()
335 {
336         if(curitem < 1)
337                 return;
338
339         curitem--;
340
341         refresh_list();
342 }
343
344 void
345 scroll_down()
346 {
347         if(curitem > db_n_items() - 2)
348                 return;
349
350         curitem++;
351
352         refresh_list();
353 }
354
355
356 void
357 page_up()
358 {
359         if(curitem < 1)
360                 return;
361
362         curitem = curitem == first_list_item ?
363                 ((curitem -= LIST_LINES) < 0 ? 0 : curitem) : first_list_item;
364
365         refresh_list();
366 }
367
368 void
369 page_down()
370 {
371         if(curitem > db_n_items() - 2)
372                 return;
373
374         if(curitem == LAST_LIST_ITEM) {
375                 if((curitem += LIST_LINES) > last_item())
376                         curitem = last_item();
377         } else {
378                 curitem = min(LAST_LIST_ITEM, last_item());
379         }
380
381         refresh_list();
382 }
383
384 void
385 select_none()
386 {
387         memset(selected, 0, db_n_items());
388 }
389
390 void
391 select_all()
392 {
393         memset(selected, 1, db_n_items());
394 }
395
396 void
397 list_set_selection(int item, int value)
398 {
399         assert(is_valid_item(item));
400
401         selected[item] = !!value;
402 }
403
404 void
405 list_invert_curitem_selection()
406 {
407         assert(is_valid_item(curitem));
408
409         selected[curitem] = !selected[curitem];
410 }
411
412 void
413 move_curitem(int direction)
414 {
415         list_item tmp;
416
417         if(curitem < 0 || curitem > last_item())
418                 return;
419
420         tmp = item_create();
421         item_copy(tmp, db_item_get(curitem));
422
423         switch(direction) {
424                 case MOVE_ITEM_UP:
425                         if( curitem < 1 )
426                                 goto out_move;
427                         item_copy(db_item_get(curitem),
428                                         db_item_get(curitem - 1));
429                         item_copy(db_item_get(curitem-1), tmp);
430                         scroll_up();
431                         break;
432
433                 case MOVE_ITEM_DOWN:
434                         if(curitem >= last_item())
435                                 goto out_move;
436                         item_copy(db_item_get(curitem),
437                                         db_item_get(curitem + 1));
438                         item_copy(db_item_get(curitem + 1), tmp);
439                         scroll_down();
440                         break;
441         }
442
443 out_move:
444         item_free(&tmp);
445 }
446
447 void
448 goto_home()
449 {
450         if(db_n_items() > 0)
451                 curitem = 0;
452
453         refresh_list();
454 }
455
456 void
457 goto_end()
458 {
459         if(db_n_items() > 0)
460                 curitem = last_item();
461
462         refresh_list();
463 }
464
465 int
466 selected_items()
467 {
468         int i, n = 0;
469
470         for(i = 0; i < db_n_items(); i++)
471                 if(selected[i])
472                         n++;
473
474         return n;
475 }
476
477 void
478 invert_selection()
479 {
480         int i;
481
482         if(list_is_empty())
483                 return;
484
485         for(i = 0; i < db_n_items(); i++)
486                 selected[i] = !selected[i];
487 }
488
489 int
490 list_is_empty()
491 {
492         return db_n_items() < 1;
493 }
494
495 int
496 list_get_curitem()
497 {
498         return curitem;
499 }
500
501 int
502 list_get_firstitem()
503 {
504         return first_list_item;
505 }
506
507 void
508 list_set_curitem(int i)
509 {
510         curitem = i;
511 }
512
513 int
514 duplicate_item()
515 {
516         list_item item;
517         
518         if(curitem < 0)
519                 return 1;
520
521         item = item_create();
522         item_duplicate(item, db_item_get(curitem));
523         if(add_item2database(item)) {
524                 item_free(&item);
525                 return 1;
526         }
527         item_free(&item);
528
529         curitem = last_item();
530         refresh_list();
531
532         return 0;
533 }
534