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