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