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