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