home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / programming / tcl / tclsrc / c / WimpDraw < prev    next >
Encoding:
Text File  |  1996-03-27  |  29.7 KB  |  1,212 lines

  1. /* Wimp draw window commands for tcl */
  2. /* (c) C.T.Stretch */
  3. /* Mon,25 Jul 1994 */
  4.  
  5. #include "h.WInt"
  6. #include "h.colourtrans"
  7. #include "h.draw"
  8. #include "h.osfile"
  9. #include <math.h>
  10.  
  11. #define tag_TEXT   1
  12. #define tag_PATH   2
  13. #define tag_SPRITE 5
  14. #define tag_GROUP  6
  15.  
  16. #define NEWTEXT  ckalloc(28+sizeof(struct text_data))
  17. #define NEWPATH  ckalloc(28+sizeof(struct path_data))
  18. #define NEWSPRITE(n)  ckalloc(28+sizeof(struct sprite_data)+n)
  19. #define NEWGROUP ckalloc(28+sizeof(object*))
  20. #define BOUNDINGBOX(x)  (draw_path*)((1u<<31)|(unsigned int)((char*)(x)))
  21. #define NEXTPE(e,n)   e=(draw_path_element*)((char*)e+4*n)
  22. #define SFLAGS osspriteop_USER_AREA|osspriteop_NAME
  23. #define DFS (draw_FILL_EXTERIOR_BOUNDARY|draw_FILL_FULL_INTERIOR|\
  24.              draw_FILL_INTERIOR_BOUNDARY)
  25.  
  26. typedef struct text_data
  27. { char *text;
  28.   os_coord xy;
  29.   os_colour fg,bg;
  30.   font_f f;
  31. } text_data;
  32.  
  33. typedef struct path_data
  34. { os_colour fc,oc;
  35.   int thickness;
  36.   draw_path *path;
  37.   draw_line_style *line_style;
  38.   draw_dash_pattern *dash_pattern;
  39. } path_data;
  40.  
  41. typedef struct sprite_data
  42. { char *name;
  43.   osspriteop_area *area;
  44.   int sx,sy;                /* size of sprite in pixels */
  45.   os_factors factors;
  46.   osspriteop_trans_tab trans;
  47. } sprite_data;
  48.  
  49. typedef struct object
  50. { struct object *next;
  51.   int tag,n;     /* object type, object number*/
  52.   os_box bb;     /* os units */
  53.   union
  54.   { struct text_data t;
  55.     struct path_data p;
  56.     struct sprite_data s;
  57.     struct object *g; /* linked list for group */
  58.   } data;
  59. } object;
  60.  
  61. typedef struct diagram
  62. { struct diagram *next;
  63.   struct view *viewlist;
  64.   struct object *objects;
  65.   char *name,*title,*close,*click;
  66.   tcl_menu *menu;
  67.   int x,y,n;
  68.   os_colour bg;
  69.   double xscale,yscale;
  70.   font_table ft;
  71. } diagram;
  72.  
  73. Tcl_HashTable diagramTable;
  74.  
  75. static diagram *diagramlist,*thisdiagram;
  76. static int bx,by,cx0,cx1,cy0,cy1; /*offsets and clipping rectangle for render*/
  77. static int overlap;
  78. static os_trfm trfm={{{1<<16,0},{0,1<<16},{0,0}}};
  79. static draw_line_style defstyle={0,0,0,0,0x40000,0x100,0x100,0x100,0x100};
  80. #define MITRE 0x10100000
  81. static draw_line_style roundstyle={1,1,1,0,0x40000,0x100,0x100,0x100,0x100};
  82. #define ROUND 0x10100015
  83. static os_box empty={INT_MAX,INT_MAX,INT_MIN,INT_MIN};
  84. static int esize[]={1,0,3,0,0,1,7,0,3};
  85. static char *savenumbers;
  86. static wimp_window dw=
  87. { {200,200,1200,1200},
  88.   0, 0, (wimp_w)-1, 0x7f01000f,
  89.   0x7, 0x2, 0x7, 0xFF,
  90.   0x3, 0x1, 0xc, 0x0,
  91.   {0,0,320,256},
  92.   0x13d, 0x3000, (osspriteop_area*)0x1, 64, 512,
  93.   "",
  94.   0
  95. };
  96.  
  97. /*
  98.  * dispose --
  99.  *        Dispose of an object!
  100.  *
  101.  * Results:
  102.  *      None.
  103.  *
  104.  * Side effects:
  105.  *      None.
  106.  *
  107. */
  108.  
  109. static void dispose(object *obj)  /* more to do */
  110. { ckfree(obj);
  111. }
  112.  
  113. /*
  114.  * findobj --
  115.  *
  116.  *      Loacte an object in a diagram from its object number.
  117.  *
  118.  * Results:
  119.  *      Returns the object or 0 if not found.
  120.  *
  121.  * Side effects:
  122.  *      None.
  123.  *
  124. */
  125.  
  126. static object *findobj(diagram *d,int n)
  127. { object *obj;
  128.   for(obj=d->objects;obj;obj=obj->next) if(obj->n==n) return obj;
  129.   return 0;
  130. }
  131.  
  132. /*
  133.  * enlarge  --
  134.  *
  135.  *      enlarge a bounding box to include another.
  136.  *
  137.  * Results:
  138.  *      Box old is enlarged to contain box new.
  139.  *
  140.  * Side effects:
  141.  *      None.
  142.  *
  143. */
  144.  
  145. static void enlarge(os_box *old,os_box *new)
  146. { if(new->x0<old->x0) old->x0=new->x0;
  147.   if(new->y0<old->y0) old->y0=new->y0;
  148.   if(new->x1>old->x1) old->x1=new->x1;
  149.   if(new->y1>old->y1) old->y1=new->y1;
  150. }
  151.  
  152. /*
  153.  *  to_os --
  154.  *
  155.  *      Converts a box in draw units to os units.
  156.  *
  157.  * Results:
  158.  *      Changes contents of box b.
  159.  *
  160.  * Side effects:
  161.  *      None.
  162.  *
  163. */
  164.  
  165. static void to_os(os_box *b)
  166. { b->x0=b->x0/draw_OS_UNIT;
  167.   b->y0=b->y0/draw_OS_UNIT;
  168.   b->x1=(b->x1+draw_OS_UNIT-1)/draw_OS_UNIT+4;
  169.   b->y1=(b->y1+draw_OS_UNIT-1)/draw_OS_UNIT+4;
  170. }
  171.  
  172. /*
  173.  * mp_to_os --
  174.  *
  175.  *      Converts a box from millipoints to os units.
  176.  *
  177.  * Results:
  178.  *      Changes content of box b.
  179.  *
  180.  * Side effects:
  181.  *      None.
  182.  *
  183. */
  184.  
  185. void mp_to_os(os_box *b)
  186. { b->x0=b->x0/font_OS_UNIT;
  187.   b->y0=b->y0/font_OS_UNIT;
  188.   b->x1=(b->x1+font_OS_UNIT-1)/font_OS_UNIT;
  189.   b->y1=(b->y1+font_OS_UNIT-1)/font_OS_UNIT;
  190. }
  191.  
  192. /*
  193.  *  render --
  194.  *
  195.  *      Render an object if it intersects the box given by cx0 to cy1.
  196.  *      Uses offsets held in bx and by.
  197.  *
  198.  * Results:
  199.  *      None.
  200.  *
  201.  * Side effects:
  202.  *      None.
  203.  *
  204. */
  205.  
  206. static void render(object *b)
  207. { int i;
  208.   object *g;
  209.   if((cx0<=b->bb.x1)&&(cx1>=b->bb.x0)&&(cy0<=b->bb.y1)&&(cy1>=b->bb.y0))
  210.   switch(b->tag)
  211.   {  case tag_TEXT:colourtrans_set_font_colours(b->data.t.f,b->data.t.bg,
  212.                      b->data.t.fg,14,0,0,0);
  213.                    font_paint(b->data.t.f,b->data.t.text,font_OS_UNITS,
  214.                      bx+b->data.t.xy.x,by+b->data.t.xy.y,0,0,0);
  215.                    break;
  216.      case tag_PATH:if(b->data.p.fc!=os_COLOUR_TRANSPARENT)
  217.                    { colourtrans_set_gcol(b->data.p.fc,
  218.                        colourtrans_SET_FG,os_ACTION_OVERWRITE,&i);
  219.                      draw_fill(b->data.p.path,0,&trfm,0);
  220.                    }
  221.                    if(b->data.p.oc!=os_COLOUR_TRANSPARENT)
  222.                    { colourtrans_set_gcol(b->data.p.oc,
  223.                        colourtrans_SET_FG,os_ACTION_OVERWRITE,&i);
  224.                      draw_stroke(b->data.p.path,DFS,&trfm,0,b->data.p.thickness,
  225.                        b->data.p.line_style,b->data.p.dash_pattern);
  226.                    }
  227.                    break;
  228.    case tag_SPRITE:xosspriteop_put_sprite_scaled(SFLAGS,
  229.                       b->data.s.area,(osspriteop_id)b->data.s.name,
  230.                       bx+b->bb.x0,by+b->bb.y0,os_ACTION_USE_MASK,
  231.                       &(b->data.s.factors),&(b->data.s.trans));
  232.                    break;
  233.     case tag_GROUP:for(g=b->data.g;g;g=g->next) render(g);
  234.                    break;
  235.   }
  236. }
  237.  
  238. /*
  239.  *  diagram_redraw --
  240.  *
  241.  *  Called in response to a redraw request.
  242.  *  If the window is a draw window it renders the required part of
  243.  *  the diagram.
  244.  *
  245.  * Results:
  246.  *      Return TRUE for a draw window.
  247.  *
  248.  * Side effects:
  249.  *      Uses variable bx,by,cx0-cy1 to pass infomation to render.
  250.  *
  251. */
  252.  
  253. bool diagram_redraw(void)
  254. { diagram *d;
  255.   view *v;
  256.   object *b;
  257.   wimp_draw *wd=(wimp_draw*)█
  258.   for(d=diagramlist;d;d=d->next) for(v=d->viewlist;v;v=v->next)
  259.   if(v->w==block.redraw.w)
  260.   { int more=wimp_redraw_window(wd);
  261.     int i;
  262.     bx=wd->box.x0-wd->xscroll;
  263.     by=wd->box.y1-wd->yscroll;
  264.     trfm.entries[2][0]=bx*draw_OS_UNIT;trfm.entries[2][1]=by*draw_OS_UNIT;
  265.     while(more)
  266.     { colourtrans_set_gcol(d->bg,colourtrans_SET_BG,os_ACTION_OVERWRITE,&i);
  267.       os_writec(os_VDU_CLG);
  268.       cx0=wd->clip.x0-bx;cx1=wd->clip.x1-bx;
  269.       cy0=wd->clip.y0-by;cy1=wd->clip.y1-by;
  270.       for(b=d->objects;b;b=b->next) render(b);
  271.       more=wimp_get_rectangle(wd);
  272.     }
  273.     return TRUE;
  274.   }
  275.   return FALSE;
  276. }
  277.  
  278. /*
  279.  *  force_redraw --
  280.  *
  281.  *      Forces part of all windows of a diagram to be redrawn.
  282.  *
  283.  * Results:
  284.  *      None.
  285.  *
  286.  * Side effects:
  287.  *      None.
  288.  *
  289. */
  290.  
  291. static void force_redraw(diagram *d,os_box *bb)
  292. { view *v;
  293.   for(v=d->viewlist;v;v=v->next)
  294.     wimp_force_redraw(v->w,bb->x0,bb->y0,bb->x1,bb->y1);
  295. }
  296.  
  297. /*
  298.  *  diagram_click --
  299.  *
  300.  *      Called in response to a mouse click from wimp_poll
  301.  *      (not a menu click)
  302.  *      If the click was in a draw window it sets the %b %w %x and %y
  303.         substitution and calls any attached script.
  304.  *
  305.  * Results:
  306.  *      Returns TRUE if the click was in a draw window.
  307.  *
  308.  * Side effects:
  309.  *      None.
  310.  *
  311. */
  312.  
  313. bool diagram_click(void)
  314. { diagram *d;
  315.   view *v;
  316.   for(d=diagramlist;d;d=d->next) for(v=d->viewlist;v;v=v->next)
  317.   if(v->w==block.pointer.w)
  318.   { if(d->click)
  319.     { wimp_window_state ws;
  320.       setbutton();
  321.       ws.w=v->w;
  322.       wimp_get_window_state(&ws);
  323.       sprintf(substr['x'-'a'],"%g",
  324.         (block.pointer.pos.x-ws.visible.x0+ws.xscroll)/d->xscale*draw_OS_UNIT);
  325.       sprintf(substr['y'-'a'],"%g",
  326.         (block.pointer.pos.y-ws.visible.y1+ws.yscroll)/d->yscale*draw_OS_UNIT);
  327.       substr['w'-'a']=d->name;
  328.       checkEval(d->click,"diagram click");
  329.     }
  330.     return TRUE;
  331.   }
  332.   return FALSE;
  333. }
  334.  
  335. /*
  336.  *  diagram_menu --
  337.  *
  338.  *      Called after a menu click is returned from wimp_poll
  339.  *      If the click was in a draw window it sets the %w
  340.  *      substitution and returns the menu.
  341.  *
  342.  * Results:
  343.  *      Returns FALSE if the click was not in a draw window.
  344.  *      Returns menu_NONE if the window does not have a menu.
  345.  *      Otherwise return the menu.
  346.  *
  347.  * Side effects:
  348.  *      None.
  349.  *
  350. */
  351.  
  352. tcl_menu *diagram_menu(void)
  353. { diagram *d;
  354.   view *v;
  355.   for(d=diagramlist;d;d=d->next) for(v=d->viewlist;v;v=v->next)
  356.   if(v->w==block.pointer.w)
  357.   { substr['w'-'a']=d->name;
  358.     return d->menu;
  359.   }
  360.   return FALSE;
  361. }
  362.  
  363. /*
  364.  * dropview  --
  365.  *
  366.  *      Deletes a window open on a diagram. Removes it from the list
  367.  *      of open windows for the diagram.
  368.  *
  369.  * Results:
  370.  *      None.
  371.  *
  372.  * Side effects:
  373.  *      None.
  374.  *
  375. */
  376.  
  377. static void dropview(view *v,diagram *d)
  378. { wimp_delete_window(v->w);
  379.   unlink(&(d->viewlist),v);
  380.   ckfree(v);
  381. }
  382.  
  383. /*
  384.  *  object_free --
  385.  *
  386.  *      Free the memory used by a list of objects.
  387.  *
  388.  * Results:
  389.  *      None.
  390.  *
  391.  * Side effects:
  392.  *      None.
  393.  *
  394. */
  395.  
  396. static void object_free(object *obj)
  397. { object *next;
  398.   for(;obj;obj=next)
  399.   { next=obj->next;
  400.     switch(obj->tag)
  401.     {  case tag_TEXT:ckfree(obj->data.t.text);
  402.                      break;
  403.        case tag_PATH:ckfree(obj->data.p.path);
  404.                      break;
  405.      case tag_SPRITE:break;
  406.       case tag_GROUP:object_free(obj->data.g);
  407.                      break;
  408.     }
  409.     ckfree(obj);
  410.   }
  411. }
  412.  
  413. /*
  414.  * diagram_free --
  415.  *
  416.  *      Free the memory used by a diagram.
  417.  *
  418.  * Results:
  419.  *      None.
  420.  *
  421.  * Side effects:
  422.  *      None.
  423.  *
  424. */
  425.  
  426. static void diagram_free(diagram *d)
  427. { ckfree(d->name);
  428.   object_free(d->objects);
  429.   if(d->title!=progname) ckfree(d->title);
  430.   ckfree(d->close);ckfree(d->click);
  431.   if(d->menu!=menu_NONE) menu_free(d->menu);
  432.   release_fonts(&(d->ft));
  433.   ckfree(d);
  434.   substr['w'-'a']=0;
  435. }
  436.  
  437. /*
  438.  *  diagram_close --
  439.  *
  440.  *      Called after a close event from wimp_poll.
  441.  *      If it is for a draw window it closes the window.
  442.  *      If it is the last window open on the diagram it disposes
  443.  *      of the diagram.
  444.  *
  445.  * Results:
  446.  *      True for a draw window.
  447.  *
  448.  * Side effects:
  449.  *      None.
  450.  *
  451. */
  452.  
  453. bool diagram_close(void)
  454. { diagram *d;
  455.   view *v;
  456.   Tcl_HashEntry *p;
  457.   int n;
  458.   for(d=diagramlist;d;d=d->next) for(v=d->viewlist;v;v=v->next)
  459.   if(v->w==block.close.w)
  460.   { if(d->viewlist->next) { dropview(v,d);return TRUE; }
  461.     else
  462.     { if(d->close)
  463.       { substr['w'-'a']=d->name;
  464.         checkEval(d->close,"diagram close");
  465.         substr['w'-'a']=0;
  466.         n=getconstant(w_Interp->result);
  467.         if(n==1) return TRUE;
  468.         if(n!=0) {dropview(v,d);return TRUE;}
  469.       }
  470.       dropview(v,d);
  471.       p=Tcl_FindHashEntry(&diagramTable,d->name);
  472.       if(!p)
  473.       { msg("Odd close request",0,0);
  474.         return TRUE;
  475.       }
  476.       Tcl_DeleteHashEntry(p);
  477.       unlink(&diagramlist,d);
  478.       diagram_free(d);
  479.       return TRUE;
  480.     }
  481.   }
  482.   return FALSE;
  483. }
  484.  
  485. /*
  486.  *  object_size --
  487.  *
  488.  *      Returns the size in bytes of a draw object.
  489.  *      Used to fill in the size field when converting an object
  490.  *      tree to a draw file for saving.
  491.  *
  492.  * Results:
  493.  *      The size.
  494.  *
  495.  * Side effects:
  496.  *      None.
  497.  *
  498. */
  499.  
  500. int object_size(object *obj)
  501. { int m=24,n;
  502.   osspriteop_header *hdr;
  503.   switch(obj->tag)
  504.   {  case tag_TEXT:m+=(strlen(obj->data.t.text)/4)*4+32;
  505.                    break;
  506.      case tag_PATH:{ path_data *p=&obj->data.p;
  507.                      draw_path_element *e;
  508.                      m+=20;
  509.                      for(e=p->path->elements;e->tag;NEXTPE(e,n))
  510.                      { n=esize[e->tag];
  511.                        m+=4*n;
  512.                      }
  513.                    }
  514.                    break;
  515.    case tag_SPRITE:hdr=osspriteop_select_sprite(osspriteop_USER_AREA,
  516.                                                 obj->data.s.area,
  517.                                       (osspriteop_id)obj->data.s.name);
  518.                    m+=hdr->size;
  519.                    break;
  520.     case tag_GROUP:m+=12;
  521.                    for(obj=obj->data.g;obj;obj=obj->next) m+=object_size(obj);
  522.                    break;
  523.   }
  524.   return m;
  525. }
  526.  
  527. /*
  528.  *  object_save--
  529.  *
  530.  *       Writes the draw file representation of obj to file f.
  531.  *
  532.  * Results:
  533.  *      None.
  534.  *
  535.  * Side effects:
  536.  *      None.
  537.  *
  538. */
  539.  
  540. void object_save(object *obj,FILE *f)
  541. { int n=object_size(obj);
  542.   int size[2];
  543.   os_box box;
  544.   osspriteop_header *hdr;
  545.   fwrite(&obj->tag,4,1,f);
  546.   fwrite(&n,4,1,f);
  547.   box.x0=obj->bb.x0<<8;
  548.   box.x1=obj->bb.x1<<8;
  549.   box.y0=obj->bb.y0<<8;
  550.   box.y1=obj->bb.y1<<8;
  551.   fwrite(&box,4,4,f);
  552.   switch(obj->tag)
  553.   {  case tag_TEXT:{ text_data *t=&obj->data.t;
  554.                      fwrite(&t->fg,4,2,f);
  555.                      n=savenumbers[t->f];
  556.                      fwrite(&n,4,1,f);
  557.                      font_read_defn(t->f,size,size+1,0,0,0,0);
  558.                      size[0]*=40;size[1]*=40;
  559.                      fwrite(size,4,2,f);
  560.                      size[0]=t->xy.x*256;;size[1]=t->xy.y*256;
  561.                      fwrite(size,4,2,f);
  562.                      fputs(t->text,f);
  563.                      n=strlen(t->text);
  564.                      do {fputc(0,f);n++;} while(n%4);
  565.                    }
  566.                    break;
  567.      case tag_PATH:{ path_data *p=&obj->data.p;
  568.                      draw_path_element *e;
  569.                      fwrite(&p->fc,4,3,f);
  570.                      n=(p->line_style->join_style)?ROUND:MITRE;
  571.                      fwrite(&n,4,1,f);
  572.                      for(e=p->path->elements;e->tag;NEXTPE(e,n))
  573.                      { n=esize[e->tag];
  574.                        fwrite(e,4,n,f);
  575.                      }
  576.                      fwrite(e,4,1,f);
  577.                    }
  578.                    break;
  579.    case tag_SPRITE:hdr=osspriteop_select_sprite(osspriteop_USER_AREA,
  580.                                                 obj->data.s.area,
  581.                                       (osspriteop_id)obj->data.s.name);
  582.                    fwrite(hdr,hdr->size,1,f);
  583.                    break;
  584.     case tag_GROUP:fputs("            ",f);
  585.                    for(obj=obj->data.g;obj;obj=obj->next) object_save(obj,f);
  586.                    break;
  587.   }
  588. }
  589.  
  590. /*
  591.  * diagram_save --
  592.  *
  593.  *      Save a draw object to a named file.
  594.  *
  595.  * Results:
  596.  *      None.
  597.  *
  598.  * Side effects:
  599.  *      None.
  600.  *
  601. */
  602.  
  603. void diagram_save(diagram *d,char *fname)
  604. { FILE *f=fopen(fname,"wb");
  605.   int header[]={0x77617244,201,0};
  606.   int i,j,used,ftsize=0;
  607.   os_box bb=empty;
  608.   object *obj;
  609.   char buf[256];
  610.   char *names[256];
  611.   char numbers[256];
  612.   int nextname=1;
  613.   if(!f) return;
  614.   fwrite(header,4,3,f);
  615.   fprintf(f,"%-12.12s",progname);
  616.   for(obj=d->objects;obj;obj=obj->next) enlarge(&bb,&obj->bb);
  617.   fwrite(&bb,4,4,f);
  618.   for(i=1;i<256;i++) if(d->ft.used[i])
  619.   { xfont_read_identifier(i,0,&used);
  620.     if(used>256) strcpy(buf,"Trinity.Medium");
  621.     else xfont_read_identifier(i,(byte*)buf,0);
  622.     for(j=1;j<nextname;j++) if(!strcmp(buf,names[j])) break;
  623.     if(j==nextname)
  624.     { names[nextname++]=scopy(buf);
  625.       ftsize+=((1+strlen(buf))/4)*4+4;
  626.     }
  627.     numbers[i]=j;
  628.   }
  629.   savenumbers=numbers;
  630.   if(ftsize>0)
  631.   { i=0;ftsize+=8;
  632.     fwrite(&i,4,1,f);
  633.     fwrite(&ftsize,4,1,f);
  634.     for(i=1;i<nextname;i++)
  635.     { fputc(i,f);
  636.       fputs(names[i],f);
  637.       j=1+strlen(names[i]);
  638.       do {fputc(0,f);j++;} while(j%4);
  639.       ckfree(names[i]);
  640.     }
  641.   }
  642.   for(obj=d->objects;obj;obj=obj->next) object_save(obj,f);
  643.   sprintf(w_Interp->result,"1");
  644.   fclose(f);
  645.   xosfile_set_type(fname,0xAFF);
  646. }
  647.  
  648. /*
  649.  *  getdbl --
  650.  *
  651.  *      Converts a shade string representation of a length to
  652.  *      double (in inches).
  653.  *
  654.  * Results:
  655.  *      The length in inches.
  656.  *
  657.  * Side effects:
  658.  *      None.
  659.  *
  660. */
  661.  
  662. static double getdbl(char *s,double f)
  663. { double d=strtod(s,&s);
  664.   switch(*s)
  665.   { case 'i':break;
  666.     case 'm':d=d/25.4;break;
  667.     case 'c':d=d/2.54;break;
  668.     case 'p':d=d/72;break;
  669.     case 'd':d=d/draw_INCH;break;
  670.     default:d=d/os_INCH;
  671.   }
  672.   return d*f;
  673. }
  674.  
  675. /*
  676.  *  getsize --
  677.  *
  678.  *      Converts a shade length to int (in inches).
  679.  *
  680.  * Results:
  681.  *      Length in inches.
  682.  *
  683.  * Side effects:
  684.  *      None.
  685.  *
  686. */
  687.  
  688. int getsize(char *s,double f) {return (int)getdbl(s,f);}
  689.  
  690. /*
  691.  *  setpagesize --
  692.  *
  693.  *      Sets the page size of diagram d from the string p.
  694.  *
  695.  * Results:
  696.  *      None.
  697.  *
  698.  * Side effects:
  699.  *      None.
  700.  *
  701. */
  702.  
  703. static void setpagesize(diagram *d,char *p)
  704. { if(!p) p="A4";
  705.   if(p[0]=='A'||p[0]=='a')
  706.   { char *q;
  707.     double n=strtod(p+1,&q);
  708.     d->x=(int)(pow(2,-.25-0.5*n)*(18000/2.54));
  709.     d->y=(int)(pow(2,.25-0.5*n)*(18000/2.54));
  710.     if(*q=='l') { int t; t=d->x;d->x=d->y;d->y=t; }
  711.   }
  712.   else
  713.   { char *q=strchr(p,'x');
  714.     if(q) { d->x=getsize(p,180);d->y=getsize(q+1,180);}
  715.   }
  716.   if(d->x<100) d->x=100;
  717.   if(d->y<100) d->y=100;
  718. }
  719.  
  720. /*
  721.  *  getpoint --
  722.  *
  723.  *      Reads a shade point (number,number) and sets xy.
  724.  *      The numbers in diagram units, and are converted
  725.  *      to draw units.
  726.  *
  727.  *
  728.  * Results:
  729.  *      Return TRUE if the point parses correctly.
  730.  *      points p to the character after the end ).
  731.  *
  732.  * Side effects:
  733.  *      None.
  734.  *
  735. */
  736.  
  737. static bool getpoint( char **p,os_coord *xy)
  738. { char *q=*p;
  739.   if(*q++!='(') return FALSE;
  740.   xy->x=(int)(strtod(q,&q)*thisdiagram->xscale);
  741.   if(*q++!=',') return FALSE;
  742.   xy->y=(int)(strtod(q,&q)*thisdiagram->yscale);
  743.   if(*q++!=')') return FALSE;
  744.   *p=q;
  745.   return TRUE;
  746. }
  747.  
  748. /*
  749.  *  getmpoint --
  750.  *      Reads a shade point (number,number) and sets xy.
  751.  *      The numbers in diagram units, and are converted
  752.  *      to  millipoints.
  753.  *
  754.  * Results:
  755.  *      Return TRUE if the point parses correctly.
  756.  *      points p to the character after the end ).
  757.  *
  758.  * Side effects:
  759.  *      None.
  760.  *
  761. */
  762.  
  763. static bool getmpoint( char **p,os_coord *xy)
  764. { char *q=*p;
  765.   if(*q++!='(') return FALSE;
  766.   xy->x=(int)(strtod(q,&q)*thisdiagram->xscale*font_OS_UNIT/draw_OS_UNIT);
  767.   if(*q++!=',') return FALSE;
  768.   xy->y=(int)(strtod(q,&q)*thisdiagram->yscale*font_OS_UNIT/draw_OS_UNIT);
  769.   if(*q++!=')') return FALSE;
  770.   *p=q;
  771.   return TRUE;
  772. }
  773.  
  774. /*
  775.  *  getpath --
  776.  *
  777.  *      Constructs a draw_path from a w textual description
  778.  *
  779.  * Results:
  780.  *      A draw path or 0 for a bad description.
  781.  *
  782.  * Side effects:
  783.  *      None.
  784.  *
  785. */
  786.  
  787. #define TAG(x) *((int*)&(e->tag))=x /* set tag and reserved bytes */
  788.  
  789. static draw_path *getpath(char *p)
  790. { char *q;
  791.   int n=2;
  792.   draw_path *dp;
  793.   draw_path_element *e;
  794.   bool started=TRUE;
  795.   for(q=p;*q;q++) switch(*q)
  796.   { case '(': n+=3;break;
  797.     case '<': n-=2;break;
  798.     case '.': n++;break;
  799.   }
  800.   if(n<=2) goto badpath;
  801.   dp=ckalloc(n*sizeof(int));
  802.   e=dp->elements;
  803.   q=p;
  804.   TAG(draw_MOVE_TO);
  805.   if(!getpoint(&q,&(e->data.move_to))) goto badpath;
  806.   NEXTPE(e,3);
  807.   for(;*q;) switch(*q)
  808.   { case '(':TAG(draw_MOVE_TO);
  809.              if(!getpoint(&q,&(e->data.move_to))) goto badpath;
  810.              NEXTPE(e,3);started=TRUE;
  811.              break;
  812.     case '-':if(!started) goto badpath;
  813.              TAG(draw_LINE_TO);q++;
  814.              if(!getpoint(&q,&(e->data.line_to))) goto badpath;
  815.              NEXTPE(e,3);
  816.              break;
  817.     case '<':if(!started) goto badpath;
  818.              TAG(draw_BEZIER_TO);q++;
  819.              if(!getpoint(&q,&(e->data.bezier_to[0]))) goto badpath;
  820.              if(!getpoint(&q,&(e->data.bezier_to[1]))) goto badpath;
  821.              if(*q++!='>') goto badpath;
  822.              if(!getpoint(&q,&(e->data.bezier_to[2]))) goto badpath;
  823.              NEXTPE(e,7);
  824.              break;
  825.     case '.':TAG(draw_CLOSE_LINE);q++;
  826.              NEXTPE(e,1);started=FALSE;
  827.              break;
  828.      default:goto badpath;
  829.   }
  830.   TAG(draw_END_PATH);
  831.   e->data.end_path=4*n;
  832.   return dp;
  833.   badpath:return wrong0("bad path description: ",p);
  834. }
  835.  
  836. /*
  837.  *  setfactors --
  838.  *
  839.  *      Set the scaling factors for a sprite object
  840.  *
  841.  * Results:
  842.  *      None.
  843.  *
  844.  * Side effects:
  845.  *      None.
  846.  *
  847. */
  848.  
  849. static void setfactors(object *obj)
  850. {   obj->data.s.factors.xdiv=obj->data.s.sx<<current_ex;
  851.     obj->data.s.factors.ydiv=obj->data.s.sy<<current_ey;
  852. }
  853.  
  854. /*
  855.  *  settrans --
  856.  *
  857.  *      Set the pixel translation table for a sprite
  858.  *
  859.  * Results:
  860.  *      TRUE if OK
  861.  *
  862.  * Side effects:
  863.  *      None.
  864.  *
  865. */
  866.  
  867. bool settrans(object *obj)
  868. { os_error *e;
  869.   os_mode mode;
  870.   int log2bpp;
  871.   int psize;
  872.   e=xosspriteop_read_palette_size(SFLAGS,
  873.        obj->data.s.area,(osspriteop_id)obj->data.s.name,&psize,0,&mode);
  874.   if(e) return 0;
  875.   if(!psize) xos_read_mode_variable(mode,os_MODEVAR_LOG2_BPP,&log2bpp,0);
  876.   if(!psize&&log2bpp<3)
  877.   { int i,p[20];
  878.     xwimp_read_palette((os_palette*)p);
  879.     for(i=0;i<16;i++) p[i]=p[i]|(p[i]>>4);
  880.     e=xcolourtrans_generate_table(mode,
  881.       (os_palette*)p,(os_mode)-1,(os_palette*)-1,&(obj->data.s.trans),0,0,0,0);
  882.   }
  883.   else
  884.   e=xcolourtrans_generate_table_for_sprite(obj->data.s.area,
  885.   (osspriteop_id)obj->data.s.name,
  886.        (os_mode)-1,(os_palette*)-1,&(obj->data.s.trans),0,0,0,0);
  887.   return !e;
  888. }
  889.  
  890. static void modechange(object *obj)
  891. { for(;obj;obj=obj->next) switch (obj->tag)
  892.   { case tag_SPRITE:settrans(obj);setfactors(obj);break;
  893.     case tag_GROUP:modechange(obj->data.g);
  894.   }
  895. }
  896.  
  897. void diagram_modechange(void)
  898. { diagram *d;
  899.   modeinfo();
  900.   for(d=diagramlist;d;d=d->next) modechange(d->objects);
  901. }
  902.  
  903. static void palettechange(object *obj)
  904. { for(;obj;obj=obj->next) switch (obj->tag)
  905.   { case tag_SPRITE:settrans(obj);break;
  906.     case tag_GROUP:palettechange(obj->data.g);
  907.   }
  908. }
  909.  
  910. void diagram_palettechange(void)
  911. { diagram *d;
  912.   for(d=diagramlist;d;d=d->next) palettechange(d->objects);
  913. }
  914.  
  915. /*
  916.  *  getobject --
  917.  *
  918.  *      Construct a draw object from a textual description
  919.  *
  920.  * Results:
  921.  *      An object or 0 for a bad description.
  922.  *
  923.  * Side effects:
  924.  *      None.
  925.  *
  926. */
  927.  
  928. static object *getobject(int nargs,char **argv)
  929. { object *obj;
  930.   int i;
  931.   /* *********** get text *************** */
  932.   if(!strcmp(argv[0],"text"))
  933.   { char *p;
  934.     font_scan_block fsb[1];
  935.     char *place=0;
  936.     if(nargs<3) {return 0;}
  937.     obj=NEWTEXT;
  938.     obj->tag=tag_TEXT;
  939.     obj->data.t.f=0;
  940.     obj->data.t.bg=thisdiagram->bg;
  941.     obj->data.t.fg=os_COLOUR_BLACK;
  942.     p=argv[1];
  943.     if(!getmpoint(&p,&(obj->data.t.xy))) return wrong0("bad point: ",p);
  944.     obj->data.t.text=scopy(argv[2]);
  945.     for(i=3;i<nargs;i++)
  946.     { if(argv[i][0]=='-') switch(argv[i][1])
  947.       { case 'f':obj->data.t.f=find_font(argv[i]+1,16*72,&(thisdiagram->ft));
  948.                  continue;
  949.         case 'c':obj->data.t.fg=getcolour(argv[i]+2);continue;
  950.         case 'b':obj->data.t.bg=getcolour(argv[i]+2);continue;
  951.         case 'o':place=argv[i]+2;continue;
  952.       }
  953.       return wrong0("bad path parameter: ",argv[i]);
  954.     }
  955.     if(!obj->data.t.f) obj->data.t.f=find_font("fn",320,&(thisdiagram->ft));
  956.     fsb->space.x=0;fsb->space.y=0;fsb->letter.x=0;fsb->letter.y=0;
  957.     fsb->split_char=-1;
  958.     font_scan_string(obj->data.t.f,obj->data.t.text,
  959.       font_RETURN_BBOX|font_GIVEN_BLOCK|font_GIVEN_FONT,
  960.         INT_MAX,INT_MAX,fsb,0,0,0,0,0,0);
  961.     obj->bb=fsb->bbox;
  962.     if(place) for(;*place;place++) switch(*place)
  963.     { case 't':obj->data.t.xy.y-=obj->bb.y1;break;
  964.       case 'm':obj->data.t.xy.y-=(obj->bb.y0+obj->bb.y1)/2;break;
  965.       case 'b':obj->data.t.xy.y-=obj->bb.y0;break;
  966.       case 'r':obj->data.t.xy.x-=obj->bb.x1;break;
  967.       case 'c':obj->data.t.xy.x-=(obj->bb.x0+obj->bb.x1)/2;break;
  968.       case 'l':obj->data.t.xy.x-=obj->bb.x0;break;
  969.     }
  970.     mp_to_os(&(obj->bb));
  971.     obj->data.t.xy.x/=font_OS_UNIT;obj->data.t.xy.y/=font_OS_UNIT;
  972.     obj->bb.x0+=obj->data.t.xy.x;
  973.     obj->bb.y0+=obj->data.t.xy.y;
  974.     obj->bb.x1+=obj->data.t.xy.x;
  975.     obj->bb.y1+=obj->data.t.xy.y;
  976.     return obj;
  977.   }
  978.   /* *********** get a path *************** */
  979.   if(!strcmp(argv[0],"path"))
  980.   { draw_path *dp;
  981.     if(nargs<2) {return 0;}
  982.     dp=getpath(argv[1]);
  983.     if(!dp) return 0;
  984.     obj=NEWPATH;
  985.     obj->tag=tag_PATH;
  986.     obj->data.p.path=dp;
  987.     obj->data.p.fc=os_COLOUR_TRANSPARENT;obj->data.p.oc=os_COLOUR_BLACK;
  988.     obj->data.p.thickness=0;
  989.     obj->data.p.line_style=0;
  990.     obj->data.p.dash_pattern=0;
  991.     for(i=2;i<nargs;i++)
  992.     { if(argv[i][0]=='-') switch(argv[i][1])
  993.       { case 'i':obj->data.p.fc=getcolour(argv[i]+2);continue;
  994.         case 'c':obj->data.p.oc=getcolour(argv[i]+2);continue;
  995.         case 't':obj->data.p.thickness=getsize(argv[i]+2,draw_INCH);continue;
  996.         case 'r':obj->data.p.line_style=&roundstyle;continue;
  997.       }
  998.       return wrong0("bad path parameter: ",argv[i]);
  999.     }
  1000.     if(obj->data.p.thickness&&!obj->data.p.line_style)
  1001.       obj->data.p.line_style=&defstyle;
  1002.     draw_process_path(dp,
  1003.       DFS|draw_FILL_FLATTEN|draw_FILL_THICKEN|draw_FILL_REFLATTEN,
  1004.       0,0,obj->data.p.thickness,obj->data.p.line_style,
  1005.       obj->data.p.dash_pattern,BOUNDINGBOX(&(obj->bb)));
  1006.     to_os(&(obj->bb));
  1007.     return obj;
  1008.   }
  1009.   /* *********** get a sprite *************** */
  1010.   if(!strcmp(argv[0],"sprite"))
  1011.   { char *p;
  1012.     int x,y;
  1013.     int size;
  1014.     osspriteop_area *a;
  1015.     if(nargs<3)
  1016.        return wrong0(WNA,"w_draw <name> sprite <name> <point> ?<point>? ");
  1017.     p=scopy(argv[1]);
  1018.     a=spriteinfo(p,&x,&y,TRUE);
  1019.     if((int)a<=1) return wrong0("Sprite not found: ",p);
  1020.     xcolourtrans_generate_table_for_sprite(a,(osspriteop_id)p,
  1021.        (os_mode)-1,(os_palette*)-1,0,0,0,0,&size);
  1022.     obj=NEWSPRITE(size);
  1023.     obj->tag=tag_SPRITE;
  1024.     obj->data.s.name=p;
  1025.     obj->data.s.area=a;
  1026.     if(!settrans(obj)) return 0;
  1027.     p=argv[2];
  1028.     if(!getpoint(&p,(os_coord*)&(obj->bb)))
  1029.              return wrong0("bad point ",argv[2]);
  1030.     if(nargs==3)
  1031.     { to_os(&(obj->bb));
  1032.       obj->bb.x1=obj->bb.x0+x;
  1033.       obj->bb.y1=obj->bb.y0+y;
  1034.     }
  1035.     else
  1036.     { p=argv[3];
  1037.       if(!getpoint(&p,(os_coord*)&(obj->bb.x1)))
  1038.              return wrong0("bad point ",argv[3]);
  1039.       to_os(&(obj->bb));
  1040.     }
  1041.     a=spriteinfo(argv[1],&x,&y,FALSE);
  1042.     obj->data.s.sx=x;obj->data.s.sy=y;
  1043.     obj->data.s.factors.xmul=obj->bb.x1-obj->bb.x0;
  1044.     obj->data.s.factors.ymul=obj->bb.y1-obj->bb.y0;
  1045.     setfactors(obj);
  1046.     return obj;
  1047.   }
  1048.   /* *********** get a group **************** */
  1049.   if(!strcmp(argv[0],"group"))
  1050.   { object *gobj;
  1051.     obj=NEWGROUP;
  1052.     obj->tag=tag_GROUP;
  1053.     obj->data.g=0;
  1054.     obj->bb=empty;
  1055.     for(i=1;i<nargs;i++)
  1056.     { int nitems;
  1057.       char **items;
  1058.       if(Tcl_SplitList(w_Interp,argv[i],&nitems,&items)) return 0;
  1059.       gobj=getobject(nitems,items);
  1060.       ckfree(items);
  1061.       if(!gobj) { ckfree(obj);return 0;}
  1062.       gobj->next=obj->data.g;
  1063.       obj->data.g=gobj;
  1064.       enlarge(&(obj->bb),&(gobj->bb));
  1065.     }
  1066.     return obj;
  1067.   }
  1068.   return wrong0("unknown w_draw command: ",argv[0]);
  1069. }
  1070.  
  1071. /*
  1072.  *  w_DrawCmd --
  1073.  *
  1074.  *      Implements the w_draw command.
  1075.  *
  1076.  * Results:
  1077.  *      A standard Tcl result.
  1078.  *
  1079.  * Side effects:
  1080.  *      None.
  1081.  *
  1082. */
  1083.  
  1084. int w_DrawCmd(ClientData dummy,Tcl_Interp *interp,int argc,char **argv)
  1085. { diagram *d;
  1086.   Tcl_HashEntry *p;
  1087.   object *obj;
  1088.   if(argc<3) return wrong(WNA,"w_draw <name> <command> ...");
  1089. /* *********** create a diagram *************** */
  1090.   if(!strcmp(argv[2],"create"))
  1091.   { int new=0,m;
  1092.     char *page=0;
  1093.     p=Tcl_CreateHashEntry(&diagramTable,argv[1],&new);
  1094.     if(!new) return wrong("diagram already exists: ",argv[1]);
  1095.     d=ckcalloc(sizeof(diagram));
  1096.     d->name=scopy(argv[1]);
  1097.     d->title=progname;
  1098.     d->bg=os_COLOUR_WHITE;
  1099.     d->menu=menu_NONE;
  1100.     d->xscale=draw_INCH;d->yscale=draw_INCH;
  1101.     for(m=3;m<argc-1;m++)
  1102.     { if(!strcmp(argv[m],"-bg"))
  1103.       { d->bg=getcolour(argv[++m]);
  1104.         continue;
  1105.       }
  1106.       if(!strcmp(argv[m],"-title")) { d->title=scopy(argv[++m]);continue;}
  1107.       if(!strcmp(argv[m],"-close")) { d->close=scopy(argv[++m]);continue;}
  1108.       if(!strcmp(argv[m],"-click")) { d->click=scopy(argv[++m]);continue;}
  1109.       if(!strcmp(argv[m],"-page")) { page=argv[++m];continue;}
  1110.       if(!strcmp(argv[m],"-xscale"))
  1111.       { d->xscale=getdbl(argv[++m],draw_INCH);
  1112.         continue;
  1113.       }
  1114.       if(!strcmp(argv[m],"-yscale"))
  1115.       { d->yscale=getdbl(argv[++m],draw_INCH);
  1116.         continue;
  1117.       }
  1118.       if(!strcmp(argv[m],"-menu"))
  1119.       { d->menu=menu_make(argv[++m],d->name);
  1120.         if(d->menu==0)
  1121.         { d->menu=menu_NONE;
  1122.           return TCL_ERROR;
  1123.         }
  1124.         continue;
  1125.       }
  1126.       return wrong("unknown w_draw <name> create option: ",argv[m]);
  1127.     }
  1128.     setpagesize(d,page);
  1129.     d->next=diagramlist;
  1130.     diagramlist=d;
  1131.     Tcl_SetHashValue(p,d);
  1132.     return TCL_OK;
  1133.   }
  1134.   p=Tcl_FindHashEntry(&diagramTable,argv[1]);
  1135.   if(!p) return wrong("no such diagram: ",argv[1]);
  1136.   d=(diagram*)Tcl_GetHashValue(p);
  1137. /* *********** open a window *************** */
  1138.   if(!strcmp(argv[2],"open"))
  1139.   { view *v=ckalloc(sizeof(view));
  1140.     int x,y;
  1141.     screensize(&x,&y);
  1142.     if(overlap>y/2-INSET) overlap=0;
  1143.     dw.title_data.indirected_text.text=d->title;
  1144.     dw.title_data.indirected_text.validation=(char*)-1;
  1145.     dw.title_data.indirected_text.size=strlen(d->title);
  1146.     dw.extent.x1=d->x;
  1147.     dw.extent.y1=d->y;
  1148.     dw.visible.y1=y-INSET-overlap;
  1149.     dw.visible.y0=dw.visible.y1-(dw.extent.y1-dw.extent.y0);
  1150.     if(dw.visible.y0<INSET) dw.visible.y0=INSET;
  1151.     dw.yscroll=dw.extent.y1;
  1152.     dw.visible.x1=x-INSET-overlap/2;
  1153.     dw.visible.x0=dw.visible.x1-d->x;
  1154.     if(dw.visible.x0<INSET) dw.visible.x0=INSET;
  1155.     v->w=wimp_create_window(&dw);
  1156.     v->next=d->viewlist;
  1157.     d->viewlist=v;
  1158.     block.open.w=v->w;
  1159.     wimp_get_window_state((wimp_window_state*)&block);
  1160.     wimp_open_window((wimp_open*)&block);
  1161.     overlap+=40;
  1162.     return TCL_OK;
  1163.   }
  1164. /* *********** save a diagram *************** */
  1165.   if(!strcmp(argv[2],"save"))
  1166.   { if(argc!=4)  return wrong(WNA,"w_draw <name> <save> <filename>");
  1167.     diagram_save(d,argv[3]);
  1168.     return TCL_OK;
  1169.   }
  1170. /* *********** delete an object *************** */
  1171.   if(!strcmp(argv[2],"delete"))
  1172.   { os_box bb=empty;
  1173.     int m;
  1174.     for(m=3;m<argc;m++)
  1175.     { obj=findobj(d,atoi(argv[m]));
  1176.       if(obj)
  1177.       { unlink(&(d->objects),obj);
  1178.         enlarge(&bb,&(obj->bb));
  1179.         dispose(obj);
  1180.       }
  1181.       force_redraw(d,&bb);
  1182.     }
  1183.     return TCL_OK;
  1184.   }
  1185.   thisdiagram=d;
  1186. /* *********** add new object above object n *************** */
  1187.   if(!strcmp(argv[2],"above"))
  1188.   { object *obj1;
  1189.     if(argc<5) return wrong(WNA,"w_draw <name> after <n> <object>");
  1190.     obj=getobject(argc-4,argv+4);
  1191.     if(!obj) return TCL_ERROR;
  1192.     obj1=findobj(d,atoi(argv[3]));
  1193.     if(!obj1)
  1194.     { if(!d->objects) goto firstobj;
  1195.       for(obj1=d->objects;obj1->next;obj1=obj1->next);
  1196.     }
  1197.     obj->next=obj1->next;
  1198.     obj1->next=obj;
  1199.     goto newobj;
  1200.   }
  1201. /* *********** add new object *************** */
  1202.   obj=getobject(argc-2,argv+2);
  1203.   if(!obj) return TCL_ERROR;
  1204.   firstobj:obj->next=d->objects;
  1205.   d->objects=obj;
  1206.   newobj:force_redraw(d,&(obj->bb));
  1207.   obj->n=d->n;
  1208.   sprintf(interp->result,"%d",d->n++);
  1209.   return TCL_OK;
  1210. }
  1211.  
  1212.