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