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