]> git.deb.at Git - pkg/abook.git/blob - list.c
7a04397e493a536b21588692917d459c6de9479d
[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
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 = bytes2width(s, 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 ?
310                                 abs(e->d.field.len) : strwidth(str);
311                         if(width + x_pos > COLS)
312                                 width = bytes2width(str, COLS - x_pos);
313                         mvaddnstr(2, x_pos, str, width);
314                         x_pos += width;
315                 } else
316                         assert(0);
317
318 #if defined(A_BOLD) && defined(A_NORMAL)
319         attrset(A_NORMAL);
320 #endif
321 }
322
323 void
324 scroll_up()
325 {
326         if(curitem < 1)
327                 return;
328
329         curitem--;
330
331         refresh_list();
332 }
333
334 void
335 scroll_down()
336 {
337         if(curitem > db_n_items() - 2)
338                 return;
339
340         curitem++;
341
342         refresh_list();
343 }
344
345
346 void
347 page_up()
348 {
349         if(curitem < 1)
350                 return;
351
352         curitem = curitem == first_list_item ?
353                 ((curitem -= LIST_LINES) < 0 ? 0 : curitem) : first_list_item;
354
355         refresh_list();
356 }
357
358 void
359 page_down()
360 {
361         if(curitem > db_n_items() - 2)
362                 return;
363
364         if(curitem == LAST_LIST_ITEM) {
365                 if((curitem += LIST_LINES) > last_item())
366                         curitem = last_item();
367         } else {
368                 curitem = min(LAST_LIST_ITEM, last_item());
369         }
370
371         refresh_list();
372 }
373
374 void
375 select_none()
376 {
377         memset(selected, 0, db_n_items());
378 }
379
380 void
381 select_all()
382 {
383         memset(selected, 1, db_n_items());
384 }
385
386 void
387 list_set_selection(int item, int value)
388 {
389         assert(is_valid_item(item));
390
391         selected[item] = !!value;
392 }
393
394 void
395 list_invert_curitem_selection()
396 {
397         assert(is_valid_item(curitem));
398
399         selected[curitem] = !selected[curitem];
400 }
401
402 void
403 move_curitem(int direction)
404 {
405         list_item tmp;
406
407         if(curitem < 0 || curitem > last_item())
408                 return;
409
410         tmp = item_create();
411         item_copy(tmp, db_item_get(curitem));
412
413         switch(direction) {
414                 case MOVE_ITEM_UP:
415                         if( curitem < 1 )
416                                 goto out_move;
417                         item_copy(db_item_get(curitem),
418                                         db_item_get(curitem - 1));
419                         item_copy(db_item_get(curitem-1), tmp);
420                         scroll_up();
421                         break;
422
423                 case MOVE_ITEM_DOWN:
424                         if(curitem >= last_item())
425                                 goto out_move;
426                         item_copy(db_item_get(curitem),
427                                         db_item_get(curitem + 1));
428                         item_copy(db_item_get(curitem + 1), tmp);
429                         scroll_down();
430                         break;
431         }
432
433 out_move:
434         item_free(&tmp);
435 }
436
437 void
438 goto_home()
439 {
440         if(db_n_items() > 0)
441                 curitem = 0;
442
443         refresh_list();
444 }
445
446 void
447 goto_end()
448 {
449         if(db_n_items() > 0)
450                 curitem = last_item();
451
452         refresh_list();
453 }
454
455 int
456 selected_items()
457 {
458         int i, n = 0;
459
460         for(i = 0; i < db_n_items(); i++)
461                 if(selected[i])
462                         n++;
463
464         return n;
465 }
466
467 void
468 invert_selection()
469 {
470         int i;
471
472         if(list_is_empty())
473                 return;
474
475         for(i = 0; i < db_n_items(); i++)
476                 selected[i] = !selected[i];
477 }
478
479 int
480 list_is_empty()
481 {
482         return db_n_items() < 1;
483 }
484
485 int
486 list_get_curitem()
487 {
488         return curitem;
489 }
490
491 void
492 list_set_curitem(int i)
493 {
494         curitem = i;
495 }
496
497 int
498 duplicate_item()
499 {
500         list_item item;
501         
502         if(curitem < 0)
503                 return 1;
504
505         item = item_create();
506         item_duplicate(item, db_item_get(curitem));
507         if(add_item2database(item)) {
508                 item_free(&item);
509                 return 1;
510         }
511         item_free(&item);
512
513         curitem = last_item();
514         refresh_list();
515
516         return 0;
517 }
518