home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 301_01 / draw.c < prev    next >
C/C++ Source or Header  |  1989-12-30  |  13KB  |  622 lines

  1. /*
  2.  * this is a very simple drawing program that 
  3.  * illustrates how to build a DCUWCU application
  4.  *
  5.  * Copyright 1989 Mark A. Johnson 
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <graphics.h>
  10.  
  11. #define M_POINTER    0    /* mouse shapes */
  12. #define M_CROSS        1
  13.  
  14. #define ON        1
  15. #define OFF        0
  16.  
  17. #define MAX_OBJECT    100
  18. #define ESC    27
  19.  
  20. #define BOX    'b'    /* object types we support */
  21. #define ELLIPSE    'e'
  22. #define LINE    'l'
  23. #define TEXT    't'
  24.  
  25. #define M_MAIN    1    /* handles for the menus */
  26. #define M_FILE    2
  27. #define M_OBJ    3
  28. #define M_ACT    4
  29.  
  30. #define A_COPY    1    /* action requests for button() */
  31. #define A_MOVE    2
  32. #define A_EDIT    3
  33.  
  34. #define min(a,b) ((a) < (b) ? (a) : (b))
  35. #define max(a,b) ((a) > (b) ? (a) : (b))
  36.  
  37. typedef struct { int type, l, t, r, b; char select, *data; } Object;
  38.  
  39. Object objects[MAX_OBJECT];    /* the table of objects defined so far */
  40. int last_object;        /* the end of the object table    */
  41.  
  42. int map[] = {             /* maps a M_OBJ menu item to an object */
  43.     0, BOX, ELLIPSE, LINE, TEXT };
  44.  
  45. char *about =             /* form used on the M_MAIN About item */
  46.     "  Draw This!|     by|Mark A. Johnson|  %{continue}";
  47.  
  48. char *help =             /* help message for wrong keyboard input */
  49.  "quit refresh : box line ellipse text : delete copy move edit";
  50.  
  51. char filename[20];        /* save the filename we're working with */
  52. char text[100];            /* extra buffer for text i/o */
  53.  
  54. int actn_obj = 0;        /* flag for button(), some action req */
  55. int make_obj = 0;        /* flag for button(), need to create */
  56. int slct_cnt = 0;        /* count of selected objects */
  57. int first;            /* helps make_object collect points */
  58. int grid = 0;            /* grid displayed, snap coords */
  59.  
  60. extern int Maxx, Maxy, MaxColor;
  61.  
  62. /* start routine, called by the application driver, gets things going */
  63.  
  64. start(argc, argv) char **argv; {
  65.     add_menu(M_MAIN, "Main:About|Quit|Refresh|Grid");
  66.     add_menu(M_FILE, "File:Read|Write|Save|Print");
  67.     add_menu(M_OBJ, "Objects:Box|Ellipse|Line|Text");
  68.     add_menu(M_ACT, "Actions:Delete|Copy|Move|Edit");
  69.     menu_state(M_ACT, 0);
  70.     if (argc > 1) {
  71.         strcpy(filename, argv[1]);
  72.         read_objects();
  73.     }
  74. }
  75.  
  76. /* no timers in this application , but DCUWCU needs an entry anyway */
  77.  
  78. timer() {}
  79.  
  80. /* button routine called every time button 1 is depressed */
  81.  
  82. button(b, x, y) {
  83.     if (make_obj) { /* need points to make an object */
  84.         make_object(x, y);
  85.     }
  86.     else if (actn_obj) { /* got a point for a copy or move */
  87.         action_object(x, y);
  88.     }
  89.     else    { /* do a selection */
  90.         select_object(in_object(x, y));
  91.     }
  92.     check_menu();
  93. }
  94.  
  95. /* menu routine called every time a menu item is selected */
  96.  
  97. menu(m, i) {
  98.     char junk = 0, on;
  99.     switch (m) {
  100.     case M_MAIN: /* main menu */
  101.         switch (i) {
  102.             case 1: form(about, &junk); break;
  103.             case 2: quit(); break;
  104.             case 3: refresh(); break;
  105.             case 4: do_grid(); break;
  106.         }
  107.         break;
  108.     case M_FILE: /* file menu */
  109.         if (i < 3 && !get_name())
  110.             break;
  111.         switch (i) {
  112.             case 1: read_objects(); break;
  113.             case 2: case 3: write_objects(); break;
  114.             case 4: print(); break;
  115.         }
  116.         break;
  117.     case M_OBJ: /* objects */
  118.         start_make(map[i]);
  119.         break;
  120.     case M_ACT: /* actions */
  121.         switch (i) {
  122.             case 1: kill_object(); break;
  123.             case 2: start_actn(A_COPY); break;
  124.             case 3: start_actn(A_MOVE); break;
  125.             case 4: start_edit(); break;
  126.         }
  127.         break;
  128.     }
  129. }
  130.  
  131. keyboard(c) {
  132.     switch (c) {
  133.         case 'p': print(); break;
  134.         case 'g': do_grid(); break;
  135.         case 'r': refresh(); break;
  136.         case 'q': quit(); break;
  137.         case 'b': start_make(BOX); break;
  138.         case 't': start_make(TEXT); break;
  139.         case 'l': start_make(LINE); break;
  140.         case 'm': start_actn(A_MOVE); break;
  141.         case 'c': start_actn(A_COPY); break;
  142.         case 'd': kill_object(); break;
  143.         case 'e':
  144.             if (slct_cnt)
  145.                 start_edit();
  146.             else    start_make(ELLIPSE);
  147.             break;
  148.         default: msg(help);
  149.     }
  150. }
  151.  
  152. /* time to go, see if they really want to */
  153.  
  154. quit() {
  155.     char yes = 0, no = 0;
  156.     char *f_exit = "Are you sure?| %{yes} %{no}";
  157.     if (form(f_exit, &yes, &no) && no == 0)
  158.         finish();
  159. }
  160.  
  161. /*
  162.  * miscellaneous support routines
  163.  */
  164.  
  165. do_grid() {
  166.     char gridval, ok = 0, nok = 0, x;
  167.     switch (grid) {
  168.         case 8: gridval = 1; break;
  169.         case 16: gridval = 2; break;
  170.         default: gridval = 0; break;
  171.     }
  172.     x = form("Change Grid Size|  %[none:8:16]| %{ok} %{cancel}",
  173.         &gridval, &ok, &nok);
  174.     if (x == 0 || nok) return;
  175.     grid = gridval * 8;
  176.     refresh(); 
  177. }
  178.  
  179. /* print the current screen somewhere, Epson-compatible graphics mode */ 
  180.  
  181. print() {
  182.     static char grhd[] = { ESC, 'L', 0, 0 }; /* 960 bit graphics */
  183.     static char grlf[] = { ESC, 'J', 24, '\r' }; /* line feed */
  184.     static char prbuf[960];
  185.     int x, y, i, b, n, any, pixel, max;
  186.     max = min(Maxx, 960);
  187.     grhd[2] = max;
  188.     grhd[3] = max >> 8;
  189.     mouse_state(OFF);
  190.     b = 0x80;
  191.     any = 0;
  192.     for (y = 0; y < Maxy; y++) {
  193.         for (x = 0; x < max; x++) {
  194.             if (getpixel(x, y)) {
  195.                 any = 1;
  196.                 prbuf[x] |= b;
  197.             }
  198.         }
  199.         b >>= 1;
  200.         if (b == 0) { /* out it goes */
  201.             if (any) {
  202.                 prn(grhd, 4);
  203.                 prn(prbuf, max);
  204.             }
  205.             prn(grlf, 4);
  206.             b = 0x80;
  207.             any = 0;
  208.             for (x = 0; x < max; x++)
  209.                 prbuf[x] = 0;
  210.         }
  211.     }
  212.     mouse_state(ON);
  213. }
  214.  
  215. /* print the n bytes out the printer port */
  216.  
  217. prn(s, n) char *s; { while (n--) biosprint(0, *s++, 0); }
  218.  
  219. /* add the invert the selected item */
  220.  
  221. select_object(obj) {
  222.     int i;
  223.     Object *o;
  224.     if (obj == -1) { /* de-select all */
  225.         for (i = 0; i < last_object; i++) {
  226.             o = &objects[i];
  227.             if (o->select) {
  228.                 o->select = 0;
  229.                 highlight(o, 0);
  230.             }
  231.         }
  232.         slct_cnt = 0;
  233.     }
  234.     else    {
  235.         o = &objects[obj];
  236.         o->select = !o->select;
  237.         highlight(o, o->select);
  238.         slct_cnt += o->select ? 1 : -1;
  239.     }
  240. }
  241.  
  242. /* get a filename from the user, return 0 if abort */
  243.  
  244. get_name() {
  245.     return form("Path: %20s", filename);
  246. }
  247.  
  248. check_menu() {
  249.     menu_state(M_ACT, slct_cnt > 0);
  250.     menu_state(M_OBJ, slct_cnt <= 0);
  251. }
  252.  
  253. /* start to make an object by collection points */
  254.  
  255. start_make(type) {
  256.     char *s;
  257.     switch (make_obj = type) {
  258.         case BOX: s = "box: top left corner..."; break;
  259.         case ELLIPSE: s = "ellipse: top left corner..."; break;
  260.         case LINE: s = "line: one end..."; break;
  261.         case TEXT: s = "text: starting..."; break;
  262.     }
  263.     msg(s);
  264.     mouse_shape(M_CROSS);
  265.     first = 1;
  266. }
  267.  
  268. /* if enough points collected, make the object */
  269.  
  270. make_object(x, y) {
  271.     static int fx, fy;
  272.  
  273.     if (grid) snap(&x, &y);
  274.  
  275.     switch (make_obj) {
  276.     case TEXT:
  277.         *text = 0;
  278.         form("text: %20s", text);
  279.         add_object(TEXT, x, y, x + strlen(text)*8, y+8, text);
  280.         make_obj = 0;
  281.         mouse_shape(M_POINTER);
  282.         msg("");
  283.         break;
  284.     default:
  285.         if (first) {
  286.             fx = x;
  287.             fy = y;
  288.             first = 0;
  289.             line(x-3, y, x+3, y);
  290.             line(x, y-3, x, y+3);
  291.             if (make_obj == LINE)
  292.                 msg("other end...");
  293.             else    msg("bottom right corner...");
  294.         }
  295.         else    {
  296.             add_object(make_obj, fx, fy, x, y, 0L);
  297.             msg("");
  298.             make_obj = 0;
  299.             mouse_shape(M_POINTER);
  300.         }
  301.     }
  302. }
  303.  
  304. /* snap the coordinates to the nearest grid point */
  305.  
  306. snap(xp, yp) int *xp, *yp; {
  307.     int g2 = grid/2, g4 = grid/4, x = *xp, y = *yp;
  308.     x = ((x + g2) / grid) * grid;
  309.     y = ((y + g4) / g2) * g2;
  310.     msg("x %d->%d  y %d->%d", *xp, x, *yp, y);
  311.     *xp = x;
  312.     *yp = y;
  313. }
  314.  
  315. /* move, copy, or edit a figure */
  316.  
  317. action_object(x, y) {
  318.     int i, dx, dy;
  319.     Object *o;
  320.  
  321.     if (grid) snap(&x, &y);
  322.  
  323.     /* find reference point and compute distance moved */
  324.     dx = dy = (actn_obj == A_EDIT ? 0 : 10000);
  325.     for (i = 0; i < last_object; i++) {
  326.         o = &objects[i];
  327.         if (o->select) {
  328.             if (actn_obj == A_EDIT) {
  329.                 dx = max(o->r, dx);
  330.                 dy = max(o->b, dy);
  331.             }
  332.             else    {
  333.                 dx = min(o->l, dx);
  334.                 dy = min(o->t, dy);
  335.             }
  336.         }
  337.     }
  338.     dx = x - dx;
  339.     dy = y - dy;
  340.  
  341.     /* do it to all selected items, de-selecting as you go */
  342.     for (i = 0; i < last_object; i++) {
  343.         o = &objects[i];
  344.         if (o->select) {
  345.             o->select = 0;
  346.             highlight(o, 0);
  347.             switch (actn_obj) {
  348.             case A_COPY:
  349.                 highlight(o, 0);
  350.                 add_object(o->type,
  351.                     o->l + dx, o->t + dy,
  352.                     o->r + dx, o->b + dy,
  353.                     o->data);
  354.                 break;
  355.             case A_MOVE:
  356.                 draw_object(o, 0);
  357.                 o->l += dx;
  358.                 o->t += dy;
  359.                 o->r += dx;
  360.                 o->b += dy;
  361.                 draw_object(o, 1);
  362.                 break;
  363.             case A_EDIT:
  364.                 draw_object(o, 0);
  365.                 set_coords(o, 
  366.                     o->l, o->t, 
  367.                     o->r + dx, o->b + dy);
  368.                 draw_object(o, 1);
  369.                 break;
  370.             }
  371.         }
  372.     }
  373.  
  374.     /* deselect all and reset the mouse */
  375.     actn_obj = 0;
  376.     slct_cnt = 0;
  377.     mouse_shape(M_POINTER);
  378.     msg("");
  379.     check_menu();
  380. }
  381.  
  382. /* read objects from a file */
  383.  
  384. read_objects() {
  385.     int type, t, l, r, b;
  386.     FILE *f = fopen(filename, "r");
  387.     if (f != NULL) {
  388.         last_object = 0;
  389.         while (fgets(text, 100, f)) {
  390.             sscanf(text, "%c %d %d %d %d '%[^']\n", 
  391.                 &type, &l, &t, &r, &b, text);
  392.             add_object(type, l, t, r, b, text);
  393.         }
  394.         fclose(f);
  395.         msg("%d objects loaded", last_object);
  396.     }
  397.     else    msg("can't open '%s'", filename);
  398. }
  399.  
  400. /* write objects to a file */
  401.  
  402. write_objects() {
  403.     int i;
  404.     Object *o;
  405.     FILE *f;
  406.     if (*filename == 0 && !get_name())
  407.         return;
  408.     if ((f = fopen(filename, "w")) != NULL) {
  409.         for (i = 0; i < last_object; i++) {
  410.             o = &objects[i];
  411.             fprintf(f, "%c %d %d %d %d '%s'\n",
  412.                 o->type, o->l, o->t, o->r, o->b, 
  413.                 o->type == TEXT ? o->data : "");
  414.         }
  415.         fclose(f);
  416.     }
  417.     else    msg("can't write '%s'", filename);
  418. }
  419.  
  420. /* save the given string in malloc'ed memory */
  421.  
  422. char *
  423. strsave(s) char *s; {
  424.     char *malloc();
  425.     char *r = malloc(strlen(s)+1);
  426.     if (r) strcpy(r, s);
  427.     else msg("out of memory!!!");
  428.     return r;
  429. }
  430.  
  431. /* re-display all the objects on the screen */
  432.  
  433. refresh() {
  434.     int i, x, y, gy;
  435.     Object *o;
  436.     clearviewport();
  437.     setcolor(MaxColor);
  438.     if (grid) {
  439.         gy = grid/2;
  440.         for (x = grid; x < Maxx; x += grid)
  441.         for (y = gy; y < Maxy; y += gy)
  442.             putpixel(x, y, 1);
  443.     }
  444.     for (i = 0; i < last_object; i++) {
  445.         o = &objects[i];
  446.         draw_object(o, 1);
  447.         if (o->select) highlight(o, 1);
  448.     }
  449. }
  450.  
  451. /* (de)highlight the current selected item */
  452.  
  453. highlight(o, color) Object *o; {
  454.     setcolor(color);
  455.     rectangle(o->l-2, o->t-2, o->l+2, o->t+2);
  456.     rectangle(o->r-2, o->b-2, o->r+2, o->b+2);
  457. }
  458.  
  459. /* give the user some feedback */
  460.  
  461. msg(fmt, a, b, c, d) char *fmt; {
  462.     static int lastback = 0;
  463.     setfillstyle(EMPTY_FILL, 0);
  464.     bar(0, 0, lastback, 8);
  465.     sprintf(text, fmt, a, b, c, d);
  466.     setcolor(MaxColor);
  467.     outtextxy(0, 0, text);
  468.     lastback = strlen(text) * 8;
  469. }
  470.  
  471. /* 
  472.  * object handling 
  473.  */
  474.  
  475. /* see if x,y are in an object, begin looking at start + 1 */
  476.  
  477. in_object(x, y) {
  478.     static int last = 0;
  479.     int l, r, t, b;
  480.     Object *o;
  481.     int i = last+1, n = last_object;
  482.     while (n--) {
  483.         if (i >= last_object) i = 0;
  484.         o = &objects[i];
  485.         l = min(o->l, o->r);
  486.         r = max(o->l, o->r);
  487.         t = min(o->t, o->b);
  488.         b = max(o->t, o->b);
  489.         if (x >= l && x <= r && y >= t && y <= b)
  490.             return (last = i);
  491.         i++;
  492.     }
  493.     return (last = -1);
  494. }
  495.  
  496. /* add an object to the object table */
  497.  
  498. add_object(type, l, t, r, b, data) char *data; {
  499.     Object *o = &objects[last_object++];
  500.     char *s;
  501.     o->type = type;
  502.     set_coords(o, l, t, r, b);
  503.     o->select = 0;
  504.     if (type == TEXT)
  505.         o->data = strsave(data);
  506.     draw_object(o, 1);
  507. }
  508.  
  509. /* set the coordinates properly */
  510.  
  511. set_coords(o, l, t, r, b) Object *o; {
  512.     if (o->type == LINE) { /* no fixup on these */
  513.         o->l = l; 
  514.         o->t = t; 
  515.         o->r = r; 
  516.         o->b = b;
  517.     }
  518.     else    {
  519.         o->l = min(l, r);
  520.         o->t = min(t, b);
  521.         o->r = max(l, r);
  522.         o->b = max(t, b);
  523.     }
  524. }
  525.  
  526. /* draw an object on the screen */
  527.  
  528. draw_object(o, color) Object *o; {
  529.     int x, y, xrad, yrad;
  530.     setcolor(color);
  531.     switch (o->type) {
  532.     case TEXT:
  533.         x = strlen(o->data) * 8;
  534.         setfillstyle(EMPTY_FILL, 0);
  535.         bar(o->l, o->t, o->l + x, o->t + 8);
  536.         outtextxy(o->l, o->t, o->data);
  537.         break;
  538.     case BOX:
  539.         rectangle(o->l, o->t, o->r, o->b);
  540.         break;
  541.     case LINE:
  542.         line(o->l, o->t, o->r, o->b);
  543.         break;
  544.     case ELLIPSE:
  545.         x = o->l + (o->r - o->l)/2;
  546.         y = o->t + (o->b - o->t)/2;
  547.         xrad = o->r - x;
  548.         yrad = o->b - y;
  549.         ellipse(x, y, 0, 360, xrad, yrad);
  550.         break;
  551.     }
  552. }
  553.  
  554. /* delete an object */
  555.  
  556. kill_object() {
  557.     int i, j;
  558.     Object *o;
  559.     for (i = j = 0; i < last_object; i++) {
  560.         o = &objects[i];
  561.         if (o->select) {
  562.             highlight(o, 0);        
  563.             draw_object(o, 0);
  564.             o->select = 0;
  565.         }
  566.         else    {
  567.             if (i > j)
  568.                 objects[j++] = objects[i];
  569.             else    j++;
  570.         }
  571.     }
  572.     last_object = j;
  573.     slct_cnt = 0;
  574.     check_menu();
  575. }
  576.  
  577. /* start an edit on selected objects */
  578.  
  579. start_edit() {
  580.     int i;
  581.     Object *o;
  582.     /* edit the text objects now */
  583.     for (i = 0; i < last_object; i++) {
  584.         o =  &objects[i];
  585.         if (o->type == TEXT && o->select) {
  586.             o->select = 0;
  587.             highlight(o, 0);
  588.             draw_object(o, 0);
  589.             strcpy(text, o->data);
  590.             if (form("edit: %20s", text)) {
  591.                 free(o->data);
  592.                 o->data = strsave(text);
  593.                 o->r = o->l + strlen(text)*8;
  594.             }
  595.             draw_object(o, 1);
  596.             slct_cnt--;
  597.         }
  598.     }
  599.     if (slct_cnt > 0) { /* must be other stuff */
  600.         start_actn(A_EDIT);
  601.     }
  602.     check_menu();
  603. }
  604.  
  605. /* initiate an action on selected objects */
  606.  
  607. start_actn(actn) {
  608.     switch (actn) {
  609.     case A_COPY: 
  610.         msg("copy to..."); 
  611.         break;
  612.     case A_MOVE: 
  613.         msg("move to..."); 
  614.         break;
  615.     case A_EDIT:
  616.         msg("editing...");
  617.         break;
  618.     }
  619.     actn_obj = actn;
  620.     mouse_shape(M_CROSS);
  621. }
  622.