]> git.deb.at Git - pkg/abook.git/blob - ui.c
First attempt of the better interface for filters
[pkg/abook.git] / ui.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@bigfoot.com>
6  *
7  * Copyright (C) Jaakko Heinonen
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <ctype.h>
17 #include "abook.h"
18 #include "ui.h"
19 #include "edit.h"
20 #include "database.h"
21 #include "list.h"
22 #include "misc.h"
23 #include "options.h"
24 #include "filter.h"
25 #include "estr.h"
26 #ifdef HAVE_CONFIG_H
27 #       include "config.h"
28 #endif
29 #ifdef HAVE_TERMIOS_H
30 #       include <termios.h>
31 #else
32 #       ifdef HAVE_LINUX_TERMIOS_H
33 #               include <linux/termios.h>
34 #       endif
35 #endif
36 #ifdef HAVE_SYS_IOCTL_H
37 #       include <sys/ioctl.h>
38 #endif
39
40 #ifdef USE_ASCII_ONLY
41 #       define UI_HLINE_CHAR            '-'
42 #else
43 #       define UI_HLINE_CHAR            ACS_HLINE
44 #endif
45
46
47 /*
48  * external variables
49  */
50
51 extern int items, curitem;
52 extern char *datafile;
53
54 /*
55  * internal variables
56  */
57
58 int ui_initialized = FALSE;
59
60 int should_resize = FALSE;
61 int can_resize = FALSE;
62
63 WINDOW *top = NULL, *bottom = NULL;
64
65
66
67 static void
68 init_windows()
69 {
70         top = newwin(LIST_TOP - 1, COLS, 0, 0);
71         
72         bottom = newwin(LINES - LIST_BOTTOM, COLS, LIST_BOTTOM, 0);
73 }
74
75 static void
76 free_windows()
77 {
78         delwin(top);
79         delwin(bottom);
80 }
81
82
83 #ifdef SIGWINCH
84 static void
85 resize_abook()
86 {
87 #ifdef TIOCGWINSZ
88         struct winsize winsz;
89
90         ioctl (0, TIOCGWINSZ, &winsz);
91 #ifdef DEBUG
92         if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
93                 fprintf(stderr, "Warning: COLS=%d, LINES=%d\n", winsz.ws_col, winsz.ws_row);
94         }
95 #endif
96                 
97         if(winsz.ws_col >= MIN_COLS && winsz.ws_row >= MIN_LINES) {
98 #ifdef HAVE_RESIZETERM
99                 resizeterm(winsz.ws_row, winsz.ws_col);
100 #else
101                 COLS = winsz.ws_col;
102                 LINES = winsz.ws_row;
103 #endif
104         }
105
106         should_resize = FALSE;
107         close_list(); /* we need to recreate windows */
108         init_list();
109         free_windows();
110         init_windows();
111         refresh_screen();
112         refresh();
113 #endif /* TIOCGWINSZ */
114 }
115
116
117 static void
118 win_changed(int i)
119 {
120         if( can_resize )
121                 resize_abook();
122         else
123                 should_resize = TRUE;   
124 }
125 #endif /* SIGWINCH */
126
127
128 int
129 is_ui_initialized()
130 {
131         return ui_initialized;
132 }
133
134
135 int
136 init_ui()
137 {
138         initscr(); cbreak(); noecho();
139         nonl();
140         intrflush(stdscr, FALSE);
141         keypad(stdscr, TRUE);
142 #ifdef DEBUG
143         fprintf(stderr, "init_abook():\n");
144         fprintf(stderr, "  COLS = %d, LINES = %d\n", COLS, LINES);
145 #endif
146         if( LINES < MIN_LINES || COLS < MIN_COLS ) {
147                 clear(); refresh(); endwin();
148                 fprintf(stderr, "Your terminal size is %dx%d\n", COLS, LINES);
149                 fprintf(stderr, "Terminal is too small. Minium terminal size "
150                                 "for abook is "
151                                 "%dx%d\n", MIN_COLS, MIN_LINES);
152                 return 1;
153         }
154
155 #ifdef SIGWINCH
156         signal(SIGWINCH, win_changed);
157 #endif
158
159         init_list();
160         init_windows();
161
162         ui_initialized = TRUE;
163
164         return 0;
165 }
166
167 void
168 close_ui()
169 {
170         close_list();
171         free_windows();
172         clear();
173         refresh();
174         endwin();
175
176         ui_initialized = FALSE;
177 }
178
179
180 void
181 headerline(char *str)
182 {
183         werase(top);
184         
185         mvwhline(top, 1, 0, UI_HLINE_CHAR, COLS);
186         
187         mvwprintw(top, 0, 0, "%s | %s", PACKAGE " " VERSION, str);
188
189         refresh();
190         wrefresh(top);
191 }
192                 
193
194 void
195 refresh_screen()
196 {
197 #ifdef SIGWINCH
198         if( should_resize ) {
199                 resize_abook();
200                 return;
201         }
202 #endif
203         clear();
204         
205         refresh_statusline();
206         headerline(MAIN_HELPLINE);
207         list_headerline();
208
209         refresh_list();
210 }
211
212
213 void
214 statusline_msg(char *msg)
215 {
216         clear_statusline();
217         statusline_addstr(msg);
218         getch();
219 #ifdef DEBUG
220         fprintf(stderr, "statusline_msg(\"%s\")\n", msg);
221 #endif
222         clear_statusline();
223 }
224
225 void
226 statusline_addstr(char *str)
227 {
228         mvwaddstr(bottom, 1, 0, str);
229         refresh();
230         wrefresh(bottom);
231 }
232
233 /*
234  * function statusline_getnstr
235  *
236  * parameters:
237  *  (char *str)
238  *   if n >= 0 str is a pointer which points a place where to store
239  *   the string, else str is ingnored
240  *  (int n)
241  *   the maximum length of the string
242  *   If n < 0 function will allocate needed space for the string.
243  *   Value 0 is not allowed for n.
244  *  (int use_filesel)
245  *   if this value is nonzero the fileselector is enabled
246  *
247  *  returns (char *)
248  *   If n < 0 a pointer to a newly allocated string is returned.
249  *   If n > 0 a nonzero value is returned if user has typed a valid
250  *   string. If not NULL value is returned. Never really use the
251  *   _pointer_ if n > 0.
252  *
253  */
254
255 char *
256 statusline_getnstr(char *str, int n, int use_filesel)
257 {
258         char *buf;
259         int y, x;
260
261         getyx(bottom, y, x);
262         wmove(bottom, 1, x);
263         
264         buf = wenter_string(bottom, n,
265                         (use_filesel ? ESTR_USE_FILESEL:0) | ESTR_DONT_WRAP);
266
267         if(n < 0)
268                 return buf;
269         
270         if(buf == NULL)
271                 str[0] = 0;
272         else
273                 strncpy(str, buf, n);
274
275         str[n-1] = 0;
276
277         free(buf);
278
279         return buf;
280 }
281
282 int
283 statusline_ask_boolean(char *msg, int def)
284 {
285         int ret;
286         char *msg2 = strconcat(msg,  def ? " (Y/n)?" : " (y/N)?", NULL);
287                         
288         statusline_addstr(msg2);
289
290         free(msg2);
291
292         switch( tolower(getch()) ) {
293                 case 'n':
294                         ret = FALSE;
295                         break;
296                 case 'y':
297                         ret = TRUE;
298                         break;
299                 default:
300                         ret = def;
301                         break;
302         }
303
304         clear_statusline();
305
306         return ret;
307 }
308
309
310 void
311 refresh_statusline()
312 {
313         werase(bottom);
314
315         mvwhline(bottom, 0, 0, UI_HLINE_CHAR, COLS);
316         mvwhline(bottom, 2, 0, UI_HLINE_CHAR, COLS);
317
318         refresh();
319         wrefresh(bottom);
320 }
321         
322
323 char *
324 ask_filename(char *prompt, int flags)
325 {
326         char *buf = NULL;
327
328         clear_statusline();
329         
330         statusline_addstr(prompt);
331         buf = statusline_getnstr(NULL, -1, flags);
332
333         clear_statusline();
334
335         return buf;
336 }
337
338 void
339 clear_statusline()
340 {
341         wmove(bottom, 1, 0);
342         wclrtoeol(bottom);
343         wrefresh(bottom);
344         refresh();
345 }
346
347
348 /*
349  * help - need to rewrite
350  */
351
352
353 #include "help.h"
354
355 void
356 display_help(int help)
357 {
358         int i;
359         char **tbl;
360         WINDOW *helpw;
361
362         switch(help) {
363                 case HELP_MAIN:
364                         tbl = mainhelp;
365                         break;
366                 case HELP_EDITOR:
367                         tbl = editorhelp;
368                         break;
369                 default:return;
370         }
371
372         helpw = newwin(LINES - 5, COLS - 6, 2, 3);
373         erase();
374         headerline("help");
375         
376         for( i = 0; tbl[i] != NULL; i++) {
377                 waddstr(helpw, tbl[i]);
378                 if( ( !( (i+1) % (LINES-8) ) ) ||
379                         (tbl[i+1] == NULL) ) {
380                         refresh();
381                         wrefresh(helpw);
382                         refresh_statusline();
383                         statusline_msg("Press any key to continue...");
384                         wclear(helpw);
385                 }
386         }
387
388         clear_statusline();
389         delwin(helpw);
390 }
391
392
393 /*
394  * end of help
395  */
396
397 char *selected;
398 extern int curitem;
399
400 void
401 get_commands()
402 {
403         int ch;
404
405         for(;;) {
406                 can_resize = TRUE; /* it's safe to resize now */
407                 hide_cursor();
408                 if( should_resize )
409                         refresh_screen();
410                 ch = getch();
411                 show_cursor();
412                 can_resize = FALSE; /* it's not safe to resize anymore */
413                 switch( ch ) {
414                         case 'q': return;
415                         case '?':
416                                   display_help(HELP_MAIN);
417                                   refresh_screen();
418                                   break;
419                         case 'a': add_item();           break;
420                         case '\r': edit_item(-1);       break;
421                         case KEY_DC:
422                         case 'd':
423                         case 'r': ui_remove_items();    break;
424                         case 12: refresh_screen();      break;
425
426                         case 'k':
427                         case KEY_UP: scroll_up();       break;
428                         case 'j':
429                         case KEY_DOWN: scroll_down();   break;
430                         case 'K':
431                         case KEY_PPAGE: page_up();      break;
432                         case 'J':
433                         case KEY_NPAGE: page_down();    break;
434
435                         case 'H':
436                         case KEY_HOME: goto_home();     break;
437                         case 'E':
438                         case KEY_END: goto_end();       break;
439
440                         case 'w': save_database();
441                                   break;
442                         case 'l': ui_read_database();   break;
443                         case 'i': import_database();    break;
444                         case 'e': export_database();    break;
445                         case 'C': ui_clear_database();  break;
446
447                         case 'o': ui_open_datafile();   break;
448
449                         case 's': sort_database();      break;
450                         case 'S': sort_surname();       break;
451
452                         case '/': ui_find(0);           break;
453                         case '\\': ui_find(1);          break;
454
455                         case ' ': if(curitem >= 0) {
456                                    selected[curitem] = !selected[curitem];
457                                    ui_print_number_of_items();
458                                    refresh_list();
459                                   }
460                                 break;
461                         case '+': select_all();
462                                   refresh_list();
463                                 break;
464                         case '-': select_none();
465                                   refresh_list();
466                                 break;
467                         case '*': invert_selection();
468                                   refresh_list();
469                                  break;
470                         case 'A': move_curitem(MOVE_ITEM_UP);
471                                 break;
472                         case 'Z': move_curitem(MOVE_ITEM_DOWN);
473                                 break;
474
475                         case 'm': launch_mutt(); break;
476
477                         case 'p': ui_print_database(); break;
478
479                         case 'u': launch_lynx(); break;
480                 }
481         }
482 }
483
484
485 void
486 ui_remove_items()
487 {
488         if(list_is_empty())
489                 return;
490
491         if(statusline_ask_boolean("Remove selected item(s)", TRUE))
492                 remove_selected_items();
493
494         clear_statusline();     
495         refresh_list();
496 }
497
498 void
499 ui_clear_database()
500 {
501         if(statusline_ask_boolean("Clear WHOLE database", FALSE)) {
502                 close_database();
503                 refresh_list();
504         }
505 }
506
507 void
508 ui_find(int next)
509 {
510         int item;
511         static char findstr[81];
512
513         if(next) {
514                 if( !*findstr )
515                         return;
516         } else {
517                 clear_statusline();
518                 statusline_addstr("/");
519                 statusline_getnstr(findstr, 67, 0);
520                 clear_statusline();
521         }
522
523         if( (item = find_item(findstr, next ? curitem+1 : curitem)) >= 0 ) {
524                 curitem = item;
525                 refresh_list();
526         }
527
528 }
529
530
531 void
532 ui_print_number_of_items()
533 {
534         char *str = mkstr("     " "|%3d/%3d", selected_items(), items);
535
536         mvaddstr(0, COLS-strlen(str), str);
537
538         free(str);
539 }
540
541 void
542 ui_read_database()
543 {
544         if(items > 0)
545                 if(!statusline_ask_boolean("Your current data will be lost - "
546                                 "Press 'y' to continue", FALSE))
547                         return;
548
549         load_database(datafile);
550         refresh_list();
551 }
552
553
554 void
555 ui_print_database()
556 {
557         FILE *handle;
558         char *command = options_get_str("print_command");
559         int mode;
560
561         if( list_is_empty() )
562                 return;
563
564         statusline_addstr("Print All/Selected/Cancel (a/s/C)?");
565
566         switch( tolower(getch()) ) {
567                 case 'a':
568                         mode = ENUM_ALL;
569                         break;
570                 case 's':
571                         if( !selected_items() ) {
572                                 statusline_msg("No selected items");
573                                 return;
574                         }
575                         mode = ENUM_SELECTED;
576                         break;
577                 default:
578                         clear_statusline();
579                         return;
580         }
581
582         clear_statusline();
583
584         if( ! *command || (handle = popen(command, "w")) == NULL)
585                 return;
586
587         fexport("text", handle, mode);
588         
589         pclose(handle);
590 }
591
592
593 void
594 ui_open_datafile()
595 {
596         char *filename;
597
598         filename = ask_filename("File to open: ", 1);
599
600         if( !filename ) {
601                 refresh_screen();
602                 return;
603         }
604
605         if( options_get_int("autosave") )
606                 save_database();
607         else if(statusline_ask_boolean("Save current database", FALSE))
608                 save_database();
609
610         close_database();
611
612         load_database(filename);
613
614         if( items == 0 ) {
615                 statusline_msg("Sorry, that specified file appears not to be a valid abook addressbook");
616                 load_database(datafile);
617         } else {
618                 free(datafile);
619                 datafile = strdup(filename);
620         }
621
622         refresh_screen();
623         free(filename);
624 }