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