1 /*
2  * Author:      William Chia-Wei Cheng (bill.cheng@acm.org)
3  *
4  * Copyright (C) 2001-2009, William Chia-Wei Cheng.
5  *
6  * This file may be distributed under the terms of the Q Public License
7  * as defined by Trolltech AS of Norway and appearing in the file
8  * LICENSE.QPL included in the packaging of this file.
9  *
10  * THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING
11  * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
12  * PURPOSE.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,
13  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
14  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
16  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * @(#)$Header: /mm2/home/cvs/bc-src/tgif/group.c,v 1.7 2011/05/16 16:21:57 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_GROUP_C_
22 
23 #include "tgifdefs.h"
24 
25 #include "attr.e"
26 #include "choice.e"
27 #include "cmd.e"
28 #include "dialog.e"
29 #include "drawing.e"
30 #include "dup.e"
31 #include "file.e"
32 #include "group.e"
33 #include "mark.e"
34 #include "msg.e"
35 #include "obj.e"
36 #include "page.e"
37 #include "select.e"
38 #include "setup.e"
39 #include "strtbl.e"
40 
41 int gnDeleteAttrsWhileUngrouping=FALSE;
42 
JustCreateGroupObj()43 struct ObjRec *JustCreateGroupObj()
44 {
45    struct GroupRec *group_ptr=NULL;
46    struct ObjRec *obj_ptr=NULL;
47 
48    group_ptr = (struct GroupRec *)malloc(sizeof(struct GroupRec));
49    if (group_ptr == NULL) FailAllocMessage();
50    memset(group_ptr, 0, sizeof(struct GroupRec));
51    group_ptr->first = NULL;
52    group_ptr->last = NULL;
53    group_ptr->rotate = ROTATE0;
54    group_ptr->flip = NO_FLIP;
55    group_ptr->deck_index = (-1);
56    group_ptr->pin_connected = 0;
57    group_ptr->first_conn = group_ptr->last_conn = NULL;
58    obj_ptr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
59    if (obj_ptr == NULL) FailAllocMessage();
60    memset(obj_ptr, 0, sizeof(struct ObjRec));
61    obj_ptr->x = 0;
62    obj_ptr->y = 0;
63    obj_ptr->id = objId++;
64    obj_ptr->locked = FALSE;
65    obj_ptr->type = OBJ_GROUP;
66    obj_ptr->bbox.ltx = 0;
67    obj_ptr->bbox.lty = 0;
68    obj_ptr->bbox.rbx = 0;
69    obj_ptr->bbox.rby = 0;
70    obj_ptr->obbox.ltx = 0;
71    obj_ptr->obbox.lty = 0;
72    obj_ptr->obbox.rbx = 0;
73    obj_ptr->obbox.rby = 0;
74    obj_ptr->detail.r = group_ptr;
75    obj_ptr->fattr = obj_ptr->lattr = NULL;
76    obj_ptr->ctm = NULL;
77    obj_ptr->invisible = FALSE;
78 
79    return obj_ptr;
80 }
81 
CreateGroupObj(TopObjPtr,BotObjPtr)82 void CreateGroupObj(TopObjPtr, BotObjPtr)
83    struct ObjRec *TopObjPtr, *BotObjPtr;
84 {
85    struct ObjRec *obj_ptr=JustCreateGroupObj();
86    struct GroupRec *group_ptr=obj_ptr->detail.r;
87 
88    group_ptr->first = TopObjPtr;
89    group_ptr->last = BotObjPtr;
90    obj_ptr->x = selObjLtX; /* note:  selLtX, selLtY are absolute */
91    obj_ptr->y = selObjLtY;
92    obj_ptr->bbox.ltx = selLtX;
93    obj_ptr->bbox.lty = selLtY;
94    obj_ptr->bbox.rbx = selRbX;
95    obj_ptr->bbox.rby = selRbY;
96    obj_ptr->obbox.ltx = selObjLtX;
97    obj_ptr->obbox.lty = selObjLtY;
98    obj_ptr->obbox.rbx = selObjRbX;
99    obj_ptr->obbox.rby = selObjRbY;
100    if (numObjLocked > 0) obj_ptr->locked = TRUE;
101    AddObj(NULL, topObj, obj_ptr);
102 }
103 
SaveGroupObj(FP,ObjPtr,Level)104 void SaveGroupObj(FP, ObjPtr, Level)
105    FILE *FP;
106    struct ObjRec *ObjPtr;
107    int Level;
108 {
109    if (fprintf(FP, "group([\n") == EOF) writeFileFailed = TRUE;
110    Save(FP, ObjPtr->detail.r->last, Level+1, INVALID);
111    if (fprintf(FP, "],\n") == EOF) writeFileFailed = TRUE;
112    if (fprintf(FP, "%1d,%1d,%1d,", ObjPtr->id, ObjPtr->locked,
113          ObjPtr->invisible) == EOF) {
114       writeFileFailed = TRUE;
115    }
116    if (serializingFile) SaveCreatorID(FP, ObjPtr, "\t");
117    SaveAttrs(FP, ObjPtr->lattr);
118    if (fprintf(FP, ")") == EOF) writeFileFailed = TRUE;
119 }
120 
SaveCompObj(FP,ObjPtr,Level)121 void SaveCompObj(FP, ObjPtr, Level)
122    FILE *FP;
123    struct ObjRec *ObjPtr;
124    int Level;
125 {
126    if (fprintf(FP, "sym([\n") == EOF) writeFileFailed = TRUE;
127    Save(FP, ObjPtr->detail.r->last, Level+1, INVALID);
128    if (fprintf(FP, "],\n") == EOF) writeFileFailed = TRUE;
129    if (fprintf(FP, "%1d,%1d,%1d,", ObjPtr->id, ObjPtr->locked,
130          ObjPtr->invisible) == EOF) {
131       writeFileFailed = TRUE;
132    }
133    if (serializingFile) SaveCreatorID(FP, ObjPtr, "\t");
134    SaveAttrs(FP, ObjPtr->lattr);
135    if (fprintf(FP, ")") == EOF) writeFileFailed = TRUE;
136 }
137 
SaveIconObj(FP,ObjPtr,Level)138 void SaveIconObj(FP, ObjPtr, Level)
139    FILE *FP;
140    struct ObjRec *ObjPtr;
141    int Level;
142 {
143    if (fprintf(FP, "icon([\n") == EOF) writeFileFailed = TRUE;
144    Save(FP, ObjPtr->detail.r->last, Level+1, INVALID);
145    if (fprintf(FP, "],\n") == EOF) writeFileFailed = TRUE;
146    if (fprintf(FP, "\"%s\",%1d,%1d,%1d,%1d,%1d,",
147          ObjPtr->detail.r->s, ObjPtr->id, ObjPtr->detail.r->rotate,
148          ObjPtr->detail.r->flip, ObjPtr->locked, ObjPtr->invisible) == EOF) {
149       writeFileFailed = TRUE;
150    }
151    if (serializingFile) SaveCreatorID(FP, ObjPtr, "\t");
152    SaveAttrs(FP, ObjPtr->lattr);
153    if (fprintf(FP, ")") == EOF) writeFileFailed = TRUE;
154 }
155 
SavePinObj(FP,ObjPtr,Level)156 void SavePinObj(FP, ObjPtr, Level)
157    FILE *FP;
158    struct ObjRec *ObjPtr;
159    int Level;
160 {
161    if (fprintf(FP, "pin([\n") == EOF) writeFileFailed = TRUE;
162    Save(FP, ObjPtr->detail.r->last, Level+1, INVALID);
163    if (fprintf(FP, "],\n") == EOF) writeFileFailed = TRUE;
164 /*
165  * Need to check for pins.
166  * Need to handle the case where connection objects need to be dupped!
167  */
168    if (fprintf(FP, "\"%s\",%1d,%1d,%1d,%1d,%1d,%1d,",
169          ObjPtr->detail.r->s, ObjPtr->id, ObjPtr->detail.r->rotate,
170          ObjPtr->detail.r->flip, ObjPtr->locked, ObjPtr->invisible,
171          ObjPtr->detail.r->pin_connected) == EOF) {
172       writeFileFailed = TRUE;
173    }
174    if (serializingFile) SaveCreatorID(FP, ObjPtr, "\t");
175    SaveAttrs(FP, ObjPtr->lattr);
176    if (fprintf(FP, ")") == EOF) writeFileFailed = TRUE;
177 }
178 
179 static int gnPinWarning=FALSE;
180 
181 #define GETGROUPVALUE(val,name) ScanValue("%d", &(val), name, "group")
182 #define GETSYMVALUE(val,name) ScanValue("%d", &(val), name, "sym")
183 #define GETICONVALUE(val,name) ScanValue("%d", &(val), name, "icon")
184 #define GETPINVALUE(val,name) ScanValue("%d", &(val), name, "pin")
185 
ReadGroupObj(FP,ObjType,ObjPtr)186 void ReadGroupObj(FP, ObjType, ObjPtr)
187    FILE *FP;
188    int ObjType;
189    struct ObjRec **ObjPtr;
190 {
191    struct GroupRec *group_ptr;
192    struct ObjRec *top_obj=NULL, *bot_obj=NULL, *obj_ptr;
193    int ltx, lty, rbx, rby, id=0, locked=FALSE;
194    int obj_ltx, obj_lty, obj_rbx, obj_rby, rotate=0, flip=0;
195    int invisible=FALSE, pin_connected=0;
196    char line[MAXSTRING+1], *s, *s1, tmp_str[MAXSTRING+1];
197 
198    *ObjPtr = NULL;
199    while (ReadObj(FP, &obj_ptr)) {
200       if (obj_ptr == NULL) return;
201 
202       obj_ptr->next = top_obj;
203       if (top_obj == NULL) {
204          bot_obj = obj_ptr;
205       } else {
206          top_obj->prev = obj_ptr;
207       }
208       top_obj = obj_ptr;
209    }
210    if (top_obj == NULL) return;
211 
212    if (fileVersion <= 20 && (ObjType==OBJ_GROUP || ObjType==OBJ_SYM)) {
213       id = objId++;
214    } else {
215       if (fgets(line, MAXSTRING, FP) == NULL) return;
216       scanLineNum++;
217 
218       switch (ObjType) {
219       case OBJ_GROUP:
220          InitScan(line, "\t\n, []");
221          if (fileVersion <= 25) {
222             if (GETGROUPVALUE(id, "id") == INVALID) return;
223          } else if (fileVersion <= 32) {
224             if (GETGROUPVALUE(id, "id") == INVALID ||
225                 GETGROUPVALUE(locked, "locked") == INVALID) {
226                return;
227             }
228          } else {
229             if (GETGROUPVALUE(id, "id") == INVALID ||
230                 GETGROUPVALUE(locked, "locked") == INVALID ||
231                 GETGROUPVALUE(invisible, "invisible") == INVALID) {
232                return;
233             }
234          }
235          if (id >= objId) objId = id+1;
236          break;
237       case OBJ_SYM:
238          InitScan(line, "\t\n, []");
239          if (fileVersion <= 25) {
240             if (GETSYMVALUE(id, "id") == INVALID) return;
241          } else if (fileVersion <= 32) {
242             if (GETSYMVALUE(id, "id") == INVALID ||
243                 GETSYMVALUE(locked, "locked") == INVALID) {
244                return;
245             }
246          } else {
247             if (GETSYMVALUE(id, "id") == INVALID ||
248                 GETSYMVALUE(locked, "locked") == INVALID ||
249                 GETSYMVALUE(invisible, "invisible") == INVALID) {
250                return;
251             }
252          }
253          if (id >= objId) objId = id+1;
254          break;
255       case OBJ_ICON:
256          strcpy(tmp_str, FindChar((int)'"', line));
257          s = FindChar((int)'"', tmp_str);
258          if (fileVersion == INVALID) return;
259 
260          if (fileVersion <= 12) {
261             s1 = FindChar((int)',', s);
262             InitScan(s1, "\t\n, ");
263             if (GETICONVALUE(id, "id") == INVALID) return;
264          } else if (fileVersion <= 25) {
265             s1 = FindChar((int)',', s);
266             InitScan(s1, "\t\n, ");
267             if (GETICONVALUE(id, "id") == INVALID ||
268                 GETICONVALUE(rotate, "rotation") == INVALID ||
269                 GETICONVALUE(flip, "flip") == INVALID) {
270                return;
271             }
272          } else if (fileVersion <= 32) {
273             s1 = FindChar((int)',', s);
274             InitScan(s1, "\t\n, ");
275             if (GETICONVALUE(id, "id") == INVALID ||
276                 GETICONVALUE(rotate, "rotation") == INVALID ||
277                 GETICONVALUE(flip, "flip") == INVALID ||
278                 GETICONVALUE(locked, "locked") == INVALID) {
279                return;
280             }
281          } else {
282             s1 = FindChar((int)',', s);
283             InitScan(s1, "\t\n, ");
284             if (GETICONVALUE(id, "id") == INVALID ||
285                 GETICONVALUE(rotate, "rotation") == INVALID ||
286                 GETICONVALUE(flip, "flip") == INVALID ||
287                 GETICONVALUE(locked, "locked") == INVALID ||
288                 GETICONVALUE(invisible, "invisible") == INVALID) {
289                return;
290             }
291          }
292          if (id >= objId) objId = id+1;
293          *(--s) = '\0';
294          break;
295       case OBJ_PIN:
296          strcpy(tmp_str, FindChar((int)'"', line));
297          s = FindChar((int)'"', tmp_str);
298          if (fileVersion == INVALID) return;
299 
300          if (fileVersion >= 34) {
301             s1 = FindChar((int)',', s);
302             InitScan(s1, "\t\n, ");
303 /*
304  * Need to check for pins.
305  * Need to handle the case where connection objects need to be dupped!
306  */
307             if (GETPINVALUE(id, "id") == INVALID ||
308                 GETPINVALUE(rotate, "rotation") == INVALID ||
309                 GETPINVALUE(flip, "flip") == INVALID ||
310                 GETPINVALUE(locked, "locked") == INVALID ||
311                 GETPINVALUE(invisible, "invisible") == INVALID ||
312                 GETPINVALUE(pin_connected, "pin_connected") == INVALID) {
313                return;
314             }
315          }
316          if (id >= objId) objId = id+1;
317          *(--s) = '\0';
318          if (!gnPinWarning) {
319             gnPinWarning = TRUE;
320             strcpy(gszMsgBox, TgLoadString(STID_WARN_PIN_NOT_SUPPORTED));
321             if (PRTGIF) {
322                fprintf(stderr, "%s\n", gszMsgBox);
323             } else {
324                MsgBox(gszMsgBox, TOOL_NAME, STOP_MB);
325             }
326          }
327          break;
328       }
329    }
330 
331    *ObjPtr = (struct ObjRec *)malloc(sizeof(struct ObjRec));
332    if (*ObjPtr == NULL) FailAllocMessage();
333    memset(*ObjPtr, 0, sizeof(struct ObjRec));
334 
335    top_obj->prev = NULL;
336 
337    group_ptr = (struct GroupRec *)malloc(sizeof(struct GroupRec));
338    if (group_ptr == NULL) FailAllocMessage();
339    memset(group_ptr, 0, sizeof(struct GroupRec));
340    group_ptr->first = top_obj;
341    group_ptr->last = bot_obj;
342    group_ptr->rotate = rotate;
343    group_ptr->flip = flip;
344    group_ptr->deck_index = (-1);
345    group_ptr->pin_connected = pin_connected;
346    group_ptr->first_conn = group_ptr->last_conn = NULL;
347    if (ObjType == OBJ_ICON || ObjType == OBJ_PIN) {
348       strcpy(group_ptr->s, tmp_str);
349    }
350    ltx = top_obj->bbox.ltx;
351    lty = top_obj->bbox.lty;
352    rbx = top_obj->bbox.rbx;
353    rby = top_obj->bbox.rby;
354    obj_ltx = top_obj->obbox.ltx;
355    obj_lty = top_obj->obbox.lty;
356    obj_rbx = top_obj->obbox.rbx;
357    obj_rby = top_obj->obbox.rby;
358    for (obj_ptr = top_obj->next; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
359       if (obj_ptr->bbox.ltx < ltx) ltx = obj_ptr->bbox.ltx;
360       if (obj_ptr->bbox.lty < lty) lty = obj_ptr->bbox.lty;
361       if (obj_ptr->bbox.rbx > rbx) rbx = obj_ptr->bbox.rbx;
362       if (obj_ptr->bbox.rby > rby) rby = obj_ptr->bbox.rby;
363       if (obj_ptr->obbox.ltx < obj_ltx) obj_ltx = obj_ptr->obbox.ltx;
364       if (obj_ptr->obbox.lty < obj_lty) obj_lty = obj_ptr->obbox.lty;
365       if (obj_ptr->obbox.rbx > obj_rbx) obj_rbx = obj_ptr->obbox.rbx;
366       if (obj_ptr->obbox.rby > obj_rby) obj_rby = obj_ptr->obbox.rby;
367    }
368 
369    (*ObjPtr)->x = obj_ltx;
370    (*ObjPtr)->y = obj_lty;
371    (*ObjPtr)->dirty = FALSE;
372    (*ObjPtr)->id = id;
373    (*ObjPtr)->locked = locked;
374    (*ObjPtr)->type = ObjType;
375    (*ObjPtr)->bbox.ltx = ltx;
376    (*ObjPtr)->bbox.lty = lty;
377    (*ObjPtr)->bbox.rbx = rbx;
378    (*ObjPtr)->bbox.rby = rby;
379    (*ObjPtr)->obbox.ltx = obj_ltx;
380    (*ObjPtr)->obbox.lty = obj_lty;
381    (*ObjPtr)->obbox.rbx = obj_rbx;
382    (*ObjPtr)->obbox.rby = obj_rby;
383    (*ObjPtr)->detail.r = group_ptr;
384    (*ObjPtr)->ctm = NULL;
385    (*ObjPtr)->invisible = invisible;
386 }
387 
FreeGroupObj(ObjPtr)388 void FreeGroupObj(ObjPtr)
389    struct ObjRec *ObjPtr;
390 {
391    register struct ObjRec *ptr, *next_obj;
392 
393    for (ptr=ObjPtr->detail.r->first; ptr != NULL; ptr=next_obj) {
394       next_obj = ptr->next;
395       FreeObj(ptr);
396    }
397    free(ObjPtr->detail.r);
398    free(ObjPtr);
399 }
400 
UngroupObj(ObjPtr,TopSelPtr,BotSelPtr)401 void UngroupObj(ObjPtr, TopSelPtr, BotSelPtr)
402    struct ObjRec *ObjPtr;
403    struct SelRec **TopSelPtr, **BotSelPtr;
404    /* ungroup the grouped object ObjPtr to a list of objects   */
405    /* when returns, a list of select pointers will be created, */
406    /*    *TopSelPtr will point to the top of the list, and     */
407    /*    *BotSelPtr will point to the bottom of the list.      */
408 {
409    struct ObjRec *obj_ptr=ObjPtr->detail.r->last;
410 
411    for ( ; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
412       AddObjIntoSel(obj_ptr, NULL, *TopSelPtr, TopSelPtr, BotSelPtr);
413    }
414    (*TopSelPtr)->prev = NULL;
415 }
416 
417 static
NoObjToUngroup(pn_force_ungroup)418 int NoObjToUngroup(pn_force_ungroup)
419    int *pn_force_ungroup;
420 {
421    struct SelRec *sel_ptr=NULL;
422    int every_obj_is_sym_or_icon=TRUE;
423 
424    if (pn_force_ungroup != NULL && topSel != NULL && topSel == botSel) {
425       int obj_type=topSel->obj->type;
426 
427       *pn_force_ungroup = FALSE;
428       if (obj_type==OBJ_ICON || obj_type==OBJ_SYM) {
429          /*
430           * Need to check for pins.
431           */
432          if (MsgBox(TgLoadString(STID_ONE_SIMPLE_GROUP_UNGROUP_ANY), TOOL_NAME,
433                YNC_MB) == MB_ID_YES) {
434             *pn_force_ungroup = TRUE;
435             return FALSE;
436          }
437          return TRUE;
438       }
439    }
440    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
441       int obj_type=sel_ptr->obj->type;
442 
443       if (obj_type == OBJ_GROUP) {
444          return FALSE;
445       } else if (obj_type==OBJ_ICON || obj_type==OBJ_SYM) {
446          /*
447           * Need to check for pins.
448           */
449       } else {
450          every_obj_is_sym_or_icon = FALSE;
451       }
452    }
453    if (every_obj_is_sym_or_icon) {
454       if (MsgBox(TgLoadString(STID_ALL_SIMPLE_GROUP_UNGROUP_ANY), TOOL_NAME,
455             YNC_MB) == MB_ID_YES) {
456          *pn_force_ungroup = TRUE;
457          return FALSE;
458       }
459    }
460    return TRUE;
461 }
462 
UngroupSelObj(highlight,record_cmd)463 void UngroupSelObj(highlight, record_cmd)
464    int highlight, record_cmd;
465 {
466    struct SelRec *sel_ptr=NULL, *next_sel=NULL;
467    struct ObjRec *obj_ptr=NULL;
468    int sel_ltx, sel_lty, sel_rbx, sel_rby;
469    int changed=FALSE, force_ungroup_single_obj=FALSE;
470 
471    if (topSel==NULL || NoObjToUngroup(&force_ungroup_single_obj)) return;
472 
473    sel_ltx = selLtX; sel_lty = selLtY;
474    sel_rbx = selRbX; sel_rby = selRbY;
475 
476    if (highlight) HighLightReverse();
477    if (record_cmd) StartCompositeCmd();
478    if (force_ungroup_single_obj) {
479       for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
480          struct AttrRec *attr_ptr=NULL;
481 
482          obj_ptr = sel_ptr->obj;
483 
484          switch (obj_ptr->type) {
485          case OBJ_ICON:
486             PrepareToReplaceAnObj(obj_ptr);
487             obj_ptr->type = OBJ_GROUP;
488             attr_ptr = obj_ptr->fattr;
489             for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
490                attr_ptr->inherited = FALSE;
491             }
492             AdjObjBBox(obj_ptr);
493             RecordReplaceAnObj(obj_ptr);
494             break;
495          case OBJ_SYM:
496             PrepareToReplaceAnObj(obj_ptr);
497             obj_ptr->type = OBJ_GROUP;
498             AdjObjBBox(obj_ptr);
499             RecordReplaceAnObj(obj_ptr);
500             break;
501          case OBJ_PIN:
502             /*
503              * Need to check for pins.
504              */
505             break;
506          default: break;
507          }
508       }
509    }
510    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=next_sel) {
511       next_sel = sel_ptr->next;
512       obj_ptr = sel_ptr->obj;
513       if (obj_ptr->type == OBJ_GROUP) {
514          int count;
515          struct SelRec *tmp_sel_ptr, *tmp_top_sel=NULL, *tmp_bot_sel=NULL;
516 
517          changed = TRUE;
518          if (record_cmd) PrepareToReplaceAnObj(obj_ptr);
519 
520          UngroupObj(obj_ptr, &tmp_top_sel, &tmp_bot_sel);
521          if (gnDeleteAttrsWhileUngrouping) {
522             DelAllAttrs(obj_ptr->fattr);
523          } else {
524             DetachGroupAttrs(obj_ptr, &tmp_top_sel, &tmp_bot_sel);
525          }
526          obj_ptr->detail.r->first->prev = obj_ptr->prev;
527          if (obj_ptr->prev == NULL) {
528             curPage->top = topObj = obj_ptr->detail.r->first;
529          } else {
530             obj_ptr->prev->next = obj_ptr->detail.r->first;
531          }
532          obj_ptr->detail.r->last->next = obj_ptr->next;
533          if (obj_ptr->next == NULL) {
534             curPage->bot = botObj = obj_ptr->detail.r->last;
535          } else {
536             obj_ptr->next->prev = obj_ptr->detail.r->last;
537          }
538          count = 0;
539          for (tmp_sel_ptr=tmp_top_sel; tmp_sel_ptr!=NULL;
540                tmp_sel_ptr=tmp_sel_ptr->next) {
541             count++;
542          }
543          if (record_cmd) {
544             RecordCmd(CMD_ONE_TO_MANY, NULL, tmp_top_sel, tmp_bot_sel, count);
545          }
546          tmp_top_sel->prev = sel_ptr->prev;
547          if (sel_ptr->prev == NULL) {
548             topSel = tmp_top_sel;
549          } else {
550             sel_ptr->prev->next = tmp_top_sel;
551          }
552          tmp_bot_sel->next = sel_ptr->next;
553          if (sel_ptr->next == NULL) {
554             botSel = tmp_bot_sel;
555          } else {
556             sel_ptr->next->prev = tmp_bot_sel;
557          }
558          free(sel_ptr);
559          /*
560           * What about obj_ptr->detail.r->first_conn and
561           *       obj_ptr->detail.r->last_conn?
562           */
563          free(obj_ptr->detail.r);
564          free(obj_ptr);
565       }
566    }
567    if (record_cmd) EndCompositeCmd();
568    if (changed) {
569       UpdSelBBox();
570       RedrawAreas(botObj,
571             sel_ltx-GRID_ABS_SIZE(1), sel_lty-GRID_ABS_SIZE(1),
572             sel_rbx+GRID_ABS_SIZE(1), sel_rby+GRID_ABS_SIZE(1),
573             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
574             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
575       SetFileModified(TRUE);
576       justDupped = FALSE;
577       Msg(TgLoadString(STID_SEL_OBJ_ARE_UNGROUPED));
578    }
579    if (highlight) HighLightForward();
580 }
581 
LockSelObj()582 void LockSelObj()
583 {
584    register struct SelRec *sel_ptr;
585    register struct ObjRec *obj_ptr;
586    int changed=FALSE;
587 
588    if (topSel==NULL) {
589       Msg(TgLoadString(STID_NO_OBJ_TO_LOCK));
590       return;
591    }
592    if (curChoice==VERTEXMODE) {
593       Msg(TgLoadString(STID_CANNOT_LOCK_IN_VERTEX_MODE));
594       return;
595    }
596    HighLightReverse();
597    StartCompositeCmd();
598    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
599       obj_ptr = sel_ptr->obj;
600       if (!obj_ptr->locked) {
601          changed = TRUE;
602          PrepareToReplaceAnObj(obj_ptr);
603          obj_ptr->locked = TRUE;
604          RecordReplaceAnObj(obj_ptr);
605       }
606    }
607    EndCompositeCmd();
608    HighLightForward();
609    if (changed) {
610       UpdSelBBox();
611       SetFileModified(TRUE);
612       justDupped = FALSE;
613       Msg(TgLoadString(STID_SEL_OBJ_ARE_LOCKED));
614    }
615 }
616 
UnlockSelObj()617 void UnlockSelObj()
618 {
619    register struct SelRec *sel_ptr;
620    register struct ObjRec *obj_ptr;
621    int changed=FALSE;
622 
623    if (topSel==NULL) {
624       Msg(TgLoadString(STID_NO_OBJ_TO_UNLOCK));
625       return;
626    }
627    if (curChoice==VERTEXMODE) {
628       Msg(TgLoadString(STID_CANNOT_UNLOCK_IN_VERTEX_MODE));
629       return;
630    }
631    HighLightReverse();
632    StartCompositeCmd();
633    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
634       obj_ptr = sel_ptr->obj;
635       if (obj_ptr->locked) {
636          changed = TRUE;
637          PrepareToReplaceAnObj(obj_ptr);
638          obj_ptr->locked = FALSE;
639          RecordReplaceAnObj(obj_ptr);
640       }
641    }
642    EndCompositeCmd();
643    HighLightForward();
644    if (changed) {
645       UpdSelBBox();
646       SetFileModified(TRUE);
647       justDupped = FALSE;
648       Msg(TgLoadString(STID_SEL_OBJ_ARE_UNLOCKED));
649    }
650 }
651