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