1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Sun Feb 22 18:12:36 2009
19 ****************************************************************************/
20 #include "tkgate.h"
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #define ENGLISH_LINESPACE	12	/* Line spacing for English text */
25 #define KANJI_LINESPACE		15	/* Line spacing for lines with kanji characters */
26 
27 static char *psComment[] = {
28   "%",
29   "% An COMMENT gate",
30   "%",
31   "/pscomment {",
32   "  0 startgate",
33   "  12 cfont",
34   "  newpath 0 0 moveto",
35   "  { {} forall exch neg 0 exch rmoveto gsave show grestore } forall",
36   "  grestore",
37   "} bind def",
38   0
39 };
40 
41 GCElement *Comment_Make(EditState **es,GModuleDef *env,int GType,
42 		      int x,int y,int r,const char *Name,int noWire,const char**,int);
43 void Comment_Delete(GCElement *g,GModuleDef *env,int drawp);
44 void Comment_GetExtents(GCElement *g,TargetDev_e target,int *minx,int *miny,int *maxx,int *maxy,int *bd);
45 int Comment_HitDistance(GCElement *g,int x,int y);
46 void Comment_Draw(GCElement *g,int md);
47 void Comment_PSWrite(GPrint *P,GModLayout *L,GCElement *g);
48 void Comment_VerSave(FILE *f,GCElement *g);
49 int Comment_EditProps(GCElement *g,int isLoadDialog);
50 void Comment_SetProp(GCElement*,const char*,const void*);
51 GCElement *Comment_Copy(GModuleDef *M,GCElement *g,int x,int y,unsigned);
52 void Comment_Init(GCElement*g);
53 void Comment_Rotate(GCElement *g,int centX, int centY,int rdir);
54 
55 GGateInfo gate_comment_info = {
56   GC_COMMENT,
57   "COMMENT",
58   "comment",0x0,
59   "pscomment",psComment,
60   -1,-1,
61 
62   {{"C",	{0,0},			{"gm.comment",0,0},		"gat_make COMMENT"},
63    {0}},
64   0,
65 
66   0,{{0}},
67   {{0,-12,CT},{12,0,LJ},{0,-12,CT},{12,0,LJ}},	/* Are these even used? */
68   {1},
69 
70   {0},
71 
72   Comment_Make,
73   Nop_WriteCellDef,
74   Comment_Init,
75   Comment_Delete,
76   Comment_GetExtents,
77   Comment_HitDistance,
78   Comment_Draw,
79   Generic_Move,
80   Comment_Copy,
81   Err_AddInput,
82   Err_AddOutput,
83   Err_AddInOut,
84   Comment_Rotate,
85   Err_RemovePort,
86   Err_ChangePin,
87   Nop_SimInitFunc,
88   Nop_SimHitFunc,
89   Comment_PSWrite,
90   Comment_EditProps,
91   Comment_VerSave,
92   Comment_SetProp
93 };
94 
95 #if 0
96 static int hasKanji(char *s)
97 {
98   for (;*s;s++)
99     if ((*s & 0x80)) return 1;
100 
101   return 0;
102 }
103 #endif
104 
105 /*
106  * Get the value of a built-in variable and put it in p.
107  */
Comment_getValue(char * p,const char * name)108 int Comment_getValue(char *p,const char *name)
109 {
110   if (strcasecmp(name,"tkgate-version") == 0) {
111     strcpy(p,TKGATE_FULL_VERSION);
112   } else if (strcasecmp(name,"tkgate-homepage") == 0) {
113     strcpy(p,PACKAGE_URL);
114   } else if (strcasecmp(name,"tkgate-mailcontact") == 0) {
115     strcpy(p,PACKAGE_BUGREPORT);
116   } else if (strcasecmp(name,"tkgate-copyright") == 0) {
117     strcpy(p,TKGATE_COPYRIGHT);
118   } else {
119     *p = 0;
120     return -1;
121   }
122 
123   return 0;
124 }
125 
126 
127 /*
128  * Expand any value strings in a comment line.
129  *
130  * Currently the only recognized tags are of the form:
131  *
132  * <value-of name="varname">
133  *
134  * where "varname" is the name of a variable.  The recognized variables are:
135  *
136  *     tkgate-version			TkGate version number.
137  */
Comment_expandLine(char * xline,const char * sline)138 void Comment_expandLine(char *xline,const char *sline)
139 {
140   const char *p;
141   char *x = xline;
142   char tag[STRMAX],name[STRMAX];
143 
144   for (p = sline;p && *p;) {
145     int did_tag = 0;
146 
147     if (sscanf(p,"<%s",tag) == 1 && strcasecmp(tag,"value-of") == 0) {
148       did_tag = 1;
149 
150       if (sscanf(p,"<%*s %[^=]=\"%[^\"]",tag,name) == 2) {
151 	if (strcasecmp(tag,"name") == 0) {
152 	  Comment_getValue(x,name);
153 	  x += strlen(x);
154 	}
155       }
156 
157       p = strchr(p,'>');
158       if (p) p++;
159     }
160 
161     if (!did_tag) {
162       *x++ = *p++;
163     }
164   }
165   *x = 0;
166 }
167 
Comment_Init(GCElement * g)168 void Comment_Init(GCElement*g)
169 {
170   Generic_Init(g);
171   ob_touch(g);
172   g->u.comment.width = g->u.comment.height = 0;
173   g->u.comment.first = g->u.comment.last = 0;
174   g->u.comment.html = 0;
175   g->u.comment.doLink = 0;
176   g->u.comment.link = 0;
177 }
178 
Comment_flushLines(GCElement * g)179 void Comment_flushLines(GCElement *g)
180 {
181   TextLine *L,*N;
182 
183   ob_touch(g);
184 
185   for (L = g->u.comment.first;L;L = N) {
186     N = L->next;
187     ob_free(L->text);
188     ob_free(L);
189   }
190   g->u.comment.first = g->u.comment.last = 0;
191 
192   if (g->u.comment.html)
193     delete_Html(g->u.comment.html);
194   g->u.comment.html = 0;
195 }
196 
Comment_addLine(GCElement * g,const char * text)197 void Comment_addLine(GCElement *g,const char *text)
198 {
199   TextLine *L = OM_MALLOC(TextLine);
200 
201   ob_touch(L);
202   L->text = ob_strdup(text);
203   L->next = 0;
204 
205   ob_touch(g);
206   if (g->u.comment.last) {
207     ob_touch(g->u.comment.last);
208     g->u.comment.last->next = L;
209     g->u.comment.last = L;
210   } else
211     g->u.comment.first = g->u.comment.last = L;
212 }
213 
Comment_prependLine(GCElement * g,const char * text)214 void Comment_prependLine(GCElement *g,const char *text)
215 {
216   TextLine *L = (TextLine*) ob_malloc(sizeof(TextLine),"TextLine");
217 
218   L->text = ob_strdup(text);
219   L->next = g->u.comment.first;
220 
221   ob_touch(g);
222   g->u.comment.first = L;
223   if (!g->u.comment.last) {
224     g->u.comment.last = L;
225   }
226 }
227 
Comment_Make(EditState ** es,GModuleDef * env,int GType,int x,int y,int r,const char * Name,int noWire,const char ** options,int nOptions)228 GCElement *Comment_Make(EditState **es,GModuleDef *env,int GType,
229 		      int x,int y,int r,const char *Name,int noWire,const char **options,int nOptions)
230 {
231   GCElement *g;
232   const char *nodialog = 0;
233 
234   if (!(g = Generic_Make(es,env,GType,x,y,r,Name,noWire,options,nOptions)))
235     return NULL;
236 
237   ob_touch(g);
238   g->u.comment.first = g->u.comment.last = 0;
239   g->u.comment.html = 0;
240 
241   nodialog  = seekOption("-nodialog",options,nOptions);
242 
243   if (es && (!nodialog || *nodialog != '1')) {
244     GGateInfo *gi = g->typeinfo;
245     int ok;
246     const char *temp;
247 
248     (*gi->EditProps)(g,1);
249     DoTcl("EditGate::post");
250     if ((temp = Tcl_GetVar(TkGate.tcl,"edgat_ok",TCL_GLOBAL_ONLY)) && sscanf(temp,"%d",&ok) == 1 && ok) {
251       if ((*gi->EditProps)(g,0) != 0)
252 	return 0;
253     }    else {
254       gate_delete(g,(*es)->env,1);
255       return 0;
256     }
257 
258   }
259 
260   return g;
261 }
262 
Comment_Delete(GCElement * g,GModuleDef * M,int drawp)263 void Comment_Delete(GCElement *g,GModuleDef *M,int drawp)
264 {
265   if (M)     gate_remove(M,g);
266   if (drawp) gate_draw(g,0);
267 
268   Comment_flushLines(g);
269 }
270 
Comment_flushHtml(GCElement * g)271 static void Comment_flushHtml(GCElement *g)
272 {
273   if (g->u.comment.html) delete_Html(g->u.comment.html);
274   g->u.comment.html = 0;
275 }
276 
277 /*****************************************************************************
278  *
279  * Build the html for a comment gate.
280  *
281  *****************************************************************************/
Comment_buildHtml(GCElement * g)282 static int Comment_buildHtml(GCElement *g)
283 {
284   TextLine *L;
285   Html *h;
286   int is_empty = 0;
287 
288   /*
289      Already built and with the correct zoom factor
290   */
291   if (g->u.comment.html
292       && g->u.comment.html->h_zoom == TkGate.circuit->zoom_factor
293       && g->u.comment.html->h_locale == TkGate.circuit->c_locale)
294     return 0;
295 
296   ob_begin_framef("-BuildHtml",FF_TRANSPARENT);
297   ob_touch(g);
298 
299   if (g->u.comment.html) {
300     delete_Html(g->u.comment.html);
301     g->u.comment.html = 0;
302   }
303 
304   h = new_Html(TD_X11);
305   for (L = g->u.comment.first;L;L = L->next) {
306     Html_addLine(h,L->text);
307   }
308   Html_format(h);
309 
310   if (!h->h_isVisible) {
311     delete_Html(h);
312     h = new_Html(TD_X11);
313     Html_addLine(h,msgLookup("comment.empty"));
314     Html_format(h);
315     is_empty = 1;
316   }
317 
318   g->u.comment.html = h;
319   g->u.comment.width = h->h_width;
320   g->u.comment.height = h->h_height;
321 
322   ob_end_frame();
323 
324   return is_empty;
325 }
326 
Comment_GetExtents(GCElement * g,TargetDev_e target,int * minx,int * miny,int * maxx,int * maxy,int * bd)327 void Comment_GetExtents(GCElement *g,TargetDev_e target,
328 			int *minx,int *miny,int *maxx,int *maxy,int *bd)
329 {
330   switch (target) {
331   case TD_X11 :
332     Comment_buildHtml(g);
333 
334     break;
335   case TD_PRINT :
336     {
337       Html *h = new_Html(TD_PRINT);
338       g->u.comment.width = h->h_width;
339       g->u.comment.height = h->h_height;
340       delete_Html(h);
341     }
342     break;
343   }
344 
345   *minx = g->xpos;
346   *miny = g->ypos;
347   *maxx = g->xpos + g->u.comment.width;
348   *maxy = g->ypos + g->u.comment.height;
349 
350   if (bd) *bd = 20;
351 }
352 
Comment_HitDistance(GCElement * g,int x,int y)353 int Comment_HitDistance(GCElement *g,int x,int y)
354 {
355   ob_touch(g);
356 
357   g->left = 0;
358   g->right = 0;
359   g->top = 0;
360   g->bottom = 0;
361 
362   if ((x > g->xpos - 10) &&
363       (x < g->xpos + g->u.frame.width + 10) &&
364       (y > g->ypos - 10) &&
365       (y < g->ypos + g->u.frame.height + 10)) {
366     int rel_x = (x-g->xpos)*TkGate.circuit->zoom_factor;
367     int rel_y = (y-g->ypos)*TkGate.circuit->zoom_factor;
368 
369     if (g->selected) {
370       if (x < g->xpos + 10) {
371 	g->left = 1;
372       }
373       if (x > g->xpos + g->u.frame.width - 10) {
374 	g->right = 1;
375       }
376       if (y < g->ypos + 10) {
377 	g->top = 1;
378       }
379       if (y > g->ypos + g->u.frame.height - 10) {
380 	g->bottom = 1;
381       }
382       if (g->left || g->right || g->top || g->bottom) {
383 	return 0;
384       }
385     }
386 
387     Comment_buildHtml(g);
388     if (!Html_isHit(g->u.comment.html,rel_x,rel_y)) {
389       return NOHIT;
390     }
391 
392     return GATERANGE-1;
393   }
394 
395   return NOHIT;
396 }
397 
Comment_Copy(GModuleDef * M,GCElement * g,int x,int y,unsigned flags)398 GCElement *Comment_Copy(GModuleDef *M,GCElement *g,int x,int y,unsigned flags)
399 {
400   GCElement *ng;
401   TextLine *L;
402 
403   ng = Generic_Copy(M,g,x,y,flags);
404   ob_touch(ng);
405   ng->u.comment.first = ng->u.comment.last = 0;
406   for (L = g->u.comment.first;L;L = L->next) {
407     Comment_addLine(ng,L->text);
408   }
409 
410   ng->u.comment.reqWidth = g->u.comment.reqWidth;
411 
412   Comment_buildHtml(ng);
413 
414   return ng;
415 }
416 
Comment_Draw(GCElement * g,int md)417 void Comment_Draw(GCElement *g,int md)
418 {
419   GC gc = GatePainterContext_gc(TkGate.commentContext);
420   int x,y;
421 
422   Comment_buildHtml(g);
423 
424   x = g->xpos;
425   y = g->ypos;
426 
427   Html_draw(g->u.comment.html,x,y);
428 
429   if (g->selected) {
430     ZDrawRectangle(TkGate.D,TkGate.W,gc,
431 		   ctow_x(g->xpos),ctow_y(g->ypos),g->u.comment.width,g->u.comment.height);
432   }
433 }
434 
Comment_PSWrite(GPrint * P,GModLayout * L,GCElement * g)435 void Comment_PSWrite(GPrint *P,GModLayout *L,GCElement *g)
436 {
437   TextLine *l;
438   Html *h;
439 
440   h = new_Html(TD_PRINT);
441   for (l = g->u.comment.first;l;l = l->next) {
442     Html_addLine(h,l->text);
443   }
444   Html_format(h);
445   Html_psPrint(h,P,g->xpos,g->ypos);
446   delete_Html(h);
447 }
448 
Comment_VerSave(FILE * f,GCElement * g)449 void Comment_VerSave(FILE *f,GCElement *g)
450 {
451   char buf[STRMAX],buf2[STRMAX];
452   TextLine *L;
453   void *encoder = Circuit_getSaveFileEncoder(TkGate.circuit);
454 
455   fprintf(f,"  //: comment %s",g->ename);
456 
457   VerilogBasicGateComment(f,g,0);
458   fprintf(f,"\n");
459 
460   for (L = g->u.comment.first;L;L = L->next) {
461     recodeText(encoder,buf,STRMAX,L->text);
462     fprintf(f,"  //: /line:\"%s\"\n",quoteChars(buf2,buf,"\"\\"));
463   }
464   fprintf(f,"  //: /end\n");
465 }
466 
Comment_EditProps(GCElement * g,int isLoadDialog)467 int Comment_EditProps(GCElement *g,int isLoadDialog)
468 {
469   char buf[STRMAX],pos[64];
470 
471   Generic_EditProps(g,isLoadDialog);
472 
473   if (isLoadDialog) {
474     TextLine *L;
475     int n = 0;
476 
477     for (L = g->u.comment.first;L;L = L->next) {
478       sprintf(pos,"%d",n++);
479       Tcl_SetVar2(TkGate.tcl,"edgat_commentLines",pos,L->text,TCL_GLOBAL_ONLY);
480     }
481 
482     sprintf(buf,"%d",n);
483     Tcl_SetVar(TkGate.tcl,"edgat_commentLen",buf,TCL_GLOBAL_ONLY);
484   } else {
485     const char *p;
486     int n = 0;
487     int i;
488     Tcl_Interp *tcl = TkGate.tcl;
489 
490     Comment_flushLines(g);
491 
492     if ((p = Tcl_GetVar(tcl,"edgat_commentLen",TCL_GLOBAL_ONLY)))
493       sscanf(p,"%d",&n);
494     for (i = 0;i < n;i++) {
495       sprintf(buf,"%d",i);
496       p = Tcl_GetVar2(tcl,"edgat_commentLines",buf,TCL_GLOBAL_ONLY);
497       if (p) Comment_addLine(g,p);
498     }
499     if (Comment_buildHtml(g)) {
500       gate_delete(g,TkGate.circuit->es->env,1);
501       return -1;		// Indicate we deleted the gate
502     }
503    }
504   return 0;
505 }
506 
Comment_SetProp(GCElement * g,const char * prop,const void * value)507 void Comment_SetProp(GCElement *g,const char *prop,const void *value)
508 {
509   char *s = (char*)value;
510 
511   ob_touch(g);
512 
513   if (strcmp(prop,"/line") == 0) {
514     Comment_flushHtml(g);
515 
516     /*
517      * If /link and /dolink have been specified, this is an old-style link
518      * specification and we must convert it to the new style.
519      */
520     if (g->u.comment.doLink && g->u.comment.link) {
521       char buf[STRMAX];
522 
523       sprintf(buf,"<a href=\"%s\">%s</a>",g->u.comment.link,s);
524       Comment_addLine(g,buf);
525     } else
526       Comment_addLine(g,s);
527   }
528   if (strcmp(prop,"/dolink") == 0) {
529     g->u.comment.doLink = *(int*)value;
530   }
531   if (strcmp(prop,"/link") == 0) {
532     if (g->u.comment.link)
533       ob_free(g->u.comment.link);
534     g->u.comment.link = ob_strdup(s);
535   }
536 }
537 
538 
Comment_Rotate(GCElement * g,int centX,int centY,int rdir)539 void Comment_Rotate(GCElement *g,int centX, int centY,int rdir)
540 {
541   int x = g->xpos + g->u.comment.width/2;
542   int y = g->ypos + g->u.comment.height/2;
543   int nx,ny;
544 
545   ob_touch(g);
546   nx = rotateX(x - centX,y - centY, rdir) + centX;
547   ny = rotateY(x - centX,y - centY, rdir) + centY;
548 
549   g->xpos = nx - g->u.comment.width/2;
550   g->ypos = ny - g->u.comment.height/2;
551 }
552 
553 /*
554  * Test to see if (x,y) touches a hyperlink in the comment.  If so then return
555  * the link.
556  */
Comment_getHyperlink(GCElement * g,int x,int y)557 const char *Comment_getHyperlink(GCElement *g,int x,int y)
558 {
559   int rel_x = (x-g->xpos)*TkGate.circuit->zoom_factor;
560   int rel_y = (y-g->ypos)*TkGate.circuit->zoom_factor;
561 
562   Comment_buildHtml(g);
563 
564   return Html_getLink(g->u.comment.html,rel_x,rel_y);
565 }
566 
init_comment()567 void init_comment()
568 {
569   RegisterGate(&gate_comment_info);
570 }
571 
572