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