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