cardfile.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. /****************************************************************************
  2. * Copyright (c) 1999-2007,2008 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. * Author: Thomas E. Dickey
  30. *
  31. * $Id: cardfile.c,v 1.35 2008/08/05 00:42:24 tom Exp $
  32. *
  33. * File format: text beginning in column 1 is a title; other text is content.
  34. */
  35. #include <test.priv.h>
  36. #if USE_LIBFORM && USE_LIBPANEL
  37. #include <form.h>
  38. #include <panel.h>
  39. #define VISIBLE_CARDS 10
  40. #define OFFSET_CARD 2
  41. #define pair_1 1
  42. #define pair_2 2
  43. #define isVisible(cardp) ((cardp)->panel != 0)
  44. enum {
  45. MY_CTRL_x = MAX_FORM_COMMAND
  46. ,MY_CTRL_N
  47. ,MY_CTRL_P
  48. ,MY_CTRL_Q
  49. ,MY_CTRL_W
  50. };
  51. typedef struct _card {
  52. struct _card *link;
  53. PANEL *panel;
  54. FORM *form;
  55. char *title;
  56. char *content;
  57. } CARD;
  58. static CARD *all_cards;
  59. static bool try_color = FALSE;
  60. static char default_name[] = "cardfile.dat";
  61. #if !HAVE_STRDUP
  62. #define strdup my_strdup
  63. static char *
  64. strdup(const char *s)
  65. {
  66. char *p = typeMalloc(char, strlen(s) + 1);
  67. if (p)
  68. strcpy(p, s);
  69. return (p);
  70. }
  71. #endif /* not HAVE_STRDUP */
  72. static const char *
  73. skip(const char *buffer)
  74. {
  75. while (isspace(UChar(*buffer)))
  76. buffer++;
  77. return buffer;
  78. }
  79. static void
  80. trim(char *buffer)
  81. {
  82. unsigned n = strlen(buffer);
  83. while (n-- && isspace(UChar(buffer[n])))
  84. buffer[n] = 0;
  85. }
  86. /*******************************************************************************/
  87. static CARD *
  88. add_title(const char *title)
  89. {
  90. CARD *card, *p, *q;
  91. for (p = all_cards, q = 0; p != 0; q = p, p = p->link) {
  92. int cmp = strcmp(p->title, title);
  93. if (cmp == 0)
  94. return p;
  95. if (cmp > 0)
  96. break;
  97. }
  98. card = typeCalloc(CARD, 1);
  99. card->title = strdup(title);
  100. card->content = strdup("");
  101. if (q == 0) {
  102. card->link = all_cards;
  103. all_cards = card;
  104. } else {
  105. card->link = q->link;
  106. q->link = card;
  107. }
  108. return card;
  109. }
  110. static void
  111. add_content(CARD * card, const char *content)
  112. {
  113. unsigned total, offset;
  114. content = skip(content);
  115. if ((total = strlen(content)) != 0) {
  116. if (card->content != 0 && (offset = strlen(card->content)) != 0) {
  117. total += 1 + offset;
  118. card->content = typeRealloc(char, total + 1, card->content);
  119. if (card->content)
  120. strcpy(card->content + offset++, " ");
  121. } else {
  122. offset = 0;
  123. if (card->content != 0)
  124. free(card->content);
  125. card->content = typeMalloc(char, total + 1);
  126. }
  127. if (card->content)
  128. strcpy(card->content + offset, content);
  129. }
  130. }
  131. static CARD *
  132. new_card(void)
  133. {
  134. CARD *card = add_title("");
  135. add_content(card, "");
  136. return card;
  137. }
  138. static CARD *
  139. find_card(char *title)
  140. {
  141. CARD *card;
  142. for (card = all_cards; card != 0; card = card->link)
  143. if (!strcmp(card->title, title))
  144. break;
  145. return card;
  146. }
  147. static void
  148. read_data(char *fname)
  149. {
  150. FILE *fp;
  151. CARD *card = 0;
  152. char buffer[BUFSIZ];
  153. if ((fp = fopen(fname, "r")) != 0) {
  154. while (fgets(buffer, sizeof(buffer), fp)) {
  155. trim(buffer);
  156. if (isspace(UChar(*buffer))) {
  157. if (card == 0)
  158. card = add_title("");
  159. add_content(card, buffer);
  160. } else if ((card = find_card(buffer)) == 0) {
  161. card = add_title(buffer);
  162. }
  163. }
  164. fclose(fp);
  165. }
  166. }
  167. /*******************************************************************************/
  168. static void
  169. write_data(const char *fname)
  170. {
  171. FILE *fp;
  172. CARD *p = 0;
  173. int n;
  174. if (!strcmp(fname, default_name))
  175. fname = "cardfile.out";
  176. if ((fp = fopen(fname, "w")) != 0) {
  177. for (p = all_cards; p != 0; p = p->link) {
  178. FIELD **f = form_fields(p->form);
  179. for (n = 0; f[n] != 0; n++) {
  180. char *s = field_buffer(f[n], 0);
  181. if (s != 0
  182. && (s = strdup(s)) != 0) {
  183. trim(s);
  184. fprintf(fp, "%s%s\n", n ? "\t" : "", s);
  185. free(s);
  186. }
  187. }
  188. }
  189. fclose(fp);
  190. }
  191. }
  192. /*******************************************************************************/
  193. /*
  194. * Count the cards
  195. */
  196. static int
  197. count_cards(void)
  198. {
  199. CARD *p;
  200. int count = 0;
  201. for (p = all_cards; p != 0; p = p->link)
  202. count++;
  203. return count;
  204. }
  205. /*
  206. * Shuffle the panels to keep them in a natural hierarchy.
  207. */
  208. static void
  209. order_cards(CARD * first, int depth)
  210. {
  211. if (first) {
  212. if (depth && first->link)
  213. order_cards(first->link, depth - 1);
  214. if (isVisible(first))
  215. top_panel(first->panel);
  216. }
  217. }
  218. /*
  219. * Return the next card in the list
  220. */
  221. static CARD *
  222. next_card(CARD * now)
  223. {
  224. if (now->link != 0) {
  225. CARD *tst = now->link;
  226. if (isVisible(tst))
  227. now = tst;
  228. else
  229. tst = next_card(tst);
  230. }
  231. return now;
  232. }
  233. /*
  234. * Return the previous card in the list
  235. */
  236. static CARD *
  237. prev_card(CARD * now)
  238. {
  239. CARD *p;
  240. for (p = all_cards; p != 0; p = p->link) {
  241. if (p->link == now) {
  242. if (!isVisible(p))
  243. p = prev_card(p);
  244. return p;
  245. }
  246. }
  247. return now;
  248. }
  249. /*
  250. * Returns the first card in the list that we will display.
  251. */
  252. static CARD *
  253. first_card(CARD * now)
  254. {
  255. if (!isVisible(now))
  256. now = next_card(now);
  257. return now;
  258. }
  259. /*******************************************************************************/
  260. static int
  261. form_virtualize(WINDOW *w)
  262. {
  263. int c = wgetch(w);
  264. switch (c) {
  265. case CTRL('W'):
  266. return (MY_CTRL_W);
  267. case CTRL('N'):
  268. return (MY_CTRL_N);
  269. case CTRL('P'):
  270. return (MY_CTRL_P);
  271. case QUIT:
  272. case ESCAPE:
  273. return (MY_CTRL_Q);
  274. case KEY_BACKSPACE:
  275. return (REQ_DEL_PREV);
  276. case KEY_DC:
  277. return (REQ_DEL_CHAR);
  278. case KEY_LEFT:
  279. return (REQ_LEFT_CHAR);
  280. case KEY_RIGHT:
  281. return (REQ_RIGHT_CHAR);
  282. case KEY_DOWN:
  283. case KEY_NEXT:
  284. return (REQ_NEXT_FIELD);
  285. case KEY_UP:
  286. case KEY_PREVIOUS:
  287. return (REQ_PREV_FIELD);
  288. default:
  289. return (c);
  290. }
  291. }
  292. static FIELD **
  293. make_fields(CARD * p, int form_high, int form_wide)
  294. {
  295. FIELD **f = typeCalloc(FIELD *, 3);
  296. f[0] = new_field(1, form_wide, 0, 0, 0, 0);
  297. set_field_back(f[0], A_REVERSE);
  298. set_field_buffer(f[0], 0, p->title);
  299. field_opts_off(f[0], O_BLANK);
  300. f[1] = new_field(form_high - 1, form_wide, 1, 0, 0, 0);
  301. set_field_buffer(f[1], 0, p->content);
  302. set_field_just(f[1], JUSTIFY_LEFT);
  303. field_opts_off(f[1], O_BLANK);
  304. f[2] = 0;
  305. return f;
  306. }
  307. static void
  308. show_legend(void)
  309. {
  310. erase();
  311. move(LINES - 3, 0);
  312. addstr("^Q/ESC -- exit form ^W -- writes data to file\n");
  313. addstr("^N -- go to next card ^P -- go to previous card\n");
  314. addstr("Arrow keys move left/right within a field, up/down between fields");
  315. }
  316. #if (defined(KEY_RESIZE) && HAVE_WRESIZE) || NO_LEAKS
  317. static void
  318. free_form_fields(FIELD ** f)
  319. {
  320. int n;
  321. for (n = 0; f[n] != 0; ++n) {
  322. free_field(f[n]);
  323. }
  324. free(f);
  325. }
  326. #endif
  327. /*******************************************************************************/
  328. static void
  329. cardfile(char *fname)
  330. {
  331. WINDOW *win;
  332. CARD *p;
  333. CARD *top_card;
  334. int visible_cards;
  335. int panel_wide;
  336. int panel_high;
  337. int form_wide;
  338. int form_high;
  339. int y;
  340. int x;
  341. int ch = ERR;
  342. int finished = FALSE;
  343. show_legend();
  344. /* decide how many cards we can display */
  345. visible_cards = count_cards();
  346. while (
  347. (panel_wide = COLS - (visible_cards * OFFSET_CARD)) < 10 ||
  348. (panel_high = LINES - (visible_cards * OFFSET_CARD) - 5) < 5) {
  349. --visible_cards;
  350. }
  351. form_wide = panel_wide - 2;
  352. form_high = panel_high - 2;
  353. y = (visible_cards - 1) * OFFSET_CARD;
  354. x = 0;
  355. /* make a panel for each CARD */
  356. for (p = all_cards; p != 0; p = p->link) {
  357. if ((win = newwin(panel_high, panel_wide, y, x)) == 0)
  358. break;
  359. wbkgd(win, COLOR_PAIR(pair_2));
  360. keypad(win, TRUE);
  361. p->panel = new_panel(win);
  362. box(win, 0, 0);
  363. p->form = new_form(make_fields(p, form_high, form_wide));
  364. set_form_win(p->form, win);
  365. set_form_sub(p->form, derwin(win, form_high, form_wide, 1, 1));
  366. post_form(p->form);
  367. y -= OFFSET_CARD;
  368. x += OFFSET_CARD;
  369. }
  370. top_card = first_card(all_cards);
  371. order_cards(top_card, visible_cards);
  372. while (!finished) {
  373. update_panels();
  374. doupdate();
  375. ch = form_virtualize(panel_window(top_card->panel));
  376. switch (form_driver(top_card->form, ch)) {
  377. case E_OK:
  378. break;
  379. case E_UNKNOWN_COMMAND:
  380. switch (ch) {
  381. case MY_CTRL_Q:
  382. finished = TRUE;
  383. break;
  384. case MY_CTRL_P:
  385. top_card = prev_card(top_card);
  386. order_cards(top_card, visible_cards);
  387. break;
  388. case MY_CTRL_N:
  389. top_card = next_card(top_card);
  390. order_cards(top_card, visible_cards);
  391. break;
  392. case MY_CTRL_W:
  393. form_driver(top_card->form, REQ_VALIDATION);
  394. write_data(fname);
  395. break;
  396. #if defined(KEY_RESIZE) && HAVE_WRESIZE
  397. case KEY_RESIZE:
  398. /* resizeterm already did "something" reasonable, but it cannot
  399. * know much about layout. So let's make it nicer.
  400. */
  401. panel_wide = COLS - (visible_cards * OFFSET_CARD);
  402. panel_high = LINES - (visible_cards * OFFSET_CARD) - 5;
  403. form_wide = panel_wide - 2;
  404. form_high = panel_high - 2;
  405. y = (visible_cards - 1) * OFFSET_CARD;
  406. x = 0;
  407. show_legend();
  408. for (p = all_cards; p != 0; p = p->link) {
  409. FIELD **oldf = form_fields(p->form);
  410. WINDOW *olds = form_sub(p->form);
  411. if (!isVisible(p))
  412. continue;
  413. win = form_win(p->form);
  414. /* move and resize the card as needed
  415. * FIXME: if the windows are shrunk too much, this won't do
  416. */
  417. mvwin(win, y, x);
  418. wresize(win, panel_high, panel_wide);
  419. /* reconstruct each form. Forms are not resizable, and
  420. * there appears to be no good way to reload the text in
  421. * a resized window.
  422. */
  423. werase(win);
  424. unpost_form(p->form);
  425. free_form(p->form);
  426. p->form = new_form(make_fields(p, form_high, form_wide));
  427. set_form_win(p->form, win);
  428. set_form_sub(p->form, derwin(win, form_high, form_wide,
  429. 1, 1));
  430. post_form(p->form);
  431. free_form_fields(oldf);
  432. delwin(olds);
  433. box(win, 0, 0);
  434. y -= OFFSET_CARD;
  435. x += OFFSET_CARD;
  436. }
  437. break;
  438. #endif
  439. default:
  440. beep();
  441. break;
  442. }
  443. break;
  444. default:
  445. flash();
  446. break;
  447. }
  448. }
  449. #if NO_LEAKS
  450. while (all_cards != 0) {
  451. FIELD **f;
  452. int count;
  453. p = all_cards;
  454. all_cards = all_cards->link;
  455. if (isVisible(p)) {
  456. f = form_fields(p->form);
  457. count = field_count(p->form);
  458. unpost_form(p->form); /* ...so we can free it */
  459. free_form(p->form); /* this also disconnects the fields */
  460. free_form_fields(f);
  461. del_panel(p->panel);
  462. }
  463. free(p->title);
  464. free(p->content);
  465. free(p);
  466. }
  467. #endif
  468. }
  469. static void
  470. usage(void)
  471. {
  472. static const char *msg[] =
  473. {
  474. "Usage: view [options] file"
  475. ,""
  476. ,"Options:"
  477. ," -c use color if terminal supports it"
  478. };
  479. size_t n;
  480. for (n = 0; n < SIZEOF(msg); n++)
  481. fprintf(stderr, "%s\n", msg[n]);
  482. ExitProgram(EXIT_FAILURE);
  483. }
  484. /*******************************************************************************/
  485. int
  486. main(int argc, char *argv[])
  487. {
  488. int n;
  489. setlocale(LC_ALL, "");
  490. while ((n = getopt(argc, argv, "c")) != -1) {
  491. switch (n) {
  492. case 'c':
  493. try_color = TRUE;
  494. break;
  495. default:
  496. usage();
  497. }
  498. }
  499. initscr();
  500. cbreak();
  501. noecho();
  502. if (try_color) {
  503. if (has_colors()) {
  504. start_color();
  505. init_pair(pair_1, COLOR_WHITE, COLOR_BLUE);
  506. init_pair(pair_2, COLOR_WHITE, COLOR_CYAN);
  507. bkgd(COLOR_PAIR(pair_1));
  508. } else {
  509. try_color = FALSE;
  510. }
  511. }
  512. if (optind + 1 == argc) {
  513. for (n = 1; n < argc; n++)
  514. read_data(argv[n]);
  515. if (count_cards() == 0)
  516. new_card();
  517. cardfile(argv[1]);
  518. } else {
  519. read_data(default_name);
  520. if (count_cards() == 0)
  521. new_card();
  522. cardfile(default_name);
  523. }
  524. endwin();
  525. ExitProgram(EXIT_SUCCESS);
  526. }
  527. #else
  528. int
  529. main(void)
  530. {
  531. printf("This program requires the curses form and panel libraries\n");
  532. ExitProgram(EXIT_FAILURE);
  533. }
  534. #endif