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