]> git.deb.at Git - pkg/abook.git/blob - ui.c
62b5e8f327d52340f2986c46005a47ee421c9b0a
[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 "xmalloc.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 #include "abook_rl.h"
41
42 /*
43  * external variables
44  */
45
46 extern int items, curitem;
47 extern char *datafile;
48
49 extern bool alternative_datafile;
50
51 /*
52  * internal variables
53  */
54
55 static bool ui_initialized = FALSE;
56
57 static bool should_resize = FALSE;
58 static bool can_resize = FALSE;
59
60 static WINDOW *top = NULL, *bottom = NULL;
61
62
63 static void
64 init_windows()
65 {
66         top = newwin(LIST_TOP - 1, COLS, 0, 0);
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         init_list();
159         init_windows();
160
161         ui_initialized = TRUE;
162
163 #ifdef SIGWINCH
164         signal(SIGWINCH, win_changed);
165 #endif
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 int
217 statusline_msg(char *msg)
218 {
219         int c;
220
221         clear_statusline();
222         statusline_addstr(msg);
223         c = getch();
224 #ifdef DEBUG
225         fprintf(stderr, "statusline_msg(\"%s\")\n", msg);
226 #endif
227         clear_statusline();
228
229         return c;
230 }
231
232 void
233 statusline_addstr(char *str)
234 {
235         mvwaddstr(bottom, 1, 0, str);
236         refresh();
237         wrefresh(bottom);
238 }
239
240 char *
241 ui_readline(char *prompt, char *s, int limit, bool use_completion)
242 {
243         int y, x;
244         char *ret;
245
246         mvwaddstr(bottom, 1, 0, prompt);
247
248         getyx(bottom, y, x);
249
250         ret = abook_readline(bottom, y, x, s, limit, use_completion);
251
252         if(ret)
253                 strtrim(ret);
254
255         return ret;
256 }
257
258 int
259 statusline_ask_boolean(char *msg, int def)
260 {
261         int ret;
262         char *msg2 = strconcat(msg,  def ? " (Y/n)?" : " (y/N)?", NULL);
263
264         statusline_addstr(msg2);
265
266         free(msg2);
267
268         switch(tolower(getch())) {
269                 case 'n':
270                 case 'N':
271                         ret = FALSE;
272                         break;
273                 case 'y':
274                 case 'Y':
275                         ret = TRUE;
276                         break;
277                 default:
278                         ret = def;
279                         break;
280         }
281
282         clear_statusline();
283
284         return ret;
285 }
286
287 void
288 refresh_statusline()
289 {
290         werase(bottom);
291
292         mvwhline(bottom, 0, 0, UI_HLINE_CHAR, COLS);
293
294         refresh();
295         wrefresh(bottom);
296 }
297
298 char *
299 ask_filename(char *prompt)
300 {
301         char *buf = NULL;
302
303         clear_statusline();
304
305         buf = ui_readline(prompt, NULL, -1, 1);
306
307         return buf;
308 }
309
310 void
311 clear_statusline()
312 {
313         wmove(bottom, 1, 0);
314         wclrtoeol(bottom);
315         wrefresh(bottom);
316         refresh();
317 }
318
319 /*
320  * help
321  */
322
323 #include "help.h"
324
325 void
326 display_help(int help)
327 {
328         int i;
329         char **tbl;
330         WINDOW *helpw;
331
332         switch(help) {
333                 case HELP_MAIN:
334                         tbl = mainhelp;
335                         break;
336                 case HELP_EDITOR:
337                         tbl = editorhelp;
338                         break;
339                 default:return;
340         }
341
342         helpw = newwin(LINES - 5, COLS - 6, 2, 3);
343         erase();
344         headerline("help");
345
346         for(i = 0; tbl[i] != NULL; i++) {
347                 waddstr(helpw, tbl[i]);
348                 if( (!((i + 1) % (LINES - 8))) ||
349                         (tbl[i + 1] == NULL) ) {
350                         refresh();
351                         wrefresh(helpw);
352                         refresh_statusline();
353                         if(statusline_msg("Press any key to continue...")
354                                         == 'q')
355                                 break;
356                         wclear(helpw);
357                 }
358         }
359
360         clear_statusline();
361         delwin(helpw);
362 }
363
364 /*
365  * end of help
366  */
367
368 extern char *selected;
369 extern int curitem;
370
371 void
372 get_commands()
373 {
374         int ch;
375
376         for(;;) {
377                 can_resize = TRUE; /* it's safe to resize now */
378                 if(!opt_get_bool(BOOL_SHOW_CURSOR))
379                         hide_cursor();
380                 if(should_resize)
381                         refresh_screen();
382                 ch = getch();
383                 if(!opt_get_bool(BOOL_SHOW_CURSOR))
384                         show_cursor();
385                 can_resize = FALSE; /* it's not safe to resize anymore */
386                 switch(ch) {
387                         case 'q': return;
388                         case 'Q': quit_abook(QUIT_DONTSAVE);    break;
389                         case 'P': print_stderr(selected_items() ?
390                                                   -1 : list_current_item());
391                                   return;
392                         case '?':
393                                   display_help(HELP_MAIN);
394                                   refresh_screen();
395                                   break;
396                         case 'a': add_item();           break;
397                         case '\r': edit_item(-1);       break;
398                         case KEY_DC:
399                         case 'd':
400                         case 'r': ui_remove_items();    break;
401                         case 'D': duplicate_item();     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_by_field(NAME);  break;
428                         case 'S': sort_surname();       break;
429                         case 'F': sort_by_field(-1);    break;
430
431                         case '/': ui_find(0);           break;
432                         case '\\': ui_find(1);          break;
433
434                         case ' ': if(curitem >= 0) {
435                                    selected[curitem] = !selected[curitem];
436                                    ui_print_number_of_items();
437                                    refresh_list();
438                                   }
439                                 break;
440                         case '+': select_all();
441                                   refresh_list();
442                                 break;
443                         case '-': select_none();
444                                   refresh_list();
445                                 break;
446                         case '*': invert_selection();
447                                   refresh_list();
448                                  break;
449                         case 'A': move_curitem(MOVE_ITEM_UP);
450                                 break;
451                         case 'Z': move_curitem(MOVE_ITEM_DOWN);
452                                 break;
453
454                         case 'm': launch_mutt(selected_items() ?
455                                                   -1 : list_current_item());
456                                   refresh_screen();
457                                   break;
458
459                         case 'p': ui_print_database(); break;
460
461                         case 'u': launch_wwwbrowser(list_current_item());
462                                   refresh_screen();
463                                   break;
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 = -1;
494         static char findstr[MAX_FIELD_LEN];
495         int search_fields[] = {NAME, EMAIL, NICK, -1};
496
497         clear_statusline();
498
499         if(next) {
500                 if(!*findstr)
501                         return;
502         } else {
503                 char *s;
504                 s = ui_readline("/", findstr, MAX_FIELD_LEN - 1, 0);
505                 strncpy(findstr, s, MAX_FIELD_LEN);
506                 refresh_screen();
507         }
508
509         if( (item = find_item(findstr, curitem + !!next, search_fields)) < 0 &&
510                         (item = find_item(findstr, 0, search_fields)) >= 0)
511                 statusline_addstr("Search hit bottom, continuing at top");
512
513         if(item >= 0) {
514                 curitem = item;
515                 refresh_list();
516         }
517 }
518
519
520 void
521 ui_print_number_of_items()
522 {
523         char *str = mkstr("     " "|%3d/%3d", selected_items(), items);
524
525         mvaddstr(0, COLS-strlen(str), str);
526
527         free(str);
528 }
529
530 void
531 ui_read_database()
532 {
533         if(items > 0)
534                 if(!statusline_ask_boolean("Your current data will be lost - "
535                                 "Press 'y' to continue", FALSE))
536                         return;
537
538         load_database(datafile);
539         refresh_list();
540 }
541
542
543 void
544 ui_print_database()
545 {
546         FILE *handle;
547         char *command = opt_get_str(STR_PRINT_COMMAND);
548         int mode;
549
550         if(list_is_empty())
551                 return;
552
553         statusline_addstr("Print All/Selected/Cancel (a/s/C)?");
554
555         switch(tolower(getch())) {
556                 case 'a':
557                         mode = ENUM_ALL;
558                         break;
559                 case 's':
560                         if( !selected_items() ) {
561                                 statusline_msg("No selected items");
562                                 return;
563                         }
564                         mode = ENUM_SELECTED;
565                         break;
566                 default:
567                         clear_statusline();
568                         return;
569         }
570
571         clear_statusline();
572
573         if( ! *command || (handle = popen(command, "w")) == NULL)
574                 return;
575
576         fexport("text", handle, mode);
577
578         pclose(handle);
579 }
580
581
582 void
583 ui_open_datafile()
584 {
585         char *filename;
586
587         filename = ask_filename("File to open: ");
588
589         if(!filename || ! *filename) {
590                 free(filename);
591                 refresh_screen();
592                 return;
593         }
594
595         if(opt_get_bool(BOOL_AUTOSAVE))
596                 save_database();
597         else if(statusline_ask_boolean("Save current database", FALSE))
598                 save_database();
599
600         close_database();
601
602         load_database(filename);
603
604         if(items == 0) {
605                 statusline_msg("Sorry, that specified file appears not to be a valid abook addressbook");
606                 load_database(datafile);
607         } else {
608                 free(datafile);
609                 datafile = xstrdup(filename);
610         }
611
612         refresh_screen();
613         free(filename);
614
615         alternative_datafile = TRUE;
616 }