]> git.deb.at Git - pkg/abook.git/blob - list.c
New index_format option.
[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 = 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 print_list_line(int item, int line, int highlight)
196 {
197         struct index_elem *cur;
198         int x_pos = 1;
199
200         scrollok(list, FALSE);
201         if(highlight)
202                 highlight_line(list, line);
203
204         if(selected[item])
205                 mvwaddch(list, line, 0, '*' );
206
207         for(cur = index_elements; cur; cur = cur->next)
208                 switch(cur->type) {
209                         case INDEX_TEXT:
210                                 mvwaddstr(list, line, x_pos, cur->d.text);
211                                 x_pos += strwidth(cur->d.text);
212                                 break;
213                         case INDEX_FIELD:
214                                 print_list_field(item, line, &x_pos, cur);
215                                 break;
216                         default:
217                                 assert(0);
218                 }
219
220         scrollok(list, TRUE);
221         if(highlight)
222                 wstandend(list);
223 }
224
225 void
226 refresh_list()
227 {
228         int i, line;
229
230         werase(list);
231
232         ui_print_number_of_items();
233
234         if(list_is_empty()) {
235                 refresh();
236                 wrefresh(list);
237                 return;
238         }
239
240         if(curitem < 0)
241                 curitem = 0;
242
243         if(first_list_item < 0)
244                 first_list_item = 0;
245
246         if(curitem < first_list_item)
247                 first_list_item = curitem;
248         else if(curitem > LAST_LIST_ITEM)
249                 first_list_item = max(curitem - LIST_LINES + 1, 0);
250
251         for(line = 0, i = first_list_item;
252                         i <= LAST_LIST_ITEM && i < db_n_items();
253                         line++, i++) {
254
255                 print_list_line(i, line, i == curitem);
256         }
257
258         if(opt_get_bool(BOOL_SHOW_CURSOR)) {
259                 wmove(list, curitem - first_list_item, 0);
260                 /* need to call refresh() to update the cursor positions */
261                 refresh();
262         }
263         wrefresh(list);
264 }
265
266 void
267 list_headerline()
268 {
269         struct index_elem *e;
270         int x_pos = 1, width;
271         char *str = NULL;
272
273 #if defined(A_BOLD) && defined(A_NORMAL)
274         attrset(A_BOLD);
275 #endif
276
277         for(e = index_elements; e; e = e->next)
278                 if(e->type == INDEX_TEXT)
279                         x_pos += strwidth(e->d.text);
280                 else if(e->type == INDEX_FIELD) {
281                         get_field_info(e->d.field.id, NULL, &str, NULL);
282                         width = e->d.field.len ? abs(e->d.field.len) : strwidth(str);
283                         mvaddnstr(2, x_pos, str, width);
284                         x_pos += width;
285                 } else
286                         assert(0);
287
288 #if defined(A_BOLD) && defined(A_NORMAL)
289         attrset(A_NORMAL);
290 #endif
291 }
292
293 void
294 scroll_up()
295 {
296         if(curitem < 1)
297                 return;
298
299         curitem--;
300
301         refresh_list();
302 }
303
304 void
305 scroll_down()
306 {
307         if(curitem > db_n_items() - 2)
308                 return;
309
310         curitem++;
311
312         refresh_list();
313 }
314
315
316 void
317 page_up()
318 {
319         if(curitem < 1)
320                 return;
321
322         curitem = curitem == first_list_item ?
323                 ((curitem -= LIST_LINES) < 0 ? 0 : curitem) : first_list_item;
324
325         refresh_list();
326 }
327
328 void
329 page_down()
330 {
331         if(curitem > db_n_items() - 2)
332                 return;
333
334         if(curitem == LAST_LIST_ITEM) {
335                 if((curitem += LIST_LINES) > last_item())
336                         curitem = last_item();
337         } else {
338                 curitem = min(LAST_LIST_ITEM, last_item());
339         }
340
341         refresh_list();
342 }
343
344 void
345 select_none()
346 {
347         memset(selected, 0, db_n_items());
348 }
349
350 void
351 select_all()
352 {
353         memset(selected, 1, db_n_items());
354 }
355
356 void
357 list_set_selection(int item, int value)
358 {
359         assert(is_valid_item(item));
360
361         selected[item] = !!value;
362 }
363
364 void
365 list_invert_curitem_selection()
366 {
367         assert(is_valid_item(curitem));
368
369         selected[curitem] = !selected[curitem];
370 }
371
372 void
373 move_curitem(int direction)
374 {
375         list_item tmp;
376
377         if(curitem < 0 || curitem > last_item())
378                 return;
379
380         tmp = item_create();
381         item_copy(tmp, db_item_get(curitem));
382
383         switch(direction) {
384                 case MOVE_ITEM_UP:
385                         if( curitem < 1 )
386                                 goto out_move;
387                         item_copy(db_item_get(curitem),
388                                         db_item_get(curitem - 1));
389                         item_copy(db_item_get(curitem-1), tmp);
390                         scroll_up();
391                         break;
392
393                 case MOVE_ITEM_DOWN:
394                         if(curitem >= last_item())
395                                 goto out_move;
396                         item_copy(db_item_get(curitem),
397                                         db_item_get(curitem + 1));
398                         item_copy(db_item_get(curitem + 1), tmp);
399                         scroll_down();
400                         break;
401         }
402
403 out_move:
404         item_free(&tmp);
405 }
406
407 void
408 goto_home()
409 {
410         if(db_n_items() > 0)
411                 curitem = 0;
412
413         refresh_list();
414 }
415
416 void
417 goto_end()
418 {
419         if(db_n_items() > 0)
420                 curitem = last_item();
421
422         refresh_list();
423 }
424
425 void
426 highlight_line(WINDOW *win, int line)
427 {
428         wstandout(win);
429
430         /*
431          * this is a tricky one
432          */
433 #if 0
434 /*#ifdef mvwchgat*/
435         mvwchgat(win, line, 0, -1,  A_STANDOUT, 0, NULL);
436 #else
437         /*
438          * buggy function: FIXME
439          */
440         scrollok(win, FALSE);
441         {
442                 int i;
443                 wmove(win, line, 0);
444                 for(i = 0; i < COLS; i++)
445                         waddch(win, ' ');
446         /*wattrset(win, 0);*/
447         }
448         scrollok(win, TRUE);
449 #endif
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