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