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