ncurses 5.4
[ncurses.git] / test / cardfile.c
1 /****************************************************************************
2  * Copyright (c) 1999-2002,2003 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28
29 /*
30  * Author: Thomas E. Dickey <dickey@clark.net> 1999
31  *
32  * $Id: cardfile.c,v 1.23 2003/04/26 16:43:56 tom Exp $
33  *
34  * File format: text beginning in column 1 is a title; other text is content.
35  */
36
37 #include <test.priv.h>
38
39 #if USE_LIBFORM && USE_LIBPANEL
40
41 #include <form.h>
42 #include <panel.h>
43
44 #define VISIBLE_CARDS 10
45 #define OFFSET_CARD 2
46
47 enum {
48     MY_CTRL_x = MAX_FORM_COMMAND
49     ,MY_CTRL_N
50     ,MY_CTRL_P
51     ,MY_CTRL_Q
52     ,MY_CTRL_W
53 };
54
55 typedef struct _card {
56     struct _card *link;
57     PANEL *panel;
58     FORM *form;
59     char *title;
60     char *content;
61 } CARD;
62
63 static CARD *all_cards;
64 static char default_name[] = "cardfile.dat";
65
66 #if !HAVE_STRDUP
67 #define strdup my_strdup
68 static char *
69 strdup(char *s)
70 {
71     char *p = (char *) malloc(strlen(s) + 1);
72     if (p)
73         strcpy(p, s);
74     return (p);
75 }
76 #endif /* not HAVE_STRDUP */
77
78 static const char *
79 skip(const char *buffer)
80 {
81     while (isspace(UChar(*buffer)))
82         buffer++;
83     return buffer;
84 }
85
86 static void
87 trim(char *buffer)
88 {
89     unsigned n = strlen(buffer);
90     while (n-- && isspace(UChar(buffer[n])))
91         buffer[n] = 0;
92 }
93
94 /*******************************************************************************/
95
96 static CARD *
97 add_title(const char *title)
98 {
99     CARD *card, *p, *q;
100
101     for (p = all_cards, q = 0; p != 0; q = p, p = p->link) {
102         int cmp = strcmp(p->title, title);
103         if (cmp == 0)
104             return p;
105         if (cmp > 0)
106             break;
107     }
108
109     card = (CARD *) calloc(1, sizeof(CARD));
110     card->title = strdup(title);
111     card->content = strdup("");
112
113     if (q == 0) {
114         card->link = all_cards;
115         all_cards = card;
116     } else {
117         card->link = q->link;
118         q->link = card;
119     }
120
121     return card;
122 }
123
124 static void
125 add_content(CARD * card, const char *content)
126 {
127     unsigned total, offset;
128
129     content = skip(content);
130     if ((total = strlen(content)) != 0) {
131         if ((offset = strlen(card->content)) != 0) {
132             total += 1 + offset;
133             card->content = (char *) realloc(card->content, total + 1);
134             strcpy(card->content + offset++, " ");
135         } else {
136             if (card->content != 0)
137                 free(card->content);
138             card->content = (char *) malloc(total + 1);
139         }
140         strcpy(card->content + offset, content);
141     }
142 }
143
144 static CARD *
145 new_card(void)
146 {
147     CARD *card = add_title("");
148     add_content(card, "");
149     return card;
150 }
151
152 static CARD *
153 find_card(char *title)
154 {
155     CARD *card;
156
157     for (card = all_cards; card != 0; card = card->link)
158         if (!strcmp(card->title, title))
159             break;
160
161     return card;
162 }
163
164 static void
165 read_data(char *fname)
166 {
167     FILE *fp;
168     CARD *card = 0;
169     char buffer[BUFSIZ];
170
171     if ((fp = fopen(fname, "r")) != 0) {
172         while (fgets(buffer, sizeof(buffer), fp)) {
173             trim(buffer);
174             if (isspace(UChar(*buffer))) {
175                 if (card == 0)
176                     card = add_title("");
177                 add_content(card, buffer);
178             } else if ((card = find_card(buffer)) == 0) {
179                 card = add_title(buffer);
180             }
181         }
182         fclose(fp);
183     }
184 }
185
186 /*******************************************************************************/
187
188 static void
189 write_data(const char *fname)
190 {
191     FILE *fp;
192     CARD *p = 0;
193     int n;
194
195     if (!strcmp(fname, default_name))
196         fname = "cardfile.out";
197
198     if ((fp = fopen(fname, "w")) != 0) {
199         for (p = all_cards; p != 0; p = p->link) {
200             FIELD **f = form_fields(p->form);
201             for (n = 0; f[n] != 0; n++) {
202                 char *s = field_buffer(f[n], 0);
203                 if (s != 0
204                     && (s = strdup(s)) != 0) {
205                     trim(s);
206                     fprintf(fp, "%s%s\n", n ? "\t" : "", s);
207                     free(s);
208                 }
209             }
210         }
211         fclose(fp);
212     }
213 }
214
215 /*******************************************************************************/
216
217 /*
218  * Count the cards
219  */
220 static int
221 count_cards(void)
222 {
223     CARD *p;
224     int count = 0;
225
226     for (p = all_cards; p != 0; p = p->link)
227         count++;
228
229     return count;
230 }
231
232 /*
233  * Shuffle the panels to keep them in a natural hierarchy.
234  */
235 static void
236 order_cards(CARD * first, int depth)
237 {
238     if (first) {
239         if (depth && first->link)
240             order_cards(first->link, depth - 1);
241         top_panel(first->panel);
242     }
243 }
244
245 /*
246  * Return the next card in the list
247  */
248 static CARD *
249 next_card(CARD * now)
250 {
251     if (now->link)
252         now = now->link;
253     return now;
254 }
255
256 /*
257  * Return the previous card in the list
258  */
259 static CARD *
260 prev_card(CARD * now)
261 {
262     CARD *p;
263     for (p = all_cards; p != 0; p = p->link)
264         if (p->link == now)
265             return p;
266     return now;
267 }
268
269 /*******************************************************************************/
270
271 static int
272 form_virtualize(WINDOW *w)
273 {
274     int c = wgetch(w);
275
276     switch (c) {
277     case CTRL('W'):
278         return (MY_CTRL_W);
279     case CTRL('N'):
280         return (MY_CTRL_N);
281     case CTRL('P'):
282         return (MY_CTRL_P);
283     case CTRL('Q'):
284     case 033:
285         return (MY_CTRL_Q);
286
287     case KEY_BACKSPACE:
288         return (REQ_DEL_PREV);
289     case KEY_DC:
290         return (REQ_DEL_CHAR);
291     case KEY_LEFT:
292         return (REQ_LEFT_CHAR);
293     case KEY_RIGHT:
294         return (REQ_RIGHT_CHAR);
295
296     case KEY_DOWN:
297     case KEY_NEXT:
298         return (REQ_NEXT_FIELD);
299     case KEY_UP:
300     case KEY_PREVIOUS:
301         return (REQ_PREV_FIELD);
302
303     default:
304         return (c);
305     }
306 }
307
308 static FIELD **
309 make_fields(CARD * p, int form_high, int form_wide)
310 {
311     FIELD **f = (FIELD **) calloc(3, sizeof(FIELD *));
312
313     f[0] = new_field(1, form_wide, 0, 0, 0, 0);
314     set_field_back(f[0], A_REVERSE);
315     set_field_buffer(f[0], 0, p->title);
316     field_opts_off(f[0], O_BLANK);
317
318     f[1] = new_field(form_high - 1, form_wide, 1, 0, 0, 0);
319     set_field_buffer(f[1], 0, p->content);
320     set_field_just(f[1], JUSTIFY_LEFT);
321     field_opts_off(f[1], O_BLANK);
322
323     f[2] = 0;
324     return f;
325 }
326
327 static void
328 show_legend(void)
329 {
330     erase();
331     move(LINES - 3, 0);
332     addstr("^Q/ESC -- exit form            ^W   -- writes data to file\n");
333     addstr("^N   -- go to next card        ^P   -- go to previous card\n");
334     addstr("Arrow keys move left/right within a field, up/down between fields");
335 }
336
337 #if (defined(KEY_RESIZE) && HAVE_WRESIZE) || NO_LEAKS
338 static void
339 free_form_fields(FIELD ** f)
340 {
341     int n;
342
343     for (n = 0; f[n] != 0; ++n) {
344         free_field(f[n]);
345     }
346     free(f);
347 }
348 #endif
349
350 /*******************************************************************************/
351
352 static void
353 cardfile(char *fname)
354 {
355     WINDOW *win;
356     CARD *p;
357     CARD *top_card;
358     int visible_cards = count_cards();
359     int panel_wide = COLS - (visible_cards * OFFSET_CARD);
360     int panel_high = LINES - (visible_cards * OFFSET_CARD) - 5;
361     int form_wide = panel_wide - 2;
362     int form_high = panel_high - 2;
363     int y = (visible_cards - 1) * OFFSET_CARD;
364     int x = 0;
365     int ch = ERR;
366     int last_ch;
367     int finished = FALSE;
368
369     show_legend();
370
371     /* make a panel for each CARD */
372     for (p = all_cards; p != 0; p = p->link) {
373
374         win = newwin(panel_high, panel_wide, y, x);
375         keypad(win, TRUE);
376         p->panel = new_panel(win);
377         box(win, 0, 0);
378
379         p->form = new_form(make_fields(p, form_high, form_wide));
380         set_form_win(p->form, win);
381         set_form_sub(p->form, derwin(win, form_high, form_wide, 1, 1));
382         post_form(p->form);
383
384         y -= OFFSET_CARD;
385         x += OFFSET_CARD;
386     }
387
388     order_cards(top_card = all_cards, visible_cards);
389
390     while (!finished) {
391         update_panels();
392         doupdate();
393
394         last_ch = ch;
395         ch = form_virtualize(panel_window(top_card->panel));
396         switch (form_driver(top_card->form, ch)) {
397         case E_OK:
398             break;
399         case E_UNKNOWN_COMMAND:
400             switch (ch) {
401             case MY_CTRL_Q:
402                 finished = TRUE;
403                 break;
404             case MY_CTRL_P:
405                 top_card = prev_card(top_card);
406                 order_cards(top_card, visible_cards);
407                 break;
408             case MY_CTRL_N:
409                 top_card = next_card(top_card);
410                 order_cards(top_card, visible_cards);
411                 break;
412             case MY_CTRL_W:
413                 form_driver(top_card->form, REQ_VALIDATION);
414                 write_data(fname);
415                 break;
416 #if defined(KEY_RESIZE) && HAVE_WRESIZE
417             case KEY_RESIZE:
418                 /* resizeterm already did "something" reasonable, but it cannot
419                  * know much about layout.  So let's make it nicer.
420                  */
421                 panel_wide = COLS - (visible_cards * OFFSET_CARD);
422                 panel_high = LINES - (visible_cards * OFFSET_CARD) - 5;
423
424                 form_wide = panel_wide - 2;
425                 form_high = panel_high - 2;
426
427                 y = (visible_cards - 1) * OFFSET_CARD;
428                 x = 0;
429
430                 show_legend();
431                 for (p = all_cards; p != 0; p = p->link) {
432                     FIELD **oldf = form_fields(p->form);
433                     WINDOW *olds = form_sub(p->form);
434
435                     win = form_win(p->form);
436
437                     /* move and resize the card as needed
438                      * FIXME: if the windows are shrunk too much, this won't do
439                      */
440                     mvwin(win, y, x);
441                     wresize(win, panel_high, panel_wide);
442
443                     /* reconstruct each form.  Forms are not resizable, and
444                      * there appears to be no good way to reload the text in
445                      * a resized window.
446                      */
447                     werase(win);
448
449                     unpost_form(p->form);
450                     free_form(p->form);
451
452                     p->form = new_form(make_fields(p, form_high, form_wide));
453                     set_form_win(p->form, win);
454                     set_form_sub(p->form, derwin(win, form_high, form_wide,
455                                                  1, 1));
456                     post_form(p->form);
457
458                     free_form_fields(oldf);
459                     delwin(olds);
460
461                     box(win, 0, 0);
462
463                     y -= OFFSET_CARD;
464                     x += OFFSET_CARD;
465                 }
466                 break;
467 #endif
468             default:
469                 beep();
470                 break;
471             }
472             break;
473         default:
474             flash();
475             break;
476         }
477     }
478 #if NO_LEAKS
479     while (all_cards != 0) {
480         FIELD **f;
481         int count;
482
483         p = all_cards;
484         all_cards = all_cards->link;
485
486         f = form_fields(p->form);
487         count = field_count(p->form);
488
489         unpost_form(p->form);   /* ...so we can free it */
490         free_form(p->form);     /* this also disconnects the fields */
491
492         free_form_fields(f);
493
494         del_panel(p->panel);
495         free(p->title);
496         free(p->content);
497         free(p);
498     }
499 #endif
500 }
501
502 /*******************************************************************************/
503
504 int
505 main(int argc, char *argv[])
506 {
507     int n;
508
509     setlocale(LC_ALL, "");
510
511     initscr();
512     cbreak();
513     noecho();
514
515     if (argc > 1) {
516         for (n = 1; n < argc; n++)
517             read_data(argv[n]);
518         if (count_cards() == 0)
519             new_card();
520         cardfile(argv[1]);
521     } else {
522         read_data(default_name);
523         if (count_cards() == 0)
524             new_card();
525         cardfile(default_name);
526     }
527
528     endwin();
529
530     ExitProgram(EXIT_SUCCESS);
531 }
532 #else
533 int
534 main(void)
535 {
536     printf("This program requires the curses form and panel libraries\n");
537     ExitProgram(EXIT_FAILURE);
538 }
539 #endif