1 /*
2  * selUnselect.c --
3  *
4  * the program you can't see in your mirror.  And why
5  * does it only come around after dark?
6  *
7  */
8 
9 #include <stdio.h>
10 
11 #include "utils/magic.h"
12 #include "utils/geometry.h"
13 #include "utils/geofast.h"
14 #include "tiles/tile.h"
15 #include "utils/hash.h"
16 #include "database/database.h"
17 #include "windows/windows.h"
18 #include "dbwind/dbwind.h"
19 #include "commands/commands.h"
20 #include "utils/main.h"
21 #include "select/select.h"
22 #include "select/selInt.h"
23 #include "utils/malloc.h"
24 #include "textio/textio.h"
25 
26 
27 /*
28  * ----------------------------------------------------------------------------
29  *
30  * selUnselFunc --
31  *
32  *	This function is used by SelRemoveSel2; it is passed to DBSrPaintArea as
33  *	the search function for a search of the select2 cell, and erases
34  *	equivalent areas of paint in the select cell (in effect, it deletes
35  *	the contents of select2 from select).
36  *
37  * Results:
38  *	Always returns zero (so the search will continue).
39  *
40  * Side effects:
41  *	Paint is removed from the select cell.
42  *
43  * ----------------------------------------------------------------------------
44  */
45 
46 int
selUnselFunc(tile,arg)47 selUnselFunc(tile, arg)
48      Tile *tile;
49      ClientData *arg;
50 {
51   TileType type;
52   Rect rect;
53 
54   if ((type = TiGetType(tile)) >= DBNumUserLayers) return 0;
55   TiToRect(tile, &rect);
56   DBErase(SelectDef, &rect, type);
57   return 0;
58 }
59 
60 /*
61  * ----------------------------------------------------------------------------
62  *
63  * selRemoveCellFunc --
64  *
65  *	This procedure hides some (limited number of) cell uses for later
66  *	munging; it is a search procedure for DBCellSrArea.
67  *
68  * Results:
69  *	Always aborts array enumeration (we only need one placement in order
70  *	to rip out the whole array).  Aborts the search once MAXUNSELUSES
71  *	cell uses have been hidden away.
72  *
73  * Side effects:
74  *	Cell uses are entered in the array selRemoveUses; the variable
75  *	selNRemove is incremented.
76  *
77  * ----------------------------------------------------------------------------
78  */
79 
80 #define MAXUNSELUSES 3
81 static CellUse *(selRemoveUses[MAXUNSELUSES]);
82 static int selNRemove;
83 
84 int
selRemoveCellFunc(scx,cdarg)85 selRemoveCellFunc(scx, cdarg)
86      SearchContext *scx;
87      Rect *cdarg;
88 
89 {
90   ASSERT((selNRemove < MAXUNSELUSES) && (selNRemove >= 0),
91 	 "selRemoveCellFunc(selNRemove)");
92   selRemoveUses[selNRemove] = scx->scx_use;
93   GeoIncludeAll(&scx->scx_use->cu_bbox, cdarg);
94   if (++selNRemove >= MAXUNSELUSES) return 1;
95   else return 2;
96 }
97 
98 /*
99  * ----------------------------------------------------------------------------
100  *
101  * SelRemoveArea --
102  *
103  *	Remove a rectangular chunk of the select cell, possibly masked
104  *	by a user-specified mask (which may include the pseudo-levels
105  *	L_CELL and L_LABEL).
106  *
107  * Results:
108  *	None.
109  *
110  * Side effects:
111  *	Paint, labels, and/or cell uses may be removed from the select cell.
112  *	The selection highlights are redrawn, and undo checkpoints are saved,
113  *	so this thrilling process may be undone or redone.  The select cell's
114  *	bounding box is updated.
115  *
116  * ----------------------------------------------------------------------------
117  */
118 
119 void
SelRemoveArea(area,mask,globmatch)120 SelRemoveArea(area, mask, globmatch)
121     Rect *area;
122     TileTypeBitMask *mask;
123     char *globmatch;
124 {
125   SearchContext scx;
126   Rect bbox, areaReturn;
127 
128   /* get ready; save checkpoint for undo. */
129 
130   SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
131 
132   /* defenestrate a few labels.  Do this before the paint, or	*/
133   /* we will not have a proper calculation of the bounding box	*/
134   /* of the erasure for rendered text.				*/
135 
136   areaReturn = *area;
137   if (TTMaskHasType(mask, L_LABEL))
138   {
139 	if (globmatch != NULL)
140 	    DBEraseGlobLabel(SelectDef, area, &DBAllTypeBits, &areaReturn, globmatch);
141 	else
142 	    DBEraseLabel(SelectDef, area, &DBAllTypeBits, &areaReturn);
143   }
144   else
145 	DBEraseLabel(SelectDef, area, mask, &areaReturn);
146 
147   /* erase ordinary paint in area */
148 
149   DBEraseMask(SelectDef, area, mask);
150 
151   /* now blast away at cells; we do this a tiny bit at a time, as in
152      selectClear.  We search, and remember up to MAXUNSELUSES cell
153      placements in each search; then we rip out those placements, and
154      search further.  As always, thou shalt not rip out what thou
155      searchest, or thine database shall be corrupt, and thy name
156      anathema in the ears of thy mask designers.  */
157 
158   bbox = *area;
159   if (TTMaskHasType(mask, L_CELL))
160     {
161       scx.scx_use = SelectUse;
162       scx.scx_trans = GeoIdentityTransform;
163       scx.scx_area = *area;
164       while (TRUE)
165 	{
166 	  int i;
167 
168 	  selNRemove = 0;
169 	  (void) DBCellSrArea(&scx, selRemoveCellFunc, (ClientData) &bbox);
170 	  for (i = 0; i < selNRemove; i++)
171 	    {
172 	      if (selectLastUse == selRemoveUses[i])
173 		selectLastUse = (CellUse *) NULL;
174 	      DBUnLinkCell(selRemoveUses[i], SelectDef);
175 	      DBDeleteCell(selRemoveUses[i]);
176 	      (void) DBCellDeleteUse(selRemoveUses[i]);
177 	    }
178 	  if (selNRemove < MAXUNSELUSES) break;
179 	}
180     }
181 
182   /* now remember stuff for redo (and fill in info for undo), redraw highlights,
183      recompute the bounding box on the off chance it has changed, tell the
184      database we've mucked around with the select cell, then go home. */
185 
186   SelRememberForUndo(FALSE, SelectRootDef, &bbox);
187   GeoInclude(&areaReturn, &bbox);
188   DBWHLRedraw(SelectRootDef, &bbox, TRUE);
189   DBReComputeBbox(SelectDef);
190   DBWAreaChanged(SelectDef, &bbox, DBW_ALLWINDOWS, (TileTypeBitMask *) NULL);
191 }
192 
193 /*
194  * ----------------------------------------------------------------------------
195  *
196  * selRemoveLabelPaintFunc --
197  *
198  *	Put labels in the select2 cell.  The SelRemoveSel2 function runs through
199  *	all the labels in the select cell, and calls DBSrPaintArea on select2 for
200  *	each one.  If there is (suitable) paint in select2 under the label, this
201  *	function gets called, and places the label in select2.
202  *
203  * Results:
204  *	Always returns one (and thus aborts the search).
205  *
206  * Side effects:
207  *	Labels may appear in select2.
208  *
209  * ----------------------------------------------------------------------------
210  */
211 
212 int
selRemoveLabelPaintFunc(tile,label)213 selRemoveLabelPaintFunc(tile, label)
214      Tile *tile;
215      Label *label;
216 {
217   (void) DBPutFontLabel(Select2Def, &label->lab_rect, label->lab_font,
218 	label->lab_size, label->lab_rotate, &label->lab_offset,
219 	label->lab_just, label->lab_text, label->lab_type,
220 	label->lab_flags, label->lab_port);
221 
222   return 1;
223 }
224 
225 /*
226  * ----------------------------------------------------------------------------
227  *
228  * SelRemoveSel2 --
229  *	Run through the select2 cell, removing corresponding paint and labels
230  *	from the select cell.
231  *
232  * Results:
233  *	Should always return zero; returns 1 if there is a problem traversing
234  *	select2.
235  *
236  * Side effects:
237  *	Paint and labels (but not cell uses) may be deleted from the select
238  *	cell.  The calling procedure is responsible for updating highlighting
239  *	and undo information.
240  *
241  *	Labels may be placed in the select2 cell; SelRemoveSel2 assumes that
242  *	there are no labels in select2 when it is called.
243  *
244  * ----------------------------------------------------------------------------
245  */
246 
247 int
SelRemoveSel2()248 SelRemoveSel2()
249 
250 {
251   int plane;
252   Label *label;
253 
254   /* Enumerate paint tiles in select2; selUnselFunc will erase corresponding
255      pieces in the select cell. */
256 
257   for (plane = PL_TECHDEPBASE; plane < DBNumPlanes; plane++)
258     {
259       if (DBSrPaintArea((Tile *) NULL, Select2Def->cd_planes[plane],
260 			&TiPlaneRect, &DBAllButSpaceAndDRCBits, selUnselFunc,
261 			(ClientData) NULL) != 0)
262 	return 1;
263     }
264 
265   /* Enumerate labels in select; selRemoveLabelPaintFunc will place a copy of
266      the label in select2 if it finds appropriate paint underneath the label. */
267 
268   ASSERT(Select2Def->cd_labels == (Label *) NULL, "SelRemoveSel2 labels");
269 
270   for (label = SelectDef->cd_labels;
271        label != (Label *) NULL;
272        label = label->lab_next)
273     {
274       Rect area, searchArea;
275 
276       if (label->lab_type == TT_SPACE) continue;
277       area = label->lab_rect;
278       GEO_EXPAND(&area, 1, &searchArea);
279       (void) DBSrPaintArea((Tile *) NULL,
280 			   Select2Def->cd_planes[DBPlane(label->lab_type)],
281 			   &searchArea, &DBConnectTbl[label->lab_type],
282 			   selRemoveLabelPaintFunc,
283 			   (ClientData) label);
284     }
285 
286   /* Now run through the labels we just copied, and delete them from
287      the select cell.  */
288 
289   for (label = Select2Def->cd_labels;
290        label != (Label *) NULL;
291        label = label->lab_next)
292     DBEraseLabelsByContent(SelectDef, &label->lab_rect, -1, label->lab_text);
293   return 0;
294 }
295 
296 typedef struct
297 {
298   CellUse *ed_use, *sel_use;
299   Transform *orient;
300 } SelRemoveCellArgs;
301 
302 /*
303  * ----------------------------------------------------------------------------
304  *
305  * SelRemoveCellSearchFunc --
306  *	find the cell use in the select cell which matches a given
307  *	cell use in the root def.
308  *
309  * Results:
310  *	Returns 1 to abort the search if it finds a match.  Otherwise
311  *	returns zero.
312  *
313  * Side effects:
314  *	fills in the sel_use field of its client argument if it finds a match.
315  *
316  * ----------------------------------------------------------------------------
317  */
318 
319 int
SelRemoveCellSearchFunc(scx,cdarg)320 SelRemoveCellSearchFunc(scx, cdarg)
321      SearchContext *scx;
322      SelRemoveCellArgs *cdarg;
323 {
324   Transform *et, *st;
325 
326   /* To match, cell uses must point to the same cell def. */
327 
328   if (scx->scx_use->cu_def != cdarg->ed_use->cu_def)
329     return 0;
330 
331   /* If these usages are in the same orientation, at the same
332      location, they match.  To check this, we compare the
333      search context transformation with the transformation
334      computed earlier for the usage in the edit cell. */
335 
336   st = &scx->scx_trans;
337   et = cdarg->orient;
338   if ((st->t_a == et->t_a) &&
339       (st->t_b == et->t_b) &&
340       (st->t_c == et->t_c) &&
341       (st->t_d == et->t_d) &&
342       (st->t_e == et->t_e) &&
343       (st->t_f == et->t_f))
344     {
345       cdarg->sel_use = scx->scx_use;
346       return 1;
347     }
348   return 0;
349 }
350 
351 /*
352  * ----------------------------------------------------------------------------
353  *
354  * SelectRemoveCellUse --
355  *	remove the cell use in the select cell which matches the given
356  *	use from the root def.
357  *
358  * Results:
359  *	Returns 1 if no such use was found; returns zero otherwise.
360  *
361  * Side effects:
362  *	If SelectRemoveCellUse returns 1, there are no side effects.
363  *	Otherwise:  undo/redo markers will be created, one cell use
364  *	will be deleted from the select cell, the select cell's bounding
365  *	box will be recomputed, the highlights redrawn, and the area
366  *	which had been covered by the cell use will be marked as
367  *	changed.  If selectLastUse was pointing to the use, it will
368  *	be set to NULL, so the select cycling code will not try
369  *	to deselect this (now trashed) cell use.
370  *
371  * ----------------------------------------------------------------------------
372  */
373 
374 int
SelectRemoveCellUse(use,trans)375 SelectRemoveCellUse(use, trans)
376      CellUse *use;
377      Transform *trans;
378 
379 {
380   SearchContext scx;
381   SelRemoveCellArgs args;
382 
383   /* The search context is the area covered by the cell's bounding box in
384      the select cell. */
385 
386   scx.scx_use = SelectUse;
387   GEOTRANSRECT(trans, &use->cu_def->cd_bbox, &scx.scx_area);
388   scx.scx_trans = GeoIdentityTransform;
389   args.ed_use = use;
390   args.orient = trans;
391 
392   /* DBCellSrArea will return all of the cells overlapping this box;
393      often, this is just one cell.  If the search runs to completion
394      (is not aborted), we did not find a match, so we quit, returning
395      1 as a failure indication.  */
396 
397   if (DBCellSrArea(&scx, SelRemoveCellSearchFunc, (ClientData) &args) == 0)
398     return 1;
399 
400   /* remunge the selectLastUse Horrid Side Effect Pointer */
401 
402   if (selectLastUse == args.sel_use)
403     selectLastUse = (CellUse *) NULL;
404 
405   /* Now remove the cell use (with appropriate undo and database
406      incantations). */
407 
408   SelRememberForUndo(TRUE, (CellDef *) NULL, (Rect *) NULL);
409   DBUnLinkCell(args.sel_use, SelectDef);
410   DBDeleteCell(args.sel_use);
411   (void) DBCellDeleteUse(args.sel_use);
412   SelRememberForUndo(FALSE, SelectRootDef, &scx.scx_area);
413   DBWHLRedraw(SelectRootDef, &scx.scx_area, TRUE);
414   DBReComputeBbox(SelectDef);
415   DBWAreaChanged(SelectDef, &scx.scx_area,
416 		 DBW_ALLWINDOWS, (TileTypeBitMask *) NULL);
417   return 0;
418 }
419 
420