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/stretch.c,v 1.63 2011/05/16 16:21:59 william Exp $
19  */
20 
21 #define _INCLUDE_FROM_STRETCH_C_
22 
23 #include "tgifdefs.h"
24 #include "cmdids.h"
25 
26 #include "align.e"
27 #include "arc.e"
28 #include "auxtext.e"
29 #include "choice.e"
30 #include "cmd.e"
31 #include "color.e"
32 #include "cursor.e"
33 #include "dialog.e"
34 #include "drawing.e"
35 #include "dup.e"
36 #include "exec.e"
37 #include "file.e"
38 #include "font.e"
39 #include "grid.e"
40 #include "mainloop.e"
41 #include "mainmenu.e"
42 #include "mark.e"
43 #include "menu.e"
44 #include "menuinfo.e"
45 #include "miniline.e"
46 #include "move.e"
47 #include "msg.e"
48 #include "navigate.e"
49 #include "obj.e"
50 #include "page.e"
51 #include "poly.e"
52 #include "raster.e"
53 #include "rect.e"
54 #include "ruler.e"
55 #include "scroll.e"
56 #include "select.e"
57 #include "setup.e"
58 #include "spline.e"
59 #include "stretch.e"
60 #include "strtbl.e"
61 #include "text.e"
62 #include "util.e"
63 #include "xbitmap.e"
64 #include "xpixmap.e"
65 
66 int stretchableText=FALSE;
67 int compoundObjWithTextStretchableForPSE=TRUE;
68 int rotationIncrement=(45<<6); /* degrees*64 */
69 
70 int autoRotatePivot=FALSE;
71 int rotatePivotAbsXYValid=FALSE;
72 int rotatePivotAbsX=0;
73 int rotatePivotAbsY=0;
74 
75 static int stretchingEverything=FALSE;
76 
77 static
PtIn4Corners(XOff,YOff,BBox,Corner)78 int PtIn4Corners(XOff, YOff, BBox, Corner)
79    int XOff, YOff, * Corner;
80    struct BBRec BBox;
81 {
82    if (PtInMark(XOff, YOff, OFFSET_X(BBox.ltx), OFFSET_Y(BBox.lty))) {
83       *Corner = 1;
84       return TRUE;
85    }
86    if (PtInMark(XOff, YOff, OFFSET_X(BBox.ltx), OFFSET_Y(BBox.rby))) {
87       *Corner = 7;
88       return TRUE;
89    }
90    if (PtInMark(XOff, YOff, OFFSET_X(BBox.rbx), OFFSET_Y(BBox.lty))) {
91       *Corner = 3;
92       return TRUE;
93    }
94    if (PtInMark(XOff, YOff, OFFSET_X(BBox.rbx), OFFSET_Y(BBox.rby))) {
95       *Corner = 5;
96       return TRUE;
97    }
98    return FALSE;
99 }
100 
101 static
PtIn8Places(XOff,YOff,BBox,Corner)102 int PtIn8Places(XOff, YOff, BBox, Corner)
103    int XOff, YOff, *Corner;
104    struct BBRec BBox;
105 {
106    register int xmid, ymid;
107 
108    if (PtIn4Corners(XOff, YOff, BBox, Corner)) return TRUE;
109 
110    xmid = ((BBox.ltx+BBox.rbx)>>1);
111    if (PtInMark(XOff, YOff, OFFSET_X(xmid), OFFSET_Y(BBox.lty))) {
112       *Corner = 2;
113       return TRUE;
114    }
115    if (PtInMark(XOff, YOff, OFFSET_X(xmid), OFFSET_Y(BBox.rby))) {
116       *Corner = 6;
117       return TRUE;
118    }
119    ymid = ((BBox.lty+BBox.rby)>>1);
120    if (PtInMark(XOff, YOff, OFFSET_X(BBox.ltx), OFFSET_Y(ymid))) {
121       *Corner = 8;
122       return TRUE;
123    }
124    if (PtInMark(XOff, YOff, OFFSET_X(BBox.rbx), OFFSET_Y(ymid))) {
125       *Corner = 4;
126       return TRUE;
127    }
128    return FALSE;
129 }
130 
PtInPolyMark(ObjPtr,XOff,YOff,NumPts,V,Index)131 int PtInPolyMark(ObjPtr, XOff, YOff, NumPts, V, Index)
132    struct ObjRec *ObjPtr;
133    int XOff, YOff, NumPts, * Index;
134    IntPoint *V;
135 {
136    int i;
137 
138    if (ObjPtr->ctm == NULL) {
139       for (i = 0; i < NumPts; i++) {
140          if (PtInMark(XOff, YOff, OFFSET_X(V[i].x), OFFSET_Y(V[i].y))) {
141             *Index = i;
142             return TRUE;
143          }
144       }
145    } else {
146       for (i = 0; i < NumPts; i++) {
147          int x, y;
148 
149          TransformPointThroughCTM(V[i].x-ObjPtr->x, V[i].y-ObjPtr->y,
150                ObjPtr->ctm, &x, &y);
151          if (PtInMark(XOff, YOff, OFFSET_X(x+ObjPtr->x),
152                OFFSET_Y(y+ObjPtr->y))) {
153             *Index = i;
154             return TRUE;
155          }
156       }
157    }
158    return FALSE;
159 }
160 
RetractedArrowAttr(obj_ptr)161 int RetractedArrowAttr(obj_ptr)
162    struct ObjRec *obj_ptr;
163 {
164    register struct AttrRec *attr_ptr;
165 
166    if (obj_ptr->type != OBJ_POLY) return FALSE;
167 
168    if (obj_ptr->detail.p->n <= 2) return FALSE;
169    for (attr_ptr=obj_ptr->lattr; attr_ptr!=NULL; attr_ptr=attr_ptr->prev) {
170       if (*attr_ptr->attr_name.s=='\0' && strcmp(attr_ptr->attr_value.s,
171             "retracted_arrows")==0) {
172          return TRUE;
173       }
174    }
175    return FALSE;
176 }
177 
AutoRetractedArrowAttr(obj_ptr,check_v_count)178 int AutoRetractedArrowAttr(obj_ptr, check_v_count)
179    /* if check_v_count == TRUE:  return FALSE if poly_ptr->n != 3 */
180    /* if check_v_count == FALSE: skip the poly_ptr->n check */
181    struct ObjRec *obj_ptr;
182    int check_v_count;
183 {
184    register struct AttrRec *attr_ptr;
185 
186    if (obj_ptr->type == OBJ_POLY) {
187       struct PolyRec *poly_ptr=obj_ptr->detail.p;
188 
189       if (poly_ptr->style == LS_PLAIN || poly_ptr->style == LS_DOUBLE ||
190             (check_v_count && poly_ptr->n != 3)) {
191          return FALSE;
192       }
193    } else {
194       return FALSE;
195    }
196    for (attr_ptr=obj_ptr->lattr; attr_ptr!=NULL; attr_ptr=attr_ptr->prev) {
197       if (*attr_ptr->attr_name.s=='\0' &&
198             strcmp(attr_ptr->attr_value.s, "auto_retracted_arrows")==0) {
199          return TRUE;
200       }
201    }
202    return FALSE;
203 }
204 
AutoCenterAttr(obj_ptr)205 int AutoCenterAttr(obj_ptr)
206    struct ObjRec *obj_ptr;
207 {
208    register struct AttrRec *attr_ptr;
209 
210    for (attr_ptr=obj_ptr->lattr; attr_ptr!=NULL; attr_ptr=attr_ptr->prev) {
211       if (*attr_ptr->attr_name.s=='\0' && strcmp(attr_ptr->attr_value.s,
212             "auto_center_attr")==0) {
213          return TRUE;
214       }
215    }
216    return FALSE;
217 }
218 
CenterObjInOBBox(TextObjPtr,OBBox,BBoxReturn)219 void CenterObjInOBBox(TextObjPtr, OBBox, BBoxReturn)
220    struct ObjRec *TextObjPtr;
221    struct BBRec OBBox, *BBoxReturn;
222 {
223    int text_w, text_h, bbox_w, bbox_h, dx, dy;
224 
225    if (BBoxReturn != NULL) {
226       BBoxReturn->ltx = TextObjPtr->bbox.ltx;
227       BBoxReturn->lty = TextObjPtr->bbox.lty;
228       BBoxReturn->rbx = TextObjPtr->bbox.rbx;
229       BBoxReturn->rby = TextObjPtr->bbox.rby;
230    }
231    text_w = TextObjPtr->obbox.rbx-TextObjPtr->obbox.ltx;
232    text_h = TextObjPtr->obbox.rby-TextObjPtr->obbox.lty;
233    bbox_w = OBBox.rbx - OBBox.ltx;
234    bbox_h = OBBox.rby - OBBox.lty;
235    if (text_w > bbox_w) {
236       dx = OBBox.ltx-((text_w-bbox_w)>>1)-TextObjPtr->obbox.ltx;
237    } else {
238       dx = OBBox.ltx+((bbox_w-text_w)>>1)-TextObjPtr->obbox.ltx;
239    }
240    if (text_h > bbox_h) {
241       dy = OBBox.lty-((text_h-bbox_h)>>1)-TextObjPtr->obbox.lty;
242    } else {
243       dy = OBBox.lty+((bbox_h-text_h)>>1)-TextObjPtr->obbox.lty;
244    }
245    MoveObj(TextObjPtr, dx, dy);
246    if (BBoxReturn != NULL) {
247       if (TextObjPtr->bbox.ltx < BBoxReturn->ltx) {
248          BBoxReturn->ltx = TextObjPtr->bbox.ltx;
249       }
250       if (TextObjPtr->bbox.lty < BBoxReturn->lty) {
251          BBoxReturn->lty = TextObjPtr->bbox.lty;
252       }
253       if (TextObjPtr->bbox.rbx > BBoxReturn->rbx) {
254          BBoxReturn->rbx = TextObjPtr->bbox.rbx;
255       }
256       if (TextObjPtr->bbox.rby > BBoxReturn->rby) {
257          BBoxReturn->rby = TextObjPtr->bbox.rby;
258       }
259    }
260 }
261 
PtInSelMark(XOff,YOff,Corner)262 struct SelRec *PtInSelMark(XOff, YOff, Corner)
263    int XOff, YOff, *Corner;
264    /* XOff and YOff are screen offsets */
265    /* 1 2 3 */
266    /* 8   4 */
267    /* 7 6 5 */
268 {
269    register struct SelRec *sel_ptr;
270    register struct ObjRec *obj_ptr;
271 
272    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
273       obj_ptr = sel_ptr->obj;
274 
275       switch (obj_ptr->type) {
276       case OBJ_POLY:
277          if (obj_ptr->detail.p->curved == LT_STRUCT_SPLINE) {
278             if (PtInPolyMark(obj_ptr, XOff, YOff, obj_ptr->detail.p->ssn,
279                   obj_ptr->detail.p->ssvlist, Corner)) {
280                return sel_ptr;
281             }
282          } else {
283             if (PtInPolyMark(obj_ptr, XOff, YOff, obj_ptr->detail.p->n,
284                   obj_ptr->detail.p->vlist, Corner)) {
285                return sel_ptr;
286             }
287          }
288          break;
289       case OBJ_POLYGON:
290          if (obj_ptr->detail.g->curved == LT_STRUCT_SPLINE) {
291             if (PtInPolyMark(obj_ptr, XOff, YOff, obj_ptr->detail.g->ssn-1,
292                   obj_ptr->detail.g->ssvlist, Corner)) {
293                return sel_ptr;
294             }
295          } else {
296             if (PtInPolyMark(obj_ptr, XOff, YOff, obj_ptr->detail.g->n-1,
297                   obj_ptr->detail.g->vlist, Corner)) {
298                return sel_ptr;
299             }
300          }
301          break;
302       case OBJ_BOX:
303       case OBJ_GROUP:
304       case OBJ_ICON:
305       case OBJ_SYM:
306       case OBJ_PIN:
307       case OBJ_OVAL:
308       case OBJ_ARC:
309       case OBJ_RCBOX:
310       case OBJ_XBM:
311       case OBJ_XPM:
312          if (PtIn8Places(XOff, YOff, obj_ptr->obbox, Corner)) {
313             return sel_ptr;
314          }
315          break;
316       case OBJ_TEXT:
317          if (curChoice == ROTATEMODE) {
318             if (PtIn8Places(XOff, YOff, obj_ptr->obbox, Corner)) {
319                return sel_ptr;
320             }
321          } else if (curChoice == NOTHING) {
322             if (stretchableText &&
323                   PtIn8Places(XOff, YOff, obj_ptr->obbox, Corner)) {
324                return sel_ptr;
325             }
326          }
327          break;
328       }
329    }
330    return NULL;
331 }
332 
HasOnReshape(obj_ptr,pp_name_attr)333 int HasOnReshape(obj_ptr, pp_name_attr)
334    struct ObjRec *obj_ptr;
335    struct AttrRec **pp_name_attr;
336    /* returns the name attribute pointer */
337 {
338    if (pp_name_attr != NULL) *pp_name_attr = NULL;
339    if ((obj_ptr->type == OBJ_POLY || obj_ptr->type == OBJ_POLYGON) &&
340          obj_ptr->ctm == NULL) {
341       if (FindAttrWithName(obj_ptr, "on_reshape=", NULL) != NULL) {
342          struct AttrRec *attr=FindAttrWithName(obj_ptr, "name=", NULL);
343 
344          if (attr != NULL && *attr->attr_value.s != '\0') {
345             if (pp_name_attr != NULL) *pp_name_attr = attr;
346             return TRUE;
347          }
348       }
349    }
350    return FALSE;
351 }
352 
353 static
SkipOnResize(obj_ptr)354 int SkipOnResize(obj_ptr)
355    struct ObjRec *obj_ptr;
356 {
357    if (obj_ptr->type != OBJ_GROUP &&
358          obj_ptr->type != OBJ_ICON &&
359          obj_ptr->type != OBJ_SYM &&
360          obj_ptr->type != OBJ_PIN) {
361       if (obj_ptr->ctm != NULL) {
362          return TRUE;
363       }
364    } else {
365       struct ObjRec *sub_obj=obj_ptr->detail.r->last;
366 
367       for ( ; sub_obj != NULL; sub_obj=sub_obj->prev) {
368          if (sub_obj->type != OBJ_GROUP &&
369                sub_obj->type != OBJ_ICON &&
370                sub_obj->type != OBJ_SYM &&
371                sub_obj->type != OBJ_PIN &&
372                sub_obj->ctm != NULL) {
373             return TRUE;
374          }
375       }
376    }
377    return FALSE;
378 }
379 
380 static
HasOnResize(obj_ptr,pp_name_attr)381 int HasOnResize(obj_ptr, pp_name_attr)
382    struct ObjRec *obj_ptr;
383    struct AttrRec **pp_name_attr;
384    /* returns the name attribute pointer */
385 {
386    if (pp_name_attr != NULL) *pp_name_attr = NULL;
387    if (!SkipOnResize(obj_ptr)) {
388       if (FindAttrWithName(obj_ptr, "on_resize=", NULL) != NULL) {
389          struct AttrRec *attr=FindAttrWithName(obj_ptr, "name=", NULL);
390 
391          if (attr != NULL && *attr->attr_value.s != '\0') {
392             if (pp_name_attr != NULL) *pp_name_attr = attr;
393             return TRUE;
394          }
395       }
396    }
397    return FALSE;
398 }
399 
400 static
NeedToProcessOnResize(pn_num_to_resize)401 char **NeedToProcessOnResize(pn_num_to_resize)
402    int *pn_num_to_resize;
403 {
404    int num_to_resize=0;
405    char **ppsz_names=NULL;
406    struct SelRec *sel_ptr=NULL;
407 
408    *pn_num_to_resize = 0;
409    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
410       struct AttrRec *name_attr=NULL;
411 
412       if (HasOnResize(sel_ptr->obj, &name_attr) && name_attr != NULL) {
413          num_to_resize++;
414          if (ppsz_names == NULL) {
415             ppsz_names = (char**)malloc(sizeof(char*));
416          } else {
417             ppsz_names = (char**)realloc(ppsz_names,
418                   num_to_resize*sizeof(char*));
419          }
420          if (ppsz_names == NULL) FailAllocMessage();
421          ppsz_names[num_to_resize-1] = (char*)(long)(sel_ptr->obj->id);
422       }
423    }
424    if (num_to_resize == 0) return NULL;
425 
426    *pn_num_to_resize = num_to_resize;
427    return ppsz_names;
428 }
429 
430 static
FindObjWithOID(oid)431 struct ObjRec *FindObjWithOID(oid)
432    int oid;
433 {
434    struct ObjRec *obj_ptr=NULL;
435 
436    for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
437       if (obj_ptr->id == oid) {
438          return obj_ptr;
439       }
440    }
441    return NULL;
442 }
443 
444 static
DoOnResize(ppsz_names,num_to_resize)445 void DoOnResize(ppsz_names, num_to_resize)
446    char **ppsz_names;
447    int num_to_resize;
448 {
449    int i=0;
450 
451    RemoveAllSel();
452    for (i=0; i < num_to_resize; i++) {
453       struct AttrRec *exec_attr=NULL;
454       struct ObjRec *obj_ptr=FindObjWithOID((int)(long)(ppsz_names[i]));
455 
456       if (obj_ptr != NULL && !SkipOnResize(obj_ptr)) {
457          exec_attr = FindAttrWithName(obj_ptr, "on_resize=", NULL);
458       }
459       if (exec_attr != NULL) {
460          DoExecLoop(obj_ptr, exec_attr);
461       } else {
462          sprintf(gszMsgBox,
463                TgLoadCachedString(CSTID_CANT_FIND_OBJ_NAME_ON_RESIZE),
464                ppsz_names[i]);
465          Msg(gszMsgBox);
466          *ppsz_names[i] = '\0';
467       }
468    }
469    for (i=0; i < num_to_resize; i++) {
470       struct ObjRec *obj_ptr=FindObjWithOID((int)(long)(ppsz_names[i]));
471 
472       if (obj_ptr != NULL) {
473          if (!AlreadySelected(obj_ptr)) {
474             AddNewSelObj(obj_ptr);
475          }
476       }
477    }
478    free(ppsz_names);
479 
480    UpdSelBBox();
481 }
482 
483 #define STRETCH_DRAW  (FALSE)
484 #define STRETCH_ERASE (TRUE)
485 #define STRETCH_CLICK (FALSE)
486 #define STRETCH_DRAG  (TRUE)
487 
488 #define STRETCH_STARTSHOW 0
489 #define STRETCH_DOSHOW    1
490 #define STRETCH_ENDSHOW   2
491 
GetVlistIndexFromStretchStructuredSplineInfo(psssi,ss_index)492 int GetVlistIndexFromStretchStructuredSplineInfo(psssi, ss_index)
493    StretchStructuredSplineInfo *psssi;
494    int ss_index;
495 {
496    if (psssi->hinge) {
497       return psssi->orig_hinge_index;
498    } if (psssi->earlier_smooth_selected) {
499       return psssi->orig_hinge_index-1;
500    } else {
501       return psssi->orig_hinge_index+1;
502    }
503 }
504 
505 static
DoStretchPolyMeasureCursor(start,num_pts,vs,index,dx,dy,erase,drag,obj_type,grid_x,grid_y)506 void DoStretchPolyMeasureCursor(start, num_pts, vs, index, dx, dy, erase, drag,
507       obj_type, grid_x, grid_y)
508    int start, num_pts, index, dx, dy, erase, drag, obj_type, grid_x, grid_y;
509    IntPoint *vs;
510 {
511    static IntPoint prev_pt={0,0}, mid_pt={0,0}, next_pt={0,0};
512    char buf[80], x_buf[80], y_buf[80], a_buf[80];
513    int x=vs[index].x+dx, y=vs[index].y+dy, angle2=0;
514 
515 #ifdef _TGIF_DBG /* debug, do not translate */
516    TgAssert((obj_type == OBJ_POLY || index != num_pts-1),
517          "index == num_pts-1 for a polygon in DoStretchPolyMeasureCursor()",
518          NULL);
519 #endif /* _TGIF_DBG */
520    if (index == 0 || index == num_pts-1) {
521       if (obj_type == OBJ_POLY) {
522          if (num_pts > 2) {
523             if (index == 0) {
524                prev_pt.x = x;       prev_pt.y = y;
525                mid_pt.x = vs[1].x;  mid_pt.y = vs[1].y;
526                next_pt.x = vs[2].x; next_pt.y = vs[2].y;
527             } else {
528                prev_pt.x = vs[num_pts-3].x; prev_pt.y = vs[num_pts-3].y;
529                mid_pt.x = vs[num_pts-2].x;  mid_pt.y = vs[num_pts-2].y;
530                next_pt.x = x;               next_pt.y = y;
531             }
532          } else {
533             if (index == 0) {
534                prev_pt.x = x;           prev_pt.y = y;
535                mid_pt.x = vs[1].x;      mid_pt.y = vs[1].y;
536                next_pt.x = vs[1].x+100; next_pt.y = vs[1].y;
537             } else {
538                prev_pt.x = x;           prev_pt.y = y;
539                mid_pt.x = vs[0].x;      mid_pt.y = vs[0].y;
540                next_pt.x = vs[0].x+100; next_pt.y = vs[0].y;
541             }
542          }
543       } else {
544          prev_pt.x = vs[1].x;         prev_pt.y = vs[1].y;
545          mid_pt.x = x;                mid_pt.y = y;
546          next_pt.x = vs[num_pts-2].x; next_pt.y = vs[num_pts-2].y;
547       }
548    } else {
549       prev_pt.x = vs[index-1].x; prev_pt.y = vs[index-1].y;
550       mid_pt.x = x;              mid_pt.y = y;
551       next_pt.x = vs[index+1].x; next_pt.y = vs[index+1].y;
552    }
553    if (num_pts == 2) {
554       if (index == 0 && x == mid_pt.x && y == mid_pt.y) {
555          strcpy(a_buf, "0");
556       } else {
557          PointsToArc(mid_pt.x, mid_pt.y, x, y, mid_pt.x+100, mid_pt.y,
558                ARC_CCW, FALSE, NULL, NULL, NULL, NULL, NULL, &angle2);
559          if (angle2 > 180*64) angle2=(360*64)-angle2;
560          FormatAngle(angle2, a_buf);
561       }
562    } else {
563       if ((index == 0 || index == num_pts-1) &&
564             (x == mid_pt.x && y == mid_pt.y)) {
565          strcpy(a_buf, "0");
566       } else if ((index != 0 && index != num_pts-1) &&
567             ((x == prev_pt.x && y == prev_pt.y) ||
568             (x == next_pt.x && y == next_pt.y))) {
569          strcpy(a_buf, "180");
570       } else {
571          PointsToArc(mid_pt.x, mid_pt.y, prev_pt.x, prev_pt.y, next_pt.x,
572                next_pt.y, ARC_CCW, FALSE, NULL, NULL, NULL, NULL, NULL,
573                &angle2);
574          if (angle2 > 180*64) angle2=(360*64)-angle2;
575          FormatAngle(angle2, a_buf);
576       }
577    }
578    PixelToMeasurementUnit(x_buf, x);
579    PixelToMeasurementUnit(y_buf, y);
580    if (curChoice == FREEHAND) {
581       sprintf(buf, "x=%s\ny=%s", x_buf, y_buf);
582    } else {
583       sprintf(buf, "x=%s\ny=%s\nangle=%s", x_buf, y_buf, a_buf);
584    }
585    switch (start) {
586    case STRETCH_STARTSHOW:
587       StartShowMeasureCursor(grid_x, grid_y, buf, TRUE);
588       break;
589    case STRETCH_DOSHOW: ShowMeasureCursor(grid_x, grid_y, buf, TRUE); break;
590    case STRETCH_ENDSHOW: EndShowMeasureCursor(grid_x, grid_y, buf, TRUE); break;
591    }
592 }
593 
594 static
DoStretchStructSplineMeasureCursor(start,start_v,dx,dy,grid_x,grid_y)595 void DoStretchStructSplineMeasureCursor(start, start_v, dx, dy, grid_x, grid_y)
596    int start, dx, dy, grid_x, grid_y;
597    IntPoint *start_v;
598 {
599    int x=0, y=0;
600    char buf[MAXSTRING], x_buf[80], y_buf[80], dx_buf[80], dy_buf[80];
601 
602    x = start_v->x + dx;
603    y = start_v->y + dy;
604    PixelToMeasurementUnit(x_buf, x);
605    PixelToMeasurementUnit(y_buf, y);
606    PixelToMeasurementUnit(dx_buf, dx);
607    PixelToMeasurementUnit(dy_buf, dy);
608    sprintf(buf, "x=%s\ny=%s\ndx=%s\ndy=%s", x_buf, y_buf, dx_buf, dy_buf);
609 
610    switch (start) {
611    case STRETCH_STARTSHOW:
612       StartShowMeasureCursor(grid_x, grid_y, buf, TRUE);
613       break;
614    case STRETCH_DOSHOW: ShowMeasureCursor(grid_x, grid_y, buf, TRUE); break;
615    case STRETCH_ENDSHOW: EndShowMeasureCursor(grid_x, grid_y, buf, TRUE); break;
616    }
617 }
618 
SetIPTInfoForStretchPoly(index,n,vs,psssi)619 void SetIPTInfoForStretchPoly(index, n, vs, psssi)
620    int index, n; /* 0 <= index < ssn */
621    IntPoint *vs;
622    StretchStructuredSplineInfo *psssi;
623 {
624    int i=0, j=0, k=0, num_hinge_vs=(n+2)/3;
625 
626 #ifdef _TGIF_DBG /* debug, do not translate */
627    TgAssert((n+2)%3 == 0, "invalid n in SetIPTInfoForStretchPoly()", NULL);
628 #endif /* _TGIF_DBG */
629 
630    memset(psssi, 0, sizeof(StretchStructuredSplineInfo));
631    for (i=0, j=0, k=0; i < num_hinge_vs; i++, j+=3, k++) {
632       int set_the_rest_for_this=FALSE;
633 
634       if (i == 0) {
635          if (k == index) {
636             psssi->hinge = TRUE;
637             set_the_rest_for_this = TRUE;
638          }
639          if (vs[0].x != vs[1].x || vs[0].y != vs[1].y) {
640             k++;
641             if (k == index) {
642                psssi->hinge = FALSE;
643                psssi->earlier_smooth_selected = FALSE;
644                set_the_rest_for_this = TRUE;
645             }
646          }
647          if (set_the_rest_for_this) {
648             psssi->orig_hinge_index = j;
649             psssi->prev_valid = FALSE;
650             psssi->next_valid = TRUE;
651             psssi->ipt.earlier_smooth_pt.x = vs[0].x;
652             psssi->ipt.earlier_smooth_pt.y = vs[0].y;
653             psssi->ipt.hinge_pt.x = vs[0].x;
654             psssi->ipt.hinge_pt.y = vs[0].y;
655             psssi->ipt.later_smooth_pt.x = vs[1].x;
656             psssi->ipt.later_smooth_pt.y = vs[1].y;
657             if (vs[0].x != vs[1].x || vs[0].y != vs[1].y) {
658                psssi->ipt.later_valid = TRUE;
659             }
660             psssi->ipt_next.earlier_smooth_pt.x = vs[2].x;
661             psssi->ipt_next.earlier_smooth_pt.y = vs[2].y;
662             psssi->ipt_next.hinge_pt.x = vs[3].x;
663             psssi->ipt_next.hinge_pt.y = vs[3].y;
664             if (num_hinge_vs > 2) {
665                psssi->ipt_next.later_smooth_pt.x = vs[4].x;
666                psssi->ipt_next.later_smooth_pt.y = vs[4].y;
667             } else {
668                /* this may not be necessary */
669                psssi->ipt_next.later_smooth_pt.x = vs[3].x;
670                psssi->ipt_next.later_smooth_pt.y = vs[3].y;
671             }
672             if (vs[2].x != vs[3].x || vs[2].y != vs[3].y) {
673                psssi->ipt_next.earlier_valid = TRUE;
674             }
675             if (vs[4].x != vs[3].x || vs[4].y != vs[3].y) {
676                psssi->ipt_next.later_valid = TRUE;
677             }
678          }
679       } else if (i == num_hinge_vs-1) {
680          if (vs[n-1].x != vs[n-2].x || vs[n-1].y != vs[n-2].y) {
681             if (k == index) {
682                psssi->hinge = FALSE;
683                psssi->earlier_smooth_selected = TRUE;
684                set_the_rest_for_this = TRUE;
685             }
686             k++;
687          }
688          if (k == index) {
689             psssi->hinge = TRUE;
690             set_the_rest_for_this = TRUE;
691          }
692          if (set_the_rest_for_this) {
693             psssi->orig_hinge_index = j;
694             psssi->prev_valid = TRUE;
695             psssi->next_valid = FALSE;
696             psssi->ipt.later_smooth_pt.x = vs[n-1].x;
697             psssi->ipt.later_smooth_pt.y = vs[n-1].y;
698             psssi->ipt.hinge_pt.x = vs[n-1].x;
699             psssi->ipt.hinge_pt.y = vs[n-1].y;
700             psssi->ipt.earlier_smooth_pt.x = vs[n-2].x;
701             psssi->ipt.earlier_smooth_pt.y = vs[n-2].y;
702             if (vs[n-1].x != vs[n-2].x || vs[n-1].y != vs[n-2].y) {
703                psssi->ipt.earlier_valid = TRUE;
704             }
705             psssi->ipt_prev.later_smooth_pt.x = vs[n-3].x;
706             psssi->ipt_prev.later_smooth_pt.y = vs[n-3].y;
707             psssi->ipt_prev.hinge_pt.x = vs[n-4].x;
708             psssi->ipt_prev.hinge_pt.y = vs[n-4].y;
709             if (num_hinge_vs > 2) {
710                psssi->ipt_prev.earlier_smooth_pt.x = vs[n-5].x;
711                psssi->ipt_prev.earlier_smooth_pt.y = vs[n-5].y;
712             } else {
713                /* this may not be necessary */
714                psssi->ipt_prev.earlier_smooth_pt.x = vs[n-4].x;
715                psssi->ipt_prev.earlier_smooth_pt.y = vs[n-4].y;
716             }
717             if (vs[n-5].x != vs[n-4].x || vs[n-5].y != vs[n-4].y) {
718                psssi->ipt_prev.earlier_valid = TRUE;
719             }
720             if (vs[n-3].x != vs[n-4].x || vs[n-3].y != vs[n-4].y) {
721                psssi->ipt_prev.later_valid = TRUE;
722             }
723          }
724       } else {
725          if (vs[j-1].x != vs[j].x || vs[j-1].y != vs[j].y) {
726             if (k == index) {
727                psssi->hinge = FALSE;
728                psssi->earlier_smooth_selected = TRUE;
729                set_the_rest_for_this = TRUE;
730             }
731             k++;
732          }
733          if (k == index) {
734             psssi->hinge = TRUE;
735             set_the_rest_for_this = TRUE;
736          }
737          if (vs[j+1].x != vs[j].x || vs[j+1].y != vs[j].y) {
738             k++;
739             if (k == index) {
740                psssi->hinge = FALSE;
741                psssi->earlier_smooth_selected = FALSE;
742                set_the_rest_for_this = TRUE;
743             }
744          }
745          if (set_the_rest_for_this) {
746             psssi->orig_hinge_index = j;
747             psssi->prev_valid = TRUE;
748             psssi->next_valid = TRUE;
749             if (i > 1) {
750                psssi->ipt_prev.earlier_smooth_pt.x = vs[j-4].x;
751                psssi->ipt_prev.earlier_smooth_pt.y = vs[j-4].y;
752                if (vs[j-4].x != vs[j-3].x || vs[j-4].y != vs[j-3].y) {
753                   psssi->ipt_prev.earlier_valid = TRUE;
754                }
755             } else {
756                /* this may not be necessary */
757                psssi->ipt_prev.earlier_smooth_pt.x = vs[j-3].x;
758                psssi->ipt_prev.earlier_smooth_pt.y = vs[j-3].y;
759             }
760             psssi->ipt_prev.hinge_pt.x = vs[j-3].x;
761             psssi->ipt_prev.hinge_pt.y = vs[j-3].y;
762             psssi->ipt_prev.later_smooth_pt.x = vs[j-2].x;
763             psssi->ipt_prev.later_smooth_pt.y = vs[j-2].y;
764             if (vs[j-2].x != vs[j-3].x || vs[j-2].y != vs[j-3].y) {
765                psssi->ipt_prev.later_valid = TRUE;
766             }
767 
768             psssi->ipt.earlier_smooth_pt.x = vs[j-1].x;
769             psssi->ipt.earlier_smooth_pt.y = vs[j-1].y;
770             psssi->ipt.hinge_pt.x = vs[j].x;
771             psssi->ipt.hinge_pt.y = vs[j].y;
772             psssi->ipt.later_smooth_pt.x = vs[j+1].x;
773             psssi->ipt.later_smooth_pt.y = vs[j+1].y;
774             if (vs[j-1].x != vs[j].x || vs[j-1].y != vs[j].y) {
775                psssi->ipt.earlier_valid = TRUE;
776             }
777             if (vs[j+1].x != vs[j].x || vs[j+1].y != vs[j].y) {
778                psssi->ipt.later_valid = TRUE;
779             }
780 
781             psssi->ipt_next.earlier_smooth_pt.x = vs[j+2].x;
782             psssi->ipt_next.earlier_smooth_pt.y = vs[j+2].y;
783             psssi->ipt_next.hinge_pt.x = vs[j+3].x;
784             psssi->ipt_next.hinge_pt.y = vs[j+3].y;
785             if (i < num_hinge_vs-2) {
786                psssi->ipt_next.later_smooth_pt.x = vs[j+4].x;
787                psssi->ipt_next.later_smooth_pt.y = vs[j+4].y;
788                if (vs[j+4].x != vs[j+3].x || vs[j+4].y != vs[j+3].y) {
789                   psssi->ipt_next.later_valid = TRUE;
790                }
791             } else {
792                /* this may not be necessary */
793                psssi->ipt_next.later_smooth_pt.x = vs[j+3].x;
794                psssi->ipt_next.later_smooth_pt.y = vs[j+3].y;
795             }
796             if (vs[j+2].x != vs[j+3].x || vs[j+2].y != vs[j+3].y) {
797                psssi->ipt_next.earlier_valid = TRUE;
798             }
799          }
800       }
801    }
802 }
803 
SetIPTInfoForStretchPolygon(index,n,vs,psssi)804 void SetIPTInfoForStretchPolygon(index, n, vs, psssi)
805    int index, n; /* 0 <= index < ssn */
806    IntPoint *vs;
807    StretchStructuredSplineInfo *psssi;
808 {
809    int i=0, j=0, k=0, num_hinge_vs=(n+2)/3;
810 
811 #ifdef _TGIF_DBG /* debug, do not translate */
812    TgAssert((n+2)%3 == 0, "invalid n in SetIPTInfoForStretchPolygon()", NULL);
813 #endif /* _TGIF_DBG */
814 
815    memset(psssi, 0, sizeof(StretchStructuredSplineInfo));
816    psssi->prev_valid = psssi->next_valid = TRUE;
817    for (i=0, j=0, k=0; i < num_hinge_vs; i++, j+=3, k++) {
818       int set_the_rest_for_this=FALSE;
819 
820       if (i == 0) {
821          if (k == index) {
822             psssi->hinge = TRUE;
823             set_the_rest_for_this = TRUE;
824          }
825          if (vs[0].x != vs[1].x || vs[0].y != vs[1].y) {
826             k++;
827             if (k == index) {
828                psssi->hinge = FALSE;
829                psssi->earlier_smooth_selected = FALSE;
830                set_the_rest_for_this = TRUE;
831             }
832          }
833          if (set_the_rest_for_this) {
834             psssi->orig_hinge_index = j;
835             psssi->ipt_prev.earlier_smooth_pt.x = vs[n-5].x;
836             psssi->ipt_prev.earlier_smooth_pt.y = vs[n-5].y;
837             psssi->ipt_prev.hinge_pt.x = vs[n-4].x;
838             psssi->ipt_prev.hinge_pt.y = vs[n-4].y;
839             psssi->ipt_prev.later_smooth_pt.x = vs[n-3].x;
840             psssi->ipt_prev.later_smooth_pt.y = vs[n-3].y;
841             if (vs[n-3].x != vs[n-4].x || vs[n-3].y != vs[n-4].y) {
842                psssi->ipt_prev.later_valid = TRUE;
843             }
844             if (vs[n-5].x != vs[n-4].x || vs[n-5].y != vs[n-4].y) {
845                psssi->ipt_prev.earlier_valid = TRUE;
846             }
847 
848             psssi->ipt.earlier_smooth_pt.x = vs[n-2].x;
849             psssi->ipt.earlier_smooth_pt.y = vs[n-2].y;
850             psssi->ipt.hinge_pt.x = vs[0].x;
851             psssi->ipt.hinge_pt.y = vs[0].y;
852             psssi->ipt.later_smooth_pt.x = vs[1].x;
853             psssi->ipt.later_smooth_pt.y = vs[1].y;
854             if (vs[1].x != vs[0].x || vs[1].y != vs[0].y) {
855                psssi->ipt.later_valid = TRUE;
856             }
857             if (vs[n-2].x != vs[0].x || vs[n-2].y != vs[0].y) {
858                psssi->ipt.earlier_valid = TRUE;
859             }
860 
861             psssi->ipt_next.earlier_smooth_pt.x = vs[2].x;
862             psssi->ipt_next.earlier_smooth_pt.y = vs[2].y;
863             psssi->ipt_next.hinge_pt.x = vs[3].x;
864             psssi->ipt_next.hinge_pt.y = vs[3].y;
865             psssi->ipt_next.later_smooth_pt.x = vs[4].x;
866             psssi->ipt_next.later_smooth_pt.y = vs[4].y;
867             if (vs[4].x != vs[3].x || vs[4].y != vs[3].y) {
868                psssi->ipt_next.later_valid = TRUE;
869             }
870             if (vs[2].x != vs[3].x || vs[2].y != vs[3].y) {
871                psssi->ipt_next.earlier_valid = TRUE;
872             }
873          }
874       } else if (i == num_hinge_vs-1) {
875          if (vs[n-1].x != vs[n-2].x || vs[n-1].y != vs[n-2].y) {
876             if (k == index) {
877                psssi->hinge = FALSE;
878                psssi->earlier_smooth_selected = TRUE;
879                set_the_rest_for_this = TRUE;
880             }
881             k++;
882          }
883          if (k == index) {
884             psssi->hinge = TRUE;
885             set_the_rest_for_this = TRUE;
886          }
887          if (set_the_rest_for_this) {
888             psssi->orig_hinge_index = j;
889             psssi->ipt_prev.earlier_smooth_pt.x = vs[n-5].x;
890             psssi->ipt_prev.earlier_smooth_pt.y = vs[n-5].y;
891             psssi->ipt_prev.hinge_pt.x = vs[n-4].x;
892             psssi->ipt_prev.hinge_pt.y = vs[n-4].y;
893             psssi->ipt_prev.later_smooth_pt.x = vs[n-3].x;
894             psssi->ipt_prev.later_smooth_pt.y = vs[n-3].y;
895             if (vs[n-3].x != vs[n-4].x || vs[n-3].y != vs[n-4].y) {
896                psssi->ipt_prev.later_valid = TRUE;
897             }
898             if (vs[n-5].x != vs[n-4].x || vs[n-5].y != vs[n-4].y) {
899                psssi->ipt_prev.earlier_valid = TRUE;
900             }
901 
902             psssi->ipt.earlier_smooth_pt.x = vs[n-2].x;
903             psssi->ipt.earlier_smooth_pt.y = vs[n-2].y;
904             psssi->ipt.hinge_pt.x = vs[n-1].x;
905             psssi->ipt.hinge_pt.y = vs[n-1].y;
906             psssi->ipt.later_smooth_pt.x = vs[1].x;
907             psssi->ipt.later_smooth_pt.y = vs[1].y;
908             if (vs[1].x != vs[n-1].x || vs[1].y != vs[n-1].y) {
909                psssi->ipt.later_valid = TRUE;
910             }
911             if (vs[n-2].x != vs[n-1].x || vs[n-2].y != vs[n-1].y) {
912                psssi->ipt.earlier_valid = TRUE;
913             }
914 
915             psssi->ipt_next.earlier_smooth_pt.x = vs[2].x;
916             psssi->ipt_next.earlier_smooth_pt.y = vs[2].y;
917             psssi->ipt_next.hinge_pt.x = vs[3].x;
918             psssi->ipt_next.hinge_pt.y = vs[3].y;
919             psssi->ipt_next.later_smooth_pt.x = vs[4].x;
920             psssi->ipt_next.later_smooth_pt.y = vs[4].y;
921             if (vs[4].x != vs[3].x || vs[4].y != vs[3].y) {
922                psssi->ipt_next.later_valid = TRUE;
923             }
924             if (vs[2].x != vs[3].x || vs[2].y != vs[3].y) {
925                psssi->ipt_next.earlier_valid = TRUE;
926             }
927          }
928       } else {
929          if (vs[j-1].x != vs[j].x || vs[j-1].y != vs[j].y) {
930             if (k == index) {
931                psssi->hinge = FALSE;
932                psssi->earlier_smooth_selected = TRUE;
933                set_the_rest_for_this = TRUE;
934             }
935             k++;
936          }
937          if (k == index) {
938             psssi->hinge = TRUE;
939             set_the_rest_for_this = TRUE;
940          }
941          if (vs[j+1].x != vs[j].x || vs[j+1].y != vs[j].y) {
942             k++;
943             if (k == index) {
944                psssi->hinge = FALSE;
945                psssi->earlier_smooth_selected = FALSE;
946                set_the_rest_for_this = TRUE;
947             }
948          }
949          if (set_the_rest_for_this) {
950             psssi->orig_hinge_index = j;
951             if (i > 1) {
952                psssi->ipt_prev.earlier_smooth_pt.x = vs[j-4].x;
953                psssi->ipt_prev.earlier_smooth_pt.y = vs[j-4].y;
954                if (vs[j-4].x != vs[j-3].x || vs[j-4].y != vs[j-3].y) {
955                   psssi->ipt_prev.earlier_valid = TRUE;
956                }
957             } else {
958                psssi->ipt_prev.earlier_smooth_pt.x = vs[n-2].x;
959                psssi->ipt_prev.earlier_smooth_pt.y = vs[n-2].y;
960                if (vs[n-2].x != vs[j-3].x || vs[n-2].y != vs[j-3].y) {
961                   psssi->ipt_prev.earlier_valid = TRUE;
962                }
963             }
964             psssi->ipt_prev.hinge_pt.x = vs[j-3].x;
965             psssi->ipt_prev.hinge_pt.y = vs[j-3].y;
966             psssi->ipt_prev.later_smooth_pt.x = vs[j-2].x;
967             psssi->ipt_prev.later_smooth_pt.y = vs[j-2].y;
968             if (vs[j-2].x != vs[j-3].x || vs[j-2].y != vs[j-3].y) {
969                psssi->ipt_prev.later_valid = TRUE;
970             }
971 
972             psssi->ipt.earlier_smooth_pt.x = vs[j-1].x;
973             psssi->ipt.earlier_smooth_pt.y = vs[j-1].y;
974             psssi->ipt.hinge_pt.x = vs[j].x;
975             psssi->ipt.hinge_pt.y = vs[j].y;
976             psssi->ipt.later_smooth_pt.x = vs[j+1].x;
977             psssi->ipt.later_smooth_pt.y = vs[j+1].y;
978             if (vs[j-1].x != vs[j].x || vs[j-1].y != vs[j].y) {
979                psssi->ipt.earlier_valid = TRUE;
980             }
981             if (vs[j+1].x != vs[j].x || vs[j+1].y != vs[j].y) {
982                psssi->ipt.later_valid = TRUE;
983             }
984 
985             psssi->ipt_next.earlier_smooth_pt.x = vs[j+2].x;
986             psssi->ipt_next.earlier_smooth_pt.y = vs[j+2].y;
987             psssi->ipt_next.hinge_pt.x = vs[j+3].x;
988             psssi->ipt_next.hinge_pt.y = vs[j+3].y;
989             if (i < num_hinge_vs-2) {
990                psssi->ipt_next.later_smooth_pt.x = vs[j+4].x;
991                psssi->ipt_next.later_smooth_pt.y = vs[j+4].y;
992                if (vs[j+4].x != vs[j+3].x || vs[j+4].y != vs[j+3].y) {
993                   psssi->ipt_next.earlier_valid = TRUE;
994                }
995    	 } else {
996                psssi->ipt_next.later_smooth_pt.x = vs[1].x;
997                psssi->ipt_next.later_smooth_pt.y = vs[1].y;
998                if (vs[1].x != vs[j+3].x || vs[1].y != vs[j+3].y) {
999                   psssi->ipt_next.earlier_valid = TRUE;
1000                }
1001             }
1002             if (vs[j+2].x != vs[j+3].x || vs[j+2].y != vs[j+3].y) {
1003                psssi->ipt_next.earlier_valid = TRUE;
1004             }
1005          }
1006       }
1007    }
1008 }
1009 
1010 static
SetVsAndVs2ForHinge(psssi,dx,dy,pn_num_vs,vs,pn_num_vs2,vs2)1011 void SetVsAndVs2ForHinge(psssi, dx, dy, pn_num_vs, vs, pn_num_vs2, vs2)
1012    StretchStructuredSplineInfo *psssi;
1013    int dx, dy, *pn_num_vs, *pn_num_vs2;
1014    IntPoint *vs, *vs2;
1015 {
1016    int num_vs=0, num_vs2=0;
1017 
1018    if (!psssi->prev_valid) {
1019       /* first poly point */
1020       num_vs = 0;
1021       vs2[0].x = psssi->ipt.hinge_pt.x + dx;
1022       vs2[0].y = psssi->ipt.hinge_pt.y + dy;
1023       if (psssi->ipt.later_valid) {
1024          vs2[1].x = psssi->ipt.later_smooth_pt.x + dx;
1025          vs2[1].y = psssi->ipt.later_smooth_pt.y + dy;
1026          if (psssi->ipt_next.earlier_valid) {
1027             num_vs2 = 4;
1028             vs2[2].x = psssi->ipt_next.earlier_smooth_pt.x;
1029             vs2[2].y = psssi->ipt_next.earlier_smooth_pt.y;
1030          } else {
1031             num_vs2 = 3;
1032          }
1033       } else {
1034          if (psssi->ipt_next.earlier_valid) {
1035             num_vs2 = 3;
1036             vs2[1].x = psssi->ipt_next.earlier_smooth_pt.x;
1037             vs2[1].y = psssi->ipt_next.earlier_smooth_pt.y;
1038          } else {
1039             num_vs2 = 2;
1040          }
1041       }
1042       vs2[num_vs2-1].x = psssi->ipt_next.hinge_pt.x;
1043       vs2[num_vs2-1].y = psssi->ipt_next.hinge_pt.y;
1044    } else if (!psssi->next_valid) {
1045       /* last poly point */
1046       num_vs2 = 0;
1047       vs[0].x = psssi->ipt.hinge_pt.x + dx;
1048       vs[0].y = psssi->ipt.hinge_pt.y + dy;
1049       if (psssi->ipt.earlier_valid) {
1050          vs[1].x = psssi->ipt.earlier_smooth_pt.x + dx;
1051          vs[1].y = psssi->ipt.earlier_smooth_pt.y + dy;
1052          if (psssi->ipt_prev.later_valid) {
1053             num_vs = 4;
1054             vs[2].x = psssi->ipt_prev.later_smooth_pt.x;
1055             vs[2].y = psssi->ipt_prev.later_smooth_pt.y;
1056          } else {
1057             num_vs = 3;
1058          }
1059       } else {
1060          if (psssi->ipt_prev.later_valid) {
1061             num_vs = 3;
1062             vs[1].x = psssi->ipt_prev.later_smooth_pt.x;
1063             vs[1].y = psssi->ipt_prev.later_smooth_pt.y;
1064          } else {
1065             num_vs = 2;
1066          }
1067       }
1068       vs[num_vs-1].x = psssi->ipt_prev.hinge_pt.x;
1069       vs[num_vs-1].y = psssi->ipt_prev.hinge_pt.y;
1070    } else {
1071       /* set the vs */
1072       vs[0].x = psssi->ipt.hinge_pt.x + dx;
1073       vs[0].y = psssi->ipt.hinge_pt.y + dy;
1074       if (psssi->ipt.earlier_valid) {
1075          vs[1].x = psssi->ipt.earlier_smooth_pt.x + dx;
1076          vs[1].y = psssi->ipt.earlier_smooth_pt.y + dy;
1077          if (psssi->ipt_prev.later_valid) {
1078             num_vs = 4;
1079             vs[2].x = psssi->ipt_prev.later_smooth_pt.x;
1080             vs[2].y = psssi->ipt_prev.later_smooth_pt.y;
1081          } else {
1082             num_vs = 3;
1083          }
1084       } else {
1085          if (psssi->ipt_prev.later_valid) {
1086             num_vs = 3;
1087             vs[1].x = psssi->ipt_prev.later_smooth_pt.x;
1088             vs[1].y = psssi->ipt_prev.later_smooth_pt.y;
1089          } else {
1090             num_vs = 2;
1091          }
1092       }
1093       vs[num_vs-1].x = psssi->ipt_prev.hinge_pt.x;
1094       vs[num_vs-1].y = psssi->ipt_prev.hinge_pt.y;
1095 
1096       /* set the vs2 */
1097       vs2[0].x = psssi->ipt.hinge_pt.x + dx;
1098       vs2[0].y = psssi->ipt.hinge_pt.y + dy;
1099       if (psssi->ipt.later_valid) {
1100          vs2[1].x = psssi->ipt.later_smooth_pt.x + dx;
1101          vs2[1].y = psssi->ipt.later_smooth_pt.y + dy;
1102          if (psssi->ipt_next.earlier_valid) {
1103             num_vs2 = 4;
1104             vs2[2].x = psssi->ipt_next.earlier_smooth_pt.x;
1105             vs2[2].y = psssi->ipt_next.earlier_smooth_pt.y;
1106          } else {
1107             num_vs2 = 3;
1108          }
1109       } else {
1110          if (psssi->ipt_next.earlier_valid) {
1111             num_vs2 = 3;
1112             vs2[1].x = psssi->ipt_next.earlier_smooth_pt.x;
1113             vs2[1].y = psssi->ipt_next.earlier_smooth_pt.y;
1114          } else {
1115             num_vs2 = 2;
1116          }
1117       }
1118       vs2[num_vs2-1].x = psssi->ipt_next.hinge_pt.x;
1119       vs2[num_vs2-1].y = psssi->ipt_next.hinge_pt.y;
1120    }
1121    *pn_num_vs = num_vs;
1122    *pn_num_vs2 = num_vs2;
1123 }
1124 
1125 static
SetVsAndVs2ForSmooth(psssi,dx,dy,pn_num_vs,vs,pn_num_vs2,vs2)1126 void SetVsAndVs2ForSmooth(psssi, dx, dy, pn_num_vs, vs, pn_num_vs2, vs2)
1127    StretchStructuredSplineInfo *psssi;
1128    int dx, dy, *pn_num_vs, *pn_num_vs2;
1129    IntPoint *vs, *vs2;
1130 {
1131    int num_vs=0, num_vs2=0;
1132 
1133    if (!psssi->prev_valid) {
1134       /* first poly point */
1135       num_vs = 0;
1136       vs2[0].x = psssi->ipt.hinge_pt.x;
1137       vs2[0].y = psssi->ipt.hinge_pt.y;
1138 #ifdef _TGIF_DBG /* debug, do not translate */
1139       TgAssert(psssi->ipt.later_valid,
1140             "psssi->ipt.later_valid is FALSE in SetVsAndVs2ForSmooth()", NULL);
1141 #endif /* _TGIF_DBG */
1142       vs2[1].x = psssi->ipt.later_smooth_pt.x + dx;
1143       vs2[1].y = psssi->ipt.later_smooth_pt.y + dy;
1144       if (psssi->ipt_next.earlier_valid) {
1145          num_vs2 = 4;
1146          vs2[2].x = psssi->ipt_next.earlier_smooth_pt.x;
1147          vs2[2].y = psssi->ipt_next.earlier_smooth_pt.y;
1148       } else {
1149          num_vs2 = 3;
1150       }
1151       vs2[num_vs2-1].x = psssi->ipt_next.hinge_pt.x;
1152       vs2[num_vs2-1].y = psssi->ipt_next.hinge_pt.y;
1153    } else if (!psssi->next_valid) {
1154       /* last poly point */
1155       num_vs2 = 0;
1156       vs[0].x = psssi->ipt.hinge_pt.x;
1157       vs[0].y = psssi->ipt.hinge_pt.y;
1158 #ifdef _TGIF_DBG /* debug, do not translate */
1159       TgAssert(psssi->ipt.earlier_valid,
1160             "psssi->ipt.earlier_valid is FALSE in SetVsAndVs2ForSmooth()",
1161             NULL);
1162 #endif /* _TGIF_DBG */
1163       vs[1].x = psssi->ipt.earlier_smooth_pt.x + dx;
1164       vs[1].y = psssi->ipt.earlier_smooth_pt.y + dy;
1165       if (psssi->ipt_prev.later_valid) {
1166          num_vs = 4;
1167          vs[2].x = psssi->ipt_prev.later_smooth_pt.x;
1168          vs[2].y = psssi->ipt_prev.later_smooth_pt.y;
1169       } else {
1170          num_vs = 3;
1171       }
1172       vs[num_vs-1].x = psssi->ipt_prev.hinge_pt.x;
1173       vs[num_vs-1].y = psssi->ipt_prev.hinge_pt.y;
1174    } else {
1175       if (psssi->earlier_smooth_selected) {
1176 #ifdef _TGIF_DBG /* debug, do not translate */
1177          TgAssert(psssi->ipt.earlier_valid,
1178                "psssi->ipt.earlier_valid is FALSE with psssi->earlier_smooth_selected in SetVsAndVs2ForSmooth()",
1179                NULL);
1180 #endif /* _TGIF_DBG */
1181          /* set the vs */
1182          vs[0].x = psssi->ipt.hinge_pt.x;
1183          vs[0].y = psssi->ipt.hinge_pt.y;
1184          vs[1].x = psssi->ipt.earlier_smooth_pt.x + dx;
1185          vs[1].y = psssi->ipt.earlier_smooth_pt.y + dy;
1186          if (psssi->ipt_prev.later_valid) {
1187             num_vs = 4;
1188             vs[2].x = psssi->ipt_prev.later_smooth_pt.x;
1189             vs[2].y = psssi->ipt_prev.later_smooth_pt.y;
1190          } else {
1191             num_vs = 3;
1192          }
1193          vs[num_vs-1].x = psssi->ipt_prev.hinge_pt.x;
1194          vs[num_vs-1].y = psssi->ipt_prev.hinge_pt.y;
1195 
1196          /* set the vs2 */
1197          vs2[0].x = psssi->ipt.hinge_pt.x;
1198          vs2[0].y = psssi->ipt.hinge_pt.y;
1199          if (psssi->ipt.later_valid) {
1200             vs2[1].x = ((psssi->ipt.hinge_pt.x) << 1) -
1201                   psssi->ipt.earlier_smooth_pt.x - dx;
1202             vs2[1].y = ((psssi->ipt.hinge_pt.y) << 1) -
1203                   psssi->ipt.earlier_smooth_pt.y - dy;
1204             if (psssi->ipt_next.earlier_valid) {
1205                num_vs2 = 4;
1206                vs2[2].x = psssi->ipt_next.earlier_smooth_pt.x;
1207                vs2[2].y = psssi->ipt_next.earlier_smooth_pt.y;
1208             } else {
1209                num_vs2 = 3;
1210             }
1211          } else {
1212             if (psssi->ipt_next.earlier_valid) {
1213                num_vs2 = 3;
1214                vs2[1].x = psssi->ipt_next.earlier_smooth_pt.x;
1215                vs2[1].y = psssi->ipt_next.earlier_smooth_pt.y;
1216             } else {
1217                num_vs2 = 2;
1218             }
1219          }
1220          vs2[num_vs2-1].x = psssi->ipt_next.hinge_pt.x;
1221          vs2[num_vs2-1].y = psssi->ipt_next.hinge_pt.y;
1222       } else {
1223          /* set the vs */
1224          vs[0].x = psssi->ipt.hinge_pt.x;
1225          vs[0].y = psssi->ipt.hinge_pt.y;
1226          if (psssi->ipt.earlier_valid) {
1227             vs[1].x = ((psssi->ipt.hinge_pt.x) << 1) -
1228                   psssi->ipt.later_smooth_pt.x - dx;
1229             vs[1].y = ((psssi->ipt.hinge_pt.y) << 1) -
1230                   psssi->ipt.later_smooth_pt.y - dy;
1231             if (psssi->ipt_prev.later_valid) {
1232                num_vs = 4;
1233                vs[2].x = psssi->ipt_prev.later_smooth_pt.x;
1234                vs[2].y = psssi->ipt_prev.later_smooth_pt.y;
1235             } else {
1236                num_vs = 3;
1237             }
1238          } else {
1239             if (psssi->ipt_prev.later_valid) {
1240                num_vs = 3;
1241                vs[1].x = psssi->ipt_prev.later_smooth_pt.x;
1242                vs[1].y = psssi->ipt_prev.later_smooth_pt.y;
1243             } else {
1244                num_vs = 2;
1245             }
1246          }
1247          vs[num_vs-1].x = psssi->ipt_prev.hinge_pt.x;
1248          vs[num_vs-1].y = psssi->ipt_prev.hinge_pt.y;
1249 
1250 #ifdef _TGIF_DBG /* debug, do not translate */
1251          TgAssert(psssi->ipt.later_valid,
1252                "psssi->ipt.later_valid is FALSE with !psssi->earlier_smooth_selected in SetVsAndVs2ForSmooth()",
1253                NULL);
1254 #endif /* _TGIF_DBG */
1255          /* set the vs2 */
1256          vs2[0].x = psssi->ipt.hinge_pt.x;
1257          vs2[0].y = psssi->ipt.hinge_pt.y;
1258          vs2[1].x = psssi->ipt.later_smooth_pt.x + dx;
1259          vs2[1].y = psssi->ipt.later_smooth_pt.y + dy;
1260          if (psssi->ipt_next.earlier_valid) {
1261             num_vs2 = 4;
1262             vs2[2].x = psssi->ipt_next.earlier_smooth_pt.x;
1263             vs2[2].y = psssi->ipt_next.earlier_smooth_pt.y;
1264          } else {
1265             num_vs2 = 3;
1266          }
1267          vs2[num_vs2-1].x = psssi->ipt_next.hinge_pt.x;
1268          vs2[num_vs2-1].y = psssi->ipt_next.hinge_pt.y;
1269       }
1270    }
1271    *pn_num_vs = num_vs;
1272    *pn_num_vs2 = num_vs2;
1273 }
1274 
SetVsAndVs2ForStretchStructSpline(psssi,dx,dy,pn_num_vs,vs,pn_num_vs2,vs2)1275 void SetVsAndVs2ForStretchStructSpline(psssi, dx, dy, pn_num_vs, vs, pn_num_vs2,
1276       vs2)
1277    StretchStructuredSplineInfo *psssi;
1278    int dx, dy, *pn_num_vs, *pn_num_vs2;
1279    IntPoint *vs, *vs2;
1280 {
1281    if (psssi->hinge) {
1282       SetVsAndVs2ForHinge(psssi, dx, dy, pn_num_vs, vs, pn_num_vs2, vs2);
1283    } else {
1284       SetVsAndVs2ForSmooth(psssi, dx, dy, pn_num_vs, vs, pn_num_vs2, vs2);
1285    }
1286 }
1287 
FixUpSmoothAndSmooth2ForStretchStructSpline(num_vs,smooth,num_vs2,smooth2)1288 void FixUpSmoothAndSmooth2ForStretchStructSpline(num_vs, smooth, num_vs2,
1289       smooth2)
1290    int num_vs, num_vs2;
1291    char *smooth, *smooth2;
1292 {
1293    int i=0;
1294 
1295    smooth[0] = FALSE;
1296    smooth[num_vs-1] = FALSE;
1297    for (i=1; i < num_vs-1; i++) smooth[i] = TRUE;
1298 
1299    smooth2[0] = FALSE;
1300    smooth2[num_vs2-1] = FALSE;
1301    for (i=1; i < num_vs2-1; i++) smooth2[i] = TRUE;
1302 }
1303 
EraseHighLightForHinge(psssi,dx,dy,draw_dashed_line,draw_vertices)1304 void EraseHighLightForHinge(psssi, dx, dy, draw_dashed_line, draw_vertices)
1305    StretchStructuredSplineInfo *psssi;
1306    int dx, dy, draw_dashed_line, draw_vertices;
1307 {
1308    int x=0, y=0;
1309    XPoint vs[2];
1310    XGCValues values;
1311 
1312    if (draw_dashed_line) {
1313       XSetDashes(mainDisplay, revDefaultGC, 0, dashList[8], dashListLength[8]);
1314    }
1315    if (!psssi->prev_valid) {
1316       /* first poly point */
1317       x = psssi->ipt.hinge_pt.x + dx;
1318       y = psssi->ipt.hinge_pt.y + dy;
1319       if (draw_vertices) {
1320          MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1321       }
1322       vs[0].x = OFFSET_X(x);
1323       vs[0].y = OFFSET_Y(y);
1324       if (psssi->ipt.later_valid) {
1325          x = psssi->ipt.later_smooth_pt.x + dx;
1326          y = psssi->ipt.later_smooth_pt.y + dy;
1327          if (draw_vertices) {
1328             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1329          }
1330          vs[1].x = OFFSET_X(x);
1331          vs[1].y = OFFSET_Y(y);
1332          if (draw_dashed_line) {
1333             values.line_style = LineOnOffDash;
1334             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1335             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1336             values.line_style = LineSolid;
1337             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1338          }
1339       }
1340    } else if (!psssi->next_valid) {
1341       /* last poly point */
1342       x = psssi->ipt.hinge_pt.x + dx;
1343       y = psssi->ipt.hinge_pt.y + dy;
1344       if (draw_vertices) {
1345          MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1346       }
1347       vs[0].x = OFFSET_X(x);
1348       vs[0].y = OFFSET_Y(y);
1349       if (psssi->ipt.earlier_valid) {
1350          x = psssi->ipt.earlier_smooth_pt.x + dx;
1351          y = psssi->ipt.earlier_smooth_pt.y + dy;
1352          if (draw_vertices) {
1353             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1354          }
1355          vs[1].x = OFFSET_X(x);
1356          vs[1].y = OFFSET_Y(y);
1357          if (draw_dashed_line) {
1358             values.line_style = LineOnOffDash;
1359             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1360             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1361             values.line_style = LineSolid;
1362             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1363          }
1364       }
1365    } else {
1366       x = psssi->ipt.hinge_pt.x + dx;
1367       y = psssi->ipt.hinge_pt.y + dy;
1368       if (draw_vertices) {
1369          MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1370       }
1371       vs[0].x = OFFSET_X(x);
1372       vs[0].y = OFFSET_Y(y);
1373       if (psssi->ipt.later_valid) {
1374          x = psssi->ipt.later_smooth_pt.x + dx;
1375          y = psssi->ipt.later_smooth_pt.y + dy;
1376          if (draw_vertices) {
1377             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1378          }
1379          vs[1].x = OFFSET_X(x);
1380          vs[1].y = OFFSET_Y(y);
1381          if (draw_dashed_line) {
1382             values.line_style = LineOnOffDash;
1383             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1384             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1385             values.line_style = LineSolid;
1386             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1387          }
1388       }
1389       if (psssi->ipt.earlier_valid) {
1390          x = psssi->ipt.earlier_smooth_pt.x + dx;
1391          y = psssi->ipt.earlier_smooth_pt.y + dy;
1392          if (draw_vertices) {
1393             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1394          }
1395          vs[1].x = OFFSET_X(x);
1396          vs[1].y = OFFSET_Y(y);
1397          if (draw_dashed_line) {
1398             values.line_style = LineOnOffDash;
1399             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1400             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1401             values.line_style = LineSolid;
1402             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1403          }
1404       }
1405    }
1406 }
1407 
EraseHighLightForSmooth(psssi,dx,dy,draw_dashed_line,draw_vertices)1408 void EraseHighLightForSmooth(psssi, dx, dy, draw_dashed_line, draw_vertices)
1409    StretchStructuredSplineInfo *psssi;
1410    int dx, dy, draw_dashed_line, draw_vertices;
1411 {
1412    int x=0, y=0;
1413    XPoint vs[2];
1414    XGCValues values;
1415 
1416    if (draw_dashed_line) {
1417       XSetDashes(mainDisplay, revDefaultGC, 0, dashList[8], dashListLength[8]);
1418    }
1419    if (!psssi->prev_valid) {
1420       /* first poly point */
1421       x = psssi->ipt.hinge_pt.x;
1422       y = psssi->ipt.hinge_pt.y;
1423       if (draw_vertices) {
1424          MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1425       }
1426       vs[0].x = OFFSET_X(x);
1427       vs[0].y = OFFSET_Y(y);
1428       if (psssi->ipt.later_valid) {
1429          x = psssi->ipt.later_smooth_pt.x + dx;
1430          y = psssi->ipt.later_smooth_pt.y + dy;
1431          if (draw_vertices) {
1432             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1433          }
1434          vs[1].x = OFFSET_X(x);
1435          vs[1].y = OFFSET_Y(y);
1436          if (draw_dashed_line) {
1437             values.line_style = LineOnOffDash;
1438             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1439             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1440             values.line_style = LineSolid;
1441             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1442          }
1443       }
1444    } else if (!psssi->next_valid) {
1445       /* last poly point */
1446       x = psssi->ipt.hinge_pt.x;
1447       y = psssi->ipt.hinge_pt.y;
1448       if (draw_vertices) {
1449          MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1450       }
1451       vs[0].x = OFFSET_X(x);
1452       vs[0].y = OFFSET_Y(y);
1453       if (psssi->ipt.earlier_valid) {
1454          x = psssi->ipt.earlier_smooth_pt.x + dx;
1455          y = psssi->ipt.earlier_smooth_pt.y + dy;
1456          if (draw_vertices) {
1457             MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1458          }
1459          vs[1].x = OFFSET_X(x);
1460          vs[1].y = OFFSET_Y(y);
1461          if (draw_dashed_line) {
1462             values.line_style = LineOnOffDash;
1463             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1464             MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1465             values.line_style = LineSolid;
1466             XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1467          }
1468       }
1469    } else {
1470       if (psssi->earlier_smooth_selected) {
1471          x = psssi->ipt.hinge_pt.x;
1472          y = psssi->ipt.hinge_pt.y;
1473          if (draw_vertices) {
1474             MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1475          }
1476          vs[0].x = OFFSET_X(x);
1477          vs[0].y = OFFSET_Y(y);
1478          if (psssi->ipt.later_valid) {
1479             x = ((psssi->ipt.hinge_pt.x) << 1) -
1480                   psssi->ipt.earlier_smooth_pt.x - dx;
1481             y = ((psssi->ipt.hinge_pt.y) << 1) -
1482                   psssi->ipt.earlier_smooth_pt.y - dy;
1483             if (draw_vertices) {
1484                MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1485             }
1486             vs[1].x = OFFSET_X(x);
1487             vs[1].y = OFFSET_Y(y);
1488             if (draw_dashed_line) {
1489                values.line_style = LineOnOffDash;
1490                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1491                MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1492                values.line_style = LineSolid;
1493                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1494             }
1495          }
1496          if (psssi->ipt.earlier_valid) {
1497             x = psssi->ipt.earlier_smooth_pt.x + dx;
1498             y = psssi->ipt.earlier_smooth_pt.y + dy;
1499             if (draw_vertices) {
1500                MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1501             }
1502             vs[1].x = OFFSET_X(x);
1503             vs[1].y = OFFSET_Y(y);
1504             if (draw_dashed_line) {
1505                values.line_style = LineOnOffDash;
1506                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1507                MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1508                values.line_style = LineSolid;
1509                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1510             }
1511          }
1512       } else {
1513          x = psssi->ipt.hinge_pt.x;
1514          y = psssi->ipt.hinge_pt.y;
1515          if (draw_vertices) {
1516             MARKHR(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1517          }
1518          vs[0].x = OFFSET_X(x);
1519          vs[0].y = OFFSET_Y(y);
1520          if (psssi->ipt.later_valid) {
1521             x = psssi->ipt.later_smooth_pt.x + dx;
1522             y = psssi->ipt.later_smooth_pt.y + dy;
1523             if (draw_vertices) {
1524                MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1525             }
1526             vs[1].x = OFFSET_X(x);
1527             vs[1].y = OFFSET_Y(y);
1528             if (draw_dashed_line) {
1529                values.line_style = LineOnOffDash;
1530                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1531                MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1532                values.line_style = LineSolid;
1533                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1534             }
1535          }
1536          if (psssi->ipt.earlier_valid) {
1537             x = ((psssi->ipt.hinge_pt.x) << 1) -
1538                   psssi->ipt.later_smooth_pt.x - dx;
1539             y = ((psssi->ipt.hinge_pt.y) << 1) -
1540                   psssi->ipt.later_smooth_pt.y - dy;
1541             if (draw_vertices) {
1542                MARKHO(drawWindow, revDefaultGC, OFFSET_X(x), OFFSET_Y(y));
1543             }
1544             vs[1].x = OFFSET_X(x);
1545             vs[1].y = OFFSET_Y(y);
1546             if (draw_dashed_line) {
1547                values.line_style = LineOnOffDash;
1548                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1549                MyDashedLine(drawWindow, revDefaultGC, vs, 2);
1550                values.line_style = LineSolid;
1551                XChangeGC(mainDisplay, revDefaultGC, GCLineStyle, &values);
1552             }
1553          }
1554       }
1555    }
1556 }
1557 
EraseHighLightForStretchStructSpline(psssi,dx,dy,draw_dashed_line,draw_vertices)1558 void EraseHighLightForStretchStructSpline(psssi, dx, dy, draw_dashed_line,
1559       draw_vertices)
1560    StretchStructuredSplineInfo *psssi;
1561    int dx, dy, draw_dashed_line, draw_vertices;
1562 {
1563    if (psssi->hinge) {
1564       EraseHighLightForHinge(psssi, dx, dy, draw_dashed_line, draw_vertices);
1565    } else {
1566       EraseHighLightForSmooth(psssi, dx, dy, draw_dashed_line, draw_vertices);
1567    }
1568 }
1569 
DupVs(pn_return,vs,n)1570 XPoint *DupVs(pn_return, vs, n)
1571    int *pn_return, n;
1572    XPoint *vs;
1573 {
1574    XPoint *vs_return=(XPoint*)malloc(n*sizeof(XPoint));
1575 
1576    if (vs_return == NULL) FailAllocMessage();
1577    memcpy(vs_return, vs, n*sizeof(XPoint));
1578    *pn_return = n;
1579 
1580    return vs_return;
1581 }
1582 
MoveATransformedPoint(obj_ptr,vs,abs_dx,abs_dy)1583 void MoveATransformedPoint(obj_ptr, vs, abs_dx, abs_dy)
1584    struct ObjRec *obj_ptr;
1585    IntPoint *vs;
1586    int abs_dx, abs_dy;
1587 {
1588    int x=0, y=0;
1589 
1590    TransformPointThroughCTM(vs->x-obj_ptr->x, vs->y-obj_ptr->y,
1591          obj_ptr->ctm, &x, &y);
1592    x += obj_ptr->x + abs_dx;
1593    y += obj_ptr->y + abs_dy;
1594    ReverseTransformPointThroughCTM(x-obj_ptr->x, y-obj_ptr->y,
1595          obj_ptr->ctm, &x, &y);
1596    vs->x = x + obj_ptr->x;
1597    vs->y = y + obj_ptr->y;
1598 }
1599 
UpdateObjForStretchStructSpline(obj_ptr,n,vs,abs_dx,abs_dy,psssi)1600 void UpdateObjForStretchStructSpline(obj_ptr, n, vs, abs_dx, abs_dy, psssi)
1601    struct ObjRec *obj_ptr;
1602    int n, abs_dx, abs_dy;
1603    IntPoint *vs;
1604    StretchStructuredSplineInfo *psssi;
1605 {
1606    int index=psssi->orig_hinge_index;
1607 
1608    if (obj_ptr->ctm == NULL) {
1609       if (psssi->hinge) {
1610          if (!psssi->prev_valid) {
1611             /* first poly point */
1612             vs[0].x += abs_dx;
1613             vs[0].y += abs_dy;
1614             vs[1].x += abs_dx;
1615             vs[1].y += abs_dy;
1616          } else if (!psssi->next_valid) {
1617             /* last poly point */
1618             vs[n-1].x += abs_dx;
1619             vs[n-1].y += abs_dy;
1620             vs[n-2].x += abs_dx;
1621             vs[n-2].y += abs_dy;
1622          } else {
1623             if (obj_ptr->type == OBJ_POLYGON && index == 0) {
1624                vs[0].x += abs_dx;
1625                vs[0].y += abs_dy;
1626                vs[1].x += abs_dx;
1627                vs[1].y += abs_dy;
1628                /* since it's a polygon */
1629                vs[n-1].x += abs_dx;
1630                vs[n-1].y += abs_dy;
1631                vs[n-2].x += abs_dx;
1632                vs[n-2].y += abs_dy;
1633             } else if (obj_ptr->type == OBJ_POLYGON && index == n-1) {
1634                vs[n-1].x += abs_dx;
1635                vs[n-1].y += abs_dy;
1636                vs[n-2].x += abs_dx;
1637                vs[n-2].y += abs_dy;
1638                /* since it's a polygon */
1639                vs[0].x += abs_dx;
1640                vs[0].y += abs_dy;
1641                vs[1].x += abs_dx;
1642                vs[1].y += abs_dy;
1643             } else {
1644                vs[index-1].x += abs_dx;
1645                vs[index-1].y += abs_dy;
1646                vs[index].x += abs_dx;
1647                vs[index].y += abs_dy;
1648                vs[index+1].x += abs_dx;
1649                vs[index+1].y += abs_dy;
1650             }
1651          }
1652       } else {
1653          if (!psssi->prev_valid) {
1654             /* first poly point */
1655             vs[1].x += abs_dx;
1656             vs[1].y += abs_dy;
1657             if (obj_ptr->type == OBJ_POLYGON && index == 0) {
1658                vs[n-2].x = ((vs[0].x) << 1) - vs[1].x;
1659                vs[n-2].y = ((vs[0].y) << 1) - vs[1].y;
1660             }
1661          } else if (!psssi->next_valid) {
1662             /* last poly point */
1663             vs[n-2].x += abs_dx;
1664             vs[n-2].y += abs_dy;
1665             if (obj_ptr->type == OBJ_POLYGON && index == n-1) {
1666                vs[1].x = ((vs[n-1].x) << 1) - vs[n-2].x;
1667                vs[1].y = ((vs[n-1].y) << 1) - vs[n-2].y;
1668             }
1669          } else {
1670             if (psssi->earlier_smooth_selected) {
1671                if (obj_ptr->type == OBJ_POLYGON &&
1672                      (index == 0 || index == n-1)) {
1673                   vs[n-2].x += abs_dx;
1674                   vs[n-2].y += abs_dy;
1675                   /* since it's a polygon */
1676                   vs[1].x = ((vs[n-1].x) << 1) - vs[n-2].x;
1677                   vs[1].y = ((vs[n-1].y) << 1) - vs[n-2].y;
1678                } else {
1679                   vs[index-1].x += abs_dx;
1680                   vs[index-1].y += abs_dy;
1681                   vs[index+1].x = ((vs[index].x) << 1) - vs[index-1].x;
1682                   vs[index+1].y = ((vs[index].y) << 1) - vs[index-1].y;
1683                }
1684             } else {
1685                if (obj_ptr->type == OBJ_POLYGON &&
1686                      (index == 0 || index == n-1)) {
1687                   vs[1].x += abs_dx;
1688                   vs[1].y += abs_dy;
1689                   /* since it's a polygon */
1690                   vs[n-2].x = ((vs[0].x) << 1) - vs[1].x;
1691                   vs[n-2].y = ((vs[0].y) << 1) - vs[1].y;
1692                } else {
1693                   vs[index+1].x += abs_dx;
1694                   vs[index+1].y += abs_dy;
1695                   vs[index-1].x = ((vs[index].x) << 1) - vs[index+1].x;
1696                   vs[index-1].y = ((vs[index].y) << 1) - vs[index+1].y;
1697                }
1698             }
1699          }
1700       }
1701    } else {
1702       if (psssi->hinge) {
1703          if (!psssi->prev_valid) {
1704             /* first poly point */
1705             MoveATransformedPoint(obj_ptr, &vs[0], abs_dx, abs_dy);
1706             MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1707             if (obj_ptr->type == OBJ_POLYGON && index == 0) {
1708                MoveATransformedPoint(obj_ptr, &vs[n-1], abs_dx, abs_dy);
1709                MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1710             }
1711          } else if (!psssi->next_valid) {
1712             /* last poly point */
1713             MoveATransformedPoint(obj_ptr, &vs[n-1], abs_dx, abs_dy);
1714             MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1715             if (obj_ptr->type == OBJ_POLYGON &&
1716                   index == n-1) {
1717                MoveATransformedPoint(obj_ptr, &vs[0], abs_dx, abs_dy);
1718                MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1719             }
1720          } else {
1721             if (obj_ptr->type == OBJ_POLYGON && index == 0) {
1722                MoveATransformedPoint(obj_ptr, &vs[0], abs_dx, abs_dy);
1723                MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1724                /* since it's a polygon */
1725                MoveATransformedPoint(obj_ptr, &vs[n-1], abs_dx, abs_dy);
1726                MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1727             } else if (obj_ptr->type == OBJ_POLYGON && index == n-1) {
1728                MoveATransformedPoint(obj_ptr, &vs[n-1], abs_dx, abs_dy);
1729                MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1730                /* since it's a polygon */
1731                MoveATransformedPoint(obj_ptr, &vs[0], abs_dx, abs_dy);
1732                MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1733             } else {
1734                MoveATransformedPoint(obj_ptr, &vs[index-1], abs_dx, abs_dy);
1735                MoveATransformedPoint(obj_ptr, &vs[index], abs_dx, abs_dy);
1736                MoveATransformedPoint(obj_ptr, &vs[index+1], abs_dx, abs_dy);
1737             }
1738          }
1739       } else {
1740          if (!psssi->prev_valid) {
1741             /* first poly point */
1742             MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1743             if (obj_ptr->type == OBJ_POLYGON && index == 0) {
1744                vs[n-2].x = ((vs[0].x) << 1) - vs[1].x;
1745                vs[n-2].y = ((vs[0].y) << 1) - vs[1].y;
1746             }
1747          } else if (!psssi->next_valid) {
1748             /* last poly point */
1749             MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1750             if (obj_ptr->type == OBJ_POLYGON && index == n-1) {
1751                vs[1].x = ((vs[n-1].x) << 1) - vs[n-2].x;
1752                vs[1].y = ((vs[n-1].y) << 1) - vs[n-2].y;
1753             }
1754          } else {
1755             if (psssi->earlier_smooth_selected) {
1756                if (obj_ptr->type == OBJ_POLYGON &&
1757                      (index == 0 || index == n-1)) {
1758                   MoveATransformedPoint(obj_ptr, &vs[n-2], abs_dx, abs_dy);
1759                   /* since it's a polygon */
1760                   vs[1].x = ((vs[n-1].x) << 1) - vs[n-2].x;
1761                   vs[1].y = ((vs[n-1].y) << 1) - vs[n-2].y;
1762                } else {
1763                   MoveATransformedPoint(obj_ptr, &vs[index-1], abs_dx, abs_dy);
1764                   vs[index+1].x = ((vs[index].x) << 1) - vs[index-1].x;
1765                   vs[index+1].y = ((vs[index].y) << 1) - vs[index-1].y;
1766                }
1767             } else {
1768                if (obj_ptr->type == OBJ_POLYGON &&
1769                      (index == 0 || index == n-1)) {
1770                   MoveATransformedPoint(obj_ptr, &vs[1], abs_dx, abs_dy);
1771                   /* since it's a polygon */
1772                   vs[n-2].x = ((vs[0].x) << 1) - vs[1].x;
1773                   vs[n-2].y = ((vs[0].y) << 1) - vs[1].y;
1774                } else {
1775                   MoveATransformedPoint(obj_ptr, &vs[index+1], abs_dx, abs_dy);
1776                   vs[index-1].x = ((vs[index].x) << 1) - vs[index+1].x;
1777                   vs[index-1].y = ((vs[index].y) << 1) - vs[index+1].y;
1778                }
1779             }
1780          }
1781       }
1782    }
1783 }
1784 
1785 static
StretchStructSpline(XGridOff,YGridOff,ObjPtr,Index)1786 void StretchStructSpline(XGridOff, YGridOff, ObjPtr, Index)
1787    int XGridOff, YGridOff, Index;
1788    struct ObjRec *ObjPtr;
1789 {
1790    struct PolyRec *poly_ptr=NULL;
1791    struct PolygonRec *polygon_ptr=NULL;
1792    struct AttrRec *name_attr=NULL, *on_reshape_attr=NULL;
1793    int i, x, y, dx, dy, stretching=TRUE;
1794    int ltx=0, lty=0, rbx=0, rby=0;
1795    int grid_x=XGridOff, grid_y=YGridOff, sn=0, sn2=0, saved_sn=0, saved_sn2=0;
1796    int auto_center_attr=AutoCenterAttr(ObjPtr);
1797    int has_on_reshape=HasOnReshape(ObjPtr, &name_attr);
1798    int num_vs=0, num_vs2=0, ruler_x=0, ruler_y=0, n=0;
1799    char smooth[5], smooth2[5];
1800    XEvent input, ev;
1801    XPoint *sv=NULL, *sv2=NULL, *saved_sv=NULL, *saved_sv2=NULL;
1802    IntPoint vs[5], vs2[5], start_v, *vlist=NULL, *pvs=NULL;
1803    StretchStructuredSplineInfo sssi;
1804 
1805    if (ObjPtr->locked) {
1806       Msg(TgLoadString(STID_LOCKED_OBJS_CANT_BE_STRETCHED));
1807       return;
1808    }
1809    memset(&sssi, 0, sizeof(StretchStructuredSplineInfo));
1810    memset(vs, 0, 5*sizeof(IntPoint));
1811    memset(vs2, 0, 5*sizeof(IntPoint));
1812 
1813    switch (ObjPtr->type) {
1814    case OBJ_POLY:
1815       poly_ptr = ObjPtr->detail.p;
1816       start_v.x = poly_ptr->ssvlist[Index].x;
1817       start_v.y = poly_ptr->ssvlist[Index].y;
1818       n = poly_ptr->n;
1819       pvs = poly_ptr->vlist;
1820       break;
1821    case OBJ_POLYGON:
1822       polygon_ptr = ObjPtr->detail.g;
1823       start_v.x = polygon_ptr->ssvlist[Index].x;
1824       start_v.y = polygon_ptr->ssvlist[Index].y;
1825       n = polygon_ptr->n;
1826       pvs = polygon_ptr->vlist;
1827       break;
1828    }
1829    vlist = (IntPoint*)malloc((n+1)*sizeof(IntPoint));
1830    if (vlist == NULL) FailAllocMessage();
1831    memset(vlist, 0, (n+1)*sizeof(IntPoint));
1832 
1833    if (ObjPtr->ctm == NULL) {
1834       for (i=0; i < n; i++) {
1835          vlist[i].x = pvs[i].x;
1836          vlist[i].y = pvs[i].y;
1837       }
1838    } else {
1839       for (i=0; i < n; i++) {
1840          TransformPointThroughCTM(pvs[i].x-ObjPtr->x, pvs[i].y-ObjPtr->y,
1841                ObjPtr->ctm, &x, &y);
1842          vlist[i].x = x+ObjPtr->x;
1843          vlist[i].y = y+ObjPtr->y;
1844       }
1845    }
1846    if (poly_ptr != NULL) {
1847       SetIPTInfoForStretchPoly(Index, n, vlist, &sssi);
1848    } else if (polygon_ptr != NULL) {
1849       SetIPTInfoForStretchPolygon(Index, n, vlist, &sssi);
1850    }
1851    SetVsAndVs2ForStretchStructSpline(&sssi, 0, 0, &num_vs, vs, &num_vs2, vs2);
1852    FixUpSmoothAndSmooth2ForStretchStructSpline(num_vs, smooth, num_vs2,
1853          smooth2);
1854    if (sssi.prev_valid) {
1855       sv = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &sn, smooth,
1856             drawOrigX, drawOrigY, num_vs, vs);
1857       saved_sv = DupVs(&saved_sn, sv, sn);
1858    }
1859    if (sssi.next_valid) {
1860       sv2 = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &sn2, smooth2,
1861             drawOrigX, drawOrigY, num_vs2, vs2);
1862       saved_sv2 = DupVs(&saved_sn2, sv2, sn2);
1863    }
1864    ltx = ObjPtr->bbox.ltx - handleSize;
1865    lty = ObjPtr->bbox.lty - handleSize;
1866    rbx = ObjPtr->bbox.rbx + handleSize;
1867    rby = ObjPtr->bbox.rby + handleSize;
1868 
1869    XFlush(mainDisplay);
1870    XSync(mainDisplay, False);
1871 
1872    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
1873          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
1874       ExposeEventHandler(&ev, TRUE);
1875    }
1876    EraseHighLightForStretchStructSpline(&sssi, 0, 0, TRUE, FALSE);
1877    DoStretchStructSplineMeasureCursor(STRETCH_STARTSHOW, &start_v, 0, 0, grid_x,
1878          grid_y);
1879    if (!debugNoPointerGrab) {
1880       XGrabPointer(mainDisplay, drawWindow, False,
1881             PointerMotionMask | ButtonReleaseMask,
1882             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
1883    }
1884    dx = dy = 0;
1885    while (stretching) {
1886       XNextEvent(mainDisplay, &input);
1887 
1888       if (input.type == Expose || input.type == VisibilityNotify) {
1889          ExposeEventHandler(&input, TRUE);
1890       } else if (input.type == ButtonRelease) {
1891          XUngrabPointer(mainDisplay, CurrentTime);
1892          XSync(mainDisplay, False);
1893          stretching = FALSE;
1894       } else if (input.type == MotionNotify || input.type == KeyPress ||
1895             input.type == KeyRelease) {
1896          DoStretchStructSplineMeasureCursor(STRETCH_DOSHOW, &start_v,
1897                ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff),
1898                grid_x, grid_y);
1899          if (input.type == KeyPress || input.type == KeyRelease ) {
1900             x = grid_x;
1901             y = grid_y;
1902          } else {
1903             x = input.xmotion.x;
1904             y = input.xmotion.y;
1905          }
1906          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
1907             if (input.type == KeyRelease) {
1908                x = input.xkey.x;
1909                y = input.xkey.y;
1910             } else {
1911                DiagGridXY(XGridOff, YGridOff, &x, &y);
1912             }
1913          }
1914          GridXY(x, y, &grid_x, &grid_y);
1915 
1916          /* erase */
1917          if (sv != NULL) {
1918             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
1919                   CoordModeOrigin);
1920             free(sv);
1921             sv = NULL;
1922          }
1923          if (sv2 != NULL) {
1924             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv2, sn2,
1925                   CoordModeOrigin);
1926             free(sv2);
1927             sv2 = NULL;
1928          }
1929          EraseHighLightForStretchStructSpline(&sssi, ABS_SIZE(dx), ABS_SIZE(dy),
1930                TRUE, TRUE);
1931          dx = grid_x - XGridOff;
1932          dy = grid_y - YGridOff;
1933          ruler_x = OFFSET_X(start_v.x) + dx;
1934          ruler_y = OFFSET_Y(start_v.y) + dy;
1935          MarkRulers(ruler_x, ruler_y);
1936 
1937          /* draw */
1938          SetVsAndVs2ForStretchStructSpline(&sssi, ABS_SIZE(dx), ABS_SIZE(dy),
1939                &num_vs, vs, &num_vs2, vs2);
1940          FixUpSmoothAndSmooth2ForStretchStructSpline(num_vs, smooth, num_vs2,
1941                smooth2);
1942          if (sssi.prev_valid) {
1943             sv = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &sn, smooth,
1944                   drawOrigX, drawOrigY, num_vs, vs);
1945             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
1946                   CoordModeOrigin);
1947          }
1948          if (sssi.next_valid) {
1949             sv2 = MakeMultiSplinePolyVertex(LT_STRUCT_SPLINE, &sn2, smooth2,
1950                   drawOrigX, drawOrigY, num_vs2, vs2);
1951             XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv2, sn2,
1952                   CoordModeOrigin);
1953          }
1954          EraseHighLightForStretchStructSpline(&sssi, ABS_SIZE(dx), ABS_SIZE(dy),
1955                TRUE, TRUE);
1956          DoStretchStructSplineMeasureCursor(STRETCH_DOSHOW, &start_v,
1957                ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff),
1958                grid_x, grid_y);
1959          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
1960       }
1961    }
1962    DoStretchStructSplineMeasureCursor(STRETCH_ENDSHOW, &start_v,
1963          ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff), grid_x, grid_y);
1964    /* erase */
1965    if (sv != NULL) {
1966       XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
1967             CoordModeOrigin);
1968       free(sv);
1969       sv = NULL;
1970    }
1971    if (sv2 != NULL) {
1972       XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv2, sn2,
1973             CoordModeOrigin);
1974       free(sv2);
1975       sv2 = NULL;
1976    }
1977    if (saved_sv != NULL) {
1978       XDrawLines(mainDisplay, drawWindow, revDefaultGC, saved_sv, saved_sn,
1979             CoordModeOrigin);
1980       free(saved_sv);
1981       saved_sv = NULL;
1982    }
1983    if (saved_sv2 != NULL) {
1984       XDrawLines(mainDisplay, drawWindow, revDefaultGC, saved_sv2, saved_sn2,
1985             CoordModeOrigin);
1986       free(saved_sv2);
1987       saved_sv2 = NULL;
1988    }
1989    EraseHighLightForStretchStructSpline(&sssi, ABS_SIZE(dx), ABS_SIZE(dy), TRUE,
1990          TRUE);
1991    EraseHighLightForHinge(&sssi, 0, 0, FALSE, TRUE);
1992 
1993    if (dx != 0 || dy != 0) {
1994       if (has_on_reshape && name_attr != NULL) {
1995          on_reshape_attr = FindAttrWithName(ObjPtr, "on_reshape=", NULL);
1996       }
1997       HighLightReverse();
1998 
1999       if (on_reshape_attr != NULL) {
2000          StartCompositeCmd();
2001       }
2002       PrepareToReplaceAnObj(ObjPtr);
2003 
2004       if (poly_ptr != NULL) {
2005          UpdateObjForStretchStructSpline(ObjPtr, poly_ptr->n, poly_ptr->vlist,
2006                ABS_SIZE(dx), ABS_SIZE(dy), &sssi);
2007          AdjObjSplineVs(ObjPtr);
2008          UpdPolyBBox(ObjPtr, poly_ptr->ssn, poly_ptr->ssvlist);
2009       } else if (polygon_ptr != NULL) {
2010          UpdateObjForStretchStructSpline(ObjPtr, polygon_ptr->n,
2011                polygon_ptr->vlist, ABS_SIZE(dx), ABS_SIZE(dy), &sssi);
2012          AdjObjSplineVs(ObjPtr);
2013          UpdPolyBBox(ObjPtr, polygon_ptr->ssn, polygon_ptr->ssvlist);
2014       }
2015       if (auto_center_attr) {
2016          struct AttrRec *attr_ptr=ObjPtr->fattr;
2017          int modified=FALSE;
2018 
2019          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
2020             if (attr_ptr->shown) {
2021                struct BBRec bbox;
2022 
2023                CenterObjInOBBox(attr_ptr->obj, ObjPtr->obbox, &bbox);
2024                if (bbox.ltx < ltx) ltx = bbox.ltx;
2025                if (bbox.lty < lty) lty = bbox.lty;
2026                if (bbox.rbx > rbx) rbx = bbox.rbx;
2027                if (bbox.rby > rby) rby = bbox.rby;
2028                modified = TRUE;
2029             }
2030          }
2031          if (modified) AdjObjBBox(ObjPtr);
2032       }
2033       RecordReplaceAnObj(ObjPtr);
2034       if (on_reshape_attr != NULL) {
2035          DoExec(on_reshape_attr, ObjPtr);
2036       }
2037       if (on_reshape_attr != NULL) {
2038          EndCompositeCmd();
2039       }
2040       UpdSelBBox();
2041       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
2042             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
2043             ObjPtr->bbox.ltx-GRID_ABS_SIZE(1),
2044             ObjPtr->bbox.lty-GRID_ABS_SIZE(1),
2045             ObjPtr->bbox.rbx+GRID_ABS_SIZE(1),
2046             ObjPtr->bbox.rby+GRID_ABS_SIZE(1));
2047       SetFileModified(TRUE);
2048       justDupped = FALSE;
2049 
2050       HighLightForward();
2051    }
2052    if (sv != NULL) free(sv);
2053    if (sv2 != NULL) free(sv2);
2054    if (vlist != NULL) free(vlist);
2055 }
2056 
2057 static XPoint v[5];
2058 
2059 static
StretchPoly(XGridOff,YGridOff,ObjPtr,NumPts,V,Index)2060 void StretchPoly(XGridOff, YGridOff, ObjPtr, NumPts, V, Index)
2061    int XGridOff, YGridOff, NumPts, Index;
2062    IntPoint *V;
2063    struct ObjRec *ObjPtr;
2064 {
2065    struct AttrRec *name_attr=NULL, *on_reshape_attr=NULL;
2066    int i, x, y, dx, dy, stretching=TRUE;
2067    int ltx, lty, rbx, rby, curved=LT_STRAIGHT;
2068    int grid_x=XGridOff, grid_y=YGridOff, sn, intn;
2069    int auto_center_attr=AutoCenterAttr(ObjPtr);
2070    int has_on_reshape=HasOnReshape(ObjPtr, &name_attr);
2071    char *smooth=NULL;
2072    XEvent input, ev;
2073    XPoint *sv=NULL;
2074    IntPoint *pv=NULL, *cntrlv=NULL;
2075 
2076    if (ObjPtr->locked) {
2077       Msg(TgLoadString(STID_LOCKED_OBJS_CANT_BE_STRETCHED));
2078       return;
2079    }
2080    pv = (IntPoint*)malloc((NumPts+1)*sizeof(IntPoint));
2081    if (pv == NULL) FailAllocMessage();
2082    memset(pv, 0, (NumPts+1)*sizeof(IntPoint));
2083 
2084    switch (ObjPtr->type) {
2085    case OBJ_POLY:
2086       curved = ObjPtr->detail.p->curved;
2087       if (curved != LT_INTSPLINE && ObjPtr->detail.p->smooth != NULL) {
2088          smooth = (char*)malloc((NumPts+1)*sizeof(char));
2089          if (smooth == NULL) FailAllocMessage();
2090       }
2091       if (ObjPtr->ctm == NULL) {
2092          for (i = 0; i < NumPts; i++) {
2093             pv[i].x = V[i].x;
2094             pv[i].y = V[i].y;
2095             if (smooth != NULL) smooth[i] = ObjPtr->detail.p->smooth[i];
2096          }
2097       } else {
2098          for (i = 0; i < NumPts; i++) {
2099             int x, y;
2100 
2101             TransformPointThroughCTM(V[i].x-ObjPtr->x, V[i].y-ObjPtr->y,
2102                   ObjPtr->ctm, &x, &y);
2103             pv[i].x = x+ObjPtr->x;
2104             pv[i].y = y+ObjPtr->y;
2105             if (smooth != NULL) smooth[i] = ObjPtr->detail.p->smooth[i];
2106          }
2107       }
2108       switch (curved) {
2109       case LT_STRAIGHT:
2110       case LT_SPLINE:
2111          sv = MakeMultiSplinePolyVertex(curved, &sn, smooth,
2112                drawOrigX, drawOrigY, NumPts, pv);
2113          break;
2114       case LT_INTSPLINE:
2115          sv = MakeIntSplinePolyVertex(&sn, &intn, &cntrlv, drawOrigX,
2116                drawOrigY, NumPts, pv);
2117          break;
2118       }
2119       break;
2120    case OBJ_POLYGON:
2121       curved = ObjPtr->detail.g->curved;
2122       if (curved != LT_INTSPLINE && ObjPtr->detail.g->smooth != NULL) {
2123          smooth = (char*)malloc((NumPts+1)*sizeof(char));
2124          if (smooth == NULL) FailAllocMessage();
2125       }
2126       if (ObjPtr->ctm == NULL) {
2127          for (i = 0; i < NumPts; i++) {
2128             pv[i].x = V[i].x;
2129             pv[i].y = V[i].y;
2130             if (smooth != NULL) smooth[i] = ObjPtr->detail.g->smooth[i];
2131          }
2132       } else {
2133          for (i = 0; i < NumPts; i++) {
2134             int x, y;
2135 
2136             TransformPointThroughCTM(V[i].x-ObjPtr->x, V[i].y-ObjPtr->y,
2137                   ObjPtr->ctm, &x, &y);
2138             pv[i].x = x+ObjPtr->x;
2139             pv[i].y = y+ObjPtr->y;
2140             if (smooth != NULL) smooth[i] = ObjPtr->detail.g->smooth[i];
2141          }
2142       }
2143       switch (curved) {
2144       case LT_STRAIGHT:
2145       case LT_SPLINE:
2146          sv = MakeMultiSplinePolygonVertex(curved, &sn, smooth,
2147                drawOrigX, drawOrigY, NumPts, pv);
2148          break;
2149       case LT_INTSPLINE:
2150          sv = MakeIntSplinePolygonVertex(&sn, &intn, &cntrlv,
2151                drawOrigX, drawOrigY, NumPts, pv);
2152          break;
2153       }
2154       break;
2155    }
2156    ltx = ObjPtr->bbox.ltx;
2157    lty = ObjPtr->bbox.lty;
2158    rbx = ObjPtr->bbox.rbx;
2159    rby = ObjPtr->bbox.rby;
2160 
2161    XFlush(mainDisplay);
2162    XSync(mainDisplay, False);
2163 
2164    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
2165          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
2166       ExposeEventHandler(&ev, TRUE);
2167    }
2168    DoStretchPolyMeasureCursor(STRETCH_STARTSHOW, NumPts, pv, Index, 0, 0,
2169          STRETCH_DRAW, STRETCH_CLICK, ObjPtr->type, grid_x, grid_y);
2170    if (!debugNoPointerGrab) {
2171       XGrabPointer(mainDisplay, drawWindow, False,
2172             PointerMotionMask | ButtonReleaseMask,
2173             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
2174    }
2175    dx = dy = 0;
2176    while (stretching) {
2177       XNextEvent(mainDisplay, &input);
2178 
2179       if (input.type == Expose || input.type == VisibilityNotify) {
2180          ExposeEventHandler(&input, TRUE);
2181       } else if (input.type == ButtonRelease) {
2182          XUngrabPointer(mainDisplay, CurrentTime);
2183          XSync(mainDisplay, False);
2184          stretching = FALSE;
2185       } else if (input.type == MotionNotify || input.type == KeyPress ||
2186             input.type == KeyRelease) {
2187          DoStretchPolyMeasureCursor(STRETCH_DOSHOW, NumPts, pv, Index,
2188                ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff),
2189                STRETCH_ERASE, STRETCH_DRAG, ObjPtr->type, grid_x, grid_y);
2190          if (input.type == KeyPress || input.type == KeyRelease ) {
2191             x = grid_x;
2192             y = grid_y;
2193          } else {
2194             x = input.xmotion.x;
2195             y = input.xmotion.y;
2196          }
2197          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
2198             if (input.type == KeyRelease) {
2199                x = input.xkey.x;
2200                y = input.xkey.y;
2201             } else {
2202                DiagGridXY(XGridOff, YGridOff, &x, &y);
2203             }
2204          }
2205          GridXY(x, y, &grid_x, &grid_y);
2206 
2207          XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
2208                CoordModeOrigin);
2209          dx = grid_x - XGridOff;
2210          dy = grid_y - YGridOff;
2211          v[1].x = OFFSET_X(V[Index].x) + dx;
2212          v[1].y = OFFSET_Y(V[Index].y) + dy;
2213          MarkRulers(v[1].x, v[1].y);
2214 
2215          if (sv != NULL) {
2216             free(sv);
2217             sv = NULL;
2218          }
2219          if (ObjPtr->type==OBJ_POLYGON && (Index==0 || Index==NumPts-1)) {
2220             if (ObjPtr->ctm == NULL) {
2221                pv[0].x = pv[NumPts-1].x = V[0].x + ABS_SIZE(dx);
2222                pv[0].y = pv[NumPts-1].y = V[0].y + ABS_SIZE(dy);
2223             } else {
2224                int x, y;
2225 
2226                TransformPointThroughCTM(V[0].x-ObjPtr->x, V[0].y-ObjPtr->y,
2227                      ObjPtr->ctm, &x, &y);
2228                pv[0].x = pv[NumPts-1].x = x + ObjPtr->x + ABS_SIZE(dx);
2229                pv[0].y = pv[NumPts-1].y = y + ObjPtr->y + ABS_SIZE(dy);
2230             }
2231          } else {
2232             if (ObjPtr->ctm == NULL) {
2233                pv[Index].x = V[Index].x + ABS_SIZE(dx);
2234                pv[Index].y = V[Index].y + ABS_SIZE(dy);
2235             } else {
2236                int x, y;
2237 
2238                TransformPointThroughCTM(V[Index].x-ObjPtr->x,
2239                      V[Index].y-ObjPtr->y, ObjPtr->ctm, &x, &y);
2240                pv[Index].x = x + ObjPtr->x + ABS_SIZE(dx);
2241                pv[Index].y = y + ObjPtr->y + ABS_SIZE(dy);
2242             }
2243          }
2244          switch (ObjPtr->type) {
2245          case OBJ_POLY:
2246             switch (curved) {
2247             case LT_STRAIGHT:
2248             case LT_SPLINE:
2249                sv = MakeMultiSplinePolyVertex(curved, &sn, smooth,
2250                      drawOrigX, drawOrigY, NumPts, pv);
2251                break;
2252             case LT_INTSPLINE:
2253                free(cntrlv);
2254                sv = MakeIntSplinePolyVertex(&sn, &intn, &cntrlv,
2255                      drawOrigX, drawOrigY, NumPts, pv);
2256                break;
2257             }
2258             break;
2259          case OBJ_POLYGON:
2260             switch (curved) {
2261             case LT_STRAIGHT:
2262             case LT_SPLINE:
2263                sv = MakeMultiSplinePolygonVertex(curved, &sn, smooth,
2264                      drawOrigX, drawOrigY, NumPts, pv);
2265                break;
2266             case LT_INTSPLINE:
2267                free(cntrlv);
2268                sv = MakeIntSplinePolygonVertex(&sn, &intn, &cntrlv,
2269                      drawOrigX, drawOrigY, NumPts, pv);
2270                break;
2271             }
2272             break;
2273          }
2274          XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
2275                CoordModeOrigin);
2276          DoStretchPolyMeasureCursor(STRETCH_DOSHOW, NumPts, pv, Index,
2277                ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff),
2278                STRETCH_DRAW, STRETCH_DRAG, ObjPtr->type, grid_x, grid_y);
2279          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
2280       }
2281    }
2282    DoStretchPolyMeasureCursor(STRETCH_ENDSHOW, NumPts, pv, Index,
2283          ABS_SIZE(grid_x-XGridOff), ABS_SIZE(grid_y-YGridOff),
2284          STRETCH_ERASE, STRETCH_DRAG, ObjPtr->type, grid_x, grid_y);
2285    if (dx != 0 || dy != 0) {
2286       if (has_on_reshape && name_attr != NULL) {
2287          on_reshape_attr = FindAttrWithName(ObjPtr, "on_reshape=", NULL);
2288       }
2289       XDrawLines(mainDisplay, drawWindow, revDefaultGC, sv, sn,
2290             CoordModeOrigin);
2291       HighLightReverse();
2292 
2293       if (on_reshape_attr != NULL) {
2294          StartCompositeCmd();
2295       }
2296       PrepareToReplaceAnObj(ObjPtr);
2297 
2298       dx = ABS_SIZE(dx);
2299       dy = ABS_SIZE(dy);
2300       switch (ObjPtr->type) {
2301       case OBJ_POLY:
2302          if (ObjPtr->ctm == NULL) {
2303             V[Index].x += dx; V[Index].y += dy;
2304          } else {
2305             int x, y, new_x, new_y;
2306 
2307             TransformPointThroughCTM(V[Index].x-ObjPtr->x,
2308                   V[Index].y-ObjPtr->y, ObjPtr->ctm, &x, &y);
2309             x += ObjPtr->x + dx;
2310             y += ObjPtr->y + dy;
2311             ReverseTransformPointThroughCTM(x-ObjPtr->x, y-ObjPtr->y,
2312                   ObjPtr->ctm, &new_x, &new_y);
2313             V[Index].x = new_x + ObjPtr->x;
2314             V[Index].y = new_y + ObjPtr->y;
2315          }
2316          AdjObjSplineVs(ObjPtr);
2317          if (ObjPtr->detail.p->curved != LT_INTSPLINE) {
2318             UpdPolyBBox(ObjPtr, NumPts, V);
2319          } else {
2320             UpdPolyBBox(ObjPtr, ObjPtr->detail.p->intn,
2321                   ObjPtr->detail.p->intvlist);
2322          }
2323          break;
2324       case OBJ_POLYGON:
2325          if (ObjPtr->ctm == NULL) {
2326             V[Index].x += dx; V[Index].y += dy;
2327          } else {
2328             int x, y, new_x, new_y;
2329 
2330             TransformPointThroughCTM(V[Index].x-ObjPtr->x,
2331                   V[Index].y-ObjPtr->y, ObjPtr->ctm, &x, &y);
2332             x += ObjPtr->x + dx;
2333             y += ObjPtr->y + dy;
2334             ReverseTransformPointThroughCTM(x-ObjPtr->x, y-ObjPtr->y,
2335                   ObjPtr->ctm, &new_x, &new_y);
2336             V[Index].x = new_x + ObjPtr->x;
2337             V[Index].y = new_y + ObjPtr->y;
2338          }
2339          if (Index == 0) {
2340             V[NumPts-1].x = V[Index].x; V[NumPts-1].y = V[Index].y;
2341          } else if (Index == NumPts-1) {
2342             V[0].x = V[Index].x; V[0].y = V[Index].y;
2343          }
2344          AdjObjSplineVs(ObjPtr);
2345          if (ObjPtr->detail.g->curved != LT_INTSPLINE) {
2346             UpdPolyBBox(ObjPtr, NumPts, V);
2347          } else {
2348             UpdPolyBBox(ObjPtr, ObjPtr->detail.g->intn,
2349                   ObjPtr->detail.g->intvlist);
2350          }
2351          break;
2352       }
2353       if (auto_center_attr) {
2354          struct AttrRec *attr_ptr=ObjPtr->fattr;
2355          int modified=FALSE;
2356 
2357          for ( ; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
2358             if (attr_ptr->shown) {
2359                struct BBRec bbox;
2360 
2361                CenterObjInOBBox(attr_ptr->obj, ObjPtr->obbox, &bbox);
2362                if (bbox.ltx < ltx) ltx = bbox.ltx;
2363                if (bbox.lty < lty) lty = bbox.lty;
2364                if (bbox.rbx > rbx) rbx = bbox.rbx;
2365                if (bbox.rby > rby) rby = bbox.rby;
2366                modified = TRUE;
2367             }
2368          }
2369          if (modified) AdjObjBBox(ObjPtr);
2370       }
2371       RecordReplaceAnObj(ObjPtr);
2372       if (on_reshape_attr != NULL) {
2373          DoExec(on_reshape_attr, ObjPtr);
2374       }
2375       if (on_reshape_attr != NULL) {
2376          EndCompositeCmd();
2377       }
2378       UpdSelBBox();
2379       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
2380             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
2381             ObjPtr->bbox.ltx-GRID_ABS_SIZE(1),
2382             ObjPtr->bbox.lty-GRID_ABS_SIZE(1),
2383             ObjPtr->bbox.rbx+GRID_ABS_SIZE(1),
2384             ObjPtr->bbox.rby+GRID_ABS_SIZE(1));
2385       SetFileModified(TRUE);
2386       justDupped = FALSE;
2387 
2388       HighLightForward();
2389    }
2390    free(pv);
2391    if (sv != NULL) free(sv);
2392    if (smooth != NULL) free(smooth);
2393    if (curved == LT_INTSPLINE && cntrlv != NULL) free(cntrlv);
2394 }
2395 
2396 static double	multX=(double)0.0, multY=(double)0.0;
2397 static int	pivotX=0, pivotY=0, changeX=0, changeY=0, moveX=0, moveY=0;
2398 static int	absPivotX=0, absPivotY=0;
2399 
2400 static
StretchedXY(X,Y,NewX,NewY)2401 void StretchedXY(X, Y, NewX, NewY)
2402    int X, Y, * NewX, * NewY; /* screen offsets */
2403 {
2404    register int dx, dy;
2405 
2406    dx = round((double)((double)(X - pivotX) * multX));
2407    dy = round((double)((double)(Y - pivotY) * multY));
2408    *NewX = pivotX + dx;
2409    *NewY = pivotY + dy;
2410 }
2411 
2412 static
StretchedAbsXY(X,Y,NewX,NewY)2413 void StretchedAbsXY(X, Y, NewX, NewY)
2414    int X, Y, * NewX, * NewY; /* screen offsets */
2415 {
2416    register int dx, dy;
2417 
2418    dx = round((double)((double)(X - absPivotX) * multX));
2419    dy = round((double)((double)(Y - absPivotY) * multY));
2420    *NewX = absPivotX + dx;
2421    *NewY = absPivotY + dy;
2422 }
2423 
2424 static
ShearedXY(Corner,x,y,dx_shear,dy_shear,dx_scale,dy_scale,new_x,new_y)2425 void ShearedXY(Corner, x, y, dx_shear, dy_shear, dx_scale, dy_scale,
2426       new_x, new_y)
2427    int Corner, x, y, *new_x, *new_y;
2428    double dx_shear, dy_shear, dx_scale, dy_scale;
2429 {
2430    double val, dx, dy;
2431 
2432    if (Corner != CORNER_NONE && Corner != CORNER_RIGHT &&
2433          Corner != CORNER_LEFT) {
2434       if (y == pivotY) {
2435          *new_x = x;
2436          *new_y = y;
2437       } else {
2438          dy = ((double)(y-pivotY))*dy_scale/1000.0;
2439          val = tan(dx_shear/1000.0)*dy;
2440          *new_x = round(val + x);
2441          *new_y = round(dy + pivotY);
2442       }
2443    }
2444    if (Corner != CORNER_NONE && Corner != CORNER_TOP &&
2445          Corner != CORNER_BOTTOM) {
2446       if (x == pivotX) {
2447          *new_x = x;
2448          *new_y = y;
2449       } else {
2450          dx = ((double)(x-pivotX))*dx_scale/1000.0;
2451          val = tan(dy_shear/1000.0)*dx;
2452          *new_x = round(dx + pivotX);
2453          *new_y = round(val + y);
2454       }
2455    }
2456 }
2457 
2458 static
ShearedAbsXY(Corner,abs_x,abs_y,dx_shear,dy_shear,dx_scale,dy_scale,new_x,new_y)2459 void ShearedAbsXY(Corner, abs_x, abs_y, dx_shear, dy_shear, dx_scale, dy_scale,
2460       new_x, new_y)
2461    int Corner, abs_x, abs_y, *new_x, *new_y;
2462    double dx_shear, dy_shear, dx_scale, dy_scale;
2463 {
2464    double val, dx, dy;
2465 
2466    if (Corner != CORNER_NONE && Corner != CORNER_RIGHT &&
2467          Corner != CORNER_LEFT) {
2468       if (abs_y == absPivotY) {
2469          *new_x = abs_x;
2470          *new_y = abs_y;
2471       } else {
2472          dy = ((double)(abs_y-absPivotY))*dy_scale/1000.0;
2473          val = tan(dx_shear/1000.0)*dy;
2474          *new_x = round(val + abs_x);
2475          *new_y = round(dy + absPivotY);
2476       }
2477    }
2478    if (Corner != CORNER_NONE && Corner != CORNER_TOP &&
2479          Corner != CORNER_BOTTOM) {
2480       if (abs_x == absPivotX) {
2481          *new_x = abs_x;
2482          *new_y = abs_y;
2483       } else {
2484          dx = ((double)(abs_x-absPivotX))*dx_scale/1000.0;
2485          val = tan(dy_shear/1000.0)*dx;
2486          *new_x = round(dx + absPivotX);
2487          *new_y = round(val + abs_y);
2488       }
2489    }
2490 }
2491 
2492 static
SetPivot(Corner,pOBBox)2493 void SetPivot(Corner, pOBBox)
2494    int Corner;
2495    struct BBRec *pOBBox;
2496    /* pivotX, pivotY, moveX, moveY will be set to screen offsets */
2497 {
2498    switch (Corner) {
2499    case CORNER_NONE: /* same as CORNER_CC */
2500       pivotX = moveX = ((pOBBox->ltx+pOBBox->rbx)>>1);
2501       pivotY = moveY = ((pOBBox->lty+pOBBox->rby)>>1);
2502       changeX = TRUE; changeY = TRUE;
2503       break;
2504    case CORNER_LT:
2505       pivotX = pOBBox->rbx; pivotY = pOBBox->rby;
2506       moveX = pOBBox->ltx; moveY = pOBBox->lty;
2507       changeX = changeY = TRUE;
2508       break;
2509    case CORNER_TOP:
2510       pivotX = moveX = ((pOBBox->ltx+pOBBox->rbx)>>1); pivotY = pOBBox->rby;
2511       moveY = pOBBox->lty;
2512       changeX = FALSE; changeY = TRUE;
2513       break;
2514    case CORNER_RT:
2515       pivotX = pOBBox->ltx; pivotY = pOBBox->rby;
2516       moveX = pOBBox->rbx; moveY = pOBBox->lty;
2517       changeX = changeY = TRUE;
2518       break;
2519    case CORNER_RIGHT:
2520       pivotX = pOBBox->ltx; pivotY = moveY = ((pOBBox->lty+pOBBox->rby)>>1);
2521       moveX = pOBBox->rbx;
2522       changeX = TRUE; changeY = FALSE;
2523       break;
2524    case CORNER_RB:
2525       pivotX = pOBBox->ltx; pivotY = pOBBox->lty;
2526       moveX = pOBBox->rbx; moveY = pOBBox->rby;
2527       changeX = changeY = TRUE;
2528       break;
2529    case CORNER_BOTTOM:
2530       pivotX = moveX = ((pOBBox->ltx+pOBBox->rbx)>>1); pivotY = pOBBox->lty;
2531       moveY = pOBBox->rby;
2532       changeX = FALSE; changeY = TRUE;
2533       break;
2534    case CORNER_LB:
2535       pivotX = pOBBox->rbx; pivotY = pOBBox->lty;
2536       moveX = pOBBox->ltx; moveY = pOBBox->rby;
2537       changeX = changeY = TRUE;
2538       break;
2539    case CORNER_LEFT:
2540       pivotX = pOBBox->rbx; pivotY = moveY = ((pOBBox->lty+pOBBox->rby)>>1);
2541       moveX = pOBBox->ltx;
2542       changeX = TRUE; changeY = FALSE;
2543       break;
2544    }
2545    multX = 1.0;
2546    multY = 1.0;
2547    absPivotX = pivotX;
2548    absPivotY = pivotY;
2549    pivotX = OFFSET_X(absPivotX);
2550    pivotY = OFFSET_Y(absPivotY);
2551    moveX = OFFSET_X(moveX);
2552    moveY = OFFSET_Y(moveY);
2553 }
2554 
ShearObj(ObjPtr,Corner,dxShear,dyShear,dxScale,dyScale,RealLtX,RealLtY)2555 void ShearObj(ObjPtr, Corner, dxShear, dyShear, dxScale, dyScale,
2556       RealLtX, RealLtY)
2557    struct ObjRec *ObjPtr;
2558    int Corner;
2559    double dxShear, dyShear, dxScale, dyScale; /* scaled by 1000 */
2560    int *RealLtX, *RealLtY;
2561 {
2562    IntPoint abs_obj_obbox_vs[5];
2563    int x, y, new_ltx, new_lty, new_rbx, new_rby;
2564    double tan_val;
2565    struct XfrmMtrxRec ctm, new_ctm;
2566    struct ObjRec *obj_ptr;
2567    struct AttrRec *attr_ptr;
2568    int auto_center_attr=AutoCenterAttr(ObjPtr);
2569 
2570    switch (ObjPtr->type) {
2571    case OBJ_GROUP:
2572    case OBJ_ICON:
2573    case OBJ_SYM:
2574    case OBJ_PIN:
2575       for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
2576             obj_ptr=obj_ptr->next) {
2577          ShearObj(obj_ptr, Corner, dxShear, dyShear, dxScale, dyScale,
2578                RealLtX, RealLtY);
2579       }
2580       break;
2581 
2582    default:
2583       if (ObjPtr->ctm == NULL) {
2584          memcpy(&ObjPtr->orig_obbox, &ObjPtr->obbox, sizeof(struct BBRec));
2585          if (ObjPtr->type == OBJ_TEXT) {
2586             memcpy(&ObjPtr->detail.t->orig_bbox, &ObjPtr->bbox,
2587                   sizeof(struct BBRec));
2588          }
2589          ObjPtr->ctm = (struct XfrmMtrxRec *)malloc(sizeof(struct XfrmMtrxRec));
2590          if (ObjPtr->ctm == NULL) FailAllocMessage();
2591          ObjPtr->ctm->m[CTM_SX] = ObjPtr->ctm->m[CTM_SY] = (double)1000;
2592          ObjPtr->ctm->m[CTM_SIN] = ObjPtr->ctm->m[CTM_MSIN] = (double)0;
2593          ObjPtr->ctm->t[CTM_TX] = ObjPtr->ctm->t[CTM_TY] = 0;
2594       }
2595       ShearedAbsXY(Corner, ObjPtr->x+ObjPtr->ctm->t[CTM_TX],
2596             ObjPtr->y+ObjPtr->ctm->t[CTM_TY], dxShear, dyShear,
2597             dxScale, dyScale, &x, &y);
2598       switch (Corner) {
2599       case CORNER_TOP:
2600       case CORNER_BOTTOM:
2601          tan_val = tan(dxShear/1000.0);
2602          ctm.m[CTM_SX] = (double)1000;
2603          ctm.m[CTM_SY] = dyScale;
2604          ctm.m[CTM_SIN] = (double)0;
2605          ctm.m[CTM_MSIN] = dyScale*tan_val;
2606          break;
2607       case CORNER_RIGHT:
2608       case CORNER_LEFT:
2609          tan_val = tan(dyShear/1000.0);
2610          ctm.m[CTM_SX] = dxScale;
2611          ctm.m[CTM_SY] = (double)1000;
2612          ctm.m[CTM_SIN] = dxScale*tan_val;
2613          ctm.m[CTM_MSIN] = (double)0;
2614          break;
2615       default:
2616          ctm.m[CTM_SX] = dxScale;
2617          ctm.m[CTM_SY] = dyScale;
2618          ctm.m[CTM_SIN] = (double)0;
2619          ctm.m[CTM_MSIN] = (double)0;
2620          break;
2621       }
2622       ctm.t[CTM_TX] = 0;
2623       ctm.t[CTM_TY] = 0;
2624       ConcatCTM(ObjPtr->ctm, &ctm, &new_ctm);
2625       new_ctm.t[CTM_TX] = x-ObjPtr->x;
2626       new_ctm.t[CTM_TY] = y-ObjPtr->y;
2627       memcpy(ObjPtr->ctm, &new_ctm, sizeof(struct XfrmMtrxRec));
2628 
2629       GetTransformedOBBoxAbsVs(ObjPtr, abs_obj_obbox_vs);
2630 
2631       new_ltx = min(min(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2632             min(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2633       new_rbx = max(max(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2634             max(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2635       new_lty = min(min(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2636             min(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2637       new_rby = max(max(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2638             max(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2639 
2640       ObjPtr->obbox.ltx = new_ltx; ObjPtr->obbox.lty = new_lty;
2641       ObjPtr->obbox.rbx = new_rbx; ObjPtr->obbox.rby = new_rby;
2642       if (RealLtX != NULL && RealLtY != NULL) {
2643          MoveObj(ObjPtr, (*RealLtX)-new_ltx, (*RealLtY)-new_lty);
2644       }
2645       if (ObjPtr->ctm->m[CTM_SX] >= 999.0 &&
2646             ObjPtr->ctm->m[CTM_SX] <= 1001.0 &&
2647             ObjPtr->ctm->m[CTM_SY] >= 999.0 &&
2648             ObjPtr->ctm->m[CTM_SY] <= 1001.0 &&
2649             ObjPtr->ctm->m[CTM_SIN] >= (-1.0) &&
2650             ObjPtr->ctm->m[CTM_SIN] <= 1.0 &&
2651             ObjPtr->ctm->m[CTM_MSIN] >= (-1.0) &&
2652             ObjPtr->ctm->m[CTM_MSIN] <= 1.0) {
2653          int dx=ObjPtr->ctm->t[CTM_TX], dy=ObjPtr->ctm->t[CTM_TY];
2654 
2655          free(ObjPtr->ctm);
2656          ObjPtr->ctm = NULL;
2657 
2658          memcpy(&ObjPtr->obbox, &ObjPtr->orig_obbox, sizeof(struct BBRec));
2659          if (ObjPtr->type == OBJ_TEXT) {
2660             memcpy(&ObjPtr->bbox, &ObjPtr->detail.t->orig_bbox,
2661                   sizeof(struct BBRec));
2662          }
2663          MoveObj(ObjPtr, dx, dy);
2664       }
2665       break;
2666    }
2667    AdjObjOBBox(ObjPtr);
2668    if (auto_center_attr) {
2669       for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
2670          ShearObj(attr_ptr->obj, Corner, dxShear, dyShear, dxScale, dyScale,
2671                NULL, NULL);
2672          if (attr_ptr->shown) {
2673             CenterObjInOBBox(attr_ptr->obj, ObjPtr->obbox, NULL);
2674          }
2675       }
2676    } else {
2677       for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
2678          ShearObj(attr_ptr->obj, Corner, dxShear, dyShear, dxScale, dyScale,
2679                NULL, NULL);
2680       }
2681    }
2682    AdjObjSplineVs(ObjPtr);
2683    AdjObjCache(ObjPtr);
2684    AdjObjBBox(ObjPtr);
2685 }
2686 
2687 static
StretchSimpleText(ObjPtr,Corner)2688 void StretchSimpleText(ObjPtr, Corner)
2689    struct ObjRec *ObjPtr;
2690    int Corner;
2691 {
2692    /* !stretchableText */
2693    if (ObjPtr->ctm == NULL) {
2694       int new_x, new_y, h=ABS_SIZE(ObjPtr->obbox.rby-ObjPtr->obbox.lty);
2695 
2696       StretchedAbsXY(ObjPtr->x, ObjPtr->y, &new_x, &new_y);
2697       MoveObj(ObjPtr, new_x-ObjPtr->x, new_y-ObjPtr->y);
2698       ObjPtr->x = new_x;
2699       ObjPtr->y = new_y;
2700       if (multX < 0) {
2701          ObjPtr->detail.t->minilines.just =
2702                MAXJUSTS-1-ObjPtr->detail.t->minilines.just;
2703       }
2704       if (multY < 0) {
2705          MoveObj(ObjPtr, 0, -h);
2706       }
2707    } else {
2708       int abs_x, abs_y, new_x, new_y;
2709 
2710       abs_x = ObjPtr->x+ObjPtr->ctm->t[CTM_TX];
2711       abs_y = ObjPtr->y+ObjPtr->ctm->t[CTM_TY];
2712       StretchedAbsXY(abs_x, abs_y, &new_x, &new_y);
2713       if (multX < 0.0 || multY < 0.0) {
2714          int new_ltx, new_lty, new_rbx, new_rby;
2715          IntPoint abs_obj_obbox_vs[5];
2716          struct XfrmMtrxRec ctm, new_ctm;
2717 
2718          ctm.m[CTM_SX] = ctm.m[CTM_SY] = (double)1000;
2719          ctm.m[CTM_SIN] = ctm.m[CTM_MSIN] = (double)0;
2720          ctm.t[CTM_TX] = ctm.t[CTM_TY] = 0;
2721          ctm.m[CTM_SX] = (multX < 0.0) ? (double)(-1000) : (double)1000;
2722          ctm.m[CTM_SY] = (multY < 0.0) ? (double)(-1000) : (double)1000;
2723          ConcatCTM(ObjPtr->ctm, &ctm, &new_ctm);
2724          new_ctm.t[CTM_TX] = new_x-ObjPtr->x;
2725          new_ctm.t[CTM_TY] = new_y-ObjPtr->y;
2726          memcpy(ObjPtr->ctm, &new_ctm, sizeof(struct XfrmMtrxRec));
2727 
2728          GetTransformedOBBoxAbsVs(ObjPtr, abs_obj_obbox_vs);
2729 
2730          new_ltx = min(min(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2731                min(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2732          new_rbx = max(max(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2733                max(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2734          new_lty = min(min(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2735                min(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2736          new_rby = max(max(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2737                max(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2738 
2739          ObjPtr->obbox.ltx = new_ltx; ObjPtr->obbox.lty = new_lty;
2740          ObjPtr->obbox.rbx = new_rbx; ObjPtr->obbox.rby = new_rby;
2741          abs_x = ObjPtr->x+ObjPtr->ctm->t[CTM_TX];
2742          abs_y = ObjPtr->y+ObjPtr->ctm->t[CTM_TY];
2743       }
2744       MoveObj(ObjPtr, new_x-abs_x, new_y-abs_y);
2745    }
2746    UpdTextBBox(ObjPtr);
2747    AdjObjSplineVs(ObjPtr);
2748    AdjObjBBox(ObjPtr);
2749 }
2750 
2751 static
CompoundObjHasTextSubObj(ObjPtr)2752 int CompoundObjHasTextSubObj(ObjPtr)
2753    struct ObjRec *ObjPtr;
2754 {
2755    struct ObjRec *sub_obj=ObjPtr->detail.r->last;
2756 
2757    for ( ; sub_obj != NULL; sub_obj=sub_obj->prev) {
2758       if (sub_obj->type == OBJ_GROUP ||
2759             sub_obj->type == OBJ_ICON ||
2760             sub_obj->type == OBJ_SYM ||
2761             sub_obj->type == OBJ_PIN) {
2762          if (CompoundObjHasTextSubObj(sub_obj)) {
2763             return TRUE;
2764          }
2765       } else {
2766          return (sub_obj->type == OBJ_TEXT);
2767       }
2768    }
2769    return FALSE;
2770 }
2771 
2772 static
JustMoveSimpleCompoundObj(ObjPtr,Corner)2773 void JustMoveSimpleCompoundObj(ObjPtr, Corner)
2774    struct ObjRec *ObjPtr;
2775    int Corner;
2776 {
2777    /* !stretchableText && !compoundObjWithTextStretchableForPSE */
2778    if (ObjPtr->ctm == NULL) {
2779       int new_x, new_y, h=ABS_SIZE(ObjPtr->obbox.rby-ObjPtr->obbox.lty);
2780 
2781       StretchedAbsXY(ObjPtr->x, ObjPtr->y, &new_x, &new_y);
2782       MoveObj(ObjPtr, new_x-ObjPtr->x, new_y-ObjPtr->y);
2783       ObjPtr->x = new_x;
2784       ObjPtr->y = new_y;
2785       if (multY < 0) {
2786          MoveObj(ObjPtr, 0, -h);
2787       }
2788    } else {
2789       int abs_x, abs_y, new_x, new_y;
2790 
2791       abs_x = ObjPtr->x+ObjPtr->ctm->t[CTM_TX];
2792       abs_y = ObjPtr->y+ObjPtr->ctm->t[CTM_TY];
2793       StretchedAbsXY(abs_x, abs_y, &new_x, &new_y);
2794       if (multX < 0.0 || multY < 0.0) {
2795          int new_ltx, new_lty, new_rbx, new_rby;
2796          IntPoint abs_obj_obbox_vs[5];
2797          struct XfrmMtrxRec ctm, new_ctm;
2798 
2799          ctm.m[CTM_SX] = ctm.m[CTM_SY] = (double)1000;
2800          ctm.m[CTM_SIN] = ctm.m[CTM_MSIN] = (double)0;
2801          ctm.t[CTM_TX] = ctm.t[CTM_TY] = 0;
2802          ctm.m[CTM_SX] = (multX < 0.0) ? (double)(-1000) : (double)1000;
2803          ctm.m[CTM_SY] = (multY < 0.0) ? (double)(-1000) : (double)1000;
2804          ConcatCTM(ObjPtr->ctm, &ctm, &new_ctm);
2805          new_ctm.t[CTM_TX] = new_x-ObjPtr->x;
2806          new_ctm.t[CTM_TY] = new_y-ObjPtr->y;
2807          memcpy(ObjPtr->ctm, &new_ctm, sizeof(struct XfrmMtrxRec));
2808 
2809          GetTransformedOBBoxAbsVs(ObjPtr, abs_obj_obbox_vs);
2810 
2811          new_ltx = min(min(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2812                min(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2813          new_rbx = max(max(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
2814                max(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
2815          new_lty = min(min(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2816                min(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2817          new_rby = max(max(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
2818                max(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
2819 
2820          ObjPtr->obbox.ltx = new_ltx; ObjPtr->obbox.lty = new_lty;
2821          ObjPtr->obbox.rbx = new_rbx; ObjPtr->obbox.rby = new_rby;
2822          abs_x = ObjPtr->x+ObjPtr->ctm->t[CTM_TX];
2823          abs_y = ObjPtr->y+ObjPtr->ctm->t[CTM_TY];
2824       }
2825       MoveObj(ObjPtr, new_x-abs_x, new_y-abs_y);
2826    }
2827    AdjObjSplineVs(ObjPtr);
2828    AdjObjBBox(ObjPtr);
2829 }
2830 
2831 static
StretchAttr(ObjPtr,Corner,dxScale,dyScale,AutoCenterAttr)2832 void StretchAttr(ObjPtr, Corner, dxScale, dyScale, AutoCenterAttr)
2833    struct ObjRec *ObjPtr;
2834    int Corner, AutoCenterAttr;
2835    double dxScale, dyScale;
2836 {
2837    struct AttrRec *attr_ptr=ObjPtr->fattr;
2838 
2839    if (attr_ptr == NULL) return;
2840 
2841    if (stretchingEverything) {
2842       struct BBRec final_obbox;
2843       double dz=(double)0;
2844 
2845       if (AutoCenterAttr) {
2846          for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL;
2847                attr_ptr=attr_ptr->next) {
2848             struct ObjRec *obj_ptr=attr_ptr->obj;
2849             int ltx=0, lty=0, rbx=0, rby=0;
2850 
2851             StretchedAbsXY(obj_ptr->obbox.ltx, obj_ptr->obbox.lty, &ltx, &lty);
2852             StretchedAbsXY(obj_ptr->obbox.rbx, obj_ptr->obbox.rby, &rbx, &rby);
2853             CalcBBox(ltx, lty, rbx, rby, &final_obbox.ltx, &final_obbox.lty,
2854                   &final_obbox.rbx, &final_obbox.rby);
2855 
2856             ShearObj(attr_ptr->obj, Corner, dz, dz, dxScale, dyScale,
2857                      &final_obbox.ltx, &final_obbox.lty);
2858             if (attr_ptr->shown) {
2859                CenterObjInOBBox(attr_ptr->obj, ObjPtr->obbox, NULL);
2860             }
2861          }
2862       } else {
2863          for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL;
2864                attr_ptr=attr_ptr->next) {
2865             struct ObjRec *obj_ptr=attr_ptr->obj;
2866             int ltx=0, lty=0, rbx=0, rby=0;
2867 
2868             StretchedAbsXY(obj_ptr->obbox.ltx, obj_ptr->obbox.lty, &ltx, &lty);
2869             StretchedAbsXY(obj_ptr->obbox.rbx, obj_ptr->obbox.rby, &rbx, &rby);
2870             CalcBBox(ltx, lty, rbx, rby, &final_obbox.ltx, &final_obbox.lty,
2871                   &final_obbox.rbx, &final_obbox.rby);
2872 
2873             ShearObj(obj_ptr, Corner, dz, dz, dxScale, dyScale,
2874                      &final_obbox.ltx, &final_obbox.lty);
2875          }
2876       }
2877    } else {
2878       if (AutoCenterAttr) {
2879          for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL;
2880                attr_ptr=attr_ptr->next) {
2881             if (attr_ptr->shown) {
2882                CenterObjInOBBox(attr_ptr->obj, ObjPtr->obbox, NULL);
2883             } else {
2884                StretchSimpleText(attr_ptr->obj, Corner);
2885             }
2886          }
2887       } else {
2888          for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL;
2889                attr_ptr=attr_ptr->next) {
2890             StretchSimpleText(attr_ptr->obj, Corner);
2891          }
2892       }
2893    }
2894 }
2895 
2896 static
StretchSimpleArc(ObjPtr)2897 void StretchSimpleArc(ObjPtr)
2898    struct ObjRec *ObjPtr;
2899 {
2900    struct ArcRec *arc_ptr=ObjPtr->detail.a;
2901    int x, y;
2902 
2903    StretchedAbsXY(arc_ptr->xc, arc_ptr->yc, &x, &y);
2904    arc_ptr->xc = ObjPtr->x = x;
2905    arc_ptr->yc = ObjPtr->y = y;
2906    StretchedAbsXY(arc_ptr->x1, arc_ptr->y1, &x, &y);
2907    arc_ptr->x1 = x;
2908    arc_ptr->y1 = y;
2909    StretchedAbsXY(arc_ptr->x2, arc_ptr->y2, &x, &y);
2910    arc_ptr->x2 = x;
2911    arc_ptr->y2 = y;
2912    StretchedAbsXY(arc_ptr->ltx, arc_ptr->lty, &x, &y);
2913    arc_ptr->ltx = arc_ptr->xc-abs(x-arc_ptr->xc);
2914    arc_ptr->lty = arc_ptr->yc-abs(y-arc_ptr->yc);
2915    arc_ptr->w = (arc_ptr->xc-arc_ptr->ltx)<<1;
2916    arc_ptr->h = (arc_ptr->yc-arc_ptr->lty)<<1;
2917 
2918    if (multX < 0) {
2919       arc_ptr->dir = !(arc_ptr->dir);
2920       arc_ptr->angle2 = -(arc_ptr->angle2);
2921       if (arc_ptr->angle1 > 0) {
2922          arc_ptr->angle1 = (180*64) - arc_ptr->angle1;
2923       } else {
2924          arc_ptr->angle1 = (-180*64) - arc_ptr->angle1;
2925       }
2926    }
2927    if (multY < 0) {
2928       arc_ptr->dir = !(arc_ptr->dir);
2929       arc_ptr->angle1 = -(arc_ptr->angle1);
2930       arc_ptr->angle2 = -(arc_ptr->angle2);
2931    }
2932    AdjObjSplineVs(ObjPtr);
2933    AdjObjBBox(ObjPtr);
2934 }
2935 
2936 static
StretchSimplePoly(ObjPtr)2937 void StretchSimplePoly(ObjPtr)
2938    struct ObjRec *ObjPtr;
2939 {
2940    int i, ltx=0, lty=0, rbx=0, rby=0;
2941    struct PolyRec *poly_ptr= ObjPtr->detail.p;
2942    IntPoint *vs=poly_ptr->vlist;
2943 
2944    for (i = 0; i < poly_ptr->n; i++) {
2945       int x, y;
2946 
2947       StretchedAbsXY(vs[i].x, vs[i].y, &x, &y);
2948       vs[i].x = x;
2949       vs[i].y = y;
2950       if (i == 0) {
2951          ltx = rbx = x;
2952          lty = rby = y;
2953       } else {
2954          if (x < ltx) ltx = x; if (y < lty) lty = y;
2955          if (x > rbx) rbx = x; if (y > rby) rby = y;
2956       }
2957    }
2958    ObjPtr->obbox.ltx = ObjPtr->x = ltx;
2959    ObjPtr->obbox.lty = ObjPtr->y = lty;
2960    ObjPtr->obbox.rbx = rbx;
2961    ObjPtr->obbox.rby = rby;
2962    AdjObjSplineVs(ObjPtr);
2963    if (poly_ptr->curved == LT_INTSPLINE) {
2964       UpdPolyBBox(ObjPtr, poly_ptr->intn, poly_ptr->intvlist);
2965    }
2966 }
2967 
2968 static
StretchSimplePolygon(ObjPtr)2969 void StretchSimplePolygon(ObjPtr)
2970    struct ObjRec *ObjPtr;
2971 {
2972    int i, ltx=0, lty=0, rbx=0, rby=0;
2973    struct PolygonRec *polygon_ptr= ObjPtr->detail.g;
2974    IntPoint *vs=polygon_ptr->vlist;
2975 
2976    for (i = 0; i < polygon_ptr->n; i++) {
2977       int x, y;
2978 
2979       StretchedAbsXY(vs[i].x, vs[i].y, &x, &y);
2980       vs[i].x = x;
2981       vs[i].y = y;
2982       if (i == 0) {
2983          ltx = rbx = x;
2984          lty = rby = y;
2985       } else {
2986          if (x < ltx) ltx = x; if (y < lty) lty = y;
2987          if (x > rbx) rbx = x; if (y > rby) rby = y;
2988       }
2989    }
2990    ObjPtr->obbox.ltx = ObjPtr->x = ltx;
2991    ObjPtr->obbox.lty = ObjPtr->y = lty;
2992    ObjPtr->obbox.rbx = rbx;
2993    ObjPtr->obbox.rby = rby;
2994    AdjObjSplineVs(ObjPtr);
2995    if (polygon_ptr->curved == LT_INTSPLINE) {
2996       UpdPolyBBox(ObjPtr, polygon_ptr->intn, polygon_ptr->intvlist);
2997    }
2998 }
2999 
3000 static
StretchSimpleObj(ObjPtr,Corner,dxScale,dyScale,FinalOBBox,auto_center_attr)3001 void StretchSimpleObj(ObjPtr, Corner, dxScale, dyScale, FinalOBBox,
3002       auto_center_attr)
3003    struct ObjRec *ObjPtr;
3004    int Corner, auto_center_attr;
3005    double dxScale, dyScale;
3006    struct BBRec *FinalOBBox;
3007 {
3008    ObjPtr->obbox.ltx = ObjPtr->x = FinalOBBox->ltx;
3009    ObjPtr->obbox.lty = ObjPtr->y = FinalOBBox->lty;
3010    ObjPtr->obbox.rbx = FinalOBBox->rbx;
3011    ObjPtr->obbox.rby = FinalOBBox->rby;
3012 
3013    switch (ObjPtr->type) {
3014    case OBJ_ARC: StretchSimpleArc(ObjPtr); break;
3015    case OBJ_POLY: StretchSimplePoly(ObjPtr); break;
3016    case OBJ_POLYGON: StretchSimplePolygon(ObjPtr); break;
3017    case OBJ_RCBOX: AdjObjSplineVs(ObjPtr); break;
3018    case OBJ_BOX: AdjObjSplineVs(ObjPtr); break;
3019    case OBJ_OVAL: AdjObjSplineVs(ObjPtr); break;
3020    }
3021    AdjObjOBBox(ObjPtr);
3022    StretchAttr(ObjPtr, Corner, dxScale, dyScale, auto_center_attr);
3023    AdjObjBBox(ObjPtr);
3024 }
3025 
3026 static
ScaleLineWidth(ObjPtr,dScale)3027 void ScaleLineWidth(ObjPtr, dScale)
3028    struct ObjRec *ObjPtr;
3029    double dScale;
3030 {
3031    double dscale=(double)(dScale/1000.0);
3032 
3033    if (!stretchingEverything) return;
3034 
3035    switch (ObjPtr->type) {
3036    case OBJ_POLY:
3037       ScaleWidthAndSpec(dscale, &ObjPtr->detail.p->width,
3038             ObjPtr->detail.p->width_spec);
3039       ScaleWidthAndSpec(dscale, &ObjPtr->detail.p->aw,
3040             ObjPtr->detail.p->aw_spec);
3041       ScaleWidthAndSpec(dscale, &ObjPtr->detail.p->ah,
3042             ObjPtr->detail.p->ah_spec);
3043       break;
3044    case OBJ_POLYGON:
3045       ScaleWidthAndSpec(dscale, &ObjPtr->detail.g->width,
3046             ObjPtr->detail.g->width_spec);
3047       break;
3048    case OBJ_BOX:
3049       ScaleWidthAndSpec(dscale, &ObjPtr->detail.b->width,
3050             ObjPtr->detail.b->width_spec);
3051       break;
3052    case OBJ_OVAL:
3053       ScaleWidthAndSpec(dscale, &ObjPtr->detail.o->width,
3054             ObjPtr->detail.o->width_spec);
3055       break;
3056    case OBJ_ARC:
3057       ScaleWidthAndSpec(dscale, &ObjPtr->detail.a->width,
3058             ObjPtr->detail.a->width_spec);
3059       ScaleWidthAndSpec(dscale, &ObjPtr->detail.a->aw,
3060             ObjPtr->detail.a->aw_spec);
3061       ScaleWidthAndSpec(dscale, &ObjPtr->detail.a->ah,
3062             ObjPtr->detail.a->ah_spec);
3063       break;
3064    case OBJ_RCBOX:
3065       ScaleWidthAndSpec(dscale, &ObjPtr->detail.rcb->width,
3066             ObjPtr->detail.rcb->width_spec);
3067       break;
3068    }
3069 }
3070 
3071 static
StretchObj(ObjPtr,Corner,dxScale,dyScale,ForceToUseCTM)3072 void StretchObj(ObjPtr, Corner, dxScale, dyScale, ForceToUseCTM)
3073    struct ObjRec *ObjPtr;
3074    int Corner, ForceToUseCTM;
3075    double dxScale, dyScale;
3076 {
3077    int ltx, lty, rbx, rby;
3078    int auto_center_attr=AutoCenterAttr(ObjPtr);
3079    struct BBRec final_obbox;
3080    struct ObjRec *obj_ptr=NULL;
3081    struct AttrRec *saved_fattr=NULL, *saved_lattr=NULL;
3082    double dz=(double)0;
3083 
3084    StretchedAbsXY(ObjPtr->obbox.ltx, ObjPtr->obbox.lty, &ltx, &lty);
3085    StretchedAbsXY(ObjPtr->obbox.rbx, ObjPtr->obbox.rby, &rbx, &rby);
3086    CalcBBox(ltx, lty, rbx, rby, &final_obbox.ltx, &final_obbox.lty,
3087          &final_obbox.rbx, &final_obbox.rby);
3088 
3089    if (ForceToUseCTM && ObjPtr->ctm == NULL &&
3090          ObjPtr->type != OBJ_GROUP && ObjPtr->type != OBJ_ICON &&
3091          ObjPtr->type != OBJ_SYM && ObjPtr->type != OBJ_PIN) {
3092       memcpy(&ObjPtr->orig_obbox, &ObjPtr->obbox, sizeof(struct BBRec));
3093       if (ObjPtr->type == OBJ_TEXT) {
3094          memcpy(&ObjPtr->detail.t->orig_bbox, &ObjPtr->bbox,
3095                sizeof(struct BBRec));
3096       }
3097       ObjPtr->ctm = (struct XfrmMtrxRec *)malloc(sizeof(struct XfrmMtrxRec));
3098       if (ObjPtr->ctm == NULL) FailAllocMessage();
3099       ObjPtr->ctm->m[CTM_SX] = ObjPtr->ctm->m[CTM_SY] = (double)1000;
3100       ObjPtr->ctm->m[CTM_SIN] = ObjPtr->ctm->m[CTM_MSIN] = (double)0;
3101       ObjPtr->ctm->t[CTM_TX] = ObjPtr->ctm->t[CTM_TY] = 0;
3102    }
3103    switch (ObjPtr->type) {
3104    case OBJ_POLY:
3105    case OBJ_POLYGON:
3106    case OBJ_BOX:
3107    case OBJ_OVAL:
3108    case OBJ_ARC:
3109    case OBJ_RCBOX:
3110       ScaleLineWidth(ObjPtr, dxScale);
3111       if (ObjPtr->ctm == NULL) {
3112          StretchSimpleObj(ObjPtr, Corner, dxScale, dyScale, &final_obbox,
3113                auto_center_attr);
3114       } else {
3115          ShearObj(ObjPtr, Corner, dz, dz, dxScale, dyScale,
3116                &final_obbox.ltx, &final_obbox.lty);
3117       }
3118       break;
3119    case OBJ_TEXT:
3120       if (stretchableText) {
3121          ShearObj(ObjPtr, Corner, dz, dz, dxScale, dyScale,
3122                &final_obbox.ltx, &final_obbox.lty);
3123       } else {
3124          StretchSimpleText(ObjPtr, Corner);
3125       }
3126       break;
3127    case OBJ_GROUP:
3128    case OBJ_ICON:
3129    case OBJ_SYM:
3130    case OBJ_PIN:
3131       if (!stretchableText && !compoundObjWithTextStretchableForPSE &&
3132             CompoundObjHasTextSubObj(ObjPtr)) {
3133          JustMoveSimpleCompoundObj(ObjPtr, Corner);
3134       } else {
3135          for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
3136                obj_ptr=obj_ptr->next) {
3137             StretchObj(obj_ptr, Corner, dxScale, dyScale, ForceToUseCTM);
3138          }
3139          AdjObjOBBox(ObjPtr);
3140          StretchAttr(ObjPtr, Corner, dxScale, dyScale, auto_center_attr);
3141          AdjObjSplineVs(ObjPtr);
3142          AdjObjBBox(ObjPtr);
3143       }
3144       break;
3145    case OBJ_XBM:
3146    case OBJ_XPM:
3147       saved_fattr = ObjPtr->fattr;
3148       saved_lattr = ObjPtr->lattr;
3149       ObjPtr->fattr = ObjPtr->lattr = NULL;
3150       ShearObj(ObjPtr, Corner, dz, dz, dxScale, dyScale,
3151             &final_obbox.ltx, &final_obbox.lty);
3152       ObjPtr->fattr = saved_fattr;
3153       ObjPtr->lattr = saved_lattr;
3154       StretchAttr(ObjPtr, Corner, dxScale, dyScale, auto_center_attr);
3155       AdjObjBBox(ObjPtr);
3156       break;
3157    }
3158 }
3159 
3160 static
StretchAllSelObjects(Corner,dxScale,dyScale)3161 void StretchAllSelObjects(Corner, dxScale, dyScale)
3162    int Corner;
3163    double dxScale, dyScale;
3164 {
3165    struct SelRec *sel_ptr;
3166 
3167    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
3168       if (!sel_ptr->obj->locked) {
3169          StretchObj(sel_ptr->obj, Corner, dxScale, dyScale, FALSE);
3170       }
3171    }
3172    if (numObjLocked != 0) {
3173       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_STRETCHED));
3174    }
3175 }
3176 
3177 static
MarkObjectsForStretch()3178 void MarkObjectsForStretch()
3179 {
3180    register struct ObjRec *obj_ptr;
3181    register struct SelRec *sel_ptr;
3182 
3183    for (obj_ptr = botObj; obj_ptr != NULL; obj_ptr = obj_ptr->prev) {
3184       obj_ptr->marked = FALSE;
3185    }
3186    for (sel_ptr = botSel; sel_ptr != NULL; sel_ptr = sel_ptr->prev) {
3187       sel_ptr->obj->marked = TRUE;
3188    }
3189 }
3190 
3191 static
ConstrainedStretchAllSel(Corner,ltx,lty,rbx,rby)3192 int ConstrainedStretchAllSel(Corner, ltx, lty, rbx, rby)
3193    int Corner;
3194    int *ltx, *lty, *rbx, *rby;
3195 {
3196    register struct ObjRec *obj_ptr;
3197    int something_stretched=FALSE, num_pts;
3198    int x_off, y_off, move_first, move_last, x, y;
3199    IntPoint *v;
3200 
3201    for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
3202       if (!obj_ptr->marked && obj_ptr->type==OBJ_POLY && !obj_ptr->locked) {
3203          num_pts = obj_ptr->detail.p->n;
3204          v = obj_ptr->detail.p->vlist;
3205 
3206          if (obj_ptr->ctm == NULL) {
3207             x_off = OFFSET_X(v[0].x); y_off = OFFSET_Y(v[0].y);
3208             move_first = EndPtInSelected(x_off, y_off);
3209             x_off = OFFSET_X(v[num_pts-1].x); y_off = OFFSET_Y(v[num_pts-1].y);
3210             move_last = EndPtInSelected(x_off, y_off);
3211          } else {
3212             int tmp_x, tmp_y;
3213 
3214             TransformPointThroughCTM(v[0].x-obj_ptr->x, v[0].y-obj_ptr->y,
3215                   obj_ptr->ctm, &tmp_x, &tmp_y);
3216             tmp_x += obj_ptr->x;
3217             tmp_y += obj_ptr->y;
3218             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
3219             move_first = EndPtInSelected(x_off, y_off);
3220             TransformPointThroughCTM(v[num_pts-1].x-obj_ptr->x,
3221                   v[num_pts-1].y-obj_ptr->y, obj_ptr->ctm, &tmp_x, &tmp_y);
3222             tmp_x += obj_ptr->x;
3223             tmp_y += obj_ptr->y;
3224             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
3225             move_last = EndPtInSelected(x_off, y_off);
3226          }
3227 
3228          if (move_first || move_last) {
3229             int index=INVALID, seg_dx, seg_dy, dx, dy, cur_seg_dx, cur_seg_dy;
3230 
3231             PrepareToReplaceAnObj(obj_ptr);
3232 
3233             if (obj_ptr->ctm != NULL) {
3234                /* Remove the transformations! */
3235                int i;
3236 
3237                for (i=0; i < num_pts; i++) {
3238                   int tmp_x, tmp_y;
3239 
3240                   TransformPointThroughCTM(v[i].x-obj_ptr->x, v[i].y-obj_ptr->y,
3241                         obj_ptr->ctm, &tmp_x, &tmp_y);
3242                   v[i].x = tmp_x+obj_ptr->x;
3243                   v[i].y = tmp_y+obj_ptr->y;
3244                }
3245                free(obj_ptr->ctm);
3246                obj_ptr->ctm = NULL;
3247                UpdPolyBBox(obj_ptr, num_pts, v);
3248             }
3249             if (something_stretched) {
3250                if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
3251                if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
3252                if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
3253                if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
3254             } else {
3255                *ltx = obj_ptr->bbox.ltx; *lty = obj_ptr->bbox.lty;
3256                *rbx = obj_ptr->bbox.rbx; *rby = obj_ptr->bbox.rby;
3257             }
3258             something_stretched = TRUE;
3259             if (move_first && move_last && num_pts==3) {
3260                StretchedAbsXY(v[0].x, v[0].y, &x, &y);
3261                dx = x-v[0].x; dy = y-v[0].y;
3262                index = 1;
3263                cur_seg_dx = v[index-1].x - v[index].x;
3264                cur_seg_dy = v[index-1].y - v[index].y;
3265                seg_dx = v[index].x - v[index+1].x;
3266                seg_dy = v[index].y - v[index+1].y;
3267 
3268                if (cur_seg_dy==0 && seg_dx==0 &&
3269                      (seg_dy!=0 || (seg_dy==0 && dx==0))) {
3270                   v[index].y += dy;
3271                } else if (cur_seg_dx==0 && seg_dy==0 &&
3272                      (seg_dx!=0 || (seg_dx==0 && dy==0))) {
3273                   v[index].x += dx;
3274                }
3275             } else {
3276                if (move_first && num_pts>2) {
3277                   StretchedAbsXY(v[0].x, v[0].y, &x, &y);
3278                   dx = x-v[0].x; dy = y-v[0].y;
3279                   index = 1;
3280                   cur_seg_dx = v[index-1].x - v[index].x;
3281                   cur_seg_dy = v[index-1].y - v[index].y;
3282                   seg_dx = v[index].x - v[index+1].x;
3283                   seg_dy = v[index].y - v[index+1].y;
3284 
3285                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
3286                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
3287                      v[index].y += dy;
3288                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
3289                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
3290                      v[index].x += dx;
3291                   }
3292                }
3293                if (move_last && num_pts>2) {
3294                   StretchedAbsXY(v[num_pts-1].x, v[num_pts-1].y, &x, &y);
3295                   dx = x-v[num_pts-1].x; dy = y-v[num_pts-1].y;
3296                   index = num_pts-2;
3297                   cur_seg_dx = v[index+1].x - v[index].x;
3298                   cur_seg_dy = v[index+1].y - v[index].y;
3299                   seg_dx = v[index].x - v[index-1].x;
3300                   seg_dy = v[index].y - v[index-1].y;
3301 
3302                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
3303                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
3304                      v[index].y += dy;
3305                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
3306                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
3307                      v[index].x += dx;
3308                   }
3309                }
3310             }
3311             if (move_first) {
3312                StretchedAbsXY(v[0].x, v[0].y, &x, &y);
3313                v[0].x = x; v[0].y = y;
3314             }
3315             if (move_last) {
3316                StretchedAbsXY(v[num_pts-1].x, v[num_pts-1].y, &x, &y);
3317                v[num_pts-1].x = x; v[num_pts-1].y = y;
3318             }
3319             AdjObjSplineVs(obj_ptr);
3320             switch (obj_ptr->type) {
3321             case OBJ_POLY:
3322                if (obj_ptr->detail.p->curved != LT_INTSPLINE) {
3323                   UpdPolyBBox(obj_ptr, num_pts, v);
3324                } else {
3325                   UpdPolyBBox(obj_ptr, obj_ptr->detail.p->intn,
3326                         obj_ptr->detail.p->intvlist);
3327                }
3328                break;
3329             case OBJ_POLYGON:
3330                if (obj_ptr->detail.g->curved != LT_INTSPLINE) {
3331                   UpdPolyBBox(obj_ptr, num_pts, v);
3332                } else {
3333                   UpdPolyBBox(obj_ptr, obj_ptr->detail.g->intn,
3334                         obj_ptr->detail.g->intvlist);
3335                }
3336                break;
3337             }
3338             AdjObjBBox(obj_ptr);
3339             if (AutoCenterAttr(obj_ptr)) {
3340                struct AttrRec *attr_ptr=obj_ptr->fattr;
3341                int modified=FALSE;
3342 
3343                for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
3344                   if (attr_ptr->shown) {
3345                      struct BBRec bbox;
3346 
3347                      CenterObjInOBBox(attr_ptr->obj, obj_ptr->obbox, &bbox);
3348                      if (bbox.ltx < *ltx) *ltx = bbox.ltx;
3349                      if (bbox.lty < *lty) *lty = bbox.lty;
3350                      if (bbox.rbx > *rbx) *rbx = bbox.rbx;
3351                      if (bbox.rby > *rby) *rby = bbox.rby;
3352                      modified = TRUE;
3353                   }
3354                }
3355                if (modified) AdjObjBBox(obj_ptr);
3356             }
3357             if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
3358             if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
3359             if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
3360             if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
3361             RecordReplaceAnObj(obj_ptr);
3362          }
3363       }
3364    }
3365    return something_stretched;
3366 }
3367 
3368 static
StretchAllSel(Corner,dxScale,dyScale)3369 void StretchAllSel(Corner, dxScale, dyScale)
3370    int Corner;
3371    double dxScale, dyScale; /* dxScale and dyScale are scaled by 1000 */
3372 {
3373    int ltx, lty, rbx, rby, saved_ltx, saved_lty, saved_rbx, saved_rby;
3374    int poly_stretched;
3375 
3376    saved_ltx = selLtX; saved_lty = selLtY;
3377    saved_rbx = selRbX; saved_rby = selRbY;
3378 
3379    if (moveMode==CONST_MOVE) {
3380       MarkObjectsForStretch();
3381 
3382       StartCompositeCmd();
3383       PrepareToRecord(CMD_STRETCH, topSel, botSel, numObjSelected);
3384       RecordCmd(CMD_STRETCH, NULL, topSel, botSel, numObjSelected);
3385 
3386       poly_stretched = ConstrainedStretchAllSel(Corner, &ltx, &lty, &rbx, &rby);
3387       StretchAllSelObjects(Corner, dxScale, dyScale);
3388       UpdSelBBox();
3389       if (poly_stretched) {
3390          ltx = min(ltx,min(selLtX,saved_ltx));
3391          lty = min(lty,min(selLtY,saved_lty));
3392          rbx = max(rbx,max(selRbX,saved_rbx));
3393          rby = max(rby,max(selRbY,saved_rby));
3394          RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
3395                rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
3396       } else {
3397          RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
3398                saved_lty-GRID_ABS_SIZE(1),
3399                saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
3400                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3401                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3402       }
3403       EndCompositeCmd();
3404    } else {
3405       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
3406       StretchAllSelObjects(Corner, dxScale, dyScale);
3407       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
3408       UpdSelBBox();
3409       RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
3410             saved_lty-GRID_ABS_SIZE(1),
3411             saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
3412             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3413             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3414    }
3415 }
3416 
3417 static
DiagonalDistance(dx,dy)3418 int DiagonalDistance(dx, dy)
3419    double dx, dy;
3420 {
3421    double dval=sqrt(dx*dx+dy*dy);
3422 
3423    return round(dval);
3424 }
3425 
3426 static
DiagonalAngle(dx,dy)3427 double DiagonalAngle(dx, dy)
3428    int dx, dy;
3429 {
3430    double theta=((dx==0) ? ((dy>=0) ? M_PI/2.0 : -M_PI/2.0) :
3431          atan2((double)(dy), (double)(dx)));
3432 
3433    return theta*((double)180.0)/M_PI;
3434 }
3435 
3436 static
GetMeasurement(ObjPtr,buf,buf1)3437 void GetMeasurement(ObjPtr, buf, buf1)
3438    struct ObjRec *ObjPtr;
3439    char *buf, *buf1;
3440 {
3441    int ltx, lty, rbx, rby, real_ltx, real_lty, real_rbx, real_rby, dx, dy;
3442    char x_buf[80], y_buf[80], d_buf[80];
3443    double diag_angle;
3444 
3445    StretchedAbsXY(ObjPtr->obbox.ltx, ObjPtr->obbox.lty, &ltx, &lty);
3446    StretchedAbsXY(ObjPtr->obbox.rbx, ObjPtr->obbox.rby, &rbx, &rby);
3447    CalcBBox(ltx, lty, rbx, rby, &real_ltx, &real_lty, &real_rbx, &real_rby);
3448    PixelToMeasurementUnit(x_buf, abs(real_rbx-real_ltx));
3449    PixelToMeasurementUnit(y_buf, abs(real_rby-real_lty));
3450    sprintf(buf, "w=%s\nh=%s", x_buf, y_buf);
3451    dx = real_rbx-real_ltx;
3452    dy = real_rby-real_lty;
3453    PixelToMeasurementUnit(d_buf, abs(DiagonalDistance((double)dx, (double)dy)));
3454    diag_angle = DiagonalAngle(dx, dy);
3455    sprintf(buf1, "Diagonal: length=%s angle=%.2f or %.2f", d_buf,
3456          (float)diag_angle, (float)(((double)360.0)-diag_angle));
3457 }
3458 
3459 static
PointsToShearScale(Corner,x_pivot,y_pivot,x_move,y_move,x_current,y_current,dx_shear,dy_shear,dx_scale,dy_scale)3460 void PointsToShearScale(Corner, x_pivot, y_pivot, x_move, y_move,
3461       x_current, y_current, dx_shear, dy_shear, dx_scale, dy_scale)
3462    int Corner, x_pivot, y_pivot, x_move, y_move, x_current, y_current;
3463    double *dx_shear, *dy_shear, *dx_scale, *dy_scale;
3464    /* the returned shear value is 1000*arctan() */
3465    /* the returned scale value is 1000*scaling */
3466 {
3467    int dx=x_current-x_move, dy=y_current-y_move;
3468 
3469    switch (Corner) {
3470    case CORNER_TOP:
3471    case CORNER_BOTTOM:
3472       if (dx_scale != NULL) *dx_scale = (double)1000;
3473       if (dy_scale != NULL) {
3474          *dy_scale = (dy == 0 ? (double)1000 :
3475                (double)(((double)(y_current-y_pivot)) /
3476                ((double)(y_move-y_pivot))*1000.0));
3477       }
3478       if (dx_shear != NULL) {
3479          *dx_shear = (dx == 0 ? (double)0 :
3480                (double)(atan2((double)dx,(double)y_current-y_pivot)*1000.0));
3481       }
3482       if (dy_shear != NULL) *dy_shear = (double)0;
3483       break;
3484    case CORNER_RIGHT:
3485    case CORNER_LEFT:
3486       if (dx_scale != NULL) {
3487          *dx_scale = (dx == 0 ? (double)1000 :
3488                (double)(((double)(x_current-x_pivot)) /
3489                ((double)(x_move-x_pivot))*1000.0));
3490       }
3491       if (dy_scale != NULL) *dy_scale = (double)1000;
3492       if (dx_shear != NULL) *dx_shear = (double)0;
3493       if (dy_shear != NULL) {
3494          *dy_shear = (dy == 0 ? (double)0 :
3495                (double)(atan2((double)dy,(double)x_current-x_pivot)*1000.0));
3496       }
3497       break;
3498    default: /* scaling only, no shearing */
3499       if (dx_scale != NULL) {
3500          *dx_scale = (dx == 0 ? (double)1000 :
3501                (double)(((double)(x_current-x_pivot)) /
3502                ((double)(x_move-x_pivot))*1000.0));
3503       }
3504       if (dy_scale != NULL) {
3505          *dy_scale = (dy == 0 ? (double)1000 :
3506                (double)(((double)(y_current-y_pivot)) /
3507                ((double)(y_move-y_pivot))*1000.0));
3508       }
3509       if (dx_shear != NULL) *dx_shear = (double)0;
3510       if (dy_shear != NULL) *dy_shear = (double)0;
3511       break;
3512    }
3513 }
3514 
3515 static
StretchBox(XGridOff,YGridOff,ObjPtr,Corner)3516 void StretchBox(XGridOff, YGridOff, ObjPtr, Corner)
3517    int XGridOff, YGridOff, Corner;
3518    struct ObjRec *ObjPtr;
3519 {
3520    int x, y, stretching=TRUE;
3521    int ltx, lty, rbx, rby, sel_ltx, sel_lty, sel_rbx, sel_rby;
3522    int stretched_ltx, stretched_lty, stretched_rbx, stretched_rby;
3523    int stretched_sel_ltx, stretched_sel_lty, stretched_sel_rbx;
3524    int stretched_sel_rby;
3525    int ruler_ltx, ruler_lty, ruler_rbx, ruler_rby;
3526    int sel_obj_ltx, sel_obj_lty, sel_obj_rbx, sel_obj_rby;
3527    int grid_x=XGridOff, grid_y=YGridOff, proportional=FALSE;
3528    char buf[80], buf1[80];
3529    double obj_w, obj_h;
3530    XEvent input, ev;
3531 
3532    if (numObjSelected == numObjLocked || ObjPtr->locked) {
3533       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_STRETCHED), TOOL_NAME,
3534             INFO_MB);
3535       return;
3536    }
3537    XFlush(mainDisplay);
3538    XSync(mainDisplay, False);
3539 
3540    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
3541          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
3542       ExposeEventHandler(&ev, TRUE);
3543    }
3544    SetPivot(Corner, &ObjPtr->obbox);
3545 
3546    stretched_sel_ltx = sel_ltx = OFFSET_X(selNoLockLtX);
3547    stretched_sel_lty = sel_lty = OFFSET_Y(selNoLockLtY);
3548    stretched_sel_rbx = sel_rbx = OFFSET_X(selNoLockRbX);
3549    stretched_sel_rby = sel_rby = OFFSET_Y(selNoLockRbY);
3550    SelBox(drawWindow, revDefaultGC, stretched_sel_ltx-2, stretched_sel_lty-2,
3551          stretched_sel_rbx+2, stretched_sel_rby+2);
3552 
3553    ruler_ltx = sel_obj_ltx = OFFSET_X(selNoLockObjLtX);
3554    ruler_lty = sel_obj_lty = OFFSET_Y(selNoLockObjLtY);
3555    ruler_rbx = sel_obj_rbx = OFFSET_X(selNoLockObjRbX);
3556    ruler_rby = sel_obj_rby = OFFSET_Y(selNoLockObjRbY);
3557 
3558    stretched_ltx = ltx = OFFSET_X(ObjPtr->obbox.ltx);
3559    stretched_lty = lty = OFFSET_Y(ObjPtr->obbox.lty);
3560    stretched_rbx = rbx = OFFSET_X(ObjPtr->obbox.rbx);
3561    stretched_rby = rby = OFFSET_Y(ObjPtr->obbox.rby);
3562    SelBox(drawWindow, revDefaultGC, stretched_ltx, stretched_lty,
3563          stretched_rbx, stretched_rby);
3564 
3565    if (ltx == rbx || lty == rby) {
3566       Msg(TgLoadString(ltx == rbx ? STID_CANT_STRETCH_OBJ_HAS_0_WIDTH :
3567             STID_CANT_STRETCH_OBJ_HAS_0_HEIGHT));
3568       SelBox(drawWindow, revDefaultGC, stretched_ltx, stretched_lty,
3569             stretched_rbx, stretched_rby);
3570       SelBox(drawWindow, revDefaultGC, stretched_sel_ltx-2,
3571             stretched_sel_lty-2, stretched_sel_rbx+2, stretched_sel_rby+2);
3572       return;
3573    }
3574    SaveStatusStrings();
3575 
3576    obj_w = (double)(moveX - pivotX);
3577    obj_h = (double)(moveY - pivotY);
3578 
3579    GetMeasurement(ObjPtr, buf, buf1);
3580    StartShowMeasureCursor(grid_x, grid_y, buf, TRUE);
3581    if (showMeasurement) SetStringStatus(buf1);
3582 
3583    BeginIntervalRulers(ruler_ltx, ruler_lty, ruler_rbx, ruler_rby);
3584    if (!debugNoPointerGrab) {
3585       XGrabPointer(mainDisplay, drawWindow, False,
3586             PointerMotionMask | ButtonReleaseMask,
3587             GrabModeAsync, GrabModeAsync, None, handCursor, CurrentTime);
3588    }
3589    while (stretching) {
3590       XNextEvent(mainDisplay, &input);
3591 
3592       if (input.type == Expose || input.type == VisibilityNotify) {
3593          ExposeEventHandler(&input, TRUE);
3594       } else if (input.type == ButtonRelease) {
3595          proportional = (input.xbutton.state & (ShiftMask|ControlMask));
3596          XUngrabPointer(mainDisplay, CurrentTime);
3597          XSync(mainDisplay, False);
3598          stretching = FALSE;
3599       } else if (input.type == MotionNotify || input.type == KeyPress ||
3600             input.type == KeyRelease) {
3601          proportional = (input.xmotion.state & (ShiftMask|ControlMask));
3602 
3603          GetMeasurement(ObjPtr, buf, buf1);
3604          EndShowMeasureCursor(grid_x, grid_y, buf, TRUE);
3605          if (showMeasurement) SetStringStatus(buf1);
3606 
3607          if (input.type == KeyPress || input.type == KeyRelease) {
3608             x = grid_x;
3609             y = grid_y;
3610          } else {
3611             x = input.xmotion.x;
3612             y = input.xmotion.y;
3613          }
3614          GridXY(x, y, &grid_x, &grid_y);
3615 
3616          SelBox(drawWindow, revDefaultGC, stretched_ltx, stretched_lty,
3617                stretched_rbx, stretched_rby);
3618          SelBox(drawWindow, revDefaultGC, stretched_sel_ltx-2,
3619                stretched_sel_lty-2, stretched_sel_rbx+2, stretched_sel_rby+2);
3620 
3621          if (proportional) {
3622             int new_w, new_h;
3623             double w_ratio, h_ratio;
3624 
3625             new_w = moveX + grid_x - XGridOff - pivotX;
3626             new_h = moveY + grid_y - YGridOff - pivotY;
3627             w_ratio = (moveX!=pivotX) ? fabs(((double)new_w)/obj_w) : 0.0;
3628             h_ratio = (moveY!=pivotY) ? fabs(((double)new_h)/obj_h) : 0.0;
3629             if (changeX && changeY) {
3630                if (w_ratio >= h_ratio) {
3631                   multX = (moveX!=pivotX) ? ((double)new_w)/obj_w : 1.0;
3632                   multY = fabs(multX) * ((new_h*obj_h>=0) ? 1.0 : -1.0);
3633                } else {
3634                   multX = fabs(multY) * ((new_w*obj_w>=0) ? 1.0 : -1.0);
3635                   multY = (moveY!=pivotY) ? ((double)new_h)/obj_h : 1.0;
3636                }
3637             } else if (changeX) {
3638                multX = (moveX!=pivotX) ? ((double)new_w)/obj_w : 1.0;
3639                multY = fabs(multX);
3640             } else if (changeY) {
3641                multX = fabs(multY);
3642                multY = (moveY!=pivotY) ? ((double)new_h)/obj_h : 1.0;
3643             }
3644          } else {
3645             if (changeX) {
3646                multX = (moveX!=pivotX) ?
3647                      (double)(moveX+grid_x-XGridOff-pivotX)/obj_w : 1.0;
3648             } else {
3649                multX = (double)1.0;
3650             }
3651             if (changeY) {
3652                multY = (moveY!=pivotY) ?
3653                      (double)(moveY+grid_y-YGridOff-pivotY)/obj_h : 1.0;
3654             } else {
3655                multY = (double)1.0;
3656             }
3657          }
3658          StretchedXY(sel_ltx, sel_lty, &stretched_sel_ltx, &stretched_sel_lty);
3659          StretchedXY(sel_rbx, sel_rby, &stretched_sel_rbx, &stretched_sel_rby);
3660          StretchedXY(ltx, lty, &stretched_ltx, &stretched_lty);
3661          StretchedXY(rbx, rby, &stretched_rbx, &stretched_rby);
3662          StretchedXY(sel_obj_ltx, sel_obj_lty, &ruler_ltx, &ruler_lty);
3663          StretchedXY(sel_obj_rbx, sel_obj_rby, &ruler_rbx, &ruler_rby);
3664 
3665          DrawIntervalRulers(ruler_ltx, ruler_lty, ruler_rbx, ruler_rby, NULL);
3666          SelBox(drawWindow, revDefaultGC, stretched_sel_ltx-2,
3667                stretched_sel_lty-2, stretched_sel_rbx+2, stretched_sel_rby+2);
3668          SelBox(drawWindow, revDefaultGC, stretched_ltx, stretched_lty,
3669                stretched_rbx, stretched_rby);
3670          GetMeasurement(ObjPtr, buf, buf1);
3671          EndShowMeasureCursor(grid_x, grid_y, buf, TRUE);
3672          if (showMeasurement) SetStringStatus(buf1);
3673          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
3674       }
3675    }
3676    EndIntervalRulers(grid_x, grid_y);
3677    GetMeasurement(ObjPtr, buf, buf1);
3678    EndShowMeasureCursor(grid_x, grid_y, buf, TRUE);
3679    if (showMeasurement) SetStringStatus(buf1);
3680 
3681    RestoreStatusStrings();
3682 
3683    SelBox(drawWindow, revDefaultGC, stretched_ltx, stretched_lty,
3684          stretched_rbx, stretched_rby);
3685    SelBox(drawWindow, revDefaultGC, stretched_sel_ltx-2,
3686          stretched_sel_lty-2, stretched_sel_rbx+2, stretched_sel_rby+2);
3687    if (multX != (double)1.0 || multY != (double)1.0) {
3688       int num_to_resize=0;
3689       double dx_scale=(double)1000, dy_scale=(double)1000;
3690       char **ppsz_names_to_resize=NULL;
3691 
3692       PointsToShearScale(Corner, pivotX, pivotY, moveX, moveY,
3693             moveX+grid_x-XGridOff, moveY+grid_y-YGridOff,
3694             NULL, NULL, &dx_scale, &dy_scale);
3695       if (proportional) {
3696          int abs_x_scale=round(dx_scale), abs_y_scale=round(dy_scale);
3697 
3698          if (abs_x_scale > abs_y_scale) {
3699             dy_scale = dx_scale;
3700          } else if (abs_x_scale < abs_y_scale) {
3701             dx_scale = dy_scale;
3702          }
3703       }
3704       HighLightReverse();
3705       ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
3706       if (ppsz_names_to_resize == NULL) {
3707          StretchAllSel(Corner, dx_scale, dy_scale);
3708       } else {
3709          StartCompositeCmd();
3710          StretchAllSel(Corner, dx_scale, dy_scale);
3711          DoOnResize(ppsz_names_to_resize, num_to_resize);
3712          EndCompositeCmd();
3713       }
3714       HighLightForward();
3715       SetFileModified(TRUE);
3716       justDupped = FALSE;
3717    }
3718 }
3719 
StretchSel(XGridOff,YGridOff,ObjPtr,Corner)3720 void StretchSel(XGridOff, YGridOff, ObjPtr, Corner)
3721    int XGridOff, YGridOff, Corner;
3722    struct ObjRec *ObjPtr;
3723 {
3724    switch (ObjPtr->type) {
3725    case OBJ_BOX:
3726    case OBJ_OVAL:
3727    case OBJ_GROUP:
3728    case OBJ_ICON:
3729    case OBJ_SYM:
3730    case OBJ_PIN:
3731    case OBJ_ARC:
3732    case OBJ_RCBOX:
3733    case OBJ_XBM:
3734    case OBJ_XPM:
3735       StretchBox(XGridOff, YGridOff, ObjPtr, Corner);
3736       break;
3737    case OBJ_POLY:
3738       if (ObjPtr->detail.p->curved == LT_STRUCT_SPLINE) {
3739          StretchStructSpline(XGridOff, YGridOff, ObjPtr, Corner);
3740       } else {
3741          StretchPoly(XGridOff, YGridOff, ObjPtr, ObjPtr->detail.p->n,
3742                ObjPtr->detail.p->vlist, Corner);
3743       }
3744       break;
3745    case OBJ_POLYGON:
3746       if (ObjPtr->detail.g->curved == LT_STRUCT_SPLINE) {
3747          StretchStructSpline(XGridOff, YGridOff, ObjPtr, Corner);
3748       } else {
3749          StretchPoly(XGridOff, YGridOff, ObjPtr, ObjPtr->detail.g->n,
3750                ObjPtr->detail.g->vlist, Corner);
3751       }
3752       break;
3753    case OBJ_TEXT:
3754       if (stretchableText) {
3755          StretchBox(XGridOff, YGridOff, ObjPtr, Corner);
3756       }
3757       break;
3758    }
3759 }
3760 
ScaleAnEPSObj(ObjPtr,ScalingFactor)3761 void ScaleAnEPSObj(ObjPtr, ScalingFactor)
3762    struct ObjRec *ObjPtr;
3763    float *ScalingFactor;
3764 {
3765    struct BBRec *obbox=(&(ObjPtr->obbox));
3766 
3767    multX = multY = (double)(*ScalingFactor);
3768    changeX = changeY = (fabs(multX-1.0) > 1.0e-6);
3769    if (!changeX && !changeY) return;;
3770 
3771    absPivotX = obbox->ltx;
3772    absPivotY = obbox->lty;
3773    moveX = obbox->rbx;
3774    moveY = obbox->rby;
3775    StretchObj(ObjPtr, CORNER_RB, (double)(multX*1000.0), (double)(multY*1000.0),
3776          FALSE);
3777 }
3778 
3779 static
ScaleAllSelObjects(Corner,dxScale,dyScale)3780 void ScaleAllSelObjects(Corner, dxScale, dyScale)
3781    int Corner;
3782    double dxScale, dyScale;
3783    /*
3784     * Force to use CTM!
3785     */
3786 {
3787    struct SelRec *sel_ptr=NULL;
3788 
3789    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
3790       sel_ptr->obj->tmp_parent = NULL;
3791    }
3792    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
3793       if (stretchingEverything || !sel_ptr->obj->locked) {
3794          StretchObj(sel_ptr->obj, Corner, dxScale, dyScale, TRUE);
3795       }
3796    }
3797    if (!stretchingEverything && numObjLocked != 0) {
3798       Msg(TgLoadString(STID_LOCKED_OBJS_CANT_BE_SCALED));
3799    }
3800 }
3801 
3802 static
ScaleAllSel(Corner,dxScale,dyScale,redraw)3803 void ScaleAllSel(Corner, dxScale, dyScale, redraw)
3804    int Corner, redraw;
3805    double dxScale, dyScale;
3806 {
3807    int ltx, lty, rbx, rby, saved_ltx, saved_lty, saved_rbx, saved_rby;
3808    int poly_stretched;
3809 
3810    saved_ltx = selLtX; saved_lty = selLtY;
3811    saved_rbx = selRbX; saved_rby = selRbY;
3812 
3813    if (moveMode == CONST_MOVE) {
3814       MarkObjectsForStretch();
3815 
3816       StartCompositeCmd();
3817       PrepareToRecord(CMD_STRETCH, topSel, botSel, numObjSelected);
3818       RecordCmd(CMD_STRETCH, NULL, topSel, botSel, numObjSelected);
3819 
3820       poly_stretched = ConstrainedStretchAllSel(Corner, &ltx, &lty, &rbx, &rby);
3821       ScaleAllSelObjects(Corner, dxScale, dyScale);
3822       UpdSelBBox();
3823       if (redraw) {
3824          if (poly_stretched) {
3825             ltx = min(ltx,min(selLtX,saved_ltx));
3826             lty = min(lty,min(selLtY,saved_lty));
3827             rbx = max(rbx,max(selRbX,saved_rbx));
3828             rby = max(rby,max(selRbY,saved_rby));
3829             RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
3830                   rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
3831          } else {
3832             RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
3833                   saved_lty-GRID_ABS_SIZE(1),
3834                   saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
3835                   selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3836                   selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3837          }
3838       }
3839       EndCompositeCmd();
3840    } else {
3841       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
3842       ScaleAllSelObjects(Corner, dxScale, dyScale);
3843       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
3844       UpdSelBBox();
3845       if (redraw) {
3846          RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
3847                saved_lty-GRID_ABS_SIZE(1),
3848                saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
3849                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
3850                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
3851       }
3852    }
3853 }
3854 
3855 static
FindColon(s)3856 char *FindColon(s)
3857    register char *s;
3858 {
3859    while (*s!=':' && *s!='x' && *s!='X' && *s!=' ' && *s!='\0') s++;
3860    return ((*s==':' || *s=='x' || *s=='X' || *s==' ') ? (s) : (char *)NULL);
3861 }
3862 
ScaleAllSelObj()3863 void ScaleAllSelObj()
3864 {
3865    char spec[MAXSTRING], *y_spec=NULL, **ppsz_names_to_resize=NULL;
3866    int corner=INVALID, saved_h_align=horiAlign, saved_v_align=vertAlign;
3867    int num_to_resize=0;
3868    struct BBRec obbox;
3869 
3870    if (topSel == NULL) {
3871       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
3872       return;
3873    }
3874    if (numObjSelected == numObjLocked) {
3875       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_SCALED), TOOL_NAME, INFO_MB);
3876       return;
3877    }
3878    *spec = '\0';
3879    Dialog(TgLoadString(STID_ENTER_SCALING_FACTORS_XY), NULL, spec);
3880    UtilTrimBlanks(spec);
3881    if (*spec == '\0') return;
3882 
3883    horiAlign = ALIGN_L;
3884    vertAlign = ALIGN_T;
3885    corner = CORNER_RB;
3886 
3887    obbox.ltx = selNoLockObjLtX; obbox.lty = selNoLockObjLtY;
3888    obbox.rbx = selNoLockObjRbX; obbox.rby = selNoLockObjRbY;
3889    SetPivot(corner, &obbox);
3890    horiAlign = saved_h_align;
3891    vertAlign = saved_v_align;
3892    if ((y_spec=FindColon(spec)) == NULL) {
3893       if (sscanf(spec, "%lf", &multX) != 1 || multX <= 0.0) {
3894          sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
3895          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3896          return;
3897       }
3898       multY = multX;
3899    } else {
3900       *y_spec++ = '\0';
3901       if (sscanf(spec, "%lf", &multX) != 1 ||
3902             sscanf(y_spec, "%lf", &multY) != 1 ||
3903             multX <= 0.0 || multY <= 0.0) {
3904          sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
3905          MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3906          return;
3907       }
3908    }
3909    changeX = (fabs(multX-1.0) > 1.0e-6);
3910    changeY = (fabs(multY-1.0) > 1.0e-6);
3911    if (!changeX && !changeY) return;
3912 
3913    horiAlign = ALIGN_L;
3914    vertAlign = ALIGN_T;
3915    HighLightReverse();
3916    ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
3917    if (ppsz_names_to_resize == NULL) {
3918       ScaleAllSel(corner, (double)(multX*1000.0), (double)(multY*1000.0), TRUE);
3919    } else {
3920       StartCompositeCmd();
3921       ScaleAllSel(corner, (double)(multX*1000.0), (double)(multY*1000.0), TRUE);
3922       DoOnResize(ppsz_names_to_resize, num_to_resize);
3923       EndCompositeCmd();
3924    }
3925    HighLightForward();
3926    SetFileModified(TRUE);
3927    justDupped = FALSE;
3928    horiAlign = saved_h_align;
3929    vertAlign = saved_v_align;
3930 }
3931 
PreciseScaleEverything()3932 void PreciseScaleEverything()
3933 {
3934    int corner=CORNER_RB, saved_h_align=horiAlign, saved_v_align=vertAlign;
3935    int saved_cur_page_num=curPageNum, prev_page_num=curPageNum;
3936    int saved_stretchable_text=stretchableText;
3937    char spec[MAXSTRING], buf[MAXSTRING];
3938    struct BBRec obbox;
3939 
3940    *spec = '\0';
3941    Dialog(TgLoadString(STID_ENTER_A_SCALING_FACTOR), NULL, spec);
3942    UtilTrimBlanks(spec);
3943    if (*spec == '\0') return;
3944 
3945    obbox.ltx = obbox.lty = 0;
3946    obbox.rbx = onePageWidth; obbox.rby = onePageHeight;
3947    SetPivot(corner, &obbox);
3948 
3949    if (FindColon(spec) != NULL) {
3950       MsgBox(TgLoadString(STID_ONLY_INPUT_ONE_NUMERIC_VAL), TOOL_NAME, INFO_MB);
3951       return;
3952    } else if (sscanf(spec, "%lf", &multX) != 1 || multX <= 0.0) {
3953       sprintf(gszMsgBox, TgLoadString(STID_INVALID_SPEC), spec);
3954       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
3955       return;
3956    } else if (fabs(multX-((float)1.0)) < INT_TOL) {
3957       return;
3958    }
3959    if (round(multX) >= 10) {
3960       sprintf(gszMsgBox, TgLoadString(STID_SCALING_EVERYTHING_LARGE_SURE),
3961             multX);
3962       if (MsgBox(gszMsgBox, TOOL_NAME, YNC_MB) != MB_ID_YES) {
3963          return;
3964       }
3965    }
3966    multY = multX;
3967 
3968    changeX = (fabs(multX-1.0) > 1.0e-6);
3969    changeY = (fabs(multY-1.0) > 1.0e-6);
3970    if (!changeX && !changeY) return;
3971 
3972    MakeQuiescent();
3973 
3974    if (firstCmd != NULL) {
3975       if (!OkToFlushUndoBuffer(
3976             TgLoadString(STID_PRECISE_SCALE_EV_CAUSE_FLUSH))) {
3977          SetCurChoice(curChoiceBeforeMakeQuiescent);
3978          return;
3979       }
3980    }
3981    corner = CORNER_RB;
3982    horiAlign = ALIGN_L;
3983    vertAlign = ALIGN_T;
3984    stretchableText = saved_stretchable_text;
3985 
3986    printMag /= multX;
3987    if (UpdPageStyle(pageStyle)) {
3988       UpdDrawWinBBox();
3989       AdjSplineVs();
3990    }
3991    FormatFloat(&printMag, buf);
3992    if (printMag <= 100.0) {
3993       sprintf(gszMsgBox, TgLoadString(STID_NEW_REDUCTION_IS_PERCENT), buf);
3994    } else {
3995       sprintf(gszMsgBox, TgLoadString(STID_NEW_ENLARGEMENT_IS_PERCENT), buf);
3996    }
3997    Msg(gszMsgBox);
3998    SaveStatusStrings();
3999 
4000    StartCompositeCmd();
4001    for (curPageNum=1; curPageNum <= lastPageNum; curPageNum++) {
4002       int num_to_resize=0;
4003       char **ppsz_names_to_resize=NULL;
4004 
4005       /* No need for the CMD_GOTO_PAGE stuff since CleanUpCmds will be called */
4006       /* PrepareToRecord(CMD_GOTO_PAGE, NULL, NULL, prev_page_num); */
4007       GotoPageNum(curPageNum);
4008       ShowPage();
4009       XSync(mainDisplay, False);
4010       /* RecordCmd(CMD_GOTO_PAGE, NULL, NULL, NULL, curPageNum); */
4011       prev_page_num = curPageNum;
4012       sprintf(gszMsgBox, TgLoadCachedString(CSTID_SCALING_EVERYTHING_PAGE),
4013             curPageNum, lastPageNum);
4014       SetStringStatus(gszMsgBox);
4015 
4016       SelAllObj(FALSE, FALSE);
4017 
4018       ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
4019       stretchingEverything = TRUE;
4020       ScaleAllSel(corner, (double)(multX*1000.0), (double)(multY*1000.0), TRUE);
4021       stretchingEverything = FALSE;
4022       if (ppsz_names_to_resize != NULL) {
4023          DoOnResize(ppsz_names_to_resize, num_to_resize);
4024       }
4025       RemoveAllSel();
4026    }
4027    /* PrepareToRecord(CMD_GOTO_PAGE, NULL, NULL, prev_page_num); */
4028    GotoPageNum(saved_cur_page_num);
4029    ShowPage();
4030    /* RecordCmd(CMD_GOTO_PAGE, NULL, NULL, NULL, saved_cur_page_num); */
4031 
4032    EndCompositeCmd();
4033    CleanUpCmds();
4034 
4035    RestoreStatusStrings();
4036    SetFileModified(TRUE);
4037    justDupped = FALSE;
4038 
4039    stretchableText = saved_stretchable_text;
4040    horiAlign = saved_h_align;
4041    vertAlign = saved_v_align;
4042 
4043    RedrawScrollBars();
4044    RedrawRulers();
4045    RedrawTitleWindow();
4046    ClearAndRedrawDrawWindow();
4047    SetCurChoice(curChoiceBeforeMakeQuiescent);
4048 }
4049 
ScaleObjLikeScaleEverything(obj_ptr,scale,redraw)4050 void ScaleObjLikeScaleEverything(obj_ptr, scale, redraw)
4051    struct ObjRec *obj_ptr;
4052    double scale;
4053    int redraw;
4054 {
4055    int corner=CORNER_RB, saved_h_align=horiAlign, saved_v_align=vertAlign;
4056    int saved_stretchable_text=stretchableText, saved_move_mode=moveMode;
4057    float saved_print_mag=printMag;
4058    struct BBRec obbox;
4059 
4060    obbox.ltx = obbox.lty = 0;
4061    obbox.rbx = onePageWidth; obbox.rby = onePageHeight;
4062    SetPivot(corner, &obbox);
4063 
4064    if (fabs(scale-((float)1.0)) < INT_TOL) {
4065       return;
4066    }
4067    multX = multY = ((double)1)/scale;
4068 
4069    changeX = (fabs(multX-1.0) > 1.0e-6);
4070    changeY = (fabs(multY-1.0) > 1.0e-6);
4071    if (!changeX && !changeY) return;
4072 
4073    corner = CORNER_RB;
4074    horiAlign = ALIGN_L;
4075    vertAlign = ALIGN_T;
4076    stretchableText = TRUE;
4077    moveMode = UNCONST_MOVE;
4078 
4079    printMag /= multX;
4080    if (UpdPageStyle(pageStyle)) {
4081       UpdDrawWinBBox();
4082       AdjSplineVs();
4083    }
4084    if (multX < 100.0) {
4085       sprintf(gszMsgBox, TgLoadString(STID_REDUCE_BY_FACTOR), multX);
4086    } else {
4087       sprintf(gszMsgBox, TgLoadString(STID_ENLARGE_BY_FACTOR), multX);
4088    }
4089    SetStringStatus(gszMsgBox);
4090    SaveStatusStrings();
4091 
4092    AddObj(NULL, topObj, obj_ptr);
4093    topSel = botSel = SelectThisObject(obj_ptr);
4094    UpdSelBBox();
4095 
4096    StartCompositeCmd();
4097 
4098    stretchingEverything = TRUE;
4099    ScaleAllSel(corner, (double)(multX*1000.0), (double)(multY*1000.0), redraw);
4100    stretchingEverything = FALSE;
4101    RemoveAllSel();
4102 
4103    EndCompositeCmd();
4104    CleanUpCmds();
4105 
4106    UnlinkObj(topObj);
4107 
4108    RestoreStatusStrings();
4109    SetFileModified(TRUE);
4110    justDupped = FALSE;
4111 
4112    moveMode = saved_move_mode;
4113    stretchableText = saved_stretchable_text;
4114    horiAlign = saved_h_align;
4115    vertAlign = saved_v_align;
4116    printMag = saved_print_mag;
4117 
4118    if (UpdPageStyle(pageStyle)) {
4119       UpdDrawWinBBox();
4120       AdjSplineVs();
4121    }
4122 }
4123 
SizeAllSelObj(AbsW,AbsH)4124 void SizeAllSelObj(AbsW, AbsH)
4125    int AbsW, AbsH;
4126 {
4127    int saved_h_align=horiAlign, saved_v_align=vertAlign, num_to_resize=0;
4128    char **ppsz_names_to_resize=NULL;
4129    struct BBRec obbox;
4130 
4131    if (topSel == NULL) {
4132       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
4133       return;
4134    }
4135    if (AbsW == selObjRbX-selObjLtX && AbsH == selObjRbY-selObjLtY) return;
4136 
4137    obbox.ltx = selObjLtX; obbox.lty = selObjLtY;
4138    obbox.rbx = selObjRbX; obbox.rby = selObjRbY;
4139    SetPivot(CORNER_RB, &obbox);
4140 
4141    horiAlign = ALIGN_L;
4142    vertAlign = ALIGN_T;
4143    multX = (selObjRbX==selObjLtX ? ((double)1.0) :
4144          ((double)AbsW) / ((double)selObjRbX-selObjLtX));
4145    multY = (selObjRbY==selObjLtY ? ((double)1.0) :
4146          ((double)AbsH) / ((double)selObjRbY-selObjLtY));
4147    changeX = (fabs(multX-1.0) > 1.0e-6);
4148    changeY = (fabs(multY-1.0) > 1.0e-6);
4149    ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
4150    if (ppsz_names_to_resize == NULL) {
4151       ScaleAllSel(CORNER_RB, (double)(multX*1000.0), (double)(multY*1000.0),
4152             TRUE);
4153    } else {
4154       StartCompositeCmd();
4155       ScaleAllSel(CORNER_RB, (double)(multX*1000.0), (double)(multY*1000.0),
4156             TRUE);
4157       DoOnResize(ppsz_names_to_resize, num_to_resize);
4158       EndCompositeCmd();
4159    }
4160    horiAlign = saved_h_align;
4161    vertAlign = saved_v_align;
4162 
4163    UpdSelBBox();
4164    SetFileModified(TRUE);
4165    justDupped = FALSE;
4166 }
4167 
SizeAnObj(ObjPtr,TopOwner,AbsW,AbsH)4168 void SizeAnObj(ObjPtr, TopOwner, AbsW, AbsH)
4169    struct ObjRec *ObjPtr, *TopOwner;
4170    int AbsW, AbsH;
4171    /* This function is meant to be called from within an internal command */
4172 {
4173    int obj_w=ObjPtr->obbox.rbx-ObjPtr->obbox.ltx;
4174    int obj_h=ObjPtr->obbox.rby-ObjPtr->obbox.lty;
4175 
4176    if (execCurDepth <= 0) {
4177 #ifdef _TGIF_DBG /* debug, do not translate */
4178       TgAssert(FALSE,
4179             "SizeAnObj() called not from an internal command!", NULL);
4180       return;
4181 #endif /* _TGIF_DBG */
4182    }
4183    if (obj_w == AbsW && obj_h == AbsH) {
4184       return;
4185    }
4186    if (ObjPtr == TopOwner) {
4187       struct SelRec *saved_top_sel=topSel, *saved_bot_sel=botSel;
4188 
4189       topSel = botSel = NULL;
4190       AddObjIntoSel(ObjPtr, NULL, topSel, &topSel, &botSel);
4191       UpdSelBBox();
4192 
4193       SizeAllSelObj(AbsW, AbsH);
4194 
4195       RemoveAllSel();
4196       topSel = saved_top_sel;
4197       botSel = saved_bot_sel;
4198       UpdSelBBox();
4199    } else {
4200       int saved_h_align=horiAlign, saved_v_align=vertAlign;
4201       int ltx=TopOwner->bbox.ltx, lty=TopOwner->bbox.lty;
4202       int rbx=TopOwner->bbox.rbx, rby=TopOwner->bbox.rby;
4203       struct SelRec *saved_top_sel=topSel, *saved_bot_sel=botSel;
4204 
4205       SetPivot(CORNER_RB, &ObjPtr->obbox);
4206 
4207       multX = (obj_w == 0) ? ((double)1.0) : ((double)AbsW) / ((double)obj_w);
4208       multY = (obj_h == 0) ? ((double)1.0) : ((double)AbsH) / ((double)obj_h);
4209       changeX = (fabs(multX-1.0) > 1.0e-6);
4210       changeY = (fabs(multY-1.0) > 1.0e-6);
4211       if (!changeX && !changeY) {
4212          return;
4213       }
4214       horiAlign = ALIGN_L;
4215       vertAlign = ALIGN_T;
4216 
4217       topSel = botSel = NULL;
4218       UpdSelBBox();
4219 
4220       PrepareToReplaceAnObj(TopOwner);
4221 
4222       StretchObj(ObjPtr, CORNER_RB, (double)(multX*1000.0),
4223             (double)(multY*1000.0), FALSE);
4224       while (ObjPtr != TopOwner) {
4225          ObjPtr = ObjPtr->tmp_parent;
4226          AdjObjBBox(ObjPtr);
4227       }
4228       RecordReplaceAnObj(TopOwner);
4229       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
4230             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
4231             TopOwner->bbox.ltx-GRID_ABS_SIZE(1),
4232             TopOwner->bbox.lty-GRID_ABS_SIZE(1),
4233             TopOwner->bbox.rbx+GRID_ABS_SIZE(1),
4234             TopOwner->bbox.rby+GRID_ABS_SIZE(1));
4235 
4236       RemoveAllSel();
4237       topSel = saved_top_sel;
4238       botSel = saved_bot_sel;
4239       UpdSelBBox();
4240 
4241       SetFileModified(TRUE);
4242       justDupped = FALSE;
4243       horiAlign = saved_h_align;
4244       vertAlign = saved_v_align;
4245    }
4246 }
4247 
4248 static
DoSizeAllSelToGivenWidthHeight(abs_w,abs_h,do_width,do_height)4249 void DoSizeAllSelToGivenWidthHeight(abs_w, abs_h, do_width, do_height)
4250    int abs_h, do_width, do_height;
4251 {
4252    struct SelRec *saved_top_sel=topSel, *saved_bot_sel=botSel, *sel_ptr=NULL;
4253    int saved_h_align=horiAlign, saved_v_align=vertAlign, num_to_resize=0;
4254    char **ppsz_names_to_resize=NULL;
4255 
4256    if (topSel == NULL || (!do_width && !do_height)) {
4257       return;
4258    }
4259    horiAlign = ALIGN_L;
4260    vertAlign = ALIGN_T;
4261 
4262    ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
4263 
4264    HighLightReverse();
4265    StartCompositeCmd();
4266    for (sel_ptr=saved_top_sel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
4267       struct ObjRec *obj_ptr=sel_ptr->obj;
4268       int w=(obj_ptr->obbox.rbx-obj_ptr->obbox.ltx);
4269       int h=(obj_ptr->obbox.rby-obj_ptr->obbox.lty);
4270 
4271       topSel = botSel = SelectThisObject(obj_ptr);
4272       UpdSelBBox();
4273       if (do_width && do_height) {
4274          SetPivot(CORNER_RB, &obj_ptr->obbox);
4275          multX = (w == 0 ? ((double)1.0) : ((double)abs_w) / ((double)w));
4276          multY = (h == 0 ? ((double)1.0) : ((double)abs_h) / ((double)h));
4277          changeX = (fabs(multX-1.0) > 1.0e-6);
4278          changeY = (fabs(multY-1.0) > 1.0e-6);
4279          ScaleAllSel(CORNER_RB, (double)(multX*1000.0), (double)(multY*1000.0),
4280                TRUE);
4281       } else if (do_width) {
4282          SetPivot(CORNER_RIGHT, &obj_ptr->obbox);
4283          multX = (w == 0 ? ((double)1.0) : ((double)abs_w) / ((double)w));
4284          changeX = (fabs(multX-1.0) > 1.0e-6);
4285          ScaleAllSel(CORNER_RIGHT, (double)(multX*1000.0), (double)1000, TRUE);
4286       } else {
4287          SetPivot(CORNER_BOTTOM, &obj_ptr->obbox);
4288          multY = (h == 0 ? ((double)1.0) : ((double)abs_h) / ((double)h));
4289          changeY = (fabs(multY-1.0) > 1.0e-6);
4290          ScaleAllSel(CORNER_BOTTOM, (double)1000, (double)(multY*1000.0), TRUE);
4291       }
4292       free(topSel);
4293    }
4294    if (ppsz_names_to_resize != NULL) {
4295       DoOnResize(ppsz_names_to_resize, num_to_resize);
4296    }
4297    EndCompositeCmd();
4298 
4299    horiAlign = saved_h_align;
4300    vertAlign = saved_v_align;
4301 
4302    topSel = saved_top_sel;
4303    botSel = saved_bot_sel;
4304    UpdSelBBox();
4305 
4306    HighLightForward();
4307    SetFileModified(TRUE);
4308    justDupped = FALSE;
4309 }
4310 
SizeAllSelToGivenWidthHeight(AbsW,AbsH)4311 void SizeAllSelToGivenWidthHeight(AbsW, AbsH)
4312    int AbsW, AbsH;
4313 {
4314    DoSizeAllSelToGivenWidthHeight(AbsW, AbsH, TRUE, TRUE);
4315 }
4316 
SizeAllSelToGivenWidth(AbsW)4317 void SizeAllSelToGivenWidth(AbsW)
4318    int AbsW;
4319 {
4320    DoSizeAllSelToGivenWidthHeight(AbsW, INVALID, TRUE, FALSE);
4321 }
4322 
SizeAllSelToGivenHeight(AbsH)4323 void SizeAllSelToGivenHeight(AbsH)
4324    int AbsH;
4325 {
4326    DoSizeAllSelToGivenWidthHeight(INVALID, AbsH, FALSE, TRUE);
4327 }
4328 
FlipObjHorizontal(ObjPtr)4329 void FlipObjHorizontal(ObjPtr)
4330    struct ObjRec *ObjPtr;
4331 {
4332    int two_x_pivot=selNoLockObjLtX+selNoLockObjRbX;
4333    int new_obj_ltx=two_x_pivot-ObjPtr->obbox.rbx;
4334    int new_obj_rbx=two_x_pivot-ObjPtr->obbox.ltx;
4335    int new_obj_lty=ObjPtr->obbox.lty;
4336 
4337    if (ObjPtr->ctm == NULL && ObjPtr->type != OBJ_XBM &&
4338          ObjPtr->type != OBJ_XPM) {
4339       register IntPoint *v;
4340       register int i;
4341       int num_pts;
4342       struct ObjRec *obj_ptr;
4343       struct AttrRec *attr_ptr;
4344       struct ArcRec *arc_ptr;
4345 
4346       switch (ObjPtr->type) {
4347       case OBJ_TEXT:
4348          if (ObjPtr->detail.t->minilines.just != JUST_C) {
4349             ObjPtr->detail.t->minilines.just =
4350                   MAXJUSTS-1-ObjPtr->detail.t->minilines.just;
4351             if (ObjPtr->detail.t->cached_bitmap != None) {
4352                XFreePixmap(mainDisplay, ObjPtr->detail.t->cached_bitmap);
4353             }
4354             ObjPtr->detail.t->cached_bitmap = None;
4355 
4356             if (zoomScale != 0) {
4357                ObjPtr->detail.t->cached_zoom = 0;
4358             }
4359          }
4360          MoveObj(ObjPtr, two_x_pivot-((ObjPtr->x)<<1), 0);
4361          UpdTextBBox(ObjPtr);
4362          break;
4363 
4364       default:
4365          switch (ObjPtr->type) {
4366          case OBJ_XBM:
4367             ObjPtr->detail.xbm->flip ^= HORI_EVEN;
4368             if (ObjPtr->detail.xbm->cached_bitmap != None) {
4369                XFreePixmap(mainDisplay, ObjPtr->detail.xbm->cached_bitmap);
4370             }
4371             ObjPtr->detail.xbm->cached_bitmap = None;
4372 
4373             if (zoomScale != 0) {
4374                ObjPtr->detail.xbm->cached_zoom = 0;
4375             }
4376             break;
4377          case OBJ_XPM:
4378             ObjPtr->detail.xpm->flip ^= HORI_EVEN;
4379             if (ObjPtr->detail.xpm->cached_pixmap != None) {
4380                XFreePixmap(mainDisplay, ObjPtr->detail.xpm->cached_pixmap);
4381             }
4382             ObjPtr->detail.xpm->cached_pixmap = None;
4383             if (ObjPtr->detail.xpm->cached_bitmap != None) {
4384                XFreePixmap(mainDisplay, ObjPtr->detail.xpm->cached_bitmap);
4385             }
4386             ObjPtr->detail.xpm->cached_bitmap = None;
4387             ObjPtr->detail.xpm->cached_color = (-1);
4388 
4389             if (zoomScale != 0) {
4390                ObjPtr->detail.xpm->cached_zoom = 0;
4391             }
4392             break;
4393          case OBJ_ICON:
4394          case OBJ_PIN:
4395             ObjPtr->detail.r->flip ^= HORI_EVEN;
4396             break;
4397          }
4398          ObjPtr->obbox.ltx = ObjPtr->x = new_obj_ltx;
4399          ObjPtr->obbox.rbx = new_obj_rbx;
4400          break;
4401       }
4402 
4403       switch (ObjPtr->type) {
4404       case OBJ_POLY:
4405          num_pts = ObjPtr->detail.p->n;
4406          v = ObjPtr->detail.p->vlist;
4407          for (i = 0; i < num_pts; i++, v++) (*v).x = two_x_pivot - (*v).x;
4408          AdjObjSplineVs(ObjPtr);
4409          attr_ptr = ObjPtr->fattr;
4410          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4411             FlipObjHorizontal(attr_ptr->obj);
4412          }
4413          break;
4414       case OBJ_POLYGON:
4415          num_pts = ObjPtr->detail.g->n;
4416          v = ObjPtr->detail.g->vlist;
4417          for (i = 0; i < num_pts; i++, v++) (*v).x = two_x_pivot - (*v).x;
4418          AdjObjSplineVs(ObjPtr);
4419          attr_ptr = ObjPtr->fattr;
4420          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4421             FlipObjHorizontal(attr_ptr->obj);
4422          }
4423          break;
4424       case OBJ_BOX:
4425       case OBJ_OVAL:
4426       case OBJ_RCBOX:
4427       case OBJ_XBM:
4428       case OBJ_XPM:
4429          attr_ptr = ObjPtr->fattr;
4430          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4431             FlipObjHorizontal(attr_ptr->obj);
4432          }
4433          AdjObjSplineVs(ObjPtr);
4434          break;
4435       case OBJ_TEXT:
4436          AdjObjSplineVs(ObjPtr);
4437          break;
4438       case OBJ_ARC:
4439          arc_ptr = ObjPtr->detail.a;
4440          arc_ptr->xc = two_x_pivot - arc_ptr->xc;
4441          arc_ptr->x1 = two_x_pivot - arc_ptr->x1;
4442          arc_ptr->x2 = two_x_pivot - arc_ptr->x2;
4443          arc_ptr->dir = !(arc_ptr->dir);
4444          arc_ptr->ltx = two_x_pivot - arc_ptr->ltx - arc_ptr->w;
4445          if (arc_ptr->angle1 > 0) {
4446             arc_ptr->angle1 = (180*64) - arc_ptr->angle1;
4447          } else {
4448             arc_ptr->angle1 = (-180)*64 - arc_ptr->angle1;
4449          }
4450          arc_ptr->angle2 = -(arc_ptr->angle2);
4451          AdjObjBBox(ObjPtr);
4452          attr_ptr = ObjPtr->fattr;
4453          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4454             FlipObjHorizontal(attr_ptr->obj);
4455          }
4456          AdjObjSplineVs(ObjPtr);
4457          break;
4458       case OBJ_GROUP:
4459       case OBJ_ICON:
4460       case OBJ_SYM:
4461       case OBJ_PIN:
4462          obj_ptr = ObjPtr->detail.r->first;
4463          for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4464             FlipObjHorizontal(obj_ptr);
4465          }
4466          attr_ptr = ObjPtr->fattr;
4467          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4468             FlipObjHorizontal(attr_ptr->obj);
4469          }
4470          AdjObjSplineVs(ObjPtr);
4471          break;
4472       }
4473       AdjObjBBox(ObjPtr);
4474    } else {
4475       double dz=(double)0, d1=(double)1000, dm1=(double)-1000;
4476 
4477       ShearObj(ObjPtr, CORNER_LEFT, dz, dz, dm1, d1, NULL, NULL);
4478       MoveObj(ObjPtr, new_obj_ltx-ObjPtr->obbox.ltx,
4479             new_obj_lty-ObjPtr->obbox.lty);
4480    }
4481    SetFileModified(TRUE);
4482 }
4483 
FlipIconHorizontal(ObjPtr)4484 void FlipIconHorizontal(ObjPtr)
4485    struct ObjRec *ObjPtr;
4486 {
4487    register int two_x_pivot;
4488    int new_obj_ltx, new_obj_rbx;
4489    struct ObjRec *obj_ptr;
4490    struct AttrRec *attr_ptr;
4491 
4492    two_x_pivot = selNoLockObjLtX + selNoLockObjRbX;
4493    new_obj_ltx = two_x_pivot - ObjPtr->obbox.rbx;
4494    new_obj_rbx = two_x_pivot - ObjPtr->obbox.ltx;
4495 
4496    ObjPtr->detail.r->flip ^= HORI_EVEN;
4497 
4498    ObjPtr->obbox.ltx = ObjPtr->x = new_obj_ltx;
4499    ObjPtr->obbox.rbx = new_obj_rbx;
4500 
4501    obj_ptr = ObjPtr->detail.r->first;
4502    for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4503       FlipObjHorizontal(obj_ptr);
4504    }
4505    attr_ptr = ObjPtr->fattr;
4506    for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4507       FlipObjHorizontal(attr_ptr->obj);
4508    }
4509    AdjObjBBox(ObjPtr);
4510 }
4511 
FlipObjVertical(ObjPtr)4512 void FlipObjVertical(ObjPtr)
4513    struct ObjRec *ObjPtr;
4514 {
4515    int two_x_pivot=selNoLockObjLtY+selNoLockObjRbY;
4516    int new_obj_lty=two_x_pivot-ObjPtr->obbox.rby;
4517    int new_obj_rby=two_x_pivot-ObjPtr->obbox.lty;
4518    int new_obj_ltx=ObjPtr->obbox.ltx;
4519 
4520    if (ObjPtr->ctm == NULL && ObjPtr->type != OBJ_XBM &&
4521          ObjPtr->type != OBJ_XPM) {
4522       register IntPoint *v;
4523       register int i;
4524       int num_pts;
4525       struct ObjRec *obj_ptr;
4526       struct AttrRec *attr_ptr;
4527       struct ArcRec *arc_ptr;
4528 
4529       switch (ObjPtr->type) {
4530       case OBJ_TEXT:
4531          MoveObj(ObjPtr, 0, new_obj_lty-ObjPtr->y);
4532          UpdTextBBox(ObjPtr);
4533          break;
4534 
4535       default:
4536          switch (ObjPtr->type) {
4537          case OBJ_XBM:
4538             ObjPtr->detail.xbm->flip ^= VERT_EVEN;
4539             if (ObjPtr->detail.xbm->cached_bitmap != None) {
4540                XFreePixmap(mainDisplay, ObjPtr->detail.xbm->cached_bitmap);
4541             }
4542             ObjPtr->detail.xbm->cached_bitmap = None;
4543 
4544             if (zoomScale != 0) {
4545                ObjPtr->detail.xbm->cached_zoom = 0;
4546             }
4547             break;
4548          case OBJ_XPM:
4549             ObjPtr->detail.xpm->flip ^= VERT_EVEN;
4550             if (ObjPtr->detail.xpm->cached_pixmap != None) {
4551                XFreePixmap(mainDisplay, ObjPtr->detail.xpm->cached_pixmap);
4552             }
4553             ObjPtr->detail.xpm->cached_pixmap = None;
4554             if (ObjPtr->detail.xpm->cached_bitmap != None) {
4555                XFreePixmap(mainDisplay, ObjPtr->detail.xpm->cached_bitmap);
4556             }
4557             ObjPtr->detail.xpm->cached_bitmap = None;
4558             ObjPtr->detail.xpm->cached_color = (-1);
4559 
4560             if (zoomScale != 0) {
4561                ObjPtr->detail.xpm->cached_zoom = 0;
4562             }
4563             break;
4564          case OBJ_ICON:
4565          case OBJ_PIN:
4566             ObjPtr->detail.r->flip ^= VERT_EVEN;
4567             break;
4568          }
4569          ObjPtr->obbox.lty = ObjPtr->y = new_obj_lty;
4570          ObjPtr->obbox.rby = new_obj_rby;
4571          break;
4572       }
4573 
4574       switch (ObjPtr->type) {
4575       case OBJ_POLY:
4576          num_pts = ObjPtr->detail.p->n;
4577          v = ObjPtr->detail.p->vlist;
4578          for (i = 0; i < num_pts; i++, v++) (*v).y = two_x_pivot - (*v).y;
4579          AdjObjSplineVs(ObjPtr);
4580          attr_ptr = ObjPtr->fattr;
4581          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4582             FlipObjVertical(attr_ptr->obj);
4583          }
4584          break;
4585       case OBJ_POLYGON:
4586          num_pts = ObjPtr->detail.g->n;
4587          v = ObjPtr->detail.g->vlist;
4588          for (i = 0; i < num_pts; i++, v++) (*v).y = two_x_pivot - (*v).y;
4589          AdjObjSplineVs(ObjPtr);
4590          attr_ptr = ObjPtr->fattr;
4591          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4592             FlipObjVertical(attr_ptr->obj);
4593          }
4594          break;
4595       case OBJ_BOX:
4596       case OBJ_OVAL:
4597       case OBJ_RCBOX:
4598       case OBJ_XBM:
4599       case OBJ_XPM:
4600          attr_ptr = ObjPtr->fattr;
4601          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4602             FlipObjVertical(attr_ptr->obj);
4603          }
4604          AdjObjSplineVs(ObjPtr);
4605          break;
4606       case OBJ_TEXT:
4607          AdjObjSplineVs(ObjPtr);
4608          break;
4609       case OBJ_ARC:
4610          arc_ptr = ObjPtr->detail.a;
4611          arc_ptr->yc = two_x_pivot - arc_ptr->yc;
4612          arc_ptr->y1 = two_x_pivot - arc_ptr->y1;
4613          arc_ptr->y2 = two_x_pivot - arc_ptr->y2;
4614          arc_ptr->dir = !(arc_ptr->dir);
4615          arc_ptr->lty = two_x_pivot - arc_ptr->lty - arc_ptr->h;
4616          arc_ptr->angle1 = -(arc_ptr->angle1);
4617          arc_ptr->angle2 = -(arc_ptr->angle2);
4618          AdjObjBBox(ObjPtr);
4619          attr_ptr = ObjPtr->fattr;
4620          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4621             FlipObjVertical(attr_ptr->obj);
4622          }
4623          AdjObjSplineVs(ObjPtr);
4624          break;
4625       case OBJ_GROUP:
4626       case OBJ_ICON:
4627       case OBJ_SYM:
4628       case OBJ_PIN:
4629          obj_ptr = ObjPtr->detail.r->first;
4630          for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4631             FlipObjVertical(obj_ptr);
4632          }
4633          attr_ptr = ObjPtr->fattr;
4634          for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4635             FlipObjVertical(attr_ptr->obj);
4636          }
4637          AdjObjSplineVs(ObjPtr);
4638          break;
4639       }
4640       AdjObjBBox(ObjPtr);
4641    } else {
4642       double dz=(double)0, d1=(double)1000, dm1=(double)-1000;
4643 
4644       ShearObj(ObjPtr, CORNER_TOP, dz, dz, d1, dm1, NULL, NULL);
4645       MoveObj(ObjPtr, new_obj_ltx-ObjPtr->obbox.ltx,
4646             new_obj_lty-ObjPtr->obbox.lty);
4647    }
4648    SetFileModified(TRUE);
4649 }
4650 
FlipIconVertical(ObjPtr)4651 void FlipIconVertical(ObjPtr)
4652    struct ObjRec *ObjPtr;
4653 {
4654    register int two_x_pivot;
4655    int new_obj_lty, new_obj_rby;
4656    struct ObjRec *obj_ptr;
4657    struct AttrRec *attr_ptr;
4658 
4659    two_x_pivot = selNoLockObjLtY + selNoLockObjRbY;
4660    new_obj_lty = two_x_pivot - ObjPtr->obbox.rby;
4661    new_obj_rby = two_x_pivot - ObjPtr->obbox.lty;
4662 
4663    ObjPtr->detail.r->flip ^= VERT_EVEN;
4664 
4665    ObjPtr->obbox.lty = ObjPtr->y = new_obj_lty;
4666    ObjPtr->obbox.rby = new_obj_rby;
4667 
4668    obj_ptr = ObjPtr->detail.r->first;
4669    for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4670       FlipObjVertical(obj_ptr);
4671    }
4672    attr_ptr = ObjPtr->fattr;
4673    for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4674       FlipObjVertical(attr_ptr->obj);
4675    }
4676    AdjObjBBox(ObjPtr);
4677 }
4678 
4679 /* --------------------- Rotation --------------------- */
4680 
4681 static int	rotatePivotX=0;
4682 static int	rotatePivotY=0;
4683 
4684 static
OkToTransform(obj_ptr,stid)4685 int OkToTransform(obj_ptr, stid)
4686    struct ObjRec *obj_ptr;
4687    int stid;
4688 {
4689    char obj_name[MAXSTRING];
4690    struct AttrRec *name_attr=NULL;
4691 
4692    if (obj_ptr->locked) return FALSE;
4693 
4694    *obj_name = '\0';
4695    if (HasOnResize(obj_ptr, &name_attr) && name_attr != NULL) {
4696       UtilStrCpyN(obj_name, sizeof(obj_name), name_attr->attr_value.s);
4697       sprintf(gszMsgBox, TgLoadString(stid), obj_name);
4698       if (MsgBox(gszMsgBox, TOOL_NAME, YN_MB) != MB_ID_YES) {
4699          return FALSE;
4700       }
4701    }
4702    return TRUE;
4703 }
4704 
SetRotatePivot()4705 void SetRotatePivot()
4706 {
4707    if (!autoRotatePivot && rotatePivotAbsXYValid) {
4708       rotatePivotX = rotatePivotAbsX;
4709       rotatePivotY = rotatePivotAbsY;
4710    } else {
4711       rotatePivotX = ((selNoLockObjLtX + selNoLockObjRbX)>>1);
4712       rotatePivotY = ((selNoLockObjLtY + selNoLockObjRbY)>>1);
4713    }
4714 }
4715 
SetRotatePivotByObject(ObjPtr)4716 void SetRotatePivotByObject(ObjPtr)
4717    struct ObjRec *ObjPtr;
4718 {
4719    rotatePivotX = ((ObjPtr->obbox.ltx + ObjPtr->obbox.rbx)>>1);
4720    rotatePivotY = ((ObjPtr->obbox.lty + ObjPtr->obbox.rby)>>1);
4721 }
4722 
4723 static
RotatePtClockWise(X,Y,NewX,NewY)4724 void RotatePtClockWise(X, Y, NewX, NewY)
4725    int X, Y, *NewX, *NewY;
4726 {
4727    *NewX = rotatePivotX + rotatePivotY - Y;
4728    *NewY = rotatePivotY - rotatePivotX + X;
4729 }
4730 
4731 static
RotatedXY(X,Y,AngleDelta,NewX,NewY)4732 void RotatedXY(X, Y, AngleDelta, NewX, NewY)
4733    int X, Y, AngleDelta, *NewX, *NewY; /* AngleDelta is degree*64 */
4734 {
4735    register double radian, sin_val, cos_val;
4736    int dx=X-pivotX, dy=Y-pivotY;
4737 
4738    if (dx == 0 && dy == 0) {
4739       *NewX = pivotX;
4740       *NewY = pivotY;
4741    } else {
4742       radian = (((double)AngleDelta)*M_PI/180.0/64.0);
4743       sin_val = sin(radian);
4744       cos_val = cos(radian);
4745       *NewX = pivotX + round(dx*cos_val - dy*sin_val);
4746       *NewY = pivotY + round(dx*sin_val + dy*cos_val);
4747    }
4748 }
4749 
4750 static
RotatedAbsXY(X,Y,AngleDelta,NewX,NewY)4751 void RotatedAbsXY(X, Y, AngleDelta, NewX, NewY)
4752    int X, Y, AngleDelta, *NewX, *NewY; /* AngleDelta is degree*64 */
4753 {
4754    register double radian, sin_val, cos_val;
4755    int dx=X-absPivotX, dy=Y-absPivotY;
4756 
4757    if (dx == 0 && dy == 0) {
4758       *NewX = absPivotX;
4759       *NewY = absPivotY;
4760    } else {
4761       radian = (((double)AngleDelta)*M_PI/180.0/64.0);
4762       sin_val = sin(radian);
4763       cos_val = cos(radian);
4764       *NewX = absPivotX + round(dx*cos_val - dy*sin_val);
4765       *NewY = absPivotY + round(dx*sin_val + dy*cos_val);
4766    }
4767 }
4768 
RotateObj(ObjPtr,Corner,AngleDelta,RealLtX,RealLtY)4769 void RotateObj(ObjPtr, Corner, AngleDelta, RealLtX, RealLtY)
4770    struct ObjRec *ObjPtr;
4771    int Corner, AngleDelta; /* AngleDelta is degree*64 */
4772    int *RealLtX, *RealLtY;
4773 {
4774    IntPoint abs_obj_obbox_vs[5];
4775    int x, y, new_ltx, new_lty, new_rbx, new_rby;
4776    double radian=(((double)AngleDelta)*M_PI/180.0/64.0);
4777    double sin_val=sin(radian), cos_val=cos(radian);
4778    struct XfrmMtrxRec ctm, new_ctm;
4779    struct ObjRec *obj_ptr;
4780    struct AttrRec *attr_ptr;
4781 
4782    switch (ObjPtr->type) {
4783    case OBJ_GROUP:
4784    case OBJ_ICON:
4785    case OBJ_SYM:
4786    case OBJ_PIN:
4787       for (obj_ptr=ObjPtr->detail.r->first; obj_ptr != NULL;
4788             obj_ptr=obj_ptr->next) {
4789          RotateObj(obj_ptr, Corner, AngleDelta, RealLtX, RealLtY);
4790       }
4791       break;
4792 
4793    default:
4794       if (ObjPtr->ctm == NULL) {
4795          memcpy(&ObjPtr->orig_obbox, &ObjPtr->obbox, sizeof(struct BBRec));
4796          if (ObjPtr->type == OBJ_TEXT) {
4797             memcpy(&ObjPtr->detail.t->orig_bbox, &ObjPtr->bbox,
4798                   sizeof(struct BBRec));
4799          }
4800          ObjPtr->ctm = (struct XfrmMtrxRec *)malloc(sizeof(struct XfrmMtrxRec));
4801          if (ObjPtr->ctm == NULL) FailAllocMessage();
4802          ObjPtr->ctm->m[CTM_SX] = ObjPtr->ctm->m[CTM_SY] = (double)1000;
4803          ObjPtr->ctm->m[CTM_SIN] = ObjPtr->ctm->m[CTM_MSIN] = (double)0;
4804          ObjPtr->ctm->t[CTM_TX] = ObjPtr->ctm->t[CTM_TY] = 0;
4805       }
4806       RotatedAbsXY(ObjPtr->x+ObjPtr->ctm->t[CTM_TX],
4807             ObjPtr->y+ObjPtr->ctm->t[CTM_TY], AngleDelta, &x, &y);
4808       ctm.m[CTM_SX] = ctm.m[CTM_SY] = ((double)1000.0)*cos_val;
4809       ctm.m[CTM_SIN] = ((double)1000.0)*sin_val;
4810       ctm.m[CTM_MSIN] = (-ctm.m[CTM_SIN]);
4811       ctm.t[CTM_TX] = 0;
4812       ctm.t[CTM_TY] = 0;
4813       ConcatCTM(ObjPtr->ctm, &ctm, &new_ctm);
4814       new_ctm.t[CTM_TX] = x-ObjPtr->x;
4815       new_ctm.t[CTM_TY] = y-ObjPtr->y;
4816       memcpy(ObjPtr->ctm, &new_ctm, sizeof(struct XfrmMtrxRec));
4817 
4818       GetTransformedOBBoxAbsVs(ObjPtr, abs_obj_obbox_vs);
4819 
4820       new_ltx = min(min(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
4821             min(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
4822       new_rbx = max(max(abs_obj_obbox_vs[0].x,abs_obj_obbox_vs[1].x),
4823             max(abs_obj_obbox_vs[2].x,abs_obj_obbox_vs[3].x));
4824       new_lty = min(min(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
4825             min(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
4826       new_rby = max(max(abs_obj_obbox_vs[0].y,abs_obj_obbox_vs[1].y),
4827             max(abs_obj_obbox_vs[2].y,abs_obj_obbox_vs[3].y));
4828 
4829       ObjPtr->obbox.ltx = new_ltx; ObjPtr->obbox.lty = new_lty;
4830       ObjPtr->obbox.rbx = new_rbx; ObjPtr->obbox.rby = new_rby;
4831       if (RealLtX != NULL && RealLtY != NULL) {
4832          int dx=(*RealLtX)-new_ltx, dy=(*RealLtY)-new_lty;
4833 
4834          ObjPtr->x += dx; ObjPtr->y += dy;
4835          ObjPtr->bbox.ltx += dx; ObjPtr->bbox.lty += dy;
4836          ObjPtr->bbox.rbx += dx; ObjPtr->bbox.rby += dy;
4837          ObjPtr->obbox.ltx += dx; ObjPtr->obbox.lty += dy;
4838          ObjPtr->obbox.rbx += dx; ObjPtr->obbox.rby += dy;
4839          MoveRotatedObjCache(ObjPtr, dx, dy);
4840       }
4841       if (ObjPtr->ctm->m[CTM_SX] >= 999.0 &&
4842             ObjPtr->ctm->m[CTM_SX] <= 1001.0 &&
4843             ObjPtr->ctm->m[CTM_SY] >= 999.0 &&
4844             ObjPtr->ctm->m[CTM_SY] <= 1001.0 &&
4845             ObjPtr->ctm->m[CTM_SIN] >= (-1.0) &&
4846             ObjPtr->ctm->m[CTM_SIN] <= 1.0 &&
4847             ObjPtr->ctm->m[CTM_MSIN] >= (-1.0) &&
4848             ObjPtr->ctm->m[CTM_MSIN] <= 1.0) {
4849          int dx=ObjPtr->ctm->t[CTM_TX], dy=ObjPtr->ctm->t[CTM_TY];
4850          struct AttrRec *saved_fattr=NULL, *saved_lattr=NULL;
4851 
4852          free(ObjPtr->ctm);
4853          ObjPtr->ctm = NULL;
4854 
4855          memcpy(&ObjPtr->obbox, &ObjPtr->orig_obbox, sizeof(struct BBRec));
4856          if (ObjPtr->type == OBJ_TEXT) {
4857             memcpy(&ObjPtr->bbox, &ObjPtr->detail.t->orig_bbox,
4858                   sizeof(struct BBRec));
4859          }
4860          saved_fattr = ObjPtr->fattr;
4861          saved_lattr = ObjPtr->lattr;
4862          ObjPtr->fattr = ObjPtr->lattr = NULL;
4863          MoveObj(ObjPtr, dx, dy);
4864          ObjPtr->fattr = saved_fattr;
4865          ObjPtr->lattr = saved_lattr;
4866       }
4867       break;
4868    }
4869    for (attr_ptr=ObjPtr->fattr; attr_ptr != NULL; attr_ptr=attr_ptr->next) {
4870       RotateObj(attr_ptr->obj, Corner, AngleDelta, RealLtX, RealLtY);
4871    }
4872    AdjObjCache(ObjPtr);
4873    AdjObjBBox(ObjPtr);
4874 }
4875 
RotateObjForLayout(ObjPtr,AngleInRadian,Corner)4876 void RotateObjForLayout(ObjPtr, AngleInRadian, Corner)
4877    struct ObjRec *ObjPtr;
4878    double AngleInRadian;
4879    int Corner;
4880 {
4881    double angle=AngleInRadian*64.0*180.0/M_PI;
4882 
4883    SetPivot(Corner, &ObjPtr->obbox);
4884    RotateObj(ObjPtr, Corner, round(angle), NULL, NULL);
4885 }
4886 
RotateObjClockWise(ObjPtr)4887 void RotateObjClockWise(ObjPtr)
4888    struct ObjRec *ObjPtr;
4889 {
4890    double angle_in_radian=((double)(rotationIncrement))*M_PI/180.0/64.0;
4891    double sin_val=sin(angle_in_radian);
4892    double cos_val=cos(angle_in_radian);
4893    int orig_x=((ObjPtr->obbox.ltx+ObjPtr->obbox.rbx)>>1);
4894    int orig_y=ObjPtr->obbox.lty;
4895    int x=0, y=0, dx=orig_x-rotatePivotX, dy=orig_y-rotatePivotY;
4896 
4897    if (dx != 0 || dy != 0) {
4898       x = (int)round(dx*cos_val - dy*sin_val);
4899       y = (int)round(dx*sin_val + dy*cos_val);
4900    }
4901    x += rotatePivotX;
4902    y += rotatePivotY;
4903    /* RotateObjForLayout() rotates about center-top */
4904    RotateObjForLayout(ObjPtr, angle_in_radian, CORNER_BOTTOM);
4905    MoveObj(ObjPtr, x-orig_x, y-orig_y);
4906    SetFileModified(TRUE);
4907 }
4908 
RotateIconClockWise(ObjPtr)4909 void RotateIconClockWise(ObjPtr)
4910    struct ObjRec *ObjPtr;
4911 {
4912    int ltx, lty, rbx, rby;
4913    struct ObjRec *obj_ptr;
4914    struct AttrRec *attr_ptr;
4915 
4916    SetRotatePivot();
4917    RotatePtClockWise(ObjPtr->obbox.ltx, ObjPtr->obbox.rby, &ltx, &lty);
4918    RotatePtClockWise(ObjPtr->obbox.rbx, ObjPtr->obbox.lty, &rbx, &rby);
4919    ObjPtr->obbox.ltx = ObjPtr->x = ltx;
4920    ObjPtr->obbox.lty = ObjPtr->y = lty;
4921    ObjPtr->obbox.rbx = rbx;
4922    ObjPtr->obbox.rby = rby;
4923 
4924    obj_ptr = ObjPtr->detail.r->first;
4925    for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4926       RotateObjClockWise(obj_ptr);
4927    }
4928    attr_ptr = ObjPtr->fattr;
4929    for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4930       RotateObjClockWise(attr_ptr->obj);
4931    }
4932    AdjObjBBox(ObjPtr);
4933 }
4934 
4935 static
RotatePtCounter(X,Y,NewX,NewY)4936 void RotatePtCounter(X, Y, NewX, NewY)
4937    int X, Y, *NewX, *NewY;
4938 {
4939    *NewX = rotatePivotX - rotatePivotY + Y;
4940    *NewY = rotatePivotY + rotatePivotX - X;
4941 }
4942 
RotateObjCounter(ObjPtr)4943 void RotateObjCounter(ObjPtr)
4944    struct ObjRec *ObjPtr;
4945 {
4946    double angle_in_radian=((double)(-rotationIncrement))*M_PI/180.0/64.0;
4947    double sin_val=sin(angle_in_radian);
4948    double cos_val=cos(angle_in_radian);
4949    int orig_x=((ObjPtr->obbox.ltx+ObjPtr->obbox.rbx)>>1);
4950    int orig_y=ObjPtr->obbox.lty;
4951    int x=0, y=0, dx=orig_x-rotatePivotX, dy=orig_y-rotatePivotY;
4952 
4953    if (dx != 0 || dy != 0) {
4954       x = (int)round(dx*cos_val - dy*sin_val);
4955       y = (int)round(dx*sin_val + dy*cos_val);
4956    }
4957    x += rotatePivotX;
4958    y += rotatePivotY;
4959    /* RotateObjForLayout() rotates about center-top */
4960    RotateObjForLayout(ObjPtr, angle_in_radian, CORNER_BOTTOM);
4961    MoveObj(ObjPtr, x-orig_x, y-orig_y);
4962    SetFileModified(TRUE);
4963 }
4964 
RotateIconCounter(ObjPtr)4965 void RotateIconCounter(ObjPtr)
4966    struct ObjRec *ObjPtr;
4967 {
4968    int ltx, lty, rbx, rby;
4969    struct ObjRec *obj_ptr;
4970    struct AttrRec *attr_ptr;
4971 
4972    SetRotatePivot();
4973 /* ObjPtr->detail.r->rotate = (ObjPtr->detail.r->rotate+4-1) % 4; */
4974    RotatePtCounter(ObjPtr->obbox.rbx, ObjPtr->obbox.lty, &ltx, &lty);
4975    RotatePtCounter(ObjPtr->obbox.ltx, ObjPtr->obbox.rby, &rbx, &rby);
4976    ObjPtr->obbox.ltx = ObjPtr->x = ltx;
4977    ObjPtr->obbox.lty = ObjPtr->y = lty;
4978    ObjPtr->obbox.rbx = rbx;
4979    ObjPtr->obbox.rby = rby;
4980 
4981    obj_ptr = ObjPtr->detail.r->first;
4982    for ( ; obj_ptr != NULL; obj_ptr = obj_ptr->next) {
4983       RotateObjCounter(obj_ptr);
4984    }
4985    attr_ptr = ObjPtr->fattr;
4986    for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
4987       RotateObjCounter(attr_ptr->obj);
4988    }
4989    AdjObjBBox(ObjPtr);
4990 }
4991 
4992 static
FlipAllSelHorizontal(saved_ltx,saved_lty,saved_rbx,saved_rby)4993 void FlipAllSelHorizontal(saved_ltx, saved_lty, saved_rbx, saved_rby)
4994    int saved_ltx, saved_lty, saved_rbx, saved_rby;
4995 {
4996    struct SelRec *sel_ptr;
4997 
4998    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
4999    JustRemoveAllVSel();
5000    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
5001       if (!sel_ptr->obj->locked) {
5002          FlipObjHorizontal(sel_ptr->obj);
5003       }
5004    }
5005    UpdSelBBox();
5006    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
5007    RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1), saved_lty-GRID_ABS_SIZE(1),
5008          saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5009          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5010          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5011 }
5012 
FlipHorizontal()5013 void FlipHorizontal()
5014 {
5015    int saved_ltx, saved_lty, saved_rbx, saved_rby, num_to_resize=0;
5016    struct BBRec sel_obbox;
5017    char **ppsz_names_to_resize=NULL;
5018 
5019    if (topSel == NULL) {
5020       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5021       return;
5022    }
5023    if (numObjSelected == numObjLocked) {
5024       Msg(TgLoadString(STID_LOCKED_OBJS_CANT_BE_FLIPPED));
5025       return;
5026    }
5027    sel_obbox.ltx = selNoLockObjLtX; sel_obbox.lty = selNoLockObjLtY;
5028    sel_obbox.rbx = selNoLockObjRbX; sel_obbox.rby = selNoLockObjRbY;
5029    SetPivot(CORNER_LEFT, &sel_obbox);
5030 
5031    saved_ltx = selLtX; saved_lty = selLtY;
5032    saved_rbx = selRbX; saved_rby = selRbY;
5033    HighLightReverse();
5034    ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
5035    if (ppsz_names_to_resize == NULL) {
5036       FlipAllSelHorizontal(saved_ltx, saved_lty, saved_rbx, saved_rby);
5037    } else {
5038       StartCompositeCmd();
5039       FlipAllSelHorizontal(saved_ltx, saved_lty, saved_rbx, saved_rby);
5040       DoOnResize(ppsz_names_to_resize, num_to_resize);
5041       EndCompositeCmd();
5042    }
5043    HighLightForward();
5044    justDupped = FALSE;
5045    if (numObjLocked != 0) {
5046       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_FLIPPED));
5047    } else {
5048       Msg(TgLoadString(STID_FLIPPED_HORIZONTALLY));
5049    }
5050 }
5051 
5052 static
FlipAllSelVertical(saved_ltx,saved_lty,saved_rbx,saved_rby)5053 void FlipAllSelVertical(saved_ltx, saved_lty, saved_rbx, saved_rby)
5054    int saved_ltx, saved_lty, saved_rbx, saved_rby;
5055 {
5056    struct SelRec *sel_ptr;
5057 
5058    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
5059    JustRemoveAllVSel();
5060    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
5061       if (!sel_ptr->obj->locked) {
5062          FlipObjVertical(sel_ptr->obj);
5063       }
5064    }
5065    UpdSelBBox();
5066    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
5067    RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1), saved_lty-GRID_ABS_SIZE(1),
5068          saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5069          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5070          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5071 }
5072 
FlipVertical()5073 void FlipVertical()
5074 {
5075    int saved_ltx, saved_lty, saved_rbx, saved_rby, num_to_resize=0;
5076    struct BBRec sel_obbox;
5077    char **ppsz_names_to_resize=NULL;
5078 
5079    if (topSel == NULL) {
5080       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5081       return;
5082    }
5083    if (numObjSelected == numObjLocked) {
5084       Msg(TgLoadString(STID_LOCKED_OBJS_CANT_BE_FLIPPED));
5085       return;
5086    }
5087    sel_obbox.ltx = selNoLockObjLtX; sel_obbox.lty = selNoLockObjLtY;
5088    sel_obbox.rbx = selNoLockObjRbX; sel_obbox.rby = selNoLockObjRbY;
5089    SetPivot(CORNER_TOP, &sel_obbox);
5090 
5091    saved_ltx = selLtX; saved_lty = selLtY;
5092    saved_rbx = selRbX; saved_rby = selRbY;
5093    HighLightReverse();
5094    ppsz_names_to_resize = NeedToProcessOnResize(&num_to_resize);
5095    if (ppsz_names_to_resize == NULL) {
5096       FlipAllSelVertical(saved_ltx, saved_lty, saved_rbx, saved_rby);
5097    } else {
5098       StartCompositeCmd();
5099       FlipAllSelVertical(saved_ltx, saved_lty, saved_rbx, saved_rby);
5100       DoOnResize(ppsz_names_to_resize, num_to_resize);
5101       EndCompositeCmd();
5102    }
5103    HighLightForward();
5104    justDupped = FALSE;
5105    if (numObjLocked != 0) {
5106       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_FLIPPED));
5107    } else {
5108       Msg(TgLoadString(STID_FLIPPED_VERTICALLY));
5109    }
5110 }
5111 
5112 /* --------------------- Rotate --------------------- */
5113 
RotateClockWise()5114 void RotateClockWise()
5115 {
5116    register struct SelRec *sel_ptr;
5117    int saved_ltx, saved_lty, saved_rbx, saved_rby;
5118    int text_obj_created, text_cursor_shown;
5119 
5120    if (topSel == NULL) {
5121       text_cursor_shown = textCursorShown;
5122       text_obj_created = TieLooseEnds();
5123       textRotation += rotationIncrement;
5124       while (textRotation < 0) textRotation += (360<<6);
5125       while (textRotation >= (360<<6)) textRotation -= (360<<6);
5126       ShowRotate();
5127       if (!text_obj_created && curChoice == DRAWTEXT && text_cursor_shown) {
5128          NewCurText();
5129          RedrawCurText();
5130       } else {
5131          textCursorShown = FALSE;
5132       }
5133       return;
5134    }
5135    if (numObjSelected == numObjLocked) {
5136       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_ROTATED), TOOL_NAME,
5137             INFO_MB);
5138       return;
5139    }
5140    saved_ltx = selLtX; saved_lty = selLtY;
5141    saved_rbx = selRbX; saved_rby = selRbY;
5142    HighLightReverse();
5143    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
5144    JustRemoveAllVSel();
5145    SetRotatePivot();
5146    for (sel_ptr=topSel; sel_ptr != NULL; sel_ptr=sel_ptr->next) {
5147       if (OkToTransform(sel_ptr->obj, STID_DISABLE_ON_RESIZE_ROTATE)) {
5148          RotateObjClockWise(sel_ptr->obj);
5149       }
5150    }
5151    UpdSelBBox();
5152    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
5153    RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1), saved_lty-GRID_ABS_SIZE(1),
5154          saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5155          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5156          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5157    HighLightForward();
5158    justDupped = FALSE;
5159    if (numObjLocked != 0) {
5160       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_ROTATED));
5161    } else {
5162       Msg(TgLoadString(STID_ROTATED_CLOCKWISE));
5163    }
5164 }
5165 
RotateCounter()5166 void RotateCounter()
5167 {
5168    register struct SelRec *sel_ptr;
5169    int saved_ltx, saved_lty, saved_rbx, saved_rby;
5170    int text_obj_created, text_cursor_shown;
5171 
5172    if (topSel == NULL) {
5173       text_cursor_shown = textCursorShown;
5174       text_obj_created = TieLooseEnds();
5175       textRotation -= rotationIncrement;
5176       while (textRotation < 0) textRotation += (360<<6);
5177       while (textRotation >= (360<<6)) textRotation -= (360<<6);
5178       ShowRotate();
5179       if (!text_obj_created && curChoice == DRAWTEXT && text_cursor_shown) {
5180          NewCurText();
5181          RedrawCurText();
5182       } else {
5183          textCursorShown = FALSE;
5184       }
5185       return;
5186    }
5187    if (numObjSelected == numObjLocked) {
5188       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_ROTATED), TOOL_NAME,
5189             INFO_MB);
5190       return;
5191    }
5192    saved_ltx = selLtX; saved_lty = selLtY;
5193    saved_rbx = selRbX; saved_rby = selRbY;
5194    HighLightReverse();
5195    PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
5196    JustRemoveAllVSel();
5197    SetRotatePivot();
5198    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
5199       if (OkToTransform(sel_ptr->obj, STID_DISABLE_ON_RESIZE_ROTATE)) {
5200          RotateObjCounter(sel_ptr->obj);
5201       }
5202    }
5203    UpdSelBBox();
5204    RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
5205    RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1), saved_lty-GRID_ABS_SIZE(1),
5206          saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5207          selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5208          selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5209    HighLightForward();
5210    justDupped = FALSE;
5211    if (numObjLocked != 0) {
5212       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_ROTATED));
5213    } else {
5214       Msg(TgLoadString(STID_ROTATED_COUNTER_CLOCKWISE));
5215    }
5216 }
5217 
SetTextRotation(pszBuf)5218 void SetTextRotation(pszBuf)
5219    char *pszBuf;
5220 {
5221    char spec[80], buf[80];
5222    float fval;
5223    int ival;
5224 
5225    *spec = '\0';
5226    if (pszBuf != NULL) {
5227       strcpy(spec, pszBuf);
5228    } else {
5229       FormatAngle(textRotation, buf);
5230       sprintf(gszMsgBox, TgLoadString(STID_ENTER_TEXT_ROT_IN_DEGREE_CUR), buf);
5231       if (Dialog(gszMsgBox, NULL, spec) == INVALID) return;
5232    }
5233    UtilTrimBlanks(spec);
5234    if (*spec == '\0') return;
5235 
5236    if (sscanf(spec, "%f", &fval) != 1) {
5237       sprintf(gszMsgBox, TgLoadString(STID_CANT_PARSE_ENTER_ONE_NUM_VAL),
5238             spec);
5239       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5240       return;
5241    }
5242    fval *= (float)64.0;
5243    ival = round(fval);
5244    if (ival < 0 || ival >= (360<<6)) {
5245       sprintf(gszMsgBox, TgLoadString(STID_INVALID_VAL_ENTERED_RNG_INC),
5246             spec, 0, 360);
5247       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5248       return;
5249    }
5250    textRotation = ival;
5251    ShowRotate();
5252    FormatAngle(textRotation, buf);
5253    sprintf(gszMsgBox, TgLoadString(STID_TEXT_ROTATION_SET_TO_GIVEN), buf);
5254    Msg(gszMsgBox);
5255 }
5256 
SetRotationIncrement(pszBuf)5257 void SetRotationIncrement(pszBuf)
5258    char *pszBuf;
5259 {
5260    char spec[80], buf[80];
5261    float fval;
5262    int ival;
5263 
5264    *spec = '\0';
5265    if (pszBuf != NULL) {
5266       strcpy(spec, pszBuf);
5267    } else {
5268       FormatAngle(rotationIncrement, buf);
5269       sprintf(gszMsgBox, TgLoadString(STID_ENTER_ROT_INC_IN_DEGREE_CUR), buf);
5270       if (Dialog(gszMsgBox, NULL, spec) == INVALID) return;
5271    }
5272    UtilTrimBlanks(spec);
5273    if (*spec == '\0') return;
5274 
5275    if (sscanf(spec, "%f", &fval) != 1) {
5276       sprintf(gszMsgBox, TgLoadString(STID_CANT_PARSE_ENTER_ONE_NUM_VAL),
5277             spec);
5278       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5279       return;
5280    }
5281    fval *= (float)64.0;
5282    ival = round(fval);
5283    if (ival <= 0 || ival >= (360<<6)) {
5284       sprintf(gszMsgBox, TgLoadString(STID_INVALID_VAL_ENTERED_RNG_EXC),
5285             spec, 0, 360);
5286       MsgBox(gszMsgBox, TOOL_NAME, INFO_MB);
5287       return;
5288    }
5289    rotationIncrement = ival;
5290    FormatAngle(rotationIncrement, buf);
5291    sprintf(gszMsgBox, TgLoadString(STID_TEXT_ROT_INC_SET_TO_GIVEN), buf);
5292    Msg(gszMsgBox);
5293 }
5294 
5295 static
IdentCTM(ctm1,ctm2)5296 int IdentCTM(ctm1, ctm2)
5297    struct XfrmMtrxRec *ctm1, *ctm2;
5298 {
5299    return ((fabs(ctm1->m[CTM_SX]-ctm2->m[CTM_SX]) < EQ_TOL) &&
5300          (fabs(ctm1->m[CTM_SY]-ctm2->m[CTM_SY]) < EQ_TOL) &&
5301          (fabs(ctm1->m[CTM_SIN]-ctm2->m[CTM_SIN]) < EQ_TOL) &&
5302          (fabs(ctm1->m[CTM_MSIN]-ctm2->m[CTM_MSIN]) < EQ_TOL));
5303 }
5304 
5305 static
SetObjCTM(ObjPtr,nTransformed,ctm)5306 int SetObjCTM(ObjPtr, nTransformed, ctm)
5307    struct ObjRec *ObjPtr;
5308    int nTransformed;
5309    struct XfrmMtrxRec *ctm;
5310 {
5311    int nReturn=FALSE, cx=0, cy=0;
5312 
5313    switch (ObjPtr->type) {
5314    case OBJ_GROUP:
5315    case OBJ_ICON:
5316    case OBJ_SYM:
5317    case OBJ_PIN:
5318       break;
5319 
5320    default:
5321       cx = ((ObjPtr->obbox.ltx+ObjPtr->obbox.rbx)>>1);
5322       cy = ((ObjPtr->obbox.lty+ObjPtr->obbox.rby)>>1);
5323       if (nTransformed) {
5324          struct XfrmMtrxRec *saved_ctm=NULL;
5325 
5326          if (ObjPtr->ctm == NULL) {
5327             SetCTM(ObjPtr, ctm);
5328             nReturn = TRUE;
5329          } else {
5330             if (!IdentCTM(ObjPtr->ctm, ctm)) {
5331                if (ObjPtr->type == OBJ_TEXT) {
5332                   saved_ctm = ObjPtr->ctm;
5333                   ObjPtr->ctm = NULL;
5334                   if (!UpdTextBBox(ObjPtr)) {
5335                      /* read-only text */
5336                      ObjPtr->ctm = saved_ctm;
5337                   } else {
5338                      free(saved_ctm);
5339                      SetCTM(ObjPtr, ctm);
5340                      UpdTextBBox(ObjPtr);
5341                      nReturn = TRUE;
5342                   }
5343                } else {
5344                   free(ObjPtr->ctm);
5345                   ObjPtr->ctm = NULL;
5346                   SetCTM(ObjPtr, ctm);
5347                   nReturn = TRUE;
5348                }
5349             }
5350          }
5351       } else {
5352          if (ObjPtr->ctm != NULL) {
5353             free(ObjPtr->ctm);
5354             ObjPtr->ctm = NULL;
5355             nReturn = TRUE;
5356          }
5357       }
5358       if (nReturn) {
5359          int new_cx=0, new_cy=0;
5360 
5361          new_cx = ((ObjPtr->obbox.ltx+ObjPtr->obbox.rbx)>>1);
5362          new_cy = ((ObjPtr->obbox.lty+ObjPtr->obbox.rby)>>1);
5363          MoveObj(ObjPtr, cx-new_cx, cy-new_cy);
5364          AdjObjCache(ObjPtr);
5365          AdjObjSplineVs(ObjPtr);
5366          AdjObjBBox(ObjPtr);
5367       }
5368       break;
5369    }
5370    return nReturn;
5371 }
5372 
SetSelCTM(nTransformed,ctm)5373 void SetSelCTM(nTransformed, ctm)
5374    int nTransformed;
5375    struct XfrmMtrxRec *ctm;
5376 {
5377    struct SelRec *sel_ptr=NULL;
5378    int ltx=selLtX, lty=selLtY, rbx=selRbX, rby=selRbY, changed=FALSE;
5379 
5380    if (topSel == NULL) {
5381       MsgBox(TgLoadCachedString(CSTID_NO_OBJ_SELECTED), TOOL_NAME, INFO_MB);
5382       return;
5383    }
5384    if (numObjSelected == numObjLocked) {
5385       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_XFORMED), TOOL_NAME,
5386             INFO_MB);
5387       return;
5388    }
5389    HighLightReverse();
5390    StartCompositeCmd();
5391    for (sel_ptr=botSel; sel_ptr != NULL; sel_ptr=sel_ptr->prev) {
5392       if (OkToTransform(sel_ptr->obj, STID_DISABLE_ON_RESIZE_TRANSFORM)) {
5393          PrepareToReplaceAnObj(sel_ptr->obj);
5394          if (SetObjCTM(sel_ptr->obj, nTransformed, ctm)) {
5395             changed = TRUE;
5396             RecordReplaceAnObj(sel_ptr->obj);
5397          } else {
5398             AbortPrepareCmd(CMD_REPLACE);
5399          }
5400       }
5401    }
5402    EndCompositeCmd();
5403    if (changed) {
5404       SetFileModified(TRUE);
5405       UpdSelBBox();
5406       RedrawAreas(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
5407             rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1),
5408             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5409             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5410       justDupped = FALSE;
5411       if (numObjLocked != 0) {
5412          Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_XFORMED));
5413       }
5414    }
5415    HighLightForward();
5416 }
5417 
5418 /* --------------------- Rotate Any Angle --------------------- */
5419 
5420 static
RotateBBoxByAnAngle(bbox,d_angle,vs)5421 void RotateBBoxByAnAngle(bbox, d_angle, vs)
5422    struct BBRec *bbox; /* the original bounding box */
5423    int d_angle; /* d_angle is degree*64 */
5424    XPoint *vs; /* array of 5 points */
5425 {
5426    int x, y;
5427 
5428    RotatedXY(bbox->ltx, bbox->lty, d_angle, &x, &y);
5429    vs[0].x = vs[4].x = x; vs[0].y = vs[4].y = y;
5430    RotatedXY(bbox->rbx, bbox->lty, d_angle, &x, &y);
5431    vs[1].x = x; vs[1].y = y;
5432    RotatedXY(bbox->rbx, bbox->rby, d_angle, &x, &y);
5433    vs[2].x = x; vs[2].y = y;
5434    RotatedXY(bbox->ltx, bbox->rby, d_angle, &x, &y);
5435    vs[3].x = x; vs[3].y = y;
5436 }
5437 
5438 static
RotateVsByAnAngle(InVs,NumPts,d_angle,OutVs)5439 void RotateVsByAnAngle(InVs, NumPts, d_angle, OutVs)
5440    XPoint *InVs, *OutVs;
5441    int NumPts, d_angle;
5442 {
5443    register int i;
5444 
5445    for (i=0; i < NumPts; i++) {
5446       int x, y;
5447 
5448       RotatedXY(InVs[i].x, InVs[i].y, d_angle, &x, &y);
5449       OutVs[i].x = x;
5450       OutVs[i].y = y;
5451    }
5452 }
5453 
5454 static
RotateAllSelObjects(Corner,AngleDelta)5455 void RotateAllSelObjects(Corner, AngleDelta)
5456    int Corner, AngleDelta; /* AngleDelta is degree*64 */
5457 {
5458    register struct SelRec *sel_ptr;
5459 
5460    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
5461       if (OkToTransform(sel_ptr->obj, STID_DISABLE_ON_RESIZE_ROTATE)) {
5462          RotateObj(sel_ptr->obj, Corner, AngleDelta, NULL, NULL);
5463       }
5464    }
5465    if (numObjLocked != 0) {
5466       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_ROTATED));
5467    }
5468 }
5469 
5470 static
ConstrainedRotateAllSel(Corner,AngleDelta,ltx,lty,rbx,rby)5471 int ConstrainedRotateAllSel(Corner, AngleDelta, ltx, lty, rbx, rby)
5472    int Corner, AngleDelta, *ltx, *lty, *rbx, *rby;
5473 {
5474    register struct ObjRec *obj_ptr;
5475    int something_stretched=FALSE, num_pts;
5476    int x_off, y_off, move_first, move_last, x, y;
5477    IntPoint *v;
5478 
5479    for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
5480       if (!obj_ptr->marked && obj_ptr->type==OBJ_POLY && !obj_ptr->locked) {
5481          num_pts = obj_ptr->detail.p->n;
5482          v = obj_ptr->detail.p->vlist;
5483 
5484          if (obj_ptr->ctm == NULL) {
5485             x_off = OFFSET_X(v[0].x); y_off = OFFSET_Y(v[0].y);
5486             move_first = EndPtInSelected(x_off, y_off);
5487             x_off = OFFSET_X(v[num_pts-1].x); y_off = OFFSET_Y(v[num_pts-1].y);
5488             move_last = EndPtInSelected(x_off, y_off);
5489          } else {
5490             int tmp_x, tmp_y;
5491 
5492             TransformPointThroughCTM(v[0].x-obj_ptr->x, v[0].y-obj_ptr->y,
5493                   obj_ptr->ctm, &tmp_x, &tmp_y);
5494             tmp_x += obj_ptr->x;
5495             tmp_y += obj_ptr->y;
5496             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
5497             move_first = EndPtInSelected(x_off, y_off);
5498             TransformPointThroughCTM(v[num_pts-1].x-obj_ptr->x,
5499                   v[num_pts-1].y-obj_ptr->y, obj_ptr->ctm, &tmp_x, &tmp_y);
5500             tmp_x += obj_ptr->x;
5501             tmp_y += obj_ptr->y;
5502             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
5503             move_last = EndPtInSelected(x_off, y_off);
5504          }
5505          if (move_first || move_last) {
5506             int index=INVALID, seg_dx, seg_dy, dx, dy, cur_seg_dx, cur_seg_dy;
5507 
5508             PrepareToReplaceAnObj(obj_ptr);
5509 
5510             if (obj_ptr->ctm != NULL) {
5511                /* Remove the transformations! */
5512                int i;
5513 
5514                for (i=0; i < num_pts; i++) {
5515                   int tmp_x, tmp_y;
5516 
5517                   TransformPointThroughCTM(v[i].x-obj_ptr->x, v[i].y-obj_ptr->y,
5518                         obj_ptr->ctm, &tmp_x, &tmp_y);
5519                   v[i].x = tmp_x+obj_ptr->x;
5520                   v[i].y = tmp_y+obj_ptr->y;
5521                }
5522                free(obj_ptr->ctm);
5523                obj_ptr->ctm = NULL;
5524                UpdPolyBBox(obj_ptr, num_pts, v);
5525             }
5526             if (something_stretched) {
5527                if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
5528                if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
5529                if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
5530                if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
5531             } else {
5532                *ltx = obj_ptr->bbox.ltx; *lty = obj_ptr->bbox.lty;
5533                *rbx = obj_ptr->bbox.rbx; *rby = obj_ptr->bbox.rby;
5534             }
5535             something_stretched = TRUE;
5536             if (move_first && move_last && num_pts==3) {
5537                RotatedAbsXY(v[0].x, v[0].y, AngleDelta, &x, &y);
5538                dx = x-v[0].x; dy = y-v[0].y;
5539                index = 1;
5540                cur_seg_dx = v[index-1].x - v[index].x;
5541                cur_seg_dy = v[index-1].y - v[index].y;
5542                seg_dx = v[index].x - v[index+1].x;
5543                seg_dy = v[index].y - v[index+1].y;
5544 
5545                if (cur_seg_dy==0 && seg_dx==0 &&
5546                      (seg_dy!=0 || (seg_dy==0 && dx==0))) {
5547                   v[index].y += dy;
5548                } else if (cur_seg_dx==0 && seg_dy==0 &&
5549                      (seg_dx!=0 || (seg_dx==0 && dy==0))) {
5550                   v[index].x += dx;
5551                }
5552             } else {
5553                if (move_first && num_pts>2) {
5554                   RotatedAbsXY(v[0].x, v[0].y, AngleDelta, &x, &y);
5555                   dx = x-v[0].x; dy = y-v[0].y;
5556                   index = 1;
5557                   cur_seg_dx = v[index-1].x - v[index].x;
5558                   cur_seg_dy = v[index-1].y - v[index].y;
5559                   seg_dx = v[index].x - v[index+1].x;
5560                   seg_dy = v[index].y - v[index+1].y;
5561 
5562                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
5563                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
5564                      v[index].y += dy;
5565                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
5566                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
5567                      v[index].x += dx;
5568                   }
5569                }
5570                if (move_last && num_pts>2) {
5571                   RotatedAbsXY(v[num_pts-1].x, v[num_pts-1].y, AngleDelta,
5572                         &x, &y);
5573                   dx = x-v[num_pts-1].x; dy = y-v[num_pts-1].y;
5574                   index = num_pts-2;
5575                   cur_seg_dx = v[index+1].x - v[index].x;
5576                   cur_seg_dy = v[index+1].y - v[index].y;
5577                   seg_dx = v[index].x - v[index-1].x;
5578                   seg_dy = v[index].y - v[index-1].y;
5579 
5580                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
5581                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
5582                      v[index].y += dy;
5583                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
5584                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
5585                      v[index].x += dx;
5586                   }
5587                }
5588             }
5589             if (move_first) {
5590                RotatedAbsXY(v[0].x, v[0].y, AngleDelta, &x, &y);
5591                v[0].x = x; v[0].y = y;
5592             }
5593             if (move_last) {
5594                RotatedAbsXY(v[num_pts-1].x, v[num_pts-1].y, AngleDelta,
5595                      &x, &y);
5596                v[num_pts-1].x = x; v[num_pts-1].y = y;
5597             }
5598             AdjObjSplineVs(obj_ptr);
5599             switch (obj_ptr->type) {
5600             case OBJ_POLY:
5601                if (obj_ptr->detail.p->curved != LT_INTSPLINE) {
5602                   UpdPolyBBox(obj_ptr, num_pts, v);
5603                } else {
5604                   UpdPolyBBox(obj_ptr, obj_ptr->detail.p->intn,
5605                         obj_ptr->detail.p->intvlist);
5606                }
5607                break;
5608             case OBJ_POLYGON:
5609                if (obj_ptr->detail.g->curved != LT_INTSPLINE) {
5610                   UpdPolyBBox(obj_ptr, num_pts, v);
5611                } else {
5612                   UpdPolyBBox(obj_ptr, obj_ptr->detail.g->intn,
5613                         obj_ptr->detail.g->intvlist);
5614                }
5615                break;
5616             }
5617             AdjObjBBox(obj_ptr);
5618             if (AutoCenterAttr(obj_ptr)) {
5619                struct AttrRec *attr_ptr=obj_ptr->fattr;
5620                int modified=FALSE;
5621 
5622                for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
5623                   if (attr_ptr->shown) {
5624                      struct BBRec bbox;
5625 
5626                      CenterObjInOBBox(attr_ptr->obj, obj_ptr->obbox, &bbox);
5627                      if (bbox.ltx < *ltx) *ltx = bbox.ltx;
5628                      if (bbox.lty < *lty) *lty = bbox.lty;
5629                      if (bbox.rbx > *rbx) *rbx = bbox.rbx;
5630                      if (bbox.rby > *rby) *rby = bbox.rby;
5631                      modified = TRUE;
5632                   }
5633                }
5634                if (modified) AdjObjBBox(obj_ptr);
5635             }
5636             if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
5637             if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
5638             if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
5639             if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
5640             RecordReplaceAnObj(obj_ptr);
5641          }
5642       }
5643    }
5644    return something_stretched;
5645 }
5646 
5647 static
RotateAllSel(Corner,AngleDelta)5648 void RotateAllSel(Corner, AngleDelta)
5649    int Corner, AngleDelta; /* AngleDelta is degree*64 */
5650 {
5651    int ltx=0, lty=0, rbx=0, rby=0, saved_ltx, saved_lty, saved_rbx, saved_rby;
5652    int poly_stretched;
5653 
5654    saved_ltx = selLtX; saved_lty = selLtY;
5655    saved_rbx = selRbX; saved_rby = selRbY;
5656 
5657    if (moveMode==CONST_MOVE) {
5658       MarkObjectsForStretch();
5659 
5660       StartCompositeCmd();
5661       PrepareToRecord(CMD_STRETCH, topSel, botSel, numObjSelected);
5662       RecordCmd(CMD_STRETCH, NULL, topSel, botSel, numObjSelected);
5663 
5664       poly_stretched = ConstrainedRotateAllSel(Corner, AngleDelta,
5665             &ltx, &lty, &rbx, &rby);
5666       RotateAllSelObjects(Corner, AngleDelta);
5667       UpdSelBBox();
5668       if (poly_stretched) {
5669          ltx = min(ltx,min(selLtX,saved_ltx));
5670          lty = min(lty,min(selLtY,saved_lty));
5671          rbx = max(rbx,max(selRbX,saved_rbx));
5672          rby = max(rby,max(selRbY,saved_rby));
5673          RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
5674                rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
5675       } else {
5676          RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
5677                saved_lty-GRID_ABS_SIZE(1),
5678                saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5679                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5680                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5681       }
5682       EndCompositeCmd();
5683    } else {
5684       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
5685       RotateAllSelObjects(Corner, AngleDelta);
5686       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
5687       UpdSelBBox();
5688       RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
5689             saved_lty-GRID_ABS_SIZE(1),
5690             saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
5691             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
5692             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
5693    }
5694 }
5695 
5696 static
FormatAngleForRotate(buf,angle)5697 void FormatAngleForRotate(buf, angle)
5698    char *buf;
5699    int angle;
5700 {
5701    float fval=(float)(((float)angle)/((float)64.0));
5702 
5703    sprintf(buf, "degree=%.2f", fval);
5704 }
5705 
5706 static
RotateSel(XGridOff,YGridOff,ObjPtr,Corner)5707 void RotateSel(XGridOff, YGridOff, ObjPtr, Corner)
5708    int XGridOff, YGridOff, Corner;
5709    struct ObjRec *ObjPtr;
5710 {
5711    register int i;
5712    XEvent ev;
5713    XPoint all_bbox_vs[5], obj_obbox_vs[5], *vs=NULL, *orig_vs=NULL;
5714    int grid_x=XGridOff, grid_y=YGridOff, dx, dy, d_angle=0;
5715    int saved_x=XGridOff, saved_y=YGridOff;
5716    int rotating=TRUE, deg360=(360<<6), n=0;
5717    char buf[80];
5718    struct BBRec orig_all_bbox, orig_obj_obbox;
5719 
5720    if (numObjSelected == numObjLocked || ObjPtr->locked) {
5721       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_ROTATED), TOOL_NAME,
5722             INFO_MB);
5723       return;
5724    }
5725    XFlush(mainDisplay);
5726    XSync(mainDisplay, False);
5727    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
5728          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
5729       ExposeEventHandler(&ev, TRUE);
5730    }
5731    if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON) {
5732       IntPoint *pv;
5733       int px, py;
5734 
5735       if (ObjPtr->ctm == NULL) {
5736          pv = (ObjPtr->type==OBJ_POLY ? &ObjPtr->detail.p->vlist[Corner] :
5737                &ObjPtr->detail.g->vlist[Corner]);
5738          px = pv->x;
5739          py = pv->y;
5740       } else {
5741          pv = (ObjPtr->type==OBJ_POLY ? &ObjPtr->detail.p->vlist[Corner] :
5742                &ObjPtr->detail.g->vlist[Corner]);
5743          TransformPointThroughCTM(pv->x-ObjPtr->x, pv->y-ObjPtr->y,
5744                ObjPtr->ctm, &px, &py);
5745          px += ObjPtr->x;
5746          py += ObjPtr->y;
5747       }
5748       absPivotX = (ObjPtr->obbox.ltx+ObjPtr->obbox.rbx)>>1;
5749       absPivotY = (ObjPtr->obbox.lty+ObjPtr->obbox.rby)>>1;
5750       moveX = OFFSET_X(px);
5751       moveY = OFFSET_Y(py);
5752       changeX = changeY = TRUE;
5753       multX = multY = 1.0;
5754       pivotX = OFFSET_X(absPivotX);
5755       pivotY = OFFSET_Y(absPivotY);
5756    } else {
5757       SetPivot(Corner, &ObjPtr->obbox);
5758    }
5759    if (!autoRotatePivot && rotatePivotAbsXYValid) {
5760       absPivotX = rotatePivotAbsX;
5761       absPivotY = rotatePivotAbsY;
5762       pivotX = OFFSET_X(absPivotX);
5763       pivotY = OFFSET_Y(absPivotY);
5764    }
5765    SetBBRec(&orig_all_bbox, OFFSET_X(selNoLockLtX)-2, OFFSET_Y(selNoLockLtY)-2,
5766          OFFSET_X(selNoLockRbX)+2, OFFSET_Y(selNoLockRbY)+2);
5767    SetRotateVs(all_bbox_vs, orig_all_bbox.ltx, orig_all_bbox.lty,
5768          orig_all_bbox.rbx, orig_all_bbox.rby);
5769    XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
5770          CoordModeOrigin);
5771 
5772    if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON) {
5773       if (ObjPtr->type == OBJ_POLY) {
5774          if (ObjPtr->ctm == NULL) {
5775             n = ObjPtr->detail.p->sn;
5776             orig_vs = (XPoint*)malloc(n*sizeof(XPoint));
5777             vs = (XPoint*)malloc(n*sizeof(XPoint));
5778             if (orig_vs == NULL || vs == NULL) FailAllocMessage();
5779             for (i=0; i < n; i++) {
5780                vs[i].x = orig_vs[i].x = ObjPtr->detail.p->svlist[i].x;
5781                vs[i].y = orig_vs[i].y = ObjPtr->detail.p->svlist[i].y;
5782             }
5783          } else {
5784             n = ObjPtr->detail.p->rotated_n;
5785             orig_vs = (XPoint*)malloc(n*sizeof(XPoint));
5786             vs = (XPoint*)malloc(n*sizeof(XPoint));
5787             if (orig_vs == NULL || vs == NULL) FailAllocMessage();
5788             for (i=0; i < n; i++) {
5789                vs[i].x = orig_vs[i].x = ObjPtr->detail.p->rotated_vlist[i].x;
5790                vs[i].y = orig_vs[i].y = ObjPtr->detail.p->rotated_vlist[i].y;
5791             }
5792          }
5793       } else {
5794          if (ObjPtr->ctm == NULL) {
5795             n = ObjPtr->detail.g->sn;
5796             orig_vs = (XPoint*)malloc(n*sizeof(XPoint));
5797             vs = (XPoint*)malloc(n*sizeof(XPoint));
5798             if (orig_vs == NULL || vs == NULL) FailAllocMessage();
5799             for (i=0; i < n; i++) {
5800                vs[i].x = orig_vs[i].x = ObjPtr->detail.g->svlist[i].x;
5801                vs[i].y = orig_vs[i].y = ObjPtr->detail.g->svlist[i].y;
5802             }
5803          } else {
5804             n = ObjPtr->detail.g->rotated_n;
5805             orig_vs = (XPoint*)malloc(n*sizeof(XPoint));
5806             vs = (XPoint*)malloc(n*sizeof(XPoint));
5807             if (orig_vs == NULL || vs == NULL) FailAllocMessage();
5808             for (i=0; i < n; i++) {
5809                vs[i].x = orig_vs[i].x = ObjPtr->detail.g->rotated_vlist[i].x;
5810                vs[i].y = orig_vs[i].y = ObjPtr->detail.g->rotated_vlist[i].y;
5811             }
5812          }
5813       }
5814       XDrawLines(mainDisplay, drawWindow, revDefaultGC, vs, n,
5815             CoordModeOrigin);
5816    } else {
5817       if (ObjPtr->ctm == NULL) {
5818          SetBBRec(&orig_obj_obbox, OFFSET_X(ObjPtr->obbox.ltx),
5819                OFFSET_Y(ObjPtr->obbox.lty), OFFSET_X(ObjPtr->obbox.rbx),
5820                OFFSET_Y(ObjPtr->obbox.rby));
5821          SetRotateVs(obj_obbox_vs, orig_obj_obbox.ltx, orig_obj_obbox.lty,
5822                orig_obj_obbox.rbx, orig_obj_obbox.rby);
5823          XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
5824                CoordModeOrigin);
5825       } else {
5826          memcpy(obj_obbox_vs, ObjPtr->rotated_obbox, 5*sizeof(XPoint));
5827          XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
5828                CoordModeOrigin);
5829       }
5830    }
5831    dx = OFFSET_X(ObjPtr->obbox.rbx) - OFFSET_X(ObjPtr->obbox.ltx);
5832    dy = OFFSET_Y(ObjPtr->obbox.rby) - OFFSET_Y(ObjPtr->obbox.lty);
5833    if (dx == 0 && dy == 0) {
5834       MsgBox(TgLoadString(STID_SEL_OBJ_TOO_SMALL_ROT_ANOTHER), TOOL_NAME,
5835             INFO_MB);
5836       return;
5837    }
5838    grid_x = moveX;
5839    grid_y = moveY;
5840    FormatAngleForRotate(buf, 0);
5841    StartShowMeasureCursor(grid_x, grid_y, buf, TRUE);
5842    if (!debugNoPointerGrab) {
5843       XGrabPointer(mainDisplay, drawWindow, False,
5844             PointerMotionMask | ButtonReleaseMask,
5845             GrabModeAsync, GrabModeAsync, None, rotatingCursor, CurrentTime);
5846    }
5847    while (rotating) {
5848       XEvent input;
5849 
5850       XNextEvent(mainDisplay, &input);
5851 
5852       if (input.type == Expose || input.type == VisibilityNotify) {
5853          ExposeEventHandler(&input, TRUE);
5854       } else if (input.type == ButtonRelease) {
5855          XUngrabPointer(mainDisplay, CurrentTime);
5856          XSync(mainDisplay, False);
5857          rotating = FALSE;
5858          FormatAngleForRotate(buf, d_angle);
5859          EndShowMeasureCursor(grid_x, grid_y, buf, TRUE);
5860          if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON) {
5861             XDrawLines(mainDisplay, drawWindow, revDefaultGC, vs, n,
5862                   CoordModeOrigin);
5863          } else {
5864             XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
5865                   CoordModeOrigin);
5866          }
5867          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
5868                CoordModeOrigin);
5869       } else if (input.type == MotionNotify || input.type == KeyPress ||
5870             input.type == KeyRelease) {
5871          int end_x=0, end_y=0;
5872 
5873          FormatAngleForRotate(buf, d_angle);
5874          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
5875          if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON) {
5876             XDrawLines(mainDisplay, drawWindow, revDefaultGC, vs, n,
5877                   CoordModeOrigin);
5878          } else {
5879             XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
5880                   CoordModeOrigin);
5881          }
5882          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
5883                CoordModeOrigin);
5884 
5885          if (input.type == KeyPress || input.type == KeyRelease) {
5886             end_x = grid_x;
5887             end_y = grid_y;
5888          } else {
5889             end_x = input.xmotion.x;
5890             end_y = input.xmotion.y;
5891          }
5892          GridXY(end_x, end_y, &grid_x, &grid_y);
5893          dx = grid_x - saved_x;
5894          dy = grid_y - saved_y;
5895          grid_x = moveX + dx;
5896          grid_y = moveY + dy;
5897          MarkRulers(grid_x, grid_y);
5898          PointsToArc(pivotX, pivotY, moveX, moveY, grid_x, grid_y,
5899                ARC_CW, FALSE, NULL, NULL, NULL, NULL, NULL, &d_angle);
5900          if (shiftForDiagMouseMove && DiagEventCheck(&input)) {
5901             int tmp_angle=(int)(((double)d_angle) / ((double)(45<<5)));
5902 
5903             if (tmp_angle & 0x1) {
5904                if (tmp_angle > 0) {
5905                   tmp_angle = ((tmp_angle+1)>>1);
5906                } else {
5907                   tmp_angle = ((tmp_angle-1)>>1);
5908                }
5909             } else {
5910                tmp_angle >>= 1;
5911             }
5912             d_angle = tmp_angle * (45<<6);
5913          }
5914          if (d_angle == deg360) d_angle = 0;
5915          d_angle = (-d_angle);
5916          while (d_angle >= (deg360)) d_angle -= (deg360);
5917 
5918          RotateBBoxByAnAngle(&orig_all_bbox, d_angle, all_bbox_vs);
5919          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
5920                CoordModeOrigin);
5921          if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON) {
5922             RotateVsByAnAngle(orig_vs, n, d_angle, vs);
5923             XDrawLines(mainDisplay, drawWindow, revDefaultGC, vs, n,
5924                   CoordModeOrigin);
5925          } else {
5926             if (ObjPtr->ctm == NULL) {
5927                RotateBBoxByAnAngle(&orig_obj_obbox, d_angle, obj_obbox_vs);
5928             } else {
5929                RotateVsByAnAngle(ObjPtr->rotated_obbox, 5, d_angle,
5930                      obj_obbox_vs);
5931             }
5932             XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
5933                  CoordModeOrigin);
5934          }
5935          FormatAngleForRotate(buf, d_angle);
5936          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
5937          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
5938       }
5939    }
5940    if (d_angle != 0) {
5941       HighLightReverse();
5942       RotateAllSel(Corner, d_angle);
5943       HighLightForward();
5944       SetFileModified(TRUE);
5945       justDupped = FALSE;
5946    }
5947    if (vs != NULL) free(vs);
5948    if (orig_vs != NULL) free(orig_vs);
5949 }
5950 
5951 /* --------------------- Shearing --------------------- */
5952 
5953 static
ShearBBox(Corner,bbox,dx_shear,dy_shear,dx_scale,dy_scale,vs)5954 void ShearBBox(Corner, bbox, dx_shear, dy_shear, dx_scale, dy_scale, vs)
5955    int Corner;
5956    double dx_shear, dy_shear, dx_scale, dy_scale;
5957    struct BBRec *bbox; /* the original bounding box */
5958    XPoint *vs; /* array of 5 points */
5959 {
5960    int x, y;
5961 
5962    switch (Corner) {
5963    case CORNER_TOP:
5964    case CORNER_BOTTOM:
5965       if (bbox->lty == pivotY) {
5966          vs[0].x = vs[4].x = bbox->ltx;
5967          vs[0].y = vs[4].y = bbox->lty;
5968          vs[1].x = bbox->rbx; vs[1].y = bbox->lty;
5969       } else {
5970          ShearedXY(Corner, bbox->ltx, bbox->lty, dx_shear, dy_shear,
5971                dx_scale, dy_scale, &x, &y);
5972          vs[0].x = vs[4].x = x; vs[0].y = vs[4].y = y;
5973          ShearedXY(Corner, bbox->rbx, bbox->lty, dx_shear, dy_shear,
5974                dx_scale, dy_scale, &x, &y);
5975          vs[1].x = x; vs[1].y = y;
5976       }
5977       if (bbox->rby == pivotY) {
5978          vs[2].x = bbox->rbx; vs[2].y = bbox->rby;
5979          vs[3].x = bbox->ltx; vs[3].y = bbox->rby;
5980       } else {
5981          ShearedXY(Corner, bbox->rbx, bbox->rby, dx_shear, dy_shear,
5982                dx_scale, dy_scale, &x, &y);
5983          vs[2].x = x; vs[2].y = y;
5984          ShearedXY(Corner, bbox->ltx, bbox->rby, dx_shear, dy_shear,
5985                dx_scale, dy_scale, &x, &y);
5986          vs[3].x = x; vs[3].y = y;
5987       }
5988       break;
5989    case CORNER_RIGHT:
5990    case CORNER_LEFT:
5991       if (bbox->ltx == pivotX) {
5992          vs[0].x = vs[4].x = bbox->ltx;
5993          vs[0].y = vs[4].y = bbox->lty;
5994          vs[3].x = bbox->ltx; vs[3].y = bbox->rby;
5995       } else {
5996          ShearedXY(Corner, bbox->ltx, bbox->lty, dx_shear, dy_shear,
5997                dx_scale, dy_scale, &x, &y);
5998          vs[0].x = vs[4].x = x; vs[0].y = vs[4].y = y;
5999          ShearedXY(Corner, bbox->ltx, bbox->rby, dx_shear, dy_shear,
6000                dx_scale, dy_scale, &x, &y);
6001          vs[3].x = x; vs[3].y = y;
6002       }
6003       if (bbox->rbx == pivotX) {
6004          vs[1].x = bbox->rbx; vs[1].y = bbox->lty;
6005          vs[2].x = bbox->rbx; vs[2].y = bbox->rby;
6006       } else {
6007          ShearedXY(Corner, bbox->rbx, bbox->lty, dx_shear, dy_shear,
6008                dx_scale, dy_scale, &x, &y);
6009          vs[1].x = x; vs[1].y = y;
6010          ShearedXY(Corner, bbox->rbx, bbox->rby, dx_shear, dy_shear,
6011                dx_scale, dy_scale, &x, &y);
6012          vs[2].x = x; vs[2].y = y;
6013       }
6014       break;
6015    }
6016 }
6017 
6018 static
ShearVs(Corner,InVs,NumPts,dx_shear,dy_shear,dx_scale,dy_scale,OutVs)6019 void ShearVs(Corner, InVs, NumPts, dx_shear, dy_shear, dx_scale, dy_scale,
6020       OutVs)
6021    int Corner, NumPts;
6022    double dx_shear, dy_shear, dx_scale, dy_scale;
6023    XPoint *InVs, *OutVs; /* array of 5 points */
6024 {
6025    register int i;
6026    int x, y;
6027 
6028    switch (Corner) {
6029    case CORNER_TOP:
6030    case CORNER_BOTTOM:
6031       for (i=0; i < NumPts; i++) {
6032          if (InVs[i].y == pivotY) {
6033             OutVs[i].x = InVs[i].x;
6034             OutVs[i].y = InVs[i].y;
6035          } else {
6036             ShearedXY(Corner, InVs[i].x, InVs[i].y, dx_shear, dy_shear,
6037                   dx_scale, dy_scale, &x, &y);
6038             OutVs[i].x = x;
6039             OutVs[i].y = y;
6040          }
6041       }
6042       break;
6043    case CORNER_RIGHT:
6044    case CORNER_LEFT:
6045       for (i=0; i < NumPts; i++) {
6046          if (InVs[i].x == pivotX) {
6047             OutVs[i].x = InVs[i].x;
6048             OutVs[i].y = InVs[i].y;
6049          } else {
6050             ShearedXY(Corner, InVs[i].x, InVs[i].y, dx_shear, dy_shear,
6051                   dx_scale, dy_scale, &x, &y);
6052             OutVs[i].x = x;
6053             OutVs[i].y = y;
6054          }
6055       }
6056       break;
6057    }
6058 }
6059 
6060 static
ShearAllSelObjects(Corner,dxShear,dyShear,dxScale,dyScale)6061 void ShearAllSelObjects(Corner, dxShear, dyShear, dxScale, dyScale)
6062    int Corner;
6063    double dxShear, dyShear, dxScale, dyScale; /* everything scaled by 1000 */
6064 {
6065    register struct SelRec *sel_ptr;
6066 
6067    for (sel_ptr = topSel; sel_ptr != NULL; sel_ptr = sel_ptr->next) {
6068       if (OkToTransform(sel_ptr->obj, STID_DISABLE_ON_RESIZE_SHEAR)) {
6069          ShearObj(sel_ptr->obj, Corner, dxShear, dyShear, dxScale, dyScale,
6070                NULL, NULL);
6071       }
6072    }
6073    if (numObjLocked != 0) {
6074       Msg(TgLoadString(STID_LOCKED_OBJS_ARE_NOT_SHEARED));
6075    }
6076 }
6077 
6078 static
ConstrainedShearAllSel(Corner,dxShear,dyShear,dxScale,dyScale,ltx,lty,rbx,rby)6079 int ConstrainedShearAllSel(Corner, dxShear, dyShear, dxScale, dyScale,
6080       ltx, lty, rbx, rby)
6081    int Corner, *ltx, *lty, *rbx, *rby;
6082    double dxShear, dyShear, dxScale, dyScale;
6083 {
6084    register struct ObjRec *obj_ptr;
6085    int something_stretched=FALSE, num_pts;
6086    int x_off, y_off, move_first, move_last, x, y;
6087    IntPoint *v;
6088 
6089    for (obj_ptr=botObj; obj_ptr != NULL; obj_ptr=obj_ptr->prev) {
6090       if (!obj_ptr->marked && obj_ptr->type==OBJ_POLY && !obj_ptr->locked) {
6091          num_pts = obj_ptr->detail.p->n;
6092          v = obj_ptr->detail.p->vlist;
6093 
6094          if (obj_ptr->ctm == NULL) {
6095             x_off = OFFSET_X(v[0].x); y_off = OFFSET_Y(v[0].y);
6096             move_first = EndPtInSelected(x_off, y_off);
6097             x_off = OFFSET_X(v[num_pts-1].x); y_off = OFFSET_Y(v[num_pts-1].y);
6098             move_last = EndPtInSelected(x_off, y_off);
6099          } else {
6100             int tmp_x, tmp_y;
6101 
6102             TransformPointThroughCTM(v[0].x-obj_ptr->x, v[0].y-obj_ptr->y,
6103                   obj_ptr->ctm, &tmp_x, &tmp_y);
6104             tmp_x += obj_ptr->x;
6105             tmp_y += obj_ptr->y;
6106             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
6107             move_first = EndPtInSelected(x_off, y_off);
6108             TransformPointThroughCTM(v[num_pts-1].x-obj_ptr->x,
6109                   v[num_pts-1].y-obj_ptr->y, obj_ptr->ctm, &tmp_x, &tmp_y);
6110             tmp_x += obj_ptr->x;
6111             tmp_y += obj_ptr->y;
6112             x_off = OFFSET_X(tmp_x); y_off = OFFSET_Y(tmp_y);
6113             move_last = EndPtInSelected(x_off, y_off);
6114          }
6115 
6116          if (move_first || move_last) {
6117             int index=INVALID, seg_dx, seg_dy, dx, dy, cur_seg_dx, cur_seg_dy;
6118 
6119             PrepareToReplaceAnObj(obj_ptr);
6120 
6121             if (obj_ptr->ctm != NULL) {
6122                /* Remove the transformations! */
6123                int i;
6124 
6125                for (i=0; i < num_pts; i++) {
6126                   int tmp_x, tmp_y;
6127 
6128                   TransformPointThroughCTM(v[i].x-obj_ptr->x, v[i].y-obj_ptr->y,
6129                         obj_ptr->ctm, &tmp_x, &tmp_y);
6130                   v[i].x = tmp_x+obj_ptr->x;
6131                   v[i].y = tmp_y+obj_ptr->y;
6132                }
6133                free(obj_ptr->ctm);
6134                obj_ptr->ctm = NULL;
6135                UpdPolyBBox(obj_ptr, num_pts, v);
6136             }
6137             if (something_stretched) {
6138                if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
6139                if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
6140                if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
6141                if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
6142             } else {
6143                *ltx = obj_ptr->bbox.ltx; *lty = obj_ptr->bbox.lty;
6144                *rbx = obj_ptr->bbox.rbx; *rby = obj_ptr->bbox.rby;
6145             }
6146             something_stretched = TRUE;
6147             if (move_first && move_last && num_pts==3) {
6148                ShearedAbsXY(Corner, v[0].x, v[0].y, dxShear, dyShear,
6149                      dxScale, dyScale, &x, &y);
6150                dx = x-v[0].x; dy = y-v[0].y;
6151                index = 1;
6152                cur_seg_dx = v[index-1].x - v[index].x;
6153                cur_seg_dy = v[index-1].y - v[index].y;
6154                seg_dx = v[index].x - v[index+1].x;
6155                seg_dy = v[index].y - v[index+1].y;
6156 
6157                if (cur_seg_dy==0 && seg_dx==0 &&
6158                      (seg_dy!=0 || (seg_dy==0 && dx==0))) {
6159                   v[index].y += dy;
6160                } else if (cur_seg_dx==0 && seg_dy==0 &&
6161                      (seg_dx!=0 || (seg_dx==0 && dy==0))) {
6162                   v[index].x += dx;
6163                }
6164             } else {
6165                if (move_first && num_pts>2) {
6166                   ShearedAbsXY(Corner, v[0].x, v[0].y, dxShear, dyShear,
6167                         dxScale, dyScale, &x, &y);
6168                   dx = x-v[0].x; dy = y-v[0].y;
6169                   index = 1;
6170                   cur_seg_dx = v[index-1].x - v[index].x;
6171                   cur_seg_dy = v[index-1].y - v[index].y;
6172                   seg_dx = v[index].x - v[index+1].x;
6173                   seg_dy = v[index].y - v[index+1].y;
6174 
6175                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
6176                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
6177                      v[index].y += dy;
6178                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
6179                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
6180                      v[index].x += dx;
6181                   }
6182                }
6183                if (move_last && num_pts>2) {
6184                   ShearedAbsXY(Corner, v[num_pts-1].x, v[num_pts-1].y,
6185                         dxShear, dyShear, dxScale, dyScale, &x, &y);
6186                   dx = x-v[num_pts-1].x; dy = y-v[num_pts-1].y;
6187                   index = num_pts-2;
6188                   cur_seg_dx = v[index+1].x - v[index].x;
6189                   cur_seg_dy = v[index+1].y - v[index].y;
6190                   seg_dx = v[index].x - v[index-1].x;
6191                   seg_dy = v[index].y - v[index-1].y;
6192 
6193                   if (cur_seg_dy==0 && cur_seg_dx!=0 &&
6194                         (seg_dy!=0 || (seg_dy==0 && dx==0))) {
6195                      v[index].y += dy;
6196                   } else if (cur_seg_dx==0 && cur_seg_dy!=0 &&
6197                         (seg_dx!=0 || (seg_dx==0 && dy==0))) {
6198                      v[index].x += dx;
6199                   }
6200                }
6201             }
6202             if (move_first) {
6203                ShearedAbsXY(Corner, v[0].x, v[0].y, dxShear, dyShear,
6204                      dxScale, dyScale, &x, &y);
6205                v[0].x = x; v[0].y = y;
6206             }
6207             if (move_last) {
6208                ShearedAbsXY(Corner, v[num_pts-1].x, v[num_pts-1].y,
6209                      dxShear, dyShear, dxScale, dyScale, &x, &y);
6210                v[num_pts-1].x = x; v[num_pts-1].y = y;
6211             }
6212             AdjObjSplineVs(obj_ptr);
6213             switch (obj_ptr->type) {
6214             case OBJ_POLY:
6215                if (obj_ptr->detail.p->curved != LT_INTSPLINE) {
6216                   UpdPolyBBox(obj_ptr, num_pts, v);
6217                } else {
6218                   UpdPolyBBox(obj_ptr, obj_ptr->detail.p->intn,
6219                         obj_ptr->detail.p->intvlist);
6220                }
6221                break;
6222             case OBJ_POLYGON:
6223                if (obj_ptr->detail.g->curved != LT_INTSPLINE) {
6224                   UpdPolyBBox(obj_ptr, num_pts, v);
6225                } else {
6226                   UpdPolyBBox(obj_ptr, obj_ptr->detail.g->intn,
6227                         obj_ptr->detail.g->intvlist);
6228                }
6229                break;
6230             }
6231             AdjObjBBox(obj_ptr);
6232             if (AutoCenterAttr(obj_ptr)) {
6233                struct AttrRec *attr_ptr=obj_ptr->fattr;
6234                int modified=FALSE;
6235 
6236                for ( ; attr_ptr != NULL; attr_ptr = attr_ptr->next) {
6237                   if (attr_ptr->shown) {
6238                      struct BBRec bbox;
6239 
6240                      CenterObjInOBBox(attr_ptr->obj, obj_ptr->obbox, &bbox);
6241                      if (bbox.ltx < *ltx) *ltx = bbox.ltx;
6242                      if (bbox.lty < *lty) *lty = bbox.lty;
6243                      if (bbox.rbx > *rbx) *rbx = bbox.rbx;
6244                      if (bbox.rby > *rby) *rby = bbox.rby;
6245                      modified = TRUE;
6246                   }
6247                }
6248                if (modified) AdjObjBBox(obj_ptr);
6249             }
6250             if (obj_ptr->bbox.ltx < *ltx) *ltx = obj_ptr->bbox.ltx;
6251             if (obj_ptr->bbox.lty < *lty) *lty = obj_ptr->bbox.lty;
6252             if (obj_ptr->bbox.rbx > *rbx) *rbx = obj_ptr->bbox.rbx;
6253             if (obj_ptr->bbox.rby > *rby) *rby = obj_ptr->bbox.rby;
6254             RecordReplaceAnObj(obj_ptr);
6255          }
6256       }
6257    }
6258    return something_stretched;
6259 }
6260 
6261 static
ShearAllSel(Corner,dxShear,dyShear,dxScale,dyScale)6262 void ShearAllSel(Corner, dxShear, dyShear, dxScale, dyScale)
6263    int Corner;
6264    double dxShear, dyShear, dxScale, dyScale; /* everything scaled by 1000 */
6265 {
6266    int ltx=0, lty=0, rbx=0, rby=0, saved_ltx, saved_lty, saved_rbx, saved_rby;
6267    int poly_stretched;
6268 
6269    saved_ltx = selLtX; saved_lty = selLtY;
6270    saved_rbx = selRbX; saved_rby = selRbY;
6271 
6272    if (moveMode==CONST_MOVE) {
6273       MarkObjectsForStretch();
6274 
6275       StartCompositeCmd();
6276       PrepareToRecord(CMD_STRETCH, topSel, botSel, numObjSelected);
6277       RecordCmd(CMD_STRETCH, NULL, topSel, botSel, numObjSelected);
6278 
6279       poly_stretched = ConstrainedShearAllSel(Corner, dxShear, dyShear, dxScale,
6280             dyScale, &ltx, &lty, &rbx, &rby);
6281       ShearAllSelObjects(Corner, dxShear, dyShear, dxScale, dyScale);
6282       UpdSelBBox();
6283       if (poly_stretched) {
6284          ltx = min(ltx,min(selLtX,saved_ltx));
6285          lty = min(lty,min(selLtY,saved_lty));
6286          rbx = max(rbx,max(selRbX,saved_rbx));
6287          rby = max(rby,max(selRbY,saved_rby));
6288          RedrawAnArea(botObj, ltx-GRID_ABS_SIZE(1), lty-GRID_ABS_SIZE(1),
6289                rbx+GRID_ABS_SIZE(1), rby+GRID_ABS_SIZE(1));
6290       } else {
6291          RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
6292                saved_lty-GRID_ABS_SIZE(1),
6293                saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
6294                selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
6295                selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
6296       }
6297       EndCompositeCmd();
6298    } else {
6299       PrepareToRecord(CMD_REPLACE, topSel, botSel, numObjSelected);
6300       ShearAllSelObjects(Corner, dxShear, dyShear, dxScale, dyScale);
6301       RecordCmd(CMD_REPLACE, NULL, topSel, botSel, numObjSelected);
6302       UpdSelBBox();
6303       RedrawAreas(botObj, saved_ltx-GRID_ABS_SIZE(1),
6304             saved_lty-GRID_ABS_SIZE(1),
6305             saved_rbx+GRID_ABS_SIZE(1), saved_rby+GRID_ABS_SIZE(1),
6306             selLtX-GRID_ABS_SIZE(1), selLtY-GRID_ABS_SIZE(1),
6307             selRbX+GRID_ABS_SIZE(1), selRbY+GRID_ABS_SIZE(1));
6308    }
6309 }
6310 
6311 static
ShearSel(XGridOff,YGridOff,ObjPtr,Corner)6312 void ShearSel(XGridOff, YGridOff, ObjPtr, Corner)
6313    int XGridOff, YGridOff, Corner;
6314    struct ObjRec *ObjPtr;
6315 {
6316    XEvent ev;
6317    XPoint all_bbox_vs[5], obj_obbox_vs[5];
6318    int grid_x=XGridOff, grid_y=YGridOff, dx, dy;
6319    int saved_x=XGridOff, saved_y=YGridOff;
6320    int shearing=TRUE, shear_hori=FALSE;
6321    double dx_scale=(double)1000, dy_scale=(double)1000;
6322    double dx_shear=(double)0, dy_shear=(double)0;
6323    char buf[80];
6324    struct BBRec orig_all_bbox, orig_obj_obbox;
6325 
6326    if (numObjSelected == numObjLocked || ObjPtr->locked) {
6327       MsgBox(TgLoadString(STID_LOCKED_OBJS_CANT_BE_ROTATED), TOOL_NAME,
6328             INFO_MB);
6329       return;
6330    }
6331    XFlush(mainDisplay);
6332    XSync(mainDisplay, False);
6333    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
6334          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
6335       ExposeEventHandler(&ev, TRUE);
6336    }
6337    SetPivot(Corner, &ObjPtr->obbox);
6338 
6339    SetBBRec(&orig_all_bbox, OFFSET_X(selNoLockLtX)-2, OFFSET_Y(selNoLockLtY)-2,
6340          OFFSET_X(selNoLockRbX)+2, OFFSET_Y(selNoLockRbY)+2);
6341    SetRotateVs(all_bbox_vs, orig_all_bbox.ltx, orig_all_bbox.lty,
6342          orig_all_bbox.rbx, orig_all_bbox.rby);
6343    XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
6344          CoordModeOrigin);
6345 
6346    if (ObjPtr->ctm == NULL) {
6347       SetBBRec(&orig_obj_obbox, OFFSET_X(ObjPtr->obbox.ltx),
6348             OFFSET_Y(ObjPtr->obbox.lty), OFFSET_X(ObjPtr->obbox.rbx),
6349             OFFSET_Y(ObjPtr->obbox.rby));
6350       SetRotateVs(obj_obbox_vs, orig_obj_obbox.ltx, orig_obj_obbox.lty,
6351             orig_obj_obbox.rbx, orig_obj_obbox.rby);
6352       XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
6353             CoordModeOrigin);
6354    } else {
6355       memcpy(obj_obbox_vs, ObjPtr->rotated_obbox, 5*sizeof(XPoint));
6356       XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
6357             CoordModeOrigin);
6358    }
6359 
6360    dx = OFFSET_X(ObjPtr->obbox.rbx) - OFFSET_X(ObjPtr->obbox.ltx);
6361    dy = OFFSET_Y(ObjPtr->obbox.rby) - OFFSET_Y(ObjPtr->obbox.lty);
6362    if (dx == 0 || dy == 0) {
6363       MsgBox(TgLoadString(STID_SEL_OBJ_TOO_SMALL_SHEAR_ANO), TOOL_NAME,
6364             INFO_MB);
6365       return;
6366    }
6367    if (Corner == CORNER_TOP || Corner == CORNER_BOTTOM) {
6368       shear_hori = TRUE;
6369       multX = 0.0;
6370       multY = (Corner == CORNER_BOTTOM ? 1.0 : (-1.0));
6371    } else {
6372       shear_hori = FALSE;
6373       multX = (Corner == CORNER_RIGHT ? 1.0 : (-1.0));
6374       multY = 0.0;
6375    }
6376    dx = dy = 0;
6377    grid_x = moveX;
6378    grid_y = moveY;
6379    sprintf(buf, "dx=0\ndy=0");
6380    StartShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6381    if (!debugNoPointerGrab) {
6382       XGrabPointer(mainDisplay, drawWindow, False,
6383             PointerMotionMask | ButtonReleaseMask,
6384             GrabModeAsync, GrabModeAsync, None,
6385             ((Corner==CORNER_TOP || Corner==CORNER_BOTTOM) ? horiShearCursor :
6386             vertShearCursor), CurrentTime);
6387    }
6388    while (shearing) {
6389       XEvent input;
6390 
6391       XNextEvent(mainDisplay, &input);
6392 
6393       if (input.type == Expose || input.type == VisibilityNotify) {
6394          ExposeEventHandler(&input, TRUE);
6395       } else if (input.type == ButtonRelease) {
6396          XUngrabPointer(mainDisplay, CurrentTime);
6397          XSync(mainDisplay, False);
6398          shearing = FALSE;
6399          sprintf(buf, "dx=%1d\ndy=%1d", dx, dy);
6400          EndShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6401          XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
6402                CoordModeOrigin);
6403          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
6404                CoordModeOrigin);
6405       } else if (input.type == MotionNotify) {
6406          sprintf(buf, "dx=%1d\ndy=%1d", dx, dy);
6407          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6408          XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
6409                CoordModeOrigin);
6410          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
6411                CoordModeOrigin);
6412 
6413          GridXY(input.xmotion.x, input.xmotion.y, &grid_x, &grid_y);
6414          dx = grid_x - saved_x;
6415          dy = grid_y - saved_y;
6416          grid_x = moveX + dx;
6417          grid_y = moveY + dy;
6418          MarkRulers(grid_x, grid_y);
6419          PointsToShearScale(Corner, pivotX, pivotY, moveX, moveY,
6420                grid_x, grid_y, &dx_shear, &dy_shear, &dx_scale, &dy_scale);
6421 
6422          ShearBBox(Corner, &orig_all_bbox, dx_shear, dy_shear,
6423                dx_scale, dy_scale, all_bbox_vs);
6424          XDrawLines(mainDisplay, drawWindow, revDefaultGC, all_bbox_vs, 5,
6425                CoordModeOrigin);
6426          if (ObjPtr->ctm == NULL) {
6427             ShearBBox(Corner, &orig_obj_obbox, dx_shear, dy_shear, dx_scale,
6428                   dy_scale, obj_obbox_vs);
6429          } else {
6430             ShearVs(Corner, ObjPtr->rotated_obbox, 5, dx_shear, dy_shear,
6431                   dx_scale, dy_scale, obj_obbox_vs);
6432          }
6433          XDrawLines(mainDisplay, drawWindow, revDefaultGC, obj_obbox_vs, 5,
6434                CoordModeOrigin);
6435          sprintf(buf, "dx=%1d\ndy=%1d", dx, dy);
6436          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6437          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
6438       }
6439    }
6440    ShowCursor();
6441    if (dx != 0 || dy != 0) {
6442       PointsToShearScale(Corner, pivotX, pivotY, moveX, moveY,
6443             moveX+dx, moveY+dy, &dx_shear, &dy_shear, &dx_scale, &dy_scale);
6444       HighLightReverse();
6445       ShearAllSel(Corner, dx_shear, dy_shear, dx_scale, dy_scale);
6446       HighLightForward();
6447       SetFileModified(TRUE);
6448       justDupped = FALSE;
6449    }
6450 }
6451 
RotateShearSel(XGridOff,YGridOff,ObjPtr,Corner)6452 void RotateShearSel(XGridOff, YGridOff, ObjPtr, Corner)
6453    int XGridOff, YGridOff, Corner;
6454    struct ObjRec *ObjPtr;
6455    /* 1 2 3 */
6456    /* 8   4 */
6457    /* 7 6 5 */
6458 {
6459    if (ObjPtr->type==OBJ_POLY || ObjPtr->type==OBJ_POLYGON || (Corner & 0x1)) {
6460       RotateSel(XGridOff, YGridOff, ObjPtr, Corner);
6461    } else {
6462       ShearSel(XGridOff, YGridOff, ObjPtr, Corner);
6463    }
6464 }
6465 
6466 /* --------------------- Rotation Pivot --------------------- */
6467 
6468 static
RefreshFlipRotateMenu(menu)6469 int RefreshFlipRotateMenu(menu)
6470    TgMenu *menu;
6471 {
6472    int ok=TRUE, can_suggest_poly=FALSE, can_suggest_arc=FALSE;
6473 
6474    /* ResetRotationPivot */
6475    ok &= TgEnableMenuItemById(menu, CMDID_RESETROTATEPIVOT,
6476          !autoRotatePivot && topSel != NULL);
6477    /* SpecifyRotationPivot */
6478    ok &= TgEnableMenuItemById(menu, CMDID_SPECIFYROTATEPIVOT,
6479          curChoice == ROTATEMODE && topSel != NULL);
6480 
6481    /* NextPolyRotationPivot */
6482    /* MoveRotationPivotToArcCenter */
6483    if (!autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL &&
6484          topSel == botSel) {
6485       struct ObjRec *obj_ptr=topSel->obj;
6486 
6487       if (obj_ptr->type == OBJ_POLY || obj_ptr->type == OBJ_POLYGON) {
6488          can_suggest_poly = TRUE;
6489       } else if (obj_ptr->type == OBJ_ARC) {
6490          can_suggest_arc = TRUE;
6491       }
6492    }
6493    ok &= TgEnableMenuItemById(menu, CMDID_NEXTPOLYROTATEPIVOT,
6494          can_suggest_poly);
6495    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTARCCNTR,
6496          can_suggest_arc);
6497 
6498    return ok;
6499 }
6500 
CreateFlipRotateMenu(parent_menu,x,y,menu_info,status_str_xlated)6501 TgMenu *CreateFlipRotateMenu(parent_menu, x, y, menu_info, status_str_xlated)
6502    TgMenu *parent_menu;
6503    int x, y;
6504    TgMenuInfo *menu_info;
6505    int status_str_xlated; /* ignored, always 0 */
6506 {
6507    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
6508 
6509    if (menu != NULL) {
6510       menu->track_menubar = TRUE;
6511       if (!RefreshFlipRotateMenu(menu)) {
6512          return TgDestroyMenu(menu, TRUE);
6513       }
6514    }
6515    return menu;
6516 }
6517 
6518 static
HighLightRotatePivotXY(abs_x,abs_y,Dir)6519 void HighLightRotatePivotXY(abs_x, abs_y, Dir)
6520    int abs_x, abs_y, Dir;
6521 {
6522    int scr_x=OFFSET_X(abs_x), scr_y=OFFSET_Y(abs_y);
6523 
6524    switch (Dir) {
6525    case FORWARD:
6526       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6527          scr_x-((handleSize<<1)+1), scr_y, scr_x+((handleSize<<1)+1), scr_y);
6528       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6529          scr_x, scr_y-((handleSize<<1)+1), scr_x, scr_y+((handleSize<<1)+1));
6530       XDrawArc(mainDisplay, drawWindow, revDefaultGC,
6531          scr_x-((handleSize<<1)+1), scr_y-((handleSize<<1)+1),
6532          ((handleSize<<2)+2), ((handleSize<<2)+2), 0, (360<<6));
6533       break;
6534    case REVERSE:
6535       XDrawArc(mainDisplay, drawWindow, revDefaultGC,
6536          scr_x-((handleSize<<1)+1), scr_y-((handleSize<<1)+1),
6537          ((handleSize<<2)+2), ((handleSize<<2)+2), 0, (360<<6));
6538       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6539          scr_x, scr_y-((handleSize<<1)+1), scr_x, scr_y+((handleSize<<1)+1));
6540       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6541          scr_x-((handleSize<<1)+1), scr_y, scr_x+((handleSize<<1)+1), scr_y);
6542       break;
6543    }
6544 }
6545 
HighLightRotatePivot(Dir)6546 void HighLightRotatePivot(Dir)
6547    int Dir;
6548 {
6549    int scr_x=0, scr_y=0;
6550 
6551    if (topSel == NULL || autoRotatePivot) return;
6552 
6553    if (!rotatePivotAbsXYValid) {
6554       rotatePivotAbsX = ((selObjLtX+selObjRbX)>>1);
6555       rotatePivotAbsY = ((selObjLtY+selObjRbY)>>1);
6556       rotatePivotAbsXYValid = TRUE;
6557 
6558       sprintf(gszMsgBox, TgLoadString(STID_NEW_ROTATE_PIVOT_IS),
6559             rotatePivotAbsX, rotatePivotAbsY);
6560       Msg(gszMsgBox);
6561    }
6562    scr_x = OFFSET_X(rotatePivotAbsX);
6563    scr_y = OFFSET_Y(rotatePivotAbsY);
6564 
6565    switch (Dir) {
6566    case FORWARD:
6567       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6568          scr_x-((handleSize<<1)+1), scr_y, scr_x+((handleSize<<1)+1), scr_y);
6569       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6570          scr_x, scr_y-((handleSize<<1)+1), scr_x, scr_y+((handleSize<<1)+1));
6571       XDrawArc(mainDisplay, drawWindow, revDefaultGC,
6572          scr_x-((handleSize<<1)+1), scr_y-((handleSize<<1)+1),
6573          ((handleSize<<2)+2), ((handleSize<<2)+2), 0, (360<<6));
6574       break;
6575    case REVERSE:
6576       XDrawArc(mainDisplay, drawWindow, revDefaultGC,
6577          scr_x-((handleSize<<1)+1), scr_y-((handleSize<<1)+1),
6578          ((handleSize<<2)+2), ((handleSize<<2)+2), 0, (360<<6));
6579       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6580          scr_x, scr_y-((handleSize<<1)+1), scr_x, scr_y+((handleSize<<1)+1));
6581       XDrawLine(mainDisplay, drawWindow, revDefaultGC,
6582          scr_x-((handleSize<<1)+1), scr_y, scr_x+((handleSize<<1)+1), scr_y);
6583       break;
6584    }
6585 }
6586 
PtInRotatePivot(mouse_x,mouse_y)6587 int PtInRotatePivot(mouse_x, mouse_y)
6588    int mouse_x, mouse_y;
6589 {
6590    int scr_x=0, scr_y=0;
6591    struct BBRec bbox;
6592 
6593    if (!rotatePivotAbsXYValid) return FALSE;
6594 
6595    scr_x = OFFSET_X(rotatePivotAbsX);
6596    scr_y = OFFSET_Y(rotatePivotAbsY);
6597 
6598    bbox.ltx = scr_x-((handleSize<<1)+1);
6599    bbox.lty = scr_y-((handleSize<<1)+1);
6600    bbox.rbx = scr_x+((handleSize<<1)+1);
6601    bbox.rby = scr_y+((handleSize<<1)+1);
6602 
6603    return PointInBBox(mouse_x, mouse_y, bbox);
6604 }
6605 
6606 static
RefreshMoveStdRotatePivotMenu(menu)6607 int RefreshMoveStdRotatePivotMenu(menu)
6608    TgMenu *menu;
6609 {
6610    int ok=TRUE;
6611 
6612    /* MoveRotationPivotToCenter */
6613    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTCENTER,
6614          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6615    /* MoveRotationPivotToLeftTopCorner */
6616    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTLT,
6617          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6618    /* MoveRotationPivotToRightTopCorner */
6619    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTRT,
6620          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6621    /* MoveRotationPivotToLeftBottomCorner */
6622    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTLB,
6623          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6624    /* MoveRotationPivotToRightBottomCorner */
6625    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTRB,
6626          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6627    /* MoveRotationPivotToLeftCorner */
6628    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTLEFT,
6629          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6630    /* MoveRotationPivotToRightCorner */
6631    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTRIGHT,
6632          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6633    /* MoveRotationPivotToTopCorner */
6634    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTTOP,
6635          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6636    /* MoveRotationPivotToBottomCorner */
6637    ok &= TgEnableMenuItemById(menu, CMDID_MOVEROTATEPIVOTBOTTOM,
6638          !autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL);
6639 
6640    return ok;
6641 }
6642 
CreateMoveStdRotatePivotMenu(parent_menu,x,y,menu_info,status_str_xlated)6643 TgMenu *CreateMoveStdRotatePivotMenu(parent_menu, x, y, menu_info,
6644       status_str_xlated)
6645    TgMenu *parent_menu;
6646    int x, y;
6647    TgMenuInfo *menu_info;
6648    int status_str_xlated; /* ignored, always 0 */
6649 {
6650    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
6651 
6652    if (menu != NULL) {
6653       menu->track_menubar = TRUE;
6654       if (!RefreshMoveStdRotatePivotMenu(menu)) {
6655          return TgDestroyMenu(menu, TRUE);
6656       }
6657    }
6658    return menu;
6659 }
6660 
RefreshAutoRotatePivotMenu(menu)6661 void RefreshAutoRotatePivotMenu(menu)
6662    TgMenu *menu;
6663 {
6664    int i, num_items=menu->num_items;
6665    TgMenuItem *menuitems=menu->menuitems;
6666 
6667    for (i=0; i < num_items; i++) {
6668       TgMenuItem *menu_item=(&menuitems[i]);
6669       TgMenuItem stMenuItem;
6670 
6671       memset(&stMenuItem, 0, sizeof(TgMenuItem));
6672       stMenuItem.state = TGBS_NORMAL;
6673       stMenuItem.checked = (i == (!autoRotatePivot));
6674       TgSetMenuItemInfo(menu_item, TGMU_MASK_STATE|TGMU_MASK_CHECK,
6675             &stMenuItem);
6676    }
6677 }
6678 
CreateAutoRotatePivotMenu(parent_menu,x,y,menu_info,status_str_xlated)6679 TgMenu *CreateAutoRotatePivotMenu(parent_menu, x, y, menu_info,
6680       status_str_xlated)
6681    TgMenu *parent_menu;
6682    int x, y;
6683    TgMenuInfo *menu_info;
6684    int status_str_xlated; /* ignored, always 0 */
6685 {
6686    TgMenu *menu=TgCreateMenuFromMenuInfo(parent_menu, x, y, menu_info, FALSE);
6687 
6688    if (menu != NULL) {
6689       TgMenuItem *menu_item=NULL;
6690       TgMenuItem stMenuItem;
6691 
6692       menu->track_menubar = TRUE;
6693       TgAdjustMenuGeometry(menu, choiceImageW, choiceImageH, MAX_ROTATE_PIVOT);
6694       menu_item = (&menu->menuitems[!autoRotatePivot]);
6695 
6696       memset(&stMenuItem, 0, sizeof(TgMenuItem));
6697       stMenuItem.checked = TRUE;
6698       if (!TgSetMenuItemInfo(menu_item, TGMU_MASK_CHECK, &stMenuItem)) {
6699          return TgDestroyMenu(menu, TRUE);
6700       }
6701       menu->refresh_proc = ((RefreshMenuFunc*)RefreshAutoRotatePivotMenu);
6702    }
6703    return menu;
6704 }
6705 
AutoRotatePivotMenu(X,Y,TrackMenubar)6706 int AutoRotatePivotMenu(X, Y, TrackMenubar)
6707    int X, Y, TrackMenubar;
6708 {
6709    int rc=INVALID;
6710    TgMenu *menu=(autoRotatePivotMenuInfo.create_proc)(NULL, X, Y,
6711          &autoRotatePivotMenuInfo, INVALID);
6712 
6713    activeMenu = INVALID;
6714    if (menu != NULL) {
6715       menu->track_menubar = TrackMenubar;
6716 
6717       rc = TgMenuLoop(menu);
6718       TgDestroyMenu(menu, TRUE);
6719    }
6720    return rc;
6721 }
6722 
ResetRotatePivotValidInfo()6723 void ResetRotatePivotValidInfo()
6724 {
6725    rotatePivotAbsXYValid = FALSE;
6726    rotatePivotAbsX = 0;
6727    rotatePivotAbsY = 0;
6728 }
6729 
ReadRotatePivotInfo(buf)6730 int ReadRotatePivotInfo(buf)
6731    char *buf;
6732 {
6733    int auto_pivot=TRUE, xy_valid=0, abs_x=0, abs_y=0;
6734    char *psz=NULL;
6735 
6736    if (importingFile) return TRUE;
6737 
6738    psz = FindChar((int)'(', buf);
6739    InitScan(psz, "\t\n, ");
6740    if (GETINT("rotate_pivot", auto_pivot, "auto_pivot") == INVALID ||
6741        GETINT("rotate_pivot", xy_valid, "xy_valid") == INVALID ||
6742        GETINT("rotate_pivot", abs_x, "x") == INVALID ||
6743        GETINT("rotate_pivot", abs_y, "y") == INVALID) {
6744       return FALSE;
6745    }
6746    autoRotatePivot = auto_pivot;
6747    rotatePivotAbsXYValid = xy_valid;
6748    rotatePivotAbsX = abs_x;
6749    rotatePivotAbsY = abs_y;
6750    if (!PRTGIF || cmdLineOpenDisplay) {
6751       choicePixmap[ROTATEMODE] = rotateModePixmap[!autoRotatePivot];
6752       RedrawModeWindow();
6753       UpdatePinnedMenu(MENU_MODE);
6754    }
6755    return TRUE;
6756 }
6757 
ContinueMoveRotatePivot(OrigX,OrigY)6758 void ContinueMoveRotatePivot(OrigX, OrigY)
6759    int OrigX, OrigY;
6760 {
6761    int moving=TRUE, dx=0, dy=0, grid_x=OrigX, grid_y=OrigY;
6762    char buf[80], x_buf[80], y_buf[80];
6763    XEvent ev;
6764 
6765    XFlush(mainDisplay);
6766    XSync(mainDisplay, False);
6767 
6768    if (XCheckMaskEvent(mainDisplay, ExposureMask, &ev) ||
6769          XCheckMaskEvent(mainDisplay, VisibilityChangeMask, &ev)) {
6770       ExposeEventHandler(&ev, TRUE);
6771    }
6772    PixelToMeasurementUnit(x_buf, rotatePivotAbsX);
6773    PixelToMeasurementUnit(y_buf, rotatePivotAbsY);
6774    sprintf(buf, "x=%s\ny=%s", x_buf, y_buf);
6775    StartShowMeasureCursor(OrigX, OrigY, buf, TRUE);
6776 
6777    if (!debugNoPointerGrab) {
6778       XGrabPointer(mainDisplay, drawWindow, FALSE,
6779             PointerMotionMask | ButtonReleaseMask,
6780             GrabModeAsync, GrabModeAsync, None, moveCursor, CurrentTime);
6781    }
6782    dx = dy = 0;
6783 
6784    while (moving) {
6785       XEvent input;
6786 
6787       XNextEvent(mainDisplay, &input);
6788 
6789       if (input.type == Expose || input.type == VisibilityNotify) {
6790          ExposeEventHandler(&input, TRUE);
6791       } else if (input.type == ButtonRelease) {
6792          XUngrabPointer(mainDisplay, CurrentTime);
6793          XSync(mainDisplay, False);
6794          moving = FALSE;
6795 
6796          /* erase */
6797          PixelToMeasurementUnit(x_buf, rotatePivotAbsX+ABS_SIZE(dx));
6798          PixelToMeasurementUnit(y_buf, rotatePivotAbsY+ABS_SIZE(dy));
6799          sprintf(buf, "x=%s\ny=%s", x_buf, y_buf);
6800          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6801          HighLightRotatePivotXY(rotatePivotAbsX+ABS_SIZE(dx),
6802                rotatePivotAbsY+ABS_SIZE(dy), REVERSE);
6803 
6804          dx = grid_x - OrigX;
6805          dy = grid_y - OrigY;
6806       } else if (input.type == MotionNotify) {
6807          int x=0, y=0;
6808 
6809          /* erase */
6810          PixelToMeasurementUnit(x_buf, rotatePivotAbsX+ABS_SIZE(dx));
6811          PixelToMeasurementUnit(y_buf, rotatePivotAbsY+ABS_SIZE(dy));
6812          sprintf(buf, "x=%s\ny=%s", x_buf, y_buf);
6813          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6814          HighLightRotatePivotXY(rotatePivotAbsX+ABS_SIZE(dx),
6815                rotatePivotAbsY+ABS_SIZE(dy), REVERSE);
6816 
6817          x = input.xmotion.x;
6818          y = input.xmotion.y;
6819          GridXY(x, y, &grid_x, &grid_y);
6820 
6821          dx = grid_x - OrigX;
6822          dy = grid_y - OrigY;
6823 
6824          HighLightRotatePivotXY(rotatePivotAbsX+ABS_SIZE(dx),
6825                rotatePivotAbsY+ABS_SIZE(dy), FORWARD);
6826          PixelToMeasurementUnit(x_buf, rotatePivotAbsX+ABS_SIZE(dx));
6827          PixelToMeasurementUnit(y_buf, rotatePivotAbsY+ABS_SIZE(dy));
6828          sprintf(buf, "x=%s\ny=%s", x_buf, y_buf);
6829          ShowMeasureCursor(grid_x, grid_y, buf, TRUE);
6830 
6831          while (XCheckMaskEvent(mainDisplay, PointerMotionMask, &ev)) ;
6832       }
6833    }
6834    if (dx != 0 || dy != 0) {
6835       rotatePivotAbsX += ABS_SIZE(dx);
6836       rotatePivotAbsY += ABS_SIZE(dy);
6837       sprintf(gszMsgBox, TgLoadString(STID_NEW_ROTATE_PIVOT_IS),
6838             rotatePivotAbsX, rotatePivotAbsY);
6839       Msg(gszMsgBox);
6840    }
6841    HighLightRotatePivotXY(rotatePivotAbsX, rotatePivotAbsY, FORWARD);
6842 }
6843 
ToggleAutoRotatePivot()6844 void ToggleAutoRotatePivot()
6845 {
6846    if (topSel != NULL && curChoice == ROTATEMODE) {
6847       if (somethingHighLighted) HighLightReverse();
6848    }
6849    autoRotatePivot = !autoRotatePivot;
6850    if (autoRotatePivot) {
6851       ResetRotatePivotValidInfo();
6852       Msg(TgLoadString(STID_SWITCHED_TO_AUTO_ROTATE_PIVOT));
6853    } else {
6854       Msg(TgLoadString(STID_SWITCHED_TO_USER_ROTATE_PIVOT));
6855    }
6856    choicePixmap[ROTATEMODE] = rotateModePixmap[!autoRotatePivot];
6857    RedrawModeWindow();
6858    UpdatePinnedMenu(MENU_MODE);
6859    if (topSel != NULL && curChoice == ROTATEMODE) {
6860       if (!somethingHighLighted) HighLightForward();
6861    }
6862 }
6863 
SpecifyRotatePivot()6864 void SpecifyRotatePivot()
6865 {
6866    char spec[MAXSTRING];
6867    int abs_x=0, abs_y=0;
6868 
6869    if (autoRotatePivot) {
6870       UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6871             TgLoadString(STID_ENTER_ROT_PIVOT));
6872    } else if (rotatePivotAbsXYValid) {
6873       sprintf(gszMsgBox, TgLoadString(STID_ENTER_ROT_PIVOT_CUR_IS),
6874             rotatePivotAbsX, rotatePivotAbsY);
6875    } else {
6876       UtilStrCpyN(gszMsgBox, sizeof(gszMsgBox),
6877             TgLoadString(STID_ENTER_ROT_PIVOT));
6878    }
6879    *spec = '\0';
6880    Dialog(gszMsgBox, TgLoadCachedString(CSTID_DLG_ACCEPT_CANCEL), spec);
6881    UtilTrimBlanks(spec);
6882    if (*spec == '\0') return;
6883 
6884    if (ParseXYSpec(spec, &abs_x, &abs_y)) {
6885       if (autoRotatePivot) {
6886          ToggleAutoRotatePivot();
6887          rotatePivotAbsXYValid = TRUE;
6888       } else if (rotatePivotAbsXYValid) {
6889          /* nothing to do here */
6890       } else {
6891          rotatePivotAbsXYValid = TRUE;
6892       }
6893       rotatePivotAbsX = abs_x;
6894       rotatePivotAbsY = abs_y;
6895       sprintf(gszMsgBox, TgLoadString(STID_ROT_PIVOT_SET_TO),
6896             rotatePivotAbsX, rotatePivotAbsY);
6897       Msg(gszMsgBox);
6898    }
6899 }
6900 
ResetRotatePivot()6901 void ResetRotatePivot()
6902 {
6903    if (topSel != NULL && curChoice == ROTATEMODE) {
6904       if (somethingHighLighted) HighLightReverse();
6905    }
6906    ResetRotatePivotValidInfo();
6907    if (topSel != NULL && curChoice == ROTATEMODE) {
6908       if (!somethingHighLighted) HighLightForward();
6909    }
6910 }
6911 
6912 static
GetPolyVertices(N,ObjPtr,NumPts,V,Curved,pn_need_to_free)6913 IntPoint *GetPolyVertices(N, ObjPtr, NumPts, V, Curved, pn_need_to_free)
6914    int *N, NumPts, Curved, *pn_need_to_free;
6915    struct ObjRec *ObjPtr;
6916    IntPoint *V;
6917 {
6918    if (ObjPtr->ctm == NULL) {
6919       *pn_need_to_free = FALSE;
6920       *N = NumPts;
6921       return V;
6922    } else {
6923       int i=0;
6924       IntPoint *v=(IntPoint*)malloc(NumPts*sizeof(IntPoint));
6925 
6926       if (v == NULL) FailAllocMessage();
6927       memset(v, 0, NumPts*sizeof(IntPoint));
6928 
6929       for (i=0; i < NumPts; i++) {
6930          int x=0, y=0;
6931 
6932          TransformPointThroughCTM(V[i].x-ObjPtr->x, V[i].y-ObjPtr->y,
6933                ObjPtr->ctm, &x, &y);
6934          v[i].x = x+ObjPtr->x;
6935          v[i].y = y+ObjPtr->y;
6936       }
6937       *pn_need_to_free = TRUE;
6938       *N = NumPts;
6939       return v;
6940    }
6941 }
6942 
NextPolyRotationPivot()6943 void NextPolyRotationPivot()
6944 {
6945    int i=0, can_suggest=FALSE, num_pts=0, need_to_free=FALSE, min_index=0;
6946    struct ObjRec *obj_ptr=NULL;
6947    IntPoint *v=NULL;
6948    double min_dist=(double)0;
6949 
6950    if (!autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL &&
6951          topSel == botSel) {
6952       obj_ptr = topSel->obj;
6953       if (obj_ptr->type == OBJ_POLY) {
6954          struct PolyRec *poly_ptr=obj_ptr->detail.p;
6955 
6956          v = GetPolyVertices(&num_pts, obj_ptr, poly_ptr->n, poly_ptr->vlist,
6957                poly_ptr->curved, &need_to_free);
6958          can_suggest = TRUE;
6959       } else if (obj_ptr->type == OBJ_POLYGON) {
6960          struct PolygonRec *polygon_ptr=obj_ptr->detail.g;
6961 
6962          v = GetPolyVertices(&num_pts, obj_ptr, polygon_ptr->n-1,
6963                polygon_ptr->vlist, polygon_ptr->curved, &need_to_free);
6964          can_suggest = TRUE;
6965       }
6966    }
6967    if (!can_suggest) {
6968       MsgBox(TgLoadString(STID_SELONLYONEPOLYOBJ), TOOL_NAME, INFO_MB);
6969       return;
6970    }
6971    HighLightReverse();
6972    if (rotatePivotAbsXYValid) {
6973       int dx=rotatePivotAbsX-v[0].x, dy=rotatePivotAbsY-v[0].y;
6974       int coincide_index=(-1);
6975       double ddx=(double)0, ddy=(double)0, dist=(double)0;
6976 
6977       min_index = 0;
6978       if (dx == 0 && dy == 0) {
6979          coincide_index = 0;
6980          min_dist = (double)0;
6981       } else {
6982          ddx = (double)dx;
6983          ddy = (double)dy;
6984          min_dist = (double)sqrt(ddx*ddx+ddy+ddy);
6985       }
6986       for (i=1; i < num_pts; i++) {
6987          dx = rotatePivotAbsX-v[i].x;
6988          dy = rotatePivotAbsY-v[i].y;
6989          if (dx == 0 && dy == 0) {
6990             if (coincide_index == (-1)) {
6991                coincide_index = i;
6992                min_index = i;
6993                min_dist = (double)0;
6994             } else if (coincide_index+1 == i) {
6995                coincide_index = i;
6996                min_index = i;
6997                min_dist = (double)0;
6998             } else {
6999                /* stick to the original coincide_index */
7000             }
7001          } else {
7002             ddx = (double)dx;
7003             ddy = (double)dy;
7004             dist = (double)sqrt(ddx*ddx+ddy+ddy);
7005             if (dist < min_dist) {
7006                min_index = i;
7007                min_dist = dist;
7008             }
7009          }
7010       }
7011       if (coincide_index != (-1)) {
7012          min_index = coincide_index+1;
7013       }
7014       if (min_index >= num_pts) {
7015          min_index = 0;
7016       }
7017    } else {
7018       min_index = 0;
7019    }
7020    rotatePivotAbsXYValid = TRUE;
7021    rotatePivotAbsX = v[min_index].x;
7022    rotatePivotAbsY = v[min_index].y;
7023 
7024    if (need_to_free) free(v);
7025 
7026    sprintf(gszMsgBox, TgLoadString(STID_NEW_ROTATE_PIVOT_IS),
7027          rotatePivotAbsX, rotatePivotAbsY);
7028    Msg(gszMsgBox);
7029 
7030    HighLightForward();
7031 }
7032 
MoveRotationPivotToArcCenter()7033 void MoveRotationPivotToArcCenter()
7034 {
7035    int can_suggest=FALSE, x=0, y=0;
7036    struct ObjRec *obj_ptr=NULL;
7037    struct ArcRec *arc_ptr=NULL;
7038 
7039    if (!autoRotatePivot && curChoice == ROTATEMODE && topSel != NULL &&
7040          topSel == botSel) {
7041       obj_ptr = topSel->obj;
7042       if (obj_ptr->type == OBJ_ARC) {
7043          arc_ptr = obj_ptr->detail.a;
7044          can_suggest = TRUE;
7045       }
7046    }
7047    if (!can_suggest) {
7048       MsgBox(TgLoadString(STID_SELONLYONEARCOBJ), TOOL_NAME, INFO_MB);
7049       return;
7050    }
7051    HighLightReverse();
7052    if (obj_ptr->ctm == NULL) {
7053       x = arc_ptr->xc;
7054       y = arc_ptr->yc;
7055    } else {
7056       TransformPointThroughCTM(arc_ptr->xc-obj_ptr->x,
7057             arc_ptr->yc-obj_ptr->y, obj_ptr->ctm, &x, &y);
7058       x += obj_ptr->x;
7059       y += obj_ptr->y;
7060    }
7061    rotatePivotAbsXYValid = TRUE;
7062    rotatePivotAbsX = x;
7063    rotatePivotAbsY = y;
7064 
7065    sprintf(gszMsgBox, TgLoadString(STID_NEW_ROTATE_PIVOT_IS),
7066          rotatePivotAbsX, rotatePivotAbsY);
7067    Msg(gszMsgBox);
7068 
7069    HighLightForward();
7070 }
7071 
MoveRotatePivot(Corner)7072 void MoveRotatePivot(Corner)
7073    int Corner;
7074 {
7075    if (topSel == NULL || curChoice != ROTATEMODE) return;
7076 
7077    HighLightReverse();
7078    switch (Corner) {
7079    case CORNER_NONE:
7080       rotatePivotAbsX = ((selObjLtX+selObjRbX)>>1);
7081       rotatePivotAbsY = ((selObjLtY+selObjRbY)>>1);
7082       break;
7083    case CORNER_LT:
7084       rotatePivotAbsX = selObjLtX;
7085       rotatePivotAbsY = selObjLtY;
7086       break;
7087    case CORNER_RT:
7088       rotatePivotAbsX = selObjRbX;
7089       rotatePivotAbsY = selObjLtY;
7090       break;
7091    case CORNER_LB:
7092       rotatePivotAbsX = selObjLtX;
7093       rotatePivotAbsY = selObjRbY;
7094       break;
7095    case CORNER_RB:
7096       rotatePivotAbsX = selObjRbX;
7097       rotatePivotAbsY = selObjRbY;
7098       break;
7099    case CORNER_LEFT:
7100       rotatePivotAbsX = selObjLtX;
7101       rotatePivotAbsY = ((selObjLtY+selObjRbY)>>1);
7102       break;
7103    case CORNER_RIGHT:
7104       rotatePivotAbsX = selObjRbX;
7105       rotatePivotAbsY = ((selObjLtY+selObjRbY)>>1);
7106       break;
7107    case CORNER_TOP:
7108       rotatePivotAbsX = ((selObjLtX+selObjRbX)>>1);
7109       rotatePivotAbsY = selObjLtY;
7110       break;
7111    case CORNER_BOTTOM:
7112       rotatePivotAbsX = ((selObjLtX+selObjRbX)>>1);
7113       rotatePivotAbsY = selObjRbY;
7114       break;
7115    }
7116    rotatePivotAbsXYValid = TRUE;
7117 
7118    sprintf(gszMsgBox, TgLoadString(STID_NEW_ROTATE_PIVOT_IS),
7119          rotatePivotAbsX, rotatePivotAbsY);
7120    Msg(gszMsgBox);
7121 
7122    HighLightForward();
7123 }
7124 
AutoRotatePivotSubMenu(index)7125 void AutoRotatePivotSubMenu(index)
7126    int index;
7127 {
7128    if (!autoRotatePivot == index) return;
7129 
7130    ToggleAutoRotatePivot();
7131 }
7132 
7133