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 Wed Mar 18 05:13:57 2009
19 ****************************************************************************/
20 /*
21   Functions for manageing the multi-gate selection and cut buffer.
22 
23   We have separate handling for single-gate and multi-gate selection
24   mainly due to historical baggage.  One day, I will merge them into
25   a single selection
26 */
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <pwd.h>
31 #include <sys/time.h>
32 #include <string.h>
33 #include <assert.h>
34 #include "tkgate.h"
35 #include "comment.h"
36 
new_GSelection()37 GSelection *new_GSelection()
38 {
39   GSelection *S = (GSelection*) ob_malloc(sizeof(GSelection),"GSelection");
40 
41   ob_touch(S);
42   S->s_gates = new_SHash();
43   S->s_wires = new_NHash();
44   S->s_edgeWires = new_NHash();
45   S->s_hasAnchored = 0;
46 
47   return S;
48 }
49 
delete_GSelection(GSelection * S)50 void delete_GSelection(GSelection *S)
51 {
52   delete_SHash(S->s_gates);
53   delete_NHash(S->s_wires);
54   delete_NHash(S->s_edgeWires);
55   ob_free(S);
56 }
57 
new_GCutBuffer()58 GCutBuffer *new_GCutBuffer()
59 {
60   GCutBuffer *C = (GCutBuffer*) ob_malloc(sizeof(GCutBuffer),"GCutBuffer");
61 
62   ob_touch(C);
63   C->cb_minx = C->cb_maxx = C->cb_miny = C->cb_maxy = 0;
64   C->cb_lastTarget = 0;
65   C->cb_dx = C->cb_dy = 0;
66   C->cb_buf = new_GModuleDef("<cut-buffer>");
67 
68   return C;
69 }
70 
71 /*
72  * Compute the bounding box of a sub-circuit defined by the hash table of gates and wires.
73  */
subcircuitBBX(SHash * ghash,NHash * whash,GWireList * wires,int * minX,int * minY,int * maxX,int * maxY,int append)74 static void subcircuitBBX(SHash *ghash,NHash *whash,GWireList *wires,
75 			  int *minX,int *minY,int *maxX,int *maxY,int append)
76 {
77   HashElem *E;
78   GWireList *wl;
79   int is_first = !append;
80 
81   /*
82    * This will only be used if this is not an append, and there are no gates or wires.
83    */
84   if (is_first) {
85     *minX = 0;
86     *maxX = 0;
87     *minY = 0;
88     *maxY = 0;
89   }
90 
91   if (ghash) {
92     for (E = Hash_first(ghash);E;E = Hash_next(ghash,E)) {
93       GCElement *g = (GCElement *) HashElem_obj(E);
94       int MinX,MinY,MaxX,MaxY;
95 
96       gate_getbbx(g,TD_X11,&MinX,&MinY,&MaxX,&MaxY);
97 
98       if (is_first || MinX < *minX) *minX = MinX;
99       if (is_first || MaxX > *maxX) *maxX = MaxX;
100       if (is_first || MinY < *minY) *minY = MinY;
101       if (is_first || MaxY > *maxY) *maxY = MaxY;
102       is_first = 0;
103 
104     }
105   }
106 
107   if (whash) {
108     for (E = Hash_first(whash);E;E = Hash_next(whash,E)) {
109       GWire *w = (GWire *) HashElem_obj(E);
110       GWireNode *n;
111 
112       w = w->driver;
113       for (n = w->nodes;n;n = n->out) {
114 	int x = n->x;
115 	int y = n->y;
116 
117 	if (is_first || x < *minX) *minX = x;
118 	if (is_first || x > *maxX) *maxX = x;
119 	if (is_first || y < *minY) *minY = y;
120 	if (is_first || y > *maxY) *maxY = y;
121 	is_first = 0;
122       }
123     }
124   }
125 
126   for (wl = wires;wl;wl = wl->wl_next) {
127     GWire *w = wl->wl_wire;
128     GWireNode *n;
129 
130     w = w->driver;
131     for (n = w->nodes;n;n = n->out) {
132       int x = n->x;
133       int y = n->y;
134 
135       if (is_first || x < *minX) *minX = x;
136       if (is_first || x > *maxX) *maxX = x;
137       if (is_first || y < *minY) *minY = y;
138       if (is_first || y > *maxY) *maxY = y;
139       is_first = 0;
140     }
141   }
142 
143 }
144 
145 /*
146  * Compute the bounding box of the cut buffer.
147  */
GCutBuffer_computeBounds(GCutBuffer * C)148 void GCutBuffer_computeBounds(GCutBuffer *C)
149 {
150   subcircuitBBX(C->cb_buf->m_gates,0, C->cb_buf->m_wires,
151 		&C->cb_minx, &C->cb_miny, &C->cb_maxx, &C->cb_maxy, 0);
152 
153   C->cb_ctrx = (C->cb_minx + C->cb_maxx)/2;
154   C->cb_ctry = (C->cb_miny + C->cb_maxy)/2;
155 }
156 
delete_GCutBuffer(GCutBuffer * C)157 void delete_GCutBuffer(GCutBuffer *C)
158 {
159   delete_GModuleDef(C->cb_buf);
160   ob_free(C);
161 }
162 
163 /*
164  * Change the state of menu entries and toolbar icons depending on whether we
165  * can cut and/or paste.
166  */
sel_updateMenuState()167 void sel_updateMenuState()
168 {
169   static char *states[] = {"disabled","normal"};
170   int paste_ok = TkGate.circuit->cut_buffer != 0;
171   int sel_ok;
172 
173   if (!TkGate.tcl) return;
174 
175 
176   if (TkGate.circuit && TkGate.circuit->es && hdl_isactive) {  /* GModuleDef_getType(TkGate.circuit->es->env) == MT_TEXTHDL*/
177     DoTcl("HdlEditor::isselection");
178     sel_ok = (Tcl_GetStringResult(TkGate.tcl)[0] == '1');
179   } else {
180     sel_ok = (TkGate.circuit->select != 0) || (TkGate.circuit->mg_selection != 0);
181   }
182 
183   DoTcl("Menu::setFlags %s C %s P",(sel_ok ? "-set" : "-clear"),(paste_ok ? "-set" : "-clear"));
184 
185   DoTcl("ToolBar::toolConfigure Edit cut -state %s",states[sel_ok]);
186   DoTcl("ToolBar::toolConfigure Edit copy -state %s",states[sel_ok]);
187   DoTcl("ToolBar::toolConfigure Edit paste -state %s",states[paste_ok]);
188 
189   DoTcl("set ::tkg_cutBufferActive %d",paste_ok);
190   DoTcl("set ::tkg_selectionActive %d",sel_ok);
191 }
192 
sel_isSelGate(GCElement * g)193 int sel_isSelGate(GCElement *g)
194 {
195   if (!TkGate.circuit->mg_selection)
196     return 0;
197 
198   if (SHash_find(TkGate.circuit->mg_selection->s_gates,g->ename))
199     return 1;
200   else
201     return 0;
202 }
203 
204 
sel_num(EditState * es)205 int sel_num(EditState *es)
206 {
207   GSelection *S = TkGate.circuit->mg_selection;
208 
209   if (!S) return 0;
210 
211   return Hash_numElems(S->s_gates);
212 }
213 
sel_addGate(EditState * es,GCElement * g,int doDraw)214 static void sel_addGate(EditState *es,GCElement *g,int doDraw)
215 {
216   GSelection *S = TkGate.circuit->mg_selection;
217 
218   SHash_insert(S->s_gates,g->ename,g);
219 
220   ob_touch(S);
221   ob_touch(g);
222 
223   if (g->anchored)
224     S->s_hasAnchored = 1;
225 
226   if (doDraw) gate_draw(g,GD_NOWIRE);
227   g->selected = 1;
228   if (doDraw) gate_draw(g,GD_NOWIRE);
229 }
230 
sel_refinish(EditState * es)231 int sel_refinish(EditState *es)
232 {
233   if (TkGate.circuit->mg_selection) {
234     NHash_flush(TkGate.circuit->mg_selection->s_wires);
235     NHash_flush(TkGate.circuit->mg_selection->s_edgeWires);
236     sel_finish(es);
237   }
238   return 0;
239 }
240 
241 /*
242  * Finish up making a selection.  Add the wires to the hash tables.
243  */
sel_finish(EditState * es)244 int sel_finish(EditState *es)
245 {
246   GWireList *wl;
247   GSelection *S = TkGate.circuit->mg_selection;
248   int n = Hash_numElems(S->s_gates);
249 
250   if (n == 0) {
251     sel_updateMenuState();
252     return 0;
253   }
254 
255   ob_touch(TkGate.circuit);
256 
257   if (n == 1) {
258     GCElement *g = (GCElement*) HashElem_obj(Hash_first(TkGate.circuit->mg_selection->s_gates));
259     TkGate.circuit->select = g;
260   } else
261     TkGate.circuit->select = 0;
262 
263   for (wl = es->env->m_wires;wl;wl = wl->wl_next) {
264     GWire *w1 = wl->wl_wire;
265     GWire *w2 = w1->driver;
266 
267     if (w1 == w2) continue;	/* Process each each wire only once */
268 
269     if (!w1->gate || !SHash_find(S->s_gates,w1->gate->ename))
270       w1 = 0;
271 
272     if (!w2->gate || !SHash_find(S->s_gates,w2->gate->ename))
273       w2 = 0;
274 
275     if (w1 && w2) {
276       PHash_insert(S->s_wires,w2,w2);
277     } else if (w1) {
278       PHash_insert(S->s_edgeWires,w1,w1);
279     } else if (w2) {
280       PHash_insert(S->s_edgeWires,w2,w2);
281     }
282   }
283 
284   sel_updateMenuState();
285   return n;
286 }
287 
sel_clear(EditState * es,int doDraw)288 void sel_clear(EditState *es,int doDraw)
289 {
290   ob_touch(TkGate.circuit);
291 
292   if (TkGate.circuit->mg_selection) {
293     HashElem *gl;
294     SHash *H = TkGate.circuit->mg_selection->s_gates;
295 
296     for (gl = Hash_first(H);gl;gl = Hash_next(H,gl)) {
297       GCElement *g = (GCElement*) HashElem_obj(gl);
298       ob_touch(g);
299       if (doDraw)
300 	gate_draw(g,GD_NOWIRE);
301       g->selected = 0;
302       if (doDraw)
303 	gate_draw(g,GD_NOWIRE);
304     }
305 
306     delete_GSelection(TkGate.circuit->mg_selection);
307     TkGate.circuit->mg_selection = 0;
308   }
309 
310   TkGate.circuit->select = 0;
311 
312   sel_updateMenuState();
313 }
314 
sel_interfaceReset(EditState * es)315 void sel_interfaceReset(EditState *es)
316 {
317   if (TkGate.circuit->mg_selection) {
318     HashElem *E;
319     SHash *H = TkGate.circuit->mg_selection->s_gates;
320 
321     for (E = Hash_first(H);E;E = Hash_next(H,E)) {
322       GCElement *g = (GCElement*) HashElem_obj(E);
323       modint_reset(es,g);
324     }
325 
326     sel_clear(es,1);
327   }
328 }
329 
sel_select(EditState * es)330 int sel_select(EditState *es)
331 {
332   int x = TkGate.ed->sx;
333   int y = TkGate.ed->sy;
334   int width = TkGate.ed->tx-TkGate.ed->sx;
335   int height = TkGate.ed->ty-TkGate.ed->sy;
336   HashElem *gl;
337 
338   ob_touch(TkGate.circuit);
339 
340   if (!TkGate.circuit->mg_selection)
341     TkGate.circuit->mg_selection = new_GSelection();
342   else {
343     NHash_flush(TkGate.circuit->mg_selection->s_wires);
344     NHash_flush(TkGate.circuit->mg_selection->s_edgeWires);
345   }
346 
347   if (width < 0) {
348     width = -width;
349     x = x - width;
350   }
351   if (height < 0) {
352     height = -height;
353     y = y - height;
354   }
355 
356   for (gl = Hash_first(es->env->m_gates);gl;gl = Hash_next(es->env->m_gates,gl)) {
357     GCElement *g = (GCElement*) HashElem_obj(gl);
358     int gx,gy;
359 
360     gx = g->xpos;
361     gy = g->ypos;
362     if (g->typeinfo->code == GC_BLOCK) {
363       gx += g->u.block.gwidth/2;
364       gy += g->u.block.gheight/2;
365     }
366 
367     if (gx >= x && gx <= x+width && gy >= y && gy <=y+height)
368       sel_addGate(es,g,1);
369   }
370 
371   return sel_finish(es);
372 }
373 
374 /*
375  * Add an additional gate to the selection.
376  */
sel_appendGate(EditState * es,GCElement * g,int doDraw)377 void sel_appendGate(EditState *es,GCElement *g,int doDraw)
378 {
379   ob_touch(TkGate.circuit);
380 
381   if (!TkGate.circuit->mg_selection)
382     TkGate.circuit->mg_selection = new_GSelection();
383 
384   NHash_flush(TkGate.circuit->mg_selection->s_wires);
385   NHash_flush(TkGate.circuit->mg_selection->s_edgeWires);
386 
387   sel_addGate(es,g,doDraw);
388 }
389 
sel_unselectGate(EditState * es,GCElement * g)390 void sel_unselectGate(EditState *es,GCElement *g)
391 {
392   GSelection *S = TkGate.circuit->mg_selection;
393 
394   if (!S) return;
395 
396   SHash_remove(S->s_gates,g->ename);
397   g->selected = 0;
398 
399   if (Hash_numElems(S->s_gates) == 0)
400     sel_clear(es,1);
401 
402   sel_refinish(es);
403 }
404 
405 
sel_selectMarked(EditState * es)406 int sel_selectMarked(EditState *es)
407 {
408   HashElem *gl;
409 
410   ob_touch(TkGate.circuit);
411   TkGate.circuit->mg_selection = new_GSelection();
412 
413   for (gl = Hash_first(es->env->m_gates);gl;gl = Hash_next(es->env->m_gates,gl)) {
414     GCElement *g = (GCElement*) HashElem_obj(gl);
415     if (g->selected)
416       sel_addGate(es,g,1);
417   }
418   return sel_finish(es);
419 }
420 
sel_selectAll(EditState * es)421 int sel_selectAll(EditState *es)
422 {
423   HashElem *gl;
424 
425   ob_touch(TkGate.circuit);
426 
427   if (!TkGate.circuit->mg_selection)
428     TkGate.circuit->mg_selection = new_GSelection();
429 
430   for (gl = Hash_first(es->env->m_gates);gl;gl = Hash_next(es->env->m_gates,gl)) {
431     GCElement *g = (GCElement*) HashElem_obj(gl);
432     sel_addGate(es,g,1);
433   }
434 
435   return sel_finish(es);
436 }
437 
438 /*
439  * Rotate the selection.
440  */
sel_rotate(EditState * es,int rdir)441 void sel_rotate(EditState *es,int rdir)
442 {
443   int minX, minY, maxX, maxY, centX, centY, newcentX, newcentY;
444   GSelection *S = TkGate.circuit->mg_selection;
445   HashElem *E;
446 
447   if (!S) return;
448 
449   if (GModuleDef_isDataProtected(TkGate.circuit->es->env)) {
450     message(0,msgLookup("err.protdata"));
451     return;
452   }
453   if (S->s_hasAnchored) {
454     message(0,msgLookup("err.gatanchor"));		/* Gate(s) are anchored and can not be moved. */
455     return;
456   }
457 
458   /*
459    * If there are edge wires, make sure that non are attached to anything.
460    */
461   if (Hash_numElems(S->s_edgeWires) > 0) {
462     int ok = 1;
463 
464     for (E = Hash_first(S->s_edgeWires);E;E = Hash_next(S->s_edgeWires,E)) {
465       GWire *w = (GWire*) HashElem_obj(E);
466 
467       w = w->driver;
468       if (w->gate && !w->gate->selected)
469 	ok = 0;
470 
471       w = wire_drivee(w);
472       if (w->gate && !w->gate->selected)
473 	ok = 0;
474     }
475 
476     if (!ok) {
477       message(0,msgLookup("err.wirerot"));
478       return;
479     }
480   }
481 
482   SetModified(MF_NET|MF_GATE);
483   ob_touch(TkGate.circuit);
484 
485   subcircuitBBX(S->s_gates, S->s_wires, 0,
486 		&minX, &minY, &maxX, &maxY, 0);
487 
488   centX = (minX + maxX)/2;
489   centY = (minY + maxY)/2;
490 
491   for (E = Hash_first(S->s_wires);E;E = Hash_next(S->s_wires,E)) {
492     GWire *w = (GWire*) HashElem_obj(E);
493     GWireNode *n;
494 
495     w = w->driver;
496     for (n = w->nodes;n;n = n->out) {
497       int x = n->x;
498       int y = n->y;
499       ob_touch(n);
500       n->x = rotateX(x - centX,y - centY,rdir) + centX;
501       n->y = rotateY(x - centX,y - centY,rdir) + centY;
502     }
503 
504     ob_touch(w);
505     w->orient = (w->orient + 4 + rdir) % 4;
506     w = wire_drivee(w);
507     ob_touch(w);
508     w->orient = (w->orient + 4 + rdir) % 4;
509   }
510   for (E = Hash_first(S->s_edgeWires);E;E = Hash_next(S->s_edgeWires,E)) {
511     GWire *w = (GWire*) HashElem_obj(E);
512     GWireNode *n;
513 
514     w = w->driver;
515     for (n = w->nodes;n;n = n->out) {
516       int x = n->x;
517       int y = n->y;
518       ob_touch(n);
519       n->x = rotateX(x - centX,y - centY,rdir) + centX;
520       n->y = rotateY(x - centX,y - centY,rdir) + centY;
521     }
522 
523     ob_touch(w);
524     w->orient = (w->orient + 4 + rdir) % 4;
525     w = wire_drivee(w);
526     ob_touch(w);
527     w->orient = (w->orient + 4 + rdir) % 4;
528   }
529 
530   for (E = Hash_first(S->s_gates);E;E = Hash_next(S->s_gates,E)) {
531     GCElement *g = (GCElement*) HashElem_obj(E);
532 
533     (*g->typeinfo->Rotate)(g,centX,centY,rdir);
534   }
535 
536   /*
537    * Compute new center after rotation and nudge it back to the before
538    * rotation center position.  This prevents gates from "walking" as
539    * they are rotated.
540    */
541   subcircuitBBX(S->s_gates, S->s_wires, 0,
542 		&minX, &minY, &maxX, &maxY, 0);
543   newcentX = (minX + maxX)/2;
544   newcentY = (minY + maxY)/2;
545   if (newcentX != centX || newcentY != centY) {
546     sel_move(es,centX-newcentX,centY-newcentY);
547   }
548 }
549 
sel_move(EditState * es,int dx,int dy)550 void sel_move(EditState *es,int dx,int dy)
551 {
552   HashElem *E;
553   SHash *GH;
554   NHash *WH,*EWH;
555 
556   if (!TkGate.circuit->mg_selection) return;
557 
558   if (GModuleDef_isDataProtected(TkGate.circuit->es->env)) {
559     message(0,msgLookup("err.protdata"));
560     return;
561   }
562   if (TkGate.circuit->mg_selection->s_hasAnchored) {
563     message(0,msgLookup("err.gatanchor"));		/* Gate(s) are anchored and can not be moved. */
564     return;
565   }
566 
567   SetModified(MF_NET|MF_GATE);
568   ob_touch(TkGate.circuit);
569 
570   GH = TkGate.circuit->mg_selection->s_gates;
571   WH = TkGate.circuit->mg_selection->s_wires;
572   EWH = TkGate.circuit->mg_selection->s_edgeWires;
573 
574   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
575     GCElement *g = (GCElement*) HashElem_obj(E);
576 
577     ob_touch(g);
578     g->xpos += dx;
579     g->ypos += dy;
580 
581   }
582 
583   for (E = Hash_first(WH);E;E = Hash_next(WH,E)) {
584     GWire *w = (GWire*) HashElem_obj(E);
585     GWireNode *n;
586 
587     for (n = w->driver->nodes;n;n = n->out) {
588       ob_touch(n);
589       n->x += dx;
590       n->y += dy;
591     }
592   }
593 
594   for (E = Hash_first(EWH);E;E = Hash_next(EWH,E)) {
595     GWire *w = (GWire*) HashElem_obj(E);
596     wire_move(w->nodes,dx,dy,FULL);
597   }
598 }
599 
sel_draw(EditState * es)600 void sel_draw(EditState *es)
601 {
602   HashElem *E;
603   SHash *GH;
604   NHash *WH,*EWH;
605 
606   if (!TkGate.circuit->mg_selection) return;
607 
608   GH = TkGate.circuit->mg_selection->s_gates;
609   WH = TkGate.circuit->mg_selection->s_wires;
610   EWH = TkGate.circuit->mg_selection->s_edgeWires;
611 
612   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
613     GCElement *g = (GCElement*) HashElem_obj(E);
614     gate_draw(g,GD_NOWIRE);
615   }
616 
617   for (E = Hash_first(WH);E;E = Hash_next(WH,E)) {
618     GWire *w = (GWire*) HashElem_obj(E);
619     GWire_draw(w->driver);
620   }
621 
622   for (E = Hash_first(EWH);E;E = Hash_next(EWH,E)) {
623     GWire *w = (GWire*) HashElem_obj(E);
624     GWire_draw(w->driver);
625   }
626 }
627 
sel_dropFixup(EditState * es)628 void sel_dropFixup(EditState *es)
629 {
630   HashElem *E;
631   NHash *EWH;
632 
633   if (!TkGate.circuit->mg_selection) return;
634 
635   EWH = TkGate.circuit->mg_selection->s_edgeWires;
636 
637   for (E = Hash_first(EWH);E;E = Hash_next(EWH,E)) {
638     GWire *w = (GWire*) HashElem_obj(E);
639 
640     GWire_draw(w->driver);
641     GWire_snap(w->driver);
642     GWire_draw(w->driver);
643   }
644 }
645 
sel_copy(EditState * es)646 void sel_copy(EditState *es)
647 {
648   GModuleDef *m = es->env;
649   GModuleDef *cbm;
650 
651   if (TkGate.circuit->cut_buffer) delete_GCutBuffer(TkGate.circuit->cut_buffer);
652 
653   ob_touch(TkGate.circuit);
654 
655   TkGate.circuit->cut_buffer = new_GCutBuffer();
656   cbm = TkGate.circuit->cut_buffer->cb_buf;
657 
658   if (hdl_isactive) {
659     DoTcl("HdlEditor::dumpSelection");
660     cbm->m_type = MT_TEXTHDL;
661     GModuleDef_saveText(cbm, Tcl_GetStringResult(TkGate.tcl));
662   } else {
663     GModuleDef_copyInto(cbm, m, 0,0,1,0);
664     GCutBuffer_computeBounds(TkGate.circuit->cut_buffer);
665     TkGate.circuit->cut_buffer->cb_dx = 0;
666     TkGate.circuit->cut_buffer->cb_dy = 0;
667     TkGate.circuit->cut_buffer->cb_lastTarget = es->env;
668   }
669 
670   sel_updateMenuState();
671 }
672 
sel_copyAppend(EditState * es)673 void sel_copyAppend(EditState *es)
674 {
675   /** @TODO to remove */
676   /*
677   GModuleDef *m = es->env;
678   */
679   GModuleDef *cbm;
680 
681   ob_touch(TkGate.circuit);
682 
683   if (!TkGate.circuit->cut_buffer) {
684     printf("bad copyappend(1)\n");
685     return;
686   }
687 
688   cbm = TkGate.circuit->cut_buffer->cb_buf;
689 
690   if (!hdl_isactive) {
691     printf("bad copyappend(2)\n");
692     return;
693   }
694   if (!hdl_isactive) {
695     printf("bad copyappend(3)\n");
696     return;
697   }
698 
699   ob_touch(TkGate.circuit->cut_buffer);
700   DoTcl("HdlEditor::dumpSelection");
701 
702   ob_touch(cbm);
703   cbm->m_type = MT_TEXTHDL;
704   GModuleDef_allocText(cbm, strlen(cbm->m_text) + strlen(Tcl_GetStringResult(TkGate.tcl)) + 1);
705   strcat(cbm->m_text, Tcl_GetStringResult(TkGate.tcl));
706 
707   sel_updateMenuState();
708 }
709 
sel_kill(EditState * es)710 void sel_kill(EditState *es)
711 {
712   /** @TODO to remove */
713   /*
714   GModuleDef *m = es->env;
715   */
716   if (hdl_isactive) {
717     DoTcl("HdlEditor::isselection2");
718     if (Tcl_GetStringResult(TkGate.tcl)[0] != '1') return;
719     sel_copy(es);
720     DoTcl("HdlEditor::doDelete 0");
721   } else {
722     sel_copy(es);
723     sel_delete(es);
724     ob_touch(TkGate.circuit->cut_buffer);
725     TkGate.circuit->cut_buffer->cb_dx = 0;
726     TkGate.circuit->cut_buffer->cb_dy = 0;
727   }
728 }
729 
sel_killAppend(EditState * es)730 void sel_killAppend(EditState *es)
731 {
732   /** @TODO to remove */
733   /*
734   GModuleDef *m = es->env;
735   */
736   sel_copyAppend(es);
737 
738   if (hdl_isactive) {
739     DoTcl("HdlEditor::doDelete 0");
740   } else {
741     sel_delete(es);
742     ob_touch(TkGate.circuit->cut_buffer);
743     TkGate.circuit->cut_buffer->cb_dx = 0;
744     TkGate.circuit->cut_buffer->cb_dy = 0;
745   }
746 }
747 
sel_clearDelta()748 void sel_clearDelta()
749 {
750   if (TkGate.circuit->cut_buffer) {
751     ob_touch(TkGate.circuit->cut_buffer);
752     TkGate.circuit->cut_buffer->cb_dx = 0;
753     TkGate.circuit->cut_buffer->cb_dy = 0;
754   }
755 }
756 
757 /*****************************************************************************
758  *
759  * Yank data from the cut buffer into an HDL module.
760  *
761  *****************************************************************************/
sel_hdlyank(EditState * es)762 void sel_hdlyank(EditState *es)
763 {
764   GModuleDef *cbm;
765 
766   if (!TkGate.circuit->cut_buffer) return;
767 
768   cbm = TkGate.circuit->cut_buffer->cb_buf;
769 
770   if (hdl_isactive) {
771     DoTclL("HdlEditor::insert",cbm->m_text,NULL);
772   } else {
773     char fileName[STRMAX];
774     getSimTempFile(fileName);
775     sel_writeToFile(fileName);
776     DoTcl("HdlEditor::insertFile %s",fileName);
777     unlink(fileName);
778   }
779 }
780 
781 /*****************************************************************************
782  *
783  * Yank data from the cut buffer into a netlist module.
784  *
785  *****************************************************************************/
sel_yank(EditState * es)786 void sel_yank(EditState *es)
787 {
788   GModuleDef *cbm;
789 
790   if (!TkGate.circuit->cut_buffer) return;
791 
792   cbm = TkGate.circuit->cut_buffer->cb_buf;
793 
794   if (TkGate.ed->mark_vis) {
795     ob_touch(TkGate.circuit->cut_buffer);
796     TkGate.circuit->cut_buffer->cb_dx = TkGate.ed->tx - TkGate.circuit->cut_buffer->cb_ctrx;
797     TkGate.circuit->cut_buffer->cb_dy = TkGate.ed->ty - TkGate.circuit->cut_buffer->cb_ctry;
798     mark_unpost();
799   }
800 
801   sel_clear(es,1);
802 
803   if (hdl_isactive) {
804     GGateInfo *GI = GGateInfo_lookup("COMMENT");
805     EditState **pes = &TkGate.circuit->es;
806     GCElement *g;
807     char *text = cbm->m_text;
808 
809     if (GI) {
810       const char *argv[2];
811       argv[0] = "-nodialog";
812       argv[1] = "1";
813 
814       g = (*GI->MakeFunction)(pes,es->env,GI->code,TkGate.ed->tx,TkGate.ed->ty,0,0,0,argv,2);
815       if (g) {
816 	char *p,*q;
817 
818 	p = text;
819 	while ((q = strchr(p,'\n'))) {
820 	  *q = 0;
821 	  Comment_addLine(g,p);
822 	  *q = '\n';
823 	  p = q+1;
824 	}
825 	Comment_addLine(g,p);
826 
827 
828 	gate_draw(g,0);
829 	sel_appendGate(*pes,g,1);
830 	sel_finish(*pes);
831 	ob_touch(TkGate.circuit);
832 	TkGate.circuit->select = g;
833 	scrollbar_bbx_update();
834 	SynchronizeInterface();
835       }
836     }
837   } else {
838     ob_touch(TkGate.circuit->cut_buffer);
839     if (TkGate.circuit->cut_buffer->cb_lastTarget == es->env) {
840       TkGate.circuit->cut_buffer->cb_dx += 20;
841       TkGate.circuit->cut_buffer->cb_dy += 20;
842     }
843     TkGate.circuit->cut_buffer->cb_lastTarget = es->env;
844 
845     GModuleDef_copyInto(es->env, cbm, TkGate.circuit->cut_buffer->cb_dx,TkGate.circuit->cut_buffer->cb_dy,0,1);
846     if (sel_selectMarked(es)) {
847       EditState_setMode(MODE_MOVESEL);
848     }
849 
850     if (GModuleDef_hasSubModules(cbm))
851       SetModified(MF_NET|MF_GATE|MF_MODULE);
852     else
853       SetModified(MF_NET|MF_GATE);
854   }
855 
856   sel_updateMenuState();
857 }
858 
sel_delete(EditState * es)859 void sel_delete(EditState *es)
860 {
861   if (sel_num(es) == 0) return;
862 
863   SetModified(MF_NET|MF_GATE);
864 
865   ob_touch(TkGate.circuit);
866 
867   /*
868    * A single gate was selected.
869    */
870   if (sel_num(es) == 1) {
871 
872     if (GCElement_isModule(TkGate.circuit->select))
873       SetModified(MF_MODULE);
874 
875     gate_delete(TkGate.circuit->select,es->env,1);
876 
877     delete_GSelection(TkGate.circuit->mg_selection);
878     TkGate.circuit->mg_selection = 0;
879     TkGate.circuit->last = TkGate.circuit->select = 0;
880 
881     return;
882   }
883 
884   if (TkGate.circuit->mg_selection) {
885     HashElem *gl;
886     SHash *H = TkGate.circuit->mg_selection->s_gates;
887     PHash *delGates = new_PHash();
888 
889     for (gl = Hash_first(H);gl;gl = Hash_next(H,gl)) {
890       GCElement *g = (GCElement*) HashElem_obj(gl);
891       if (g->typeinfo->code != GC_JOINT)
892 	PHash_insert(delGates,g,g);
893     }
894 
895     for (gl = Hash_first(delGates);gl;gl = Hash_next(delGates,gl)) {
896       GCElement *g = (GCElement*) HashElem_obj(gl);
897       if (GCElement_isModule(g))
898 	SetModified(MF_MODULE);
899 
900       gate_delete(g,es->env,0);
901     }
902 
903     delete_PHash(delGates);
904 
905     delete_GSelection(TkGate.circuit->mg_selection);
906     TkGate.circuit->mg_selection = 0;
907   }
908   sel_updateMenuState();
909   TkGate.circuit->last = TkGate.circuit->select = 0;
910 
911   FlagRedraw();
912 }
913 
sel_anchor(EditState * es,int isSet)914 void sel_anchor(EditState *es,int isSet)
915 {
916   HashElem *E;
917   SHash *GH;
918 
919   if (!TkGate.circuit->mg_selection) return;
920 
921   ob_touch(TkGate.circuit->mg_selection);
922   TkGate.circuit->mg_selection->s_hasAnchored = (isSet != 0);
923 
924   GH = TkGate.circuit->mg_selection->s_gates;
925   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
926     GCElement *g = (GCElement*) HashElem_obj(E);
927     ob_touch(g);
928     g->anchored = (isSet != 0);
929   }
930 }
931 
sel_setTech(EditState * es,const char * tech)932 void sel_setTech(EditState *es,const char *tech)
933 {
934   HashElem *E;
935   SHash *GH;
936 
937   if (!TkGate.circuit->mg_selection) return;
938 
939   GH = TkGate.circuit->mg_selection->s_gates;
940   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
941     GCElement *g = (GCElement*) HashElem_obj(E);
942 
943     if (g->tech && g->typeinfo->num_delays > 0) {
944       ob_free(g->tech);
945       ob_touch(g);
946       g->tech = ob_strdup(tech);
947     }
948 
949   }
950 }
951 
952 
sel_alignHorz(EditState * es)953 void sel_alignHorz(EditState *es)
954 {
955   HashElem *E;
956   SHash *GH;
957   int y,n;
958 
959   if (!TkGate.circuit->mg_selection) return;
960 
961   n = y = 0;
962   GH = TkGate.circuit->mg_selection->s_gates;
963   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
964     GCElement *g = (GCElement*) HashElem_obj(E);
965     y += g->ypos;
966     n++;
967   }
968   if (n < 2) return;
969   y /= n;
970 
971   n = 0;
972   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
973     GCElement *g = (GCElement*) HashElem_obj(E);
974     if (!g->anchored && y != g->ypos)
975       gate_moveObject(g,0,y-g->ypos);
976     if (g->anchored) n++;
977   }
978   if (n) message(0,msgLookup("err.gatanchor"));		/* Gate(s) are anchored and can not be moved. */
979 }
980 
sel_alignVert(EditState * es)981 void sel_alignVert(EditState *es)
982 {
983   HashElem *E;
984   SHash *GH;
985   int x,n;
986 
987   if (!TkGate.circuit->mg_selection) return;
988 
989   n = x = 0;
990   GH = TkGate.circuit->mg_selection->s_gates;
991   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
992     GCElement *g = (GCElement*) HashElem_obj(E);
993     x += g->xpos;
994     n++;
995   }
996   if (n < 2) return;
997   x /= n;
998 
999   n = 0;
1000   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
1001     GCElement *g = (GCElement*) HashElem_obj(E);
1002     if (!g->anchored && x != g->xpos)
1003       gate_moveObject(g,x-g->xpos,0);
1004     if (g->anchored) n++;
1005   }
1006   if (n) message(0,msgLookup("err.gatanchor"));		/* Gate(s) are anchored and can not be moved. */
1007 }
1008 
1009 /*****************************************************************************
1010  *
1011  * Write the contents of the selection buffer to a file.
1012  *
1013  *****************************************************************************/
sel_writeToFile(const char * fileName)1014 int sel_writeToFile(const char *fileName)
1015 {
1016   HashElem *E;
1017   SHash *GH;
1018   FILE *f;
1019 
1020   GH = TkGate.circuit->cut_buffer->cb_buf->m_gates;
1021 
1022   f = fopen(fileName,"w");
1023   if (!f) return -1;
1024 
1025   VerilogWriteInit();
1026   for (E = Hash_first(GH);E;E = Hash_next(GH,E)) {
1027     GCElement *g = (GCElement*) HashElem_obj(E);
1028 
1029     if (g->typeinfo->VerSave)
1030       (*g->typeinfo->VerSave)(f,g);
1031   }
1032 
1033   fclose(f);
1034 
1035   return 0;
1036 }
1037 
1038